checkpoint, backend changes for consolidating window and screen_window to screen

This commit is contained in:
sawka 2023-03-13 01:52:30 -07:00
parent ee2fd8e98f
commit e154aacaad
11 changed files with 519 additions and 509 deletions

View File

@ -193,25 +193,20 @@ func HandleLogActiveState(w http.ResponseWriter, r *http.Request) {
return return
} }
// params: sessionid, windowid // params: screenid
func HandleGetWindow(w http.ResponseWriter, r *http.Request) { func HandleGetFullScreen(w http.ResponseWriter, r *http.Request) {
qvals := r.URL.Query() qvals := r.URL.Query()
sessionId := qvals.Get("sessionid") screenId := qvals.Get("screenid")
windowId := qvals.Get("windowid") if _, err := uuid.Parse(screenId); err != nil {
if _, err := uuid.Parse(sessionId); err != nil { WriteJsonError(w, fmt.Errorf("invalid screenid: %w", err))
WriteJsonError(w, fmt.Errorf("invalid sessionid: %w", err))
return return
} }
if _, err := uuid.Parse(windowId); err != nil { screen, err := sstore.GetFullScreenById(r.Context(), screenId)
WriteJsonError(w, fmt.Errorf("invalid windowid: %w", err))
return
}
window, err := sstore.GetWindowById(r.Context(), sessionId, windowId)
if err != nil { if err != nil {
WriteJsonError(w, err) WriteJsonError(w, err)
return return
} }
WriteJsonSuccess(w, window) WriteJsonSuccess(w, screen)
return return
} }
@ -567,7 +562,7 @@ func main() {
gr.HandleFunc("/api/ptyout", AuthKeyWrap(HandleGetPtyOut)) gr.HandleFunc("/api/ptyout", AuthKeyWrap(HandleGetPtyOut))
gr.HandleFunc("/api/remote-pty", AuthKeyWrap(HandleRemotePty)) gr.HandleFunc("/api/remote-pty", AuthKeyWrap(HandleRemotePty))
gr.HandleFunc("/api/rtnstate", AuthKeyWrap(HandleRtnState)) gr.HandleFunc("/api/rtnstate", AuthKeyWrap(HandleRtnState))
gr.HandleFunc("/api/get-window", AuthKeyWrap(HandleGetWindow)) gr.HandleFunc("/api/get-full-screen", AuthKeyWrap(HandleGetFullScreen))
gr.HandleFunc("/api/run-command", AuthKeyWrap(HandleRunCommand)).Methods("POST") gr.HandleFunc("/api/run-command", AuthKeyWrap(HandleRunCommand)).Methods("POST")
gr.HandleFunc("/api/get-client-data", AuthKeyWrap(HandleGetClientData)) gr.HandleFunc("/api/get-client-data", AuthKeyWrap(HandleGetClientData))
gr.HandleFunc("/api/set-winsize", AuthKeyWrap(HandleSetWinSize)) gr.HandleFunc("/api/set-winsize", AuthKeyWrap(HandleSetWinSize))

View File

@ -0,0 +1,3 @@
-- invalid, will throw an error, cannot migrate down past 9
SELECT x;

View File

@ -0,0 +1,56 @@
CREATE TABLE new_screen (
sessionid varchar(36) NOT NULL,
screenid varchar(36) NOT NULL,
windowid varchar(36) NOT NULL,
name varchar(50) NOT NULL,
screenidx int NOT NULL,
screenopts json NOT NULL,
ownerid varchar(36) NOT NULL,
sharemode varchar(12) NOT NULL,
curremoteownerid varchar(36) NOT NULL,
curremoteid varchar(36) NOT NULL,
curremotename varchar(50) NOT NULL,
nextlinenum int NOT NULL,
selectedline int NOT NULL,
anchor json NOT NULL,
focustype varchar(12) NOT NULL,
archived boolean NOT NULL,
archivedts bigint NOT NULL,
PRIMARY KEY (sessionid, screenid)
);
INSERT INTO new_screen
SELECT
s.sessionid,
s.screenid,
w.windowid,
s.name,
s.screenidx,
json_patch(s.screenopts, w.winopts),
s.ownerid,
s.sharemode,
w.curremoteownerid,
w.curremoteid,
w.curremotename,
w.nextlinenum,
sw.selectedline,
sw.anchor,
sw.focustype,
s.archived,
s.archivedts
FROM
screen s,
screen_window sw,
window w
WHERE
s.screenid = sw.screenid
AND sw.windowid = w.windowid
;
DROP TABLE screen;
DROP TABLE screen_window;
DROP TABLE window;
ALTER TABLE new_screen RENAME TO screen;

View File

@ -151,7 +151,6 @@ func init() {
registerCmdFn("remote:installcancel", RemoteInstallCancelCommand) registerCmdFn("remote:installcancel", RemoteInstallCancelCommand)
registerCmdFn("remote:reset", RemoteResetCommand) registerCmdFn("remote:reset", RemoteResetCommand)
registerCmdFn("sw:set", SwSetCommand)
registerCmdFn("sw:resize", SwResizeCommand) registerCmdFn("sw:resize", SwResizeCommand)
// sw:resize // sw:resize
@ -458,7 +457,7 @@ func ScreenArchiveCommand(ctx context.Context, pk *scpacket.FeCommandPacketType)
if err != nil { if err != nil {
return nil, fmt.Errorf("/screen:archive cannot re-open screen: %v", err) return nil, fmt.Errorf("/screen:archive cannot re-open screen: %v", err)
} }
screen, err := sstore.GetScreenById(ctx, ids.SessionId, screenId) screen, err := sstore.GetScreenById(ctx, screenId)
if err != nil { if err != nil {
return nil, fmt.Errorf("/screen:archive cannot get updated screen obj: %v", err) return nil, fmt.Errorf("/screen:archive cannot get updated screen obj: %v", err)
} }
@ -512,17 +511,17 @@ func ScreenSetCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (ss
return nil, err return nil, err
} }
var varsUpdated []string var varsUpdated []string
var setNonAnchor bool // anchor does not receive an update
updateMap := make(map[string]interface{})
if pk.Kwargs["name"] != "" { if pk.Kwargs["name"] != "" {
newName := pk.Kwargs["name"] newName := pk.Kwargs["name"]
err = validateName(newName, "screen") err = validateName(newName, "screen")
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = sstore.SetScreenName(ctx, ids.SessionId, ids.ScreenId, newName) updateMap[sstore.ScreenField_Name] = newName
if err != nil {
return nil, fmt.Errorf("setting screen name: %v", err)
}
varsUpdated = append(varsUpdated, "name") varsUpdated = append(varsUpdated, "name")
setNonAnchor = true
} }
if pk.Kwargs["tabcolor"] != "" { if pk.Kwargs["tabcolor"] != "" {
color := pk.Kwargs["tabcolor"] color := pk.Kwargs["tabcolor"]
@ -530,39 +529,67 @@ func ScreenSetCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (ss
if err != nil { if err != nil {
return nil, err return nil, err
} }
screenObj, err := sstore.GetScreenById(ctx, ids.SessionId, ids.ScreenId) updateMap[sstore.ScreenField_TabColor] = color
if err != nil {
return nil, err
}
opts := screenObj.ScreenOpts
if opts == nil {
opts = &sstore.ScreenOptsType{}
}
opts.TabColor = color
err = sstore.SetScreenOpts(ctx, ids.SessionId, ids.ScreenId, opts)
if err != nil {
return nil, fmt.Errorf("setting screen opts: %v", err)
}
varsUpdated = append(varsUpdated, "tabcolor") varsUpdated = append(varsUpdated, "tabcolor")
setNonAnchor = true
} }
if pk.Kwargs["pos"] != "" { if pk.Kwargs["pos"] != "" {
varsUpdated = append(varsUpdated, "pos") varsUpdated = append(varsUpdated, "pos")
setNonAnchor = true
}
if pk.Kwargs["focus"] != "" {
focusVal := pk.Kwargs["focus"]
if focusVal != sstore.ScreenFocusInput && focusVal != sstore.ScreenFocusCmd && focusVal != sstore.ScreenFocusCmdFg {
return nil, fmt.Errorf("/screen:set invalid focus argument %q, must be %s", focusVal, formatStrs([]string{sstore.ScreenFocusInput, sstore.ScreenFocusCmd, sstore.ScreenFocusCmdFg}, "or", false))
}
updateMap[sstore.ScreenField_Focus] = focusVal
setNonAnchor = true
}
if pk.Kwargs["line"] != "" {
screen, err := sstore.GetScreenById(ctx, ids.ScreenId)
if err != nil {
return nil, fmt.Errorf("/screen:set cannot get screen: %v", err)
}
var selectedLineStr string
if screen.SelectedLine > 0 {
selectedLineStr = strconv.Itoa(int(screen.SelectedLine))
}
ritem, err := resolveLine(ctx, screen.SessionId, screen.WindowId, pk.Kwargs["line"], selectedLineStr)
if err != nil {
return nil, fmt.Errorf("/screen:set error resolving line: %v", err)
}
if ritem == nil {
return nil, fmt.Errorf("/screen:set could not resolve line %q", pk.Kwargs["line"])
}
setNonAnchor = true
updateMap[sstore.ScreenField_SelectedLine] = ritem.Num
}
if pk.Kwargs["anchor"] != "" {
m := swAnchorRe.FindStringSubmatch(pk.Kwargs["anchor"])
if m == nil {
return nil, fmt.Errorf("/screen:set invalid anchor argument (must be [line] or [line]:[offset])")
}
anchorLine, _ := strconv.Atoi(m[1])
updateMap[sstore.ScreenField_AnchorLine] = anchorLine
if m[2] != "" {
anchorOffset, _ := strconv.Atoi(m[2])
updateMap[sstore.ScreenField_AnchorOffset] = anchorOffset
} else {
updateMap[sstore.ScreenField_AnchorOffset] = 0
}
} }
if len(varsUpdated) == 0 { if len(varsUpdated) == 0 {
return nil, fmt.Errorf("/screen:set no updates, can set %s", formatStrs([]string{"name", "pos", "tabcolor"}, "or", false)) return nil, fmt.Errorf("/screen:set no updates, can set %s", formatStrs([]string{"name", "pos", "tabcolor", "focus", "anchor", "line"}, "or", false))
} }
screenObj, err := sstore.GetScreenById(ctx, ids.SessionId, ids.ScreenId) screen, err := sstore.UpdateScreen(ctx, ids.ScreenId, updateMap)
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("error updating screen: %v", err)
} }
bareSession, err := sstore.GetBareSessionById(ctx, ids.SessionId) if !setNonAnchor {
if err != nil { return nil, nil
return nil, fmt.Errorf("/screen:set cannot retrieve session: %v", err)
} }
bareSession.Screens = append(bareSession.Screens, screenObj)
update := sstore.ModelUpdate{ update := sstore.ModelUpdate{
Sessions: []*sstore.SessionType{bareSession}, Screens: []*sstore.ScreenType{screen},
Info: &sstore.InfoMsgType{ Info: &sstore.InfoMsgType{
InfoMsg: fmt.Sprintf("screen updated %s", formatStrs(varsUpdated, "and", false)), InfoMsg: fmt.Sprintf("screen updated %s", formatStrs(varsUpdated, "and", false)),
TimeoutMs: 2000, TimeoutMs: 2000,
@ -593,69 +620,8 @@ func ScreenCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstor
var swAnchorRe = regexp.MustCompile("^(\\d+)(?::(-?\\d+))?$") var swAnchorRe = regexp.MustCompile("^(\\d+)(?::(-?\\d+))?$")
func SwSetCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
ids, err := resolveUiIds(ctx, pk, R_Session|R_Screen|R_Window)
if err != nil {
return nil, fmt.Errorf("/sw:set cannot resolve current screen-window: %w", err)
}
var setNonST bool // scrolltop does not receive an update
updateMap := make(map[string]interface{})
if pk.Kwargs["anchor"] != "" {
m := swAnchorRe.FindStringSubmatch(pk.Kwargs["anchor"])
if m == nil {
return nil, fmt.Errorf("/sw:set invalid anchor argument (must be [line] or [line]:[offset])")
}
anchorLine, _ := strconv.Atoi(m[1])
updateMap[sstore.SWField_AnchorLine] = anchorLine
if m[2] != "" {
anchorOffset, _ := strconv.Atoi(m[2])
updateMap[sstore.SWField_AnchorOffset] = anchorOffset
} else {
updateMap[sstore.SWField_AnchorOffset] = 0
}
}
if pk.Kwargs["focus"] != "" {
focusVal := pk.Kwargs["focus"]
if focusVal != sstore.SWFocusInput && focusVal != sstore.SWFocusCmd && focusVal != sstore.SWFocusCmdFg {
return nil, fmt.Errorf("/sw:set invalid focus argument %q, must be %s", focusVal, formatStrs([]string{sstore.SWFocusInput, sstore.SWFocusCmd, sstore.SWFocusCmdFg}, "or", false))
}
updateMap[sstore.SWField_Focus] = focusVal
setNonST = true
}
if pk.Kwargs["line"] != "" {
sw, err := sstore.GetScreenWindowByIds(ctx, ids.SessionId, ids.ScreenId, ids.WindowId)
if err != nil {
return nil, fmt.Errorf("/sw:set cannot get screen-window: %v", err)
}
var selectedLineStr string
if sw.SelectedLine > 0 {
selectedLineStr = strconv.Itoa(sw.SelectedLine)
}
ritem, err := resolveLine(ctx, ids.SessionId, ids.WindowId, pk.Kwargs["line"], selectedLineStr)
if err != nil {
return nil, fmt.Errorf("/sw:set error resolving line: %v", err)
}
if ritem == nil {
return nil, fmt.Errorf("/sw:set could not resolve line %q", pk.Kwargs["line"])
}
setNonST = true
updateMap[sstore.SWField_SelectedLine] = ritem.Num
}
if len(updateMap) == 0 {
return nil, fmt.Errorf("/sw:set no updates, can set %s", formatStrs([]string{"line", "scrolltop", "focus"}, "or", false))
}
sw, err := sstore.UpdateScreenWindow(ctx, ids.SessionId, ids.ScreenId, ids.WindowId, updateMap)
if err != nil {
return nil, fmt.Errorf("/sw:set failed to update: %v", err)
}
if !setNonST {
return nil, nil
}
return sstore.ModelUpdate{ScreenWindows: []*sstore.ScreenWindowType{sw}}, nil
}
func RemoteInstallCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) { func RemoteInstallCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
ids, err := resolveUiIds(ctx, pk, R_Session|R_Window|R_Remote) ids, err := resolveUiIds(ctx, pk, R_Session|R_Screen|R_Window|R_Remote)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -669,7 +635,7 @@ func RemoteInstallCommand(ctx context.Context, pk *scpacket.FeCommandPacketType)
} }
func RemoteInstallCancelCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) { func RemoteInstallCancelCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
ids, err := resolveUiIds(ctx, pk, R_Session|R_Window|R_Remote) ids, err := resolveUiIds(ctx, pk, R_Session|R_Screen|R_Window|R_Remote)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -683,7 +649,7 @@ func RemoteInstallCancelCommand(ctx context.Context, pk *scpacket.FeCommandPacke
} }
func RemoteConnectCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) { func RemoteConnectCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
ids, err := resolveUiIds(ctx, pk, R_Session|R_Window|R_Remote) ids, err := resolveUiIds(ctx, pk, R_Session|R_Screen|R_Window|R_Remote)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -696,7 +662,7 @@ func RemoteConnectCommand(ctx context.Context, pk *scpacket.FeCommandPacketType)
} }
func RemoteDisconnectCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) { func RemoteDisconnectCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
ids, err := resolveUiIds(ctx, pk, R_Session|R_Window|R_Remote) ids, err := resolveUiIds(ctx, pk, R_Session|R_Screen|R_Window|R_Remote)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -946,7 +912,7 @@ func RemoteNewCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (ss
} }
func RemoteSetCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) { func RemoteSetCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
ids, err := resolveUiIds(ctx, pk, R_Session|R_Window|R_Remote) ids, err := resolveUiIds(ctx, pk, R_Session|R_Screen|R_Window|R_Remote)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -976,7 +942,7 @@ func RemoteSetCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (ss
} }
func RemoteShowCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) { func RemoteShowCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
ids, err := resolveUiIds(ctx, pk, R_Session|R_Window|R_Remote) ids, err := resolveUiIds(ctx, pk, R_Session|R_Screen|R_Window|R_Remote)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1039,29 +1005,20 @@ func ScreenResetCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (
if err != nil { if err != nil {
return nil, err return nil, err
} }
screen, err := sstore.GetScreenById(ctx, ids.SessionId, ids.ScreenId)
if err != nil {
return nil, fmt.Errorf("error retrieving screen: %v", err)
}
localRemote := remote.GetLocalRemote() localRemote := remote.GetLocalRemote()
if localRemote == nil { if localRemote == nil {
return nil, fmt.Errorf("error getting local remote (not found)") return nil, fmt.Errorf("error getting local remote (not found)")
} }
rptr := sstore.RemotePtrType{RemoteId: localRemote.RemoteId} rptr := sstore.RemotePtrType{RemoteId: localRemote.RemoteId}
var windows []*sstore.WindowType
sessionUpdate := &sstore.SessionType{SessionId: ids.SessionId} sessionUpdate := &sstore.SessionType{SessionId: ids.SessionId}
for _, sw := range screen.Windows { ris, err := sstore.ScreenReset(ctx, ids.ScreenId)
ris, err := sstore.WindowReset(ctx, ids.SessionId, sw.WindowId) if err != nil {
if err != nil { return nil, fmt.Errorf("error resetting screen window: %v", err)
return nil, fmt.Errorf("error resetting screen window: %v", err) }
} sessionUpdate.Remotes = append(sessionUpdate.Remotes, ris...)
sessionUpdate.Remotes = append(sessionUpdate.Remotes, ris...) err = sstore.UpdateCurRemote(ctx, ids.ScreenId, rptr)
err = sstore.UpdateCurRemote(ctx, ids.SessionId, sw.WindowId, rptr) if err != nil {
if err != nil { return nil, fmt.Errorf("cannot reset screen remote back to local: %w", err)
return nil, fmt.Errorf("cannot reset window remote back to local: %w", err)
}
winUpdate := &sstore.WindowType{SessionId: ids.SessionId, WindowId: sw.WindowId, CurRemote: rptr}
windows = append(windows, winUpdate)
} }
outputStr := "reset screen state (all remote state reset)" outputStr := "reset screen state (all remote state reset)"
cmd, err := makeStaticCmd(ctx, "screen:reset", ids, pk.GetRawStr(), []byte(outputStr)) cmd, err := makeStaticCmd(ctx, "screen:reset", ids, pk.GetRawStr(), []byte(outputStr))
@ -1075,13 +1032,12 @@ func ScreenResetCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (
return nil, err return nil, err
} }
update.Interactive = pk.Interactive update.Interactive = pk.Interactive
update.Windows = windows
update.Sessions = []*sstore.SessionType{sessionUpdate} update.Sessions = []*sstore.SessionType{sessionUpdate}
return update, nil return update, nil
} }
func RemoteArchiveCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) { func RemoteArchiveCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
ids, err := resolveUiIds(ctx, pk, R_Session|R_Window|R_Remote) ids, err := resolveUiIds(ctx, pk, R_Session|R_Screen|R_Window|R_Remote)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1091,13 +1047,16 @@ func RemoteArchiveCommand(ctx context.Context, pk *scpacket.FeCommandPacketType)
} }
update := sstore.InfoMsgUpdate("remote [%s] archived", ids.Remote.DisplayName) update := sstore.InfoMsgUpdate("remote [%s] archived", ids.Remote.DisplayName)
localRemote := remote.GetLocalRemote() localRemote := remote.GetLocalRemote()
if localRemote != nil { rptr := sstore.RemotePtrType{RemoteId: localRemote.GetRemoteId()}
update.Windows = []*sstore.WindowType{&sstore.WindowType{ err = sstore.UpdateCurRemote(ctx, ids.ScreenId, rptr)
SessionId: ids.SessionId, if err != nil {
WindowId: ids.WindowId, return nil, fmt.Errorf("cannot switch remote back to local: %w", err)
CurRemote: sstore.RemotePtrType{RemoteId: localRemote.GetRemoteId()},
}}
} }
screen, err := sstore.GetScreenById(ctx, ids.ScreenId)
if err != nil {
return nil, fmt.Errorf("cannot get updated screen: %w", err)
}
update.Screens = []*sstore.ScreenType{screen}
return update, nil return update, nil
} }
@ -1173,7 +1132,7 @@ func GetFullRemoteDisplayName(rptr *sstore.RemotePtrType, rstate *remote.RemoteR
} }
func CrCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) { func CrCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
ids, err := resolveUiIds(ctx, pk, R_Session|R_Window) ids, err := resolveUiIds(ctx, pk, R_Session|R_Screen|R_Window)
if err != nil { if err != nil {
return nil, fmt.Errorf("/%s error: %w", GetCmdStr(pk), err) return nil, fmt.Errorf("/%s error: %w", GetCmdStr(pk), err)
} }
@ -1191,7 +1150,7 @@ func CrCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.Up
if rstate.Archived { if rstate.Archived {
return nil, fmt.Errorf("/%s error: remote %q cannot switch to archived remote", GetCmdStr(pk), newRemote) return nil, fmt.Errorf("/%s error: remote %q cannot switch to archived remote", GetCmdStr(pk), newRemote)
} }
err = sstore.UpdateCurRemote(ctx, ids.SessionId, ids.WindowId, *rptr) err = sstore.UpdateCurRemote(ctx, ids.ScreenId, *rptr)
if err != nil { if err != nil {
return nil, fmt.Errorf("/%s error: cannot update curremote: %w", GetCmdStr(pk), err) return nil, fmt.Errorf("/%s error: cannot update curremote: %w", GetCmdStr(pk), err)
} }
@ -1206,11 +1165,6 @@ func CrCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.Up
// TODO tricky error since the command was a success, but we can't show the output // TODO tricky error since the command was a success, but we can't show the output
return nil, err return nil, err
} }
update.Windows = []*sstore.WindowType{&sstore.WindowType{
SessionId: ids.SessionId,
WindowId: ids.WindowId,
CurRemote: *rptr,
}}
update.Interactive = pk.Interactive update.Interactive = pk.Interactive
return update, nil return update, nil
} }
@ -1252,27 +1206,27 @@ func addLineForCmd(ctx context.Context, metaCmd string, shouldFocus bool, ids re
if err != nil { if err != nil {
return nil, err return nil, err
} }
sw, err := sstore.GetScreenWindowByIds(ctx, ids.SessionId, ids.ScreenId, ids.WindowId) screen, err := sstore.GetScreenById(ctx, ids.ScreenId)
if err != nil { if err != nil {
// ignore error here, because the command has already run (nothing to do) // ignore error here, because the command has already run (nothing to do)
log.Printf("%s error getting screen-window: %v\n", metaCmd, err) log.Printf("%s error getting screen: %v\n", metaCmd, err)
} }
if sw != nil { if screen != nil {
updateMap := make(map[string]interface{}) updateMap := make(map[string]interface{})
updateMap[sstore.SWField_SelectedLine] = rtnLine.LineNum updateMap[sstore.ScreenField_SelectedLine] = rtnLine.LineNum
if shouldFocus { if shouldFocus {
updateMap[sstore.SWField_Focus] = sstore.SWFocusCmdFg updateMap[sstore.ScreenField_Focus] = sstore.ScreenFocusCmdFg
} }
sw, err = sstore.UpdateScreenWindow(ctx, ids.SessionId, ids.ScreenId, ids.WindowId, updateMap) screen, err = sstore.UpdateScreen(ctx, ids.ScreenId, updateMap)
if err != nil { if err != nil {
// ignore error again (nothing to do) // ignore error again (nothing to do)
log.Printf("%s error updating screen-window selected line: %v\n", metaCmd, err) log.Printf("%s error updating screen selected line: %v\n", metaCmd, err)
} }
} }
update := &sstore.ModelUpdate{ update := &sstore.ModelUpdate{
Line: rtnLine, Line: rtnLine,
Cmd: cmd, Cmd: cmd,
ScreenWindows: []*sstore.ScreenWindowType{sw}, Screens: []*sstore.ScreenType{screen},
} }
updateHistoryContext(ctx, rtnLine, cmd) updateHistoryContext(ctx, rtnLine, cmd)
return update, nil return update, nil
@ -1374,7 +1328,7 @@ func doCompGen(ctx context.Context, pk *scpacket.FeCommandPacketType, prefix str
if !packet.IsValidCompGenType(compType) { if !packet.IsValidCompGenType(compType) {
return nil, false, fmt.Errorf("/_compgen invalid type '%s'", compType) return nil, false, fmt.Errorf("/_compgen invalid type '%s'", compType)
} }
ids, err := resolveUiIds(ctx, pk, R_Session|R_Window|R_RemoteConnected) ids, err := resolveUiIds(ctx, pk, R_Session|R_Screen|R_Window|R_RemoteConnected)
if err != nil { if err != nil {
return nil, false, fmt.Errorf("/_compgen error: %w", err) return nil, false, fmt.Errorf("/_compgen error: %w", err)
} }
@ -1447,7 +1401,7 @@ func CompGenCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (ssto
} }
func CommentCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) { func CommentCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
ids, err := resolveUiIds(ctx, pk, R_Session|R_Window) ids, err := resolveUiIds(ctx, pk, R_Session|R_Screen|R_Window)
if err != nil { if err != nil {
return nil, fmt.Errorf("/comment error: %w", err) return nil, fmt.Errorf("/comment error: %w", err)
} }
@ -1461,14 +1415,14 @@ func CommentCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (ssto
} }
updateHistoryContext(ctx, rtnLine, nil) updateHistoryContext(ctx, rtnLine, nil)
updateMap := make(map[string]interface{}) updateMap := make(map[string]interface{})
updateMap[sstore.SWField_SelectedLine] = rtnLine.LineNum updateMap[sstore.ScreenField_SelectedLine] = rtnLine.LineNum
updateMap[sstore.SWField_Focus] = sstore.SWFocusInput updateMap[sstore.ScreenField_Focus] = sstore.ScreenFocusInput
sw, err := sstore.UpdateScreenWindow(ctx, ids.SessionId, ids.ScreenId, ids.WindowId, updateMap) screen, err := sstore.UpdateScreen(ctx, ids.ScreenId, updateMap)
if err != nil { if err != nil {
// ignore error again (nothing to do) // ignore error again (nothing to do)
log.Printf("/comment error updating screen-window selected line: %v\n", err) log.Printf("/comment error updating screen-window selected line: %v\n", err)
} }
update := sstore.ModelUpdate{Line: rtnLine, ScreenWindows: []*sstore.ScreenWindowType{sw}} update := sstore.ModelUpdate{Line: rtnLine, Screens: []*sstore.ScreenType{screen}}
return update, nil return update, nil
} }
@ -1809,22 +1763,22 @@ func ClearCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore
return nil, err return nil, err
} }
if resolveBool(pk.Kwargs["purge"], false) { if resolveBool(pk.Kwargs["purge"], false) {
update, err := sstore.PurgeWindowLines(ctx, ids.SessionId, ids.WindowId) update, err := sstore.PurgeScreenLines(ctx, ids.ScreenId)
if err != nil { if err != nil {
return nil, fmt.Errorf("clearing window: %v", err) return nil, fmt.Errorf("clearing screen: %v", err)
} }
update.Info = &sstore.InfoMsgType{ update.Info = &sstore.InfoMsgType{
InfoMsg: fmt.Sprintf("window cleared (all lines purged)"), InfoMsg: fmt.Sprintf("screen cleared (all lines purged)"),
TimeoutMs: 2000, TimeoutMs: 2000,
} }
return update, nil return update, nil
} else { } else {
update, err := sstore.ArchiveWindowLines(ctx, ids.SessionId, ids.WindowId) update, err := sstore.ArchiveScreenLines(ctx, ids.ScreenId)
if err != nil { if err != nil {
return nil, fmt.Errorf("clearing window: %v", err) return nil, fmt.Errorf("clearing screen: %v", err)
} }
update.Info = &sstore.InfoMsgType{ update.Info = &sstore.InfoMsgType{
InfoMsg: fmt.Sprintf("window cleared"), InfoMsg: fmt.Sprintf("screen cleared"),
TimeoutMs: 2000, TimeoutMs: 2000,
} }
return update, nil return update, nil
@ -2121,11 +2075,11 @@ func LineViewCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sst
if err != nil { if err != nil {
return nil, fmt.Errorf("/line:view invalid screen arg: %v", err) return nil, fmt.Errorf("/line:view invalid screen arg: %v", err)
} }
screen, err := sstore.GetScreenById(ctx, sessionId, screenRItem.Id) screen, err := sstore.GetScreenById(ctx, screenRItem.Id)
if err != nil { if err != nil {
return nil, fmt.Errorf("/line:view could not get screen: %v", err) return nil, fmt.Errorf("/line:view could not get screen: %v", err)
} }
lineRItem, err := resolveLine(ctx, sessionId, screen.ActiveWindowId, lineArg, "") lineRItem, err := resolveLine(ctx, sessionId, screen.WindowId, lineArg, "")
if err != nil { if err != nil {
return nil, fmt.Errorf("/line:view invalid line arg: %v", err) return nil, fmt.Errorf("/line:view invalid line arg: %v", err)
} }
@ -2134,14 +2088,14 @@ func LineViewCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sst
return nil, err return nil, err
} }
updateMap := make(map[string]interface{}) updateMap := make(map[string]interface{})
updateMap[sstore.SWField_SelectedLine] = lineRItem.Num updateMap[sstore.ScreenField_SelectedLine] = lineRItem.Num
updateMap[sstore.SWField_AnchorLine] = lineRItem.Num updateMap[sstore.ScreenField_AnchorLine] = lineRItem.Num
updateMap[sstore.SWField_AnchorOffset] = 0 updateMap[sstore.ScreenField_AnchorOffset] = 0
sw, err := sstore.UpdateScreenWindow(ctx, sessionId, screenRItem.Id, screen.ActiveWindowId, updateMap) screen, err = sstore.UpdateScreen(ctx, screenRItem.Id, updateMap)
if err != nil { if err != nil {
return nil, err return nil, err
} }
update.ScreenWindows = []*sstore.ScreenWindowType{sw} update.Screens = []*sstore.ScreenType{screen}
return update, nil return update, nil
} }

99
pkg/mapqueue/mapqueue.go Normal file
View File

@ -0,0 +1,99 @@
package mapqueue
import (
"fmt"
"log"
"runtime/debug"
"sync"
)
type MQEntry struct {
Lock *sync.Mutex
Running bool
Queue chan func()
}
type MapQueue struct {
Lock *sync.Mutex
M map[string]*MQEntry
QueueSize int
}
func MakeMapQueue(queueSize int) *MapQueue {
rtn := &MapQueue{
Lock: &sync.Mutex{},
M: make(map[string]*MQEntry),
QueueSize: queueSize,
}
return rtn
}
func (mq *MapQueue) getEntry(id string) *MQEntry {
mq.Lock.Lock()
defer mq.Lock.Unlock()
entry := mq.M[id]
if entry == nil {
entry = &MQEntry{
Lock: &sync.Mutex{},
Running: false,
Queue: make(chan func(), mq.QueueSize),
}
mq.M[id] = entry
}
return entry
}
func (entry *MQEntry) add(fn func()) error {
select {
case entry.Queue <- fn:
break
default:
return fmt.Errorf("input queue full")
}
entry.tryRun()
return nil
}
func runFn(fn func()) {
defer func() {
r := recover()
if r == nil {
return
}
log.Printf("[error] panic in MQEntry runFn: %v\n", r)
debug.PrintStack()
return
}()
fn()
}
func (entry *MQEntry) tryRun() {
entry.Lock.Lock()
defer entry.Lock.Unlock()
if entry.Running {
return
}
if len(entry.Queue) > 0 {
entry.Running = true
go entry.run()
}
}
func (entry *MQEntry) run() {
for fn := range entry.Queue {
runFn(fn)
}
entry.Lock.Lock()
entry.Running = false
entry.Lock.Unlock()
entry.tryRun()
}
func (mq *MapQueue) Enqueue(id string, fn func()) error {
entry := mq.getEntry(id)
err := entry.add(fn)
if err != nil {
return fmt.Errorf("cannot enqueue: %v", err)
}
return nil
}

View File

@ -1499,12 +1499,12 @@ func (msh *MShellProc) handleCmdDonePacket(donePk *packet.CmdDonePacketType) {
msh.WriteToPtyBuffer("*error updating cmddone: %v\n", err) msh.WriteToPtyBuffer("*error updating cmddone: %v\n", err)
return return
} }
sws, err := sstore.UpdateSWsWithCmdFg(context.Background(), donePk.CK.GetSessionId(), donePk.CK.GetCmdId()) screens, err := sstore.UpdateScreensWithCmdFg(context.Background(), donePk.CK.GetSessionId(), donePk.CK.GetCmdId())
if err != nil { if err != nil {
msh.WriteToPtyBuffer("*error trying to update cmd-fg screen windows: %v\n", err) msh.WriteToPtyBuffer("*error trying to update cmd-fg screen windows: %v\n", err)
// fall-through (nothing to do) // fall-through (nothing to do)
} }
update.ScreenWindows = sws update.Screens = screens
rct := msh.GetRunningCmd(donePk.CK) rct := msh.GetRunningCmd(donePk.CK)
var statePtr *sstore.ShellStatePtr var statePtr *sstore.ShellStatePtr
if donePk.FinalState != nil && rct != nil { if donePk.FinalState != nil && rct != nil {

View File

@ -9,6 +9,7 @@ import (
"github.com/google/uuid" "github.com/google/uuid"
"github.com/scripthaus-dev/mshell/pkg/packet" "github.com/scripthaus-dev/mshell/pkg/packet"
"github.com/scripthaus-dev/sh2-server/pkg/mapqueue"
"github.com/scripthaus-dev/sh2-server/pkg/remote" "github.com/scripthaus-dev/sh2-server/pkg/remote"
"github.com/scripthaus-dev/sh2-server/pkg/scpacket" "github.com/scripthaus-dev/sh2-server/pkg/scpacket"
"github.com/scripthaus-dev/sh2-server/pkg/sstore" "github.com/scripthaus-dev/sh2-server/pkg/sstore"
@ -17,6 +18,13 @@ import (
const WSStatePacketChSize = 20 const WSStatePacketChSize = 20
const MaxInputDataSize = 1000 const MaxInputDataSize = 1000
const RemoteInputQueueSize = 100
var RemoteInputMapQueue *mapqueue.MapQueue
func init() {
RemoteInputMapQueue = mapqueue.MakeMapQueue(RemoteInputQueueSize)
}
type WSState struct { type WSState struct {
Lock *sync.Mutex Lock *sync.Mutex
@ -227,13 +235,16 @@ func (ws *WSState) RunWSRead() {
log.Printf("[error] invalid input packet, remoteid is not set\n") log.Printf("[error] invalid input packet, remoteid is not set\n")
continue continue
} }
go func() { err := RemoteInputMapQueue.Enqueue(feInputPk.Remote.RemoteId, func() {
// TODO enforce a strong ordering (channel with list)
err = sendCmdInput(feInputPk) err = sendCmdInput(feInputPk)
if err != nil { if err != nil {
log.Printf("[error] sending command input: %v\n", err) log.Printf("[error] sending command input: %v\n", err)
} }
}() })
if err != nil {
log.Printf("[error] could not queue sendCmdInput: %v\n", err)
continue
}
continue continue
} }
if pk.GetType() == scpacket.RemoteInputPacketStr { if pk.GetType() == scpacket.RemoteInputPacketStr {

View File

@ -405,9 +405,8 @@ func GetAllSessions(ctx context.Context) (*ModelUpdate, error) {
sessionMap[session.SessionId] = session sessionMap[session.SessionId] = session
session.Full = true session.Full = true
} }
var screens []*ScreenType
query = `SELECT * FROM screen ORDER BY archived, screenidx, archivedts` query = `SELECT * FROM screen ORDER BY archived, screenidx, archivedts`
tx.Select(&screens, query) screens := SelectMapsGen[*ScreenType](tx, query)
screenMap := make(map[string][]*ScreenType) screenMap := make(map[string][]*ScreenType)
for _, screen := range screens { for _, screen := range screens {
screenArr := screenMap[screen.SessionId] screenArr := screenMap[screen.SessionId]
@ -417,20 +416,6 @@ func GetAllSessions(ctx context.Context) (*ModelUpdate, error) {
for _, session := range rtn { for _, session := range rtn {
session.Screens = screenMap[session.SessionId] session.Screens = screenMap[session.SessionId]
} }
var sws []*ScreenWindowType
query = `SELECT * FROM screen_window`
tx.Select(&sws, query)
screenIdMap := make(map[string]*ScreenType)
for _, screen := range screens {
screenIdMap[screen.SessionId+screen.ScreenId] = screen
}
for _, sw := range sws {
screen := screenIdMap[sw.SessionId+sw.ScreenId]
if screen == nil {
continue
}
screen.Windows = append(screen.Windows, sw)
}
query = `SELECT * FROM remote_instance` query = `SELECT * FROM remote_instance`
riArr := SelectMapsGen[*RemoteInstance](tx, query) riArr := SelectMapsGen[*RemoteInstance](tx, query)
for _, ri := range riArr { for _, ri := range riArr {
@ -449,22 +434,20 @@ func GetAllSessions(ctx context.Context) (*ModelUpdate, error) {
return &ModelUpdate{Sessions: rtn, ActiveSessionId: activeSessionId}, nil return &ModelUpdate{Sessions: rtn, ActiveSessionId: activeSessionId}, nil
} }
func GetWindowById(ctx context.Context, sessionId string, windowId string) (*WindowType, error) { func GetFullScreenById(ctx context.Context, screenId string) (*ScreenType, error) {
var rtnWindow *WindowType return WithTxRtn(ctx, func(tx *TxWrap) (*ScreenType, error) {
err := WithTx(ctx, func(tx *TxWrap) error { query := `SELECT * FROM screen WHERE screenid = ?`
query := `SELECT * FROM window WHERE sessionid = ? AND windowid = ?` screen := GetMapGen[*ScreenType](tx, query, screenId)
m := tx.GetMap(query, sessionId, windowId) if screen == nil {
if m == nil { return nil, nil
return nil
} }
rtnWindow = FromMap[*WindowType](m)
query = `SELECT * FROM line WHERE sessionid = ? AND windowid = ? ORDER BY linenum` query = `SELECT * FROM line WHERE sessionid = ? AND windowid = ? ORDER BY linenum`
tx.Select(&rtnWindow.Lines, query, sessionId, windowId) tx.Select(&screen.Lines, query, screen.SessionId, screen.WindowId)
query = `SELECT * FROM cmd WHERE cmdid IN (SELECT cmdid FROM line WHERE sessionid = ? AND windowid = ?)` query = `SELECT * FROM cmd WHERE cmdid IN (SELECT cmdid FROM line WHERE sessionid = ? AND windowid = ?)`
rtnWindow.Cmds = SelectMapsGen[*CmdType](tx, query, sessionId, windowId) screen.Cmds = SelectMapsGen[*CmdType](tx, query, screen.SessionId, screen.WindowId)
return nil screen.Full = true
return screen, nil
}) })
return rtnWindow, err
} }
// includes archived screens (does not include screen windows) // includes archived screens (does not include screen windows)
@ -649,11 +632,11 @@ func InsertScreen(ctx context.Context, sessionId string, origScreenName string,
if !tx.Exists(query, sessionId) { if !tx.Exists(query, sessionId) {
return fmt.Errorf("cannot create screen, no session found (or session archived)") return fmt.Errorf("cannot create screen, no session found (or session archived)")
} }
remoteId := tx.GetString(`SELECT remoteid FROM remote WHERE remotealias = ?`, LocalRemoteAlias) localRemoteId := tx.GetString(`SELECT remoteid FROM remote WHERE remotealias = ?`, LocalRemoteAlias)
if remoteId == "" { if localRemoteId == "" {
return fmt.Errorf("cannot create screen, no local remote found") return fmt.Errorf("cannot create screen, no local remote found")
} }
newWindowId := txCreateWindow(tx, sessionId, RemotePtrType{RemoteId: remoteId}) newWindowId := scbase.GenPromptUUID()
maxScreenIdx := tx.GetInt(`SELECT COALESCE(max(screenidx), 0) FROM screen WHERE sessionid = ? AND NOT archived`, sessionId) maxScreenIdx := tx.GetInt(`SELECT COALESCE(max(screenidx), 0) FROM screen WHERE sessionid = ? AND NOT archived`, sessionId)
var screenName string var screenName string
if origScreenName == "" { if origScreenName == "" {
@ -663,11 +646,25 @@ func InsertScreen(ctx context.Context, sessionId string, origScreenName string,
screenName = origScreenName screenName = origScreenName
} }
newScreenId = scbase.GenPromptUUID() newScreenId = scbase.GenPromptUUID()
query = `INSERT INTO screen (sessionid, screenid, name, activewindowid, screenidx, screenopts, ownerid, sharemode, incognito, archived, archivedts) VALUES (?, ?, ?, ?, ?, ?, '', 'local', 0, 0, 0)` screen := &ScreenType{
tx.Exec(query, sessionId, newScreenId, screenName, newWindowId, maxScreenIdx+1, ScreenOptsType{}) SessionId: sessionId,
layout := LayoutType{Type: LayoutFull} ScreenId: newScreenId,
query = `INSERT INTO screen_window (sessionid, screenid, windowid, name, layout, selectedline, anchor, focustype) VALUES (?, ?, ?, ?, ?, ?, ?, ?)` WindowId: newWindowId,
tx.Exec(query, sessionId, newScreenId, newWindowId, DefaultScreenWindowName, layout, 0, SWAnchorType{}, "input") Name: screenName,
ScreenIdx: int64(maxScreenIdx) + 1,
ScreenOpts: ScreenOptsType{},
OwnerId: "",
ShareMode: ShareModeLocal,
CurRemote: RemotePtrType{RemoteId: localRemoteId},
NextLineNum: 1,
SelectedLine: 0,
Anchor: ScreenAnchorType{},
FocusType: ScreenFocusInput,
Archived: false,
ArchivedTs: 0,
}
query = `INSERT INTO screen (sessionid, screenid, windowid, name, screenidx, screenopts, ownerid, sharemode, curremoteownerid, curremoteid, curremotename, nextlinenum, selectedline, anchor, focustype, archived, archivedts) VALUES (:sessionid,:screenid,:windowid,:name,:screenidx,:screenopts,:ownerid,:sharemode,:curremoteownerid,:curremoteid,:curremotename,:nextlinenum,:selectedline,:anchor,:focustype,:archived,:archivedts)`
tx.NamedExec(query, screen.ToMap())
if activate { if activate {
query = `UPDATE session SET activescreenid = ? WHERE sessionid = ?` query = `UPDATE session SET activescreenid = ? WHERE sessionid = ?`
tx.Exec(query, newScreenId, sessionId) tx.Exec(query, newScreenId, sessionId)
@ -677,7 +674,7 @@ func InsertScreen(ctx context.Context, sessionId string, origScreenName string,
if txErr != nil { if txErr != nil {
return nil, txErr return nil, txErr
} }
newScreen, err := GetScreenById(ctx, sessionId, newScreenId) newScreen, err := GetScreenById(ctx, newScreenId)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -689,42 +686,12 @@ func InsertScreen(ctx context.Context, sessionId string, origScreenName string,
return ModelUpdate{Sessions: []*SessionType{bareSession}}, nil return ModelUpdate{Sessions: []*SessionType{bareSession}}, nil
} }
func GetScreenById(ctx context.Context, sessionId string, screenId string) (*ScreenType, error) { func GetScreenById(ctx context.Context, screenId string) (*ScreenType, error) {
var rtnScreen *ScreenType return WithTxRtn(ctx, func(tx *TxWrap) (*ScreenType, error) {
txErr := WithTx(ctx, func(tx *TxWrap) error { query := `SELECT * FROM screen WHERE screenid = ?`
query := `SELECT * FROM screen WHERE sessionid = ? AND screenid = ?` screen := GetMapGen[*ScreenType](tx, query, screenId)
var screen ScreenType return screen, nil
found := tx.Get(&screen, query, sessionId, screenId)
if !found {
return nil
}
rtnScreen = &screen
query = `SELECT * FROM screen_window WHERE sessionid = ? AND screenid = ?`
tx.Select(&screen.Windows, query, sessionId, screenId)
screen.Full = true
return nil
}) })
if txErr != nil {
return nil, txErr
}
return rtnScreen, nil
}
func txCreateWindow(tx *TxWrap, sessionId string, curRemote RemotePtrType) string {
w := &WindowType{
SessionId: sessionId,
WindowId: scbase.GenPromptUUID(),
CurRemote: curRemote,
NextLineNum: 1,
WinOpts: WindowOptsType{},
ShareMode: ShareModeLocal,
ShareOpts: WindowShareOptsType{},
}
wmap := w.ToMap()
query := `INSERT INTO window ( sessionid, windowid, curremoteownerid, curremoteid, curremotename, nextlinenum, winopts, ownerid, sharemode, shareopts)
VALUES (:sessionid,:windowid,:curremoteownerid,:curremoteid,:curremotename,:nextlinenum,:winopts,:ownerid,:sharemode,:shareopts)`
tx.NamedExec(query, wmap)
return w.WindowId
} }
func FindLineIdByArg(ctx context.Context, sessionId string, windowId string, lineArg string) (string, error) { func FindLineIdByArg(ctx context.Context, sessionId string, windowId string, lineArg string) (string, error) {
@ -1071,7 +1038,7 @@ func ArchiveScreen(ctx context.Context, sessionId string, screenId string) (Upda
return nil, txErr return nil, txErr
} }
update := ModelUpdate{Sessions: []*SessionType{bareSession}} update := ModelUpdate{Sessions: []*SessionType{bareSession}}
newScreen, _ := GetScreenById(ctx, sessionId, screenId) newScreen, _ := GetScreenById(ctx, screenId)
if newScreen != nil { if newScreen != nil {
bareSession.Screens = append(bareSession.Screens, newScreen) bareSession.Screens = append(bareSession.Screens, newScreen)
} }
@ -1264,17 +1231,16 @@ func UpdateRemoteState(ctx context.Context, sessionId string, windowId string, r
return ri, txErr return ri, txErr
} }
func UpdateCurRemote(ctx context.Context, sessionId string, windowId string, remotePtr RemotePtrType) error { func UpdateCurRemote(ctx context.Context, screenId string, remotePtr RemotePtrType) error {
txErr := WithTx(ctx, func(tx *TxWrap) error { return WithTx(ctx, func(tx *TxWrap) error {
query := `SELECT windowid FROM window WHERE sessionid = ? AND windowid = ?` query := `SELECT screenid FROM screen WHERE screenid = ?`
if !tx.Exists(query, sessionId, windowId) { if !tx.Exists(query, screenId) {
return fmt.Errorf("cannot update curremote: no window found") return fmt.Errorf("cannot update curremote: no screen found")
} }
query = `UPDATE window SET curremoteownerid = ?, curremoteid = ?, curremotename = ? WHERE sessionid = ? AND windowid = ?` query = `UPDATE screen SET curremoteownerid = ?, curremoteid = ?, curremotename = ? WHERE screenid = ?`
tx.Exec(query, remotePtr.OwnerId, remotePtr.RemoteId, remotePtr.Name, sessionId, windowId) tx.Exec(query, remotePtr.OwnerId, remotePtr.RemoteId, remotePtr.Name, screenId)
return nil return nil
}) })
return txErr
} }
func reorderStrings(strs []string, toMove string, newIndex int) []string { func reorderStrings(strs []string, toMove string, newIndex int) []string {
@ -1353,77 +1319,65 @@ func SetScreenName(ctx context.Context, sessionId string, screenId string, name
return txErr return txErr
} }
func SetScreenOpts(ctx context.Context, sessionId string, screenId string, opts *ScreenOptsType) error { func ArchiveScreenLines(ctx context.Context, screenId string) (*ModelUpdate, error) {
if opts == nil {
return fmt.Errorf("invalid screen opts cannot be nil")
}
txErr := WithTx(ctx, func(tx *TxWrap) error { txErr := WithTx(ctx, func(tx *TxWrap) error {
query := `SELECT screenid FROM screen WHERE sessionid = ? AND screenid = ?` query := `SELECT sessionid, windowid FROM screen WHERE screenid = ?`
if !tx.Exists(query, sessionId, screenId) { var swkeys SWKeys
return fmt.Errorf("screen does not exist") tx.Get(&swkeys, query, screenId)
} if swkeys.SessionId == "" || swkeys.WindowId == "" {
query = `UPDATE screen SET screenopts = ? WHERE sessionid = ? AND screenid = ?` return fmt.Errorf("screen windowid does not exist")
tx.Exec(query, opts, sessionId, screenId)
return nil
})
return txErr
}
func ArchiveWindowLines(ctx context.Context, sessionId string, windowId string) (*ModelUpdate, error) {
txErr := WithTx(ctx, func(tx *TxWrap) error {
query := `SELECT windowid FROM window WHERE sessionid = ? AND windowid = ?`
if !tx.Exists(query, sessionId, windowId) {
return fmt.Errorf("window does not exist")
} }
query = `UPDATE line SET archived = 1 WHERE sessionid = ? AND windowid = ?` query = `UPDATE line SET archived = 1 WHERE sessionid = ? AND windowid = ?`
tx.Exec(query, sessionId, windowId) tx.Exec(query, swkeys.SessionId, swkeys.WindowId)
return nil return nil
}) })
if txErr != nil { if txErr != nil {
return nil, txErr return nil, txErr
} }
win, err := GetWindowById(ctx, sessionId, windowId) screen, err := GetFullScreenById(ctx, screenId)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &ModelUpdate{Windows: []*WindowType{win}}, nil return &ModelUpdate{Screens: []*ScreenType{screen}}, nil
} }
func PurgeWindowLines(ctx context.Context, sessionId string, windowId string) (*ModelUpdate, error) { func PurgeScreenLines(ctx context.Context, screenId string) (*ModelUpdate, error) {
var lineIds []string var lineIds []string
var swkeys SWKeys
txErr := WithTx(ctx, func(tx *TxWrap) error { txErr := WithTx(ctx, func(tx *TxWrap) error {
query := `SELECT windowid FROM window WHERE sessionid = ? AND windowid = ?` query := `SELECT sessionid, windowid FROM screen WHERE screenid = ?`
if !tx.Exists(query, sessionId, windowId) { tx.Get(&swkeys, query, screenId)
return fmt.Errorf("window does not exist") if swkeys.SessionId == "" || swkeys.WindowId == "" {
return fmt.Errorf("screen windowid does not exist")
} }
query = `SELECT lineid FROM line WHERE sessionid = ? AND windowid = ?` query = `SELECT lineid FROM line WHERE sessionid = ? AND windowid = ?`
lineIds = tx.SelectStrings(query, sessionId, windowId) lineIds = tx.SelectStrings(query, swkeys.SessionId, swkeys.WindowId)
query = `DELETE FROM line WHERE sessionid = ? AND windowid = ?` query = `DELETE FROM line WHERE sessionid = ? AND windowid = ?`
tx.Exec(query, sessionId, windowId) tx.Exec(query, swkeys.SessionId, swkeys.WindowId)
query = `DELETE FROM history WHERE sessionid = ? AND windowid = ?` query = `DELETE FROM history WHERE sessionid = ? AND windowid = ?`
tx.Exec(query, sessionId, windowId) tx.Exec(query, swkeys.SessionId, swkeys.WindowId)
query = `UPDATE window SET nextlinenum = 1 WHERE sessionid = ? AND windowid = ?` query = `UPDATE screen SET nextlinenum = 1 WHERE screenid = ?`
tx.Exec(query, sessionId, windowId) tx.Exec(query, screenId)
return nil return nil
}) })
if txErr != nil { if txErr != nil {
return nil, txErr return nil, txErr
} }
go cleanSessionCmds(context.Background(), sessionId) go cleanSessionCmds(context.Background(), swkeys.SessionId)
win, err := GetWindowById(ctx, sessionId, windowId) screen, err := GetFullScreenById(ctx, screenId)
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, lineId := range lineIds { for _, lineId := range lineIds {
line := &LineType{ line := &LineType{
SessionId: sessionId, SessionId: swkeys.SessionId,
WindowId: windowId, WindowId: swkeys.WindowId,
LineId: lineId, LineId: lineId,
Remove: true, Remove: true,
} }
win.Lines = append(win.Lines, line) screen.Lines = append(screen.Lines, line)
} }
return &ModelUpdate{Windows: []*WindowType{win}}, nil return &ModelUpdate{Screens: []*ScreenType{screen}}, nil
} }
func GetRunningWindowCmds(ctx context.Context, sessionId string, windowId string) ([]*CmdType, error) { func GetRunningWindowCmds(ctx context.Context, sessionId string, windowId string) ([]*CmdType, error) {
@ -1449,24 +1403,25 @@ func UpdateCmdTermOpts(ctx context.Context, sessionId string, cmdId string, term
} }
// returns riids of deleted RIs // returns riids of deleted RIs
func WindowReset(ctx context.Context, sessionId string, windowId string) ([]*RemoteInstance, error) { func ScreenReset(ctx context.Context, screenId string) ([]*RemoteInstance, error) {
var delRis []*RemoteInstance return WithTxRtn(ctx, func(tx *TxWrap) ([]*RemoteInstance, error) {
txErr := WithTx(ctx, func(tx *TxWrap) error { var swkeys SWKeys
query := `SELECT windowid FROM window WHERE sessionid = ? AND windowid = ?` query := `SELECT sessionid, windowid FROM screen WHERE screenid = ?`
if !tx.Exists(query, sessionId, windowId) { tx.Get(&swkeys, query, screenId)
return fmt.Errorf("window does not exist") if swkeys.SessionId == "" || swkeys.WindowId == "" {
return nil, fmt.Errorf("screen does not exist")
} }
query = `SELECT riid FROM remote_instance WHERE sessionid = ? AND windowid = ?` query = `SELECT riid FROM remote_instance WHERE sessionid = ? AND windowid = ?`
riids := tx.SelectStrings(query, sessionId, windowId) riids := tx.SelectStrings(query, swkeys.SessionId, swkeys.WindowId)
var delRis []*RemoteInstance
for _, riid := range riids { for _, riid := range riids {
ri := &RemoteInstance{SessionId: sessionId, WindowId: windowId, RIId: riid, Remove: true} ri := &RemoteInstance{SessionId: swkeys.SessionId, WindowId: swkeys.WindowId, RIId: riid, Remove: true}
delRis = append(delRis, ri) delRis = append(delRis, ri)
} }
query = `DELETE FROM remote_instance WHERE sessionid = ? AND windowid = ?` query = `DELETE FROM remote_instance WHERE sessionid = ? AND windowid = ?`
tx.Exec(query, sessionId, windowId) tx.Exec(query, swkeys.SessionId, swkeys.WindowId)
return nil return delRis, nil
}) })
return delRis, txErr
} }
func DeleteSession(ctx context.Context, sessionId string) (UpdatePacket, error) { func DeleteSession(ctx context.Context, sessionId string) (UpdatePacket, error) {
@ -1480,10 +1435,6 @@ func DeleteSession(ctx context.Context, sessionId string) (UpdatePacket, error)
tx.Exec(query, sessionId) tx.Exec(query, sessionId)
query = `DELETE FROM screen WHERE sessionid = ?` query = `DELETE FROM screen WHERE sessionid = ?`
tx.Exec(query, sessionId) tx.Exec(query, sessionId)
query = `DELETE FROM screen_window WHERE sessionid = ?`
tx.Exec(query, sessionId)
query = `DELETE FROM window WHERE sessionid = ?`
tx.Exec(query, sessionId)
query = `DELETE FROM history WHERE sessionid = ?` query = `DELETE FROM history WHERE sessionid = ?`
tx.Exec(query, sessionId) tx.Exec(query, sessionId)
query = `DELETE FROM line WHERE sessionid = ?` query = `DELETE FROM line WHERE sessionid = ?`
@ -1693,64 +1644,55 @@ func UpdateRemote(ctx context.Context, remoteId string, editMap map[string]inter
} }
const ( const (
SWField_AnchorLine = "anchorline" // int ScreenField_AnchorLine = "anchorline" // int
SWField_AnchorOffset = "anchoroffset" // int ScreenField_AnchorOffset = "anchoroffset" // int
SWField_SelectedLine = "selectedline" // int ScreenField_SelectedLine = "selectedline" // int
SWField_Focus = "focustype" // string ScreenField_Focus = "focustype" // string
ScreenField_TabColor = "tabcolor" // string
ScreenField_PTerm = "pterm" // string
ScreenField_Name = "name" // string
) )
func UpdateScreenWindow(ctx context.Context, sessionId string, screenId string, windowId string, editMap map[string]interface{}) (*ScreenWindowType, error) { func UpdateScreen(ctx context.Context, screenId string, editMap map[string]interface{}) (*ScreenType, error) {
var rtn *ScreenWindowType
txErr := WithTx(ctx, func(tx *TxWrap) error { txErr := WithTx(ctx, func(tx *TxWrap) error {
query := `SELECT sessionid FROM screen_window WHERE sessionid = ? AND screenid = ? AND windowid = ?` query := `SELECT screenid FROM screen WHERE screenid = ?`
if !tx.Exists(query, sessionId, screenId, windowId) { if !tx.Exists(query, screenId) {
return fmt.Errorf("screen-window not found") return fmt.Errorf("screen not found")
} }
if anchorLine, found := editMap[SWField_AnchorLine]; found { if anchorLine, found := editMap[ScreenField_AnchorLine]; found {
query = `UPDATE screen_window SET anchor = json_set(anchor, '$.anchorline', ?) WHERE sessionid = ? AND screenid = ? AND windowid = ?` query = `UPDATE screen SET anchor = json_set(anchor, '$.anchorline', ?) WHERE screenid = ?`
tx.Exec(query, anchorLine, sessionId, screenId, windowId) tx.Exec(query, anchorLine, screenId)
} }
if anchorOffset, found := editMap[SWField_AnchorOffset]; found { if anchorOffset, found := editMap[ScreenField_AnchorOffset]; found {
query = `UPDATE screen_window SET anchor = json_set(anchor, '$.anchoroffset', ?) WHERE sessionid = ? AND screenid = ? AND windowid = ?` query = `UPDATE screen SET anchor = json_set(anchor, '$.anchoroffset', ?) WHERE screenid = ?`
tx.Exec(query, anchorOffset, sessionId, screenId, windowId) tx.Exec(query, anchorOffset, screenId)
} }
if sline, found := editMap[SWField_SelectedLine]; found { if sline, found := editMap[ScreenField_SelectedLine]; found {
query = `UPDATE screen_window SET selectedline = ? WHERE sessionid = ? AND screenid = ? AND windowid = ?` query = `UPDATE screen SET selectedline = ? WHERE screenid = ?`
tx.Exec(query, sline, sessionId, screenId, windowId) tx.Exec(query, sline, screenId)
} }
if focusType, found := editMap[SWField_Focus]; found { if focusType, found := editMap[ScreenField_Focus]; found {
query = `UPDATE screen_window SET focustype = ? WHERE sessionid = ? AND screenid = ? AND windowid = ?` query = `UPDATE screen SET focustype = ? WHERE screenid = ?`
tx.Exec(query, focusType, sessionId, screenId, windowId) tx.Exec(query, focusType, screenId)
} }
var sw ScreenWindowType if tabColor, found := editMap[ScreenField_TabColor]; found {
query = `SELECT * FROM screen_window WHERE sessionid = ? AND screenid = ? AND windowid = ?` query = `UPDATE screen SET screenopts = json_set(screenopts, '$.tabcolor', ?) WHERE screenid = ?`
found := tx.Get(&sw, query, sessionId, screenId, windowId) tx.Exec(query, tabColor, screenId)
if found { }
rtn = &sw if pterm, found := editMap[ScreenField_PTerm]; found {
query = `UPDATE screen SET screenopts = json_set(screenopts, '$.pterm', ?) WHERE screenid = ?`
tx.Exec(query, pterm, screenId)
}
if name, found := editMap[ScreenField_Name]; found {
query = `UPDATE screen SET name = ? WHERE screenid = ?`
tx.Exec(query, name, screenId)
} }
return nil return nil
}) })
if txErr != nil { if txErr != nil {
return nil, txErr return nil, txErr
} }
return rtn, nil return GetScreenById(ctx, screenId)
}
func GetScreenWindowByIds(ctx context.Context, sessionId string, screenId string, windowId string) (*ScreenWindowType, error) {
var rtn *ScreenWindowType
txErr := WithTx(ctx, func(tx *TxWrap) error {
var sw ScreenWindowType
query := `SELECT * FROM screen_window WHERE sessionid = ? AND screenid = ? AND windowid = ?`
found := tx.Get(&sw, query, sessionId, screenId, windowId)
if found {
rtn = &sw
}
return nil
})
if txErr != nil {
return nil, txErr
}
return rtn, nil
} }
func GetLineResolveItems(ctx context.Context, sessionId string, windowId string) ([]ResolveItem, error) { func GetLineResolveItems(ctx context.Context, sessionId string, windowId string) ([]ResolveItem, error) {
@ -1766,33 +1708,32 @@ func GetLineResolveItems(ctx context.Context, sessionId string, windowId string)
return rtn, nil return rtn, nil
} }
func UpdateSWsWithCmdFg(ctx context.Context, sessionId string, cmdId string) ([]*ScreenWindowType, error) { func UpdateScreensWithCmdFg(ctx context.Context, sessionId string, cmdId string) ([]*ScreenType, error) {
var rtn []*ScreenWindowType var rtn []*ScreenType
txErr := WithTx(ctx, func(tx *TxWrap) error { txErr := WithTx(ctx, func(tx *TxWrap) error {
query := `SELECT sessionid, screenid, windowid query := `SELECT screenid
FROM screen_window sw FROM screen s
WHERE WHERE
sessionid = ? s.sessionid = ?
AND focustype = 'cmd-fg' AND s.focustype = 'cmd-fg'
AND selectedline IN (SELECT linenum AND s.selectedline IN (SELECT linenum
FROM line l FROM line l
WHERE l.sessionid = sw.sessionid WHERE l.sessionid = s.sessionid
AND l.windowid = sw.windowid AND l.windowid = s.windowid
AND l.cmdid = ? AND l.cmdid = ?
)` )`
var swKeys []SWKey screenIds := tx.SelectStrings(query, sessionId, cmdId)
tx.Select(&swKeys, query, sessionId, cmdId) if len(screenIds) == 0 {
if len(swKeys) == 0 {
return nil return nil
} }
for _, key := range swKeys { for _, screenId := range screenIds {
editMap := make(map[string]interface{}) editMap := make(map[string]interface{})
editMap[SWField_Focus] = SWFocusInput editMap[ScreenField_Focus] = ScreenFocusInput
sw, err := UpdateScreenWindow(tx.Context(), key.SessionId, key.ScreenId, key.WindowId, editMap) screen, err := UpdateScreen(tx.Context(), screenId, editMap)
if err != nil { if err != nil {
return err return err
} }
rtn = append(rtn, sw) rtn = append(rtn, screen)
} }
return nil return nil
}) })

View File

@ -17,7 +17,7 @@ import (
"github.com/golang-migrate/migrate/v4" "github.com/golang-migrate/migrate/v4"
) )
const MaxMigration = 8 const MaxMigration = 9
const MigratePrimaryScreenVersion = 9 const MigratePrimaryScreenVersion = 9
func MakeMigrate() (*migrate.Migrate, error) { func MakeMigrate() (*migrate.Migrate, error) {

View File

@ -73,9 +73,9 @@ const (
) )
const ( const (
SWFocusInput = "input" ScreenFocusInput = "input"
SWFocusCmd = "cmd" ScreenFocusCmd = "cmd"
SWFocusCmdFg = "cmd-fg" ScreenFocusCmdFg = "cmd-fg"
) )
const MaxTzNameLen = 50 const MaxTzNameLen = 50
@ -276,29 +276,6 @@ type SessionStatsType struct {
DiskStats SessionDiskSizeType `json:"diskstats"` DiskStats SessionDiskSizeType `json:"diskstats"`
} }
type WindowOptsType struct {
PTerm string `json:"pterm,omitempty"`
}
func (opts *WindowOptsType) Scan(val interface{}) error {
return quickScanJson(opts, val)
}
func (opts WindowOptsType) Value() (driver.Value, error) {
return quickValueJson(opts)
}
type WindowShareOptsType struct {
}
func (opts *WindowShareOptsType) Scan(val interface{}) error {
return quickScanJson(opts, val)
}
func (opts WindowShareOptsType) Value() (driver.Value, error) {
return quickValueJson(opts)
}
var RemoteNameRe = regexp.MustCompile("^\\*?[a-zA-Z0-9_-]+$") var RemoteNameRe = regexp.MustCompile("^\\*?[a-zA-Z0-9_-]+$")
type RemotePtrType struct { type RemotePtrType struct {
@ -361,51 +338,6 @@ func (r RemotePtrType) MakeFullRemoteRef() string {
return fmt.Sprintf("@%s:%s:%s", r.OwnerId, r.RemoteId, r.Name) return fmt.Sprintf("@%s:%s:%s", r.OwnerId, r.RemoteId, r.Name)
} }
type WindowType struct {
SessionId string `json:"sessionid"`
WindowId string `json:"windowid"`
CurRemote RemotePtrType `json:"curremote"`
WinOpts WindowOptsType `json:"winopts"`
OwnerId string `json:"ownerid"`
NextLineNum int64 `json:"nextlinenum"`
ShareMode string `json:"sharemode"`
ShareOpts WindowShareOptsType `json:"shareopts"`
Lines []*LineType `json:"lines"`
Cmds []*CmdType `json:"cmds"`
// only for updates
Remove bool `json:"remove,omitempty"`
}
func (w *WindowType) ToMap() map[string]interface{} {
rtn := make(map[string]interface{})
rtn["sessionid"] = w.SessionId
rtn["windowid"] = w.WindowId
rtn["curremoteownerid"] = w.CurRemote.OwnerId
rtn["curremoteid"] = w.CurRemote.RemoteId
rtn["curremotename"] = w.CurRemote.Name
rtn["nextlinenum"] = w.NextLineNum
rtn["winopts"] = quickJson(w.WinOpts)
rtn["ownerid"] = w.OwnerId
rtn["sharemode"] = w.ShareMode
rtn["shareopts"] = quickJson(w.ShareOpts)
return rtn
}
func (w *WindowType) FromMap(m map[string]interface{}) bool {
quickSetStr(&w.SessionId, m, "sessionid")
quickSetStr(&w.WindowId, m, "windowid")
quickSetStr(&w.CurRemote.OwnerId, m, "curremoteownerid")
quickSetStr(&w.CurRemote.RemoteId, m, "curremoteid")
quickSetStr(&w.CurRemote.Name, m, "curremotename")
quickSetInt64(&w.NextLineNum, m, "nextlinenum")
quickSetJson(&w.WinOpts, m, "winopts")
quickSetStr(&w.OwnerId, m, "ownerid")
quickSetStr(&w.ShareMode, m, "sharemode")
quickSetJson(&w.ShareOpts, m, "shareopts")
return true
}
func (h *HistoryItemType) ToMap() map[string]interface{} { func (h *HistoryItemType) ToMap() map[string]interface{} {
rtn := make(map[string]interface{}) rtn := make(map[string]interface{})
rtn["historyid"] = h.HistoryId rtn["historyid"] = h.HistoryId
@ -448,35 +380,83 @@ func (h *HistoryItemType) FromMap(m map[string]interface{}) bool {
type ScreenOptsType struct { type ScreenOptsType struct {
TabColor string `json:"tabcolor,omitempty"` TabColor string `json:"tabcolor,omitempty"`
PTerm string `json:"pterm,omitempty"`
} }
func (opts *ScreenOptsType) Scan(val interface{}) error { type SWKeys struct {
return quickScanJson(opts, val) SessionId string
} WindowId string
func (opts ScreenOptsType) Value() (driver.Value, error) {
return quickValueJson(opts)
} }
type ScreenType struct { type ScreenType struct {
SessionId string `json:"sessionid"` SessionId string `json:"sessionid"`
ScreenId string `json:"screenid"` ScreenId string `json:"screenid"`
ScreenIdx int64 `json:"screenidx"` WindowId string `json:"windowid"`
Name string `json:"name"` Name string `json:"name"`
ActiveWindowId string `json:"activewindowid"` ScreenIdx int64 `json:"screenidx"`
ScreenOpts *ScreenOptsType `json:"screenopts"` ScreenOpts ScreenOptsType `json:"screenopts"`
OwnerId string `json:"ownerid"` OwnerId string `json:"ownerid"`
ShareMode string `json:"sharemode"` ShareMode string `json:"sharemode"`
Incognito bool `json:"incognito,omitempty"` CurRemote RemotePtrType `json:"curremote"`
Archived bool `json:"archived,omitempty"` NextLineNum int64 `json:"nextlinenum"`
ArchivedTs int64 `json:"archivedts,omitempty"` SelectedLine int64 `json:"selectedline"`
Windows []*ScreenWindowType `json:"windows"` Anchor ScreenAnchorType `json:"anchor"`
FocusType string `json:"focustype"`
Archived bool `json:"archived,omitempty"`
ArchivedTs int64 `json:"archivedts,omitempty"`
// only for "full"
Lines []*LineType `json:"lines"`
Cmds []*CmdType `json:"cmds"`
// only for updates // only for updates
Remove bool `json:"remove,omitempty"` Remove bool `json:"remove,omitempty"`
Full bool `json:"full,omitempty"` Full bool `json:"full,omitempty"`
} }
func (s *ScreenType) ToMap() map[string]interface{} {
rtn := make(map[string]interface{})
rtn["sessionid"] = s.SessionId
rtn["screenid"] = s.ScreenId
rtn["windowid"] = s.WindowId
rtn["name"] = s.Name
rtn["screenidx"] = s.ScreenIdx
rtn["screenopts"] = quickJson(s.ScreenOpts)
rtn["ownerid"] = s.OwnerId
rtn["sharemode"] = s.ShareMode
rtn["curremoteownerid"] = s.CurRemote.OwnerId
rtn["curremoteid"] = s.CurRemote.RemoteId
rtn["curremotename"] = s.CurRemote.Name
rtn["nextlinenum"] = s.NextLineNum
rtn["selectedline"] = s.SelectedLine
rtn["anchor"] = quickJson(s.Anchor)
rtn["focustype"] = s.FocusType
rtn["archived"] = s.Archived
rtn["archivedts"] = s.ArchivedTs
return rtn
}
func (s *ScreenType) FromMap(m map[string]interface{}) bool {
quickSetStr(&s.SessionId, m, "sessionid")
quickSetStr(&s.ScreenId, m, "screenid")
quickSetStr(&s.WindowId, m, "windowid")
quickSetStr(&s.Name, m, "name")
quickSetInt64(&s.ScreenIdx, m, "screenidx")
quickSetJson(&s.ScreenOpts, m, "screenopts")
quickSetStr(&s.OwnerId, m, "ownerid")
quickSetStr(&s.ShareMode, m, "sharemode")
quickSetStr(&s.CurRemote.OwnerId, m, "curremoteownerid")
quickSetStr(&s.CurRemote.RemoteId, m, "curremoteid")
quickSetStr(&s.CurRemote.Name, m, "curremotename")
quickSetInt64(&s.NextLineNum, m, "nextlinenum")
quickSetInt64(&s.SelectedLine, m, "selectedline")
quickSetJson(&s.Anchor, m, "anchor")
quickSetStr(&s.FocusType, m, "focustype")
quickSetBool(&s.Archived, m, "archived")
quickSetInt64(&s.ArchivedTs, m, "archivedts")
return true
}
const ( const (
LayoutFull = "full" LayoutFull = "full"
) )
@ -502,39 +482,11 @@ func (l LayoutType) Value() (driver.Value, error) {
return quickValueJson(l) return quickValueJson(l)
} }
type SWAnchorType struct { type ScreenAnchorType struct {
AnchorLine int `json:"anchorline,omitempty"` AnchorLine int `json:"anchorline,omitempty"`
AnchorOffset int `json:"anchoroffset,omitempty"` AnchorOffset int `json:"anchoroffset,omitempty"`
} }
func (a *SWAnchorType) Scan(val interface{}) error {
return quickScanJson(a, val)
}
func (a SWAnchorType) Value() (driver.Value, error) {
return quickValueJson(a)
}
type SWKey struct {
SessionId string
ScreenId string
WindowId string
}
type ScreenWindowType struct {
SessionId string `json:"sessionid"`
ScreenId string `json:"screenid"`
WindowId string `json:"windowid"`
Name string `json:"name"`
Layout LayoutType `json:"layout"`
SelectedLine int `json:"selectedline"`
Anchor SWAnchorType `json:"anchor"`
FocusType string `json:"focustype"`
// only for updates
Remove bool `json:"remove,omitempty"`
}
type HistoryItemType struct { type HistoryItemType struct {
HistoryId string `json:"historyid"` HistoryId string `json:"historyid"`
Ts int64 `json:"ts"` Ts int64 `json:"ts"`

View File

@ -30,24 +30,23 @@ func (PtyDataUpdate) UpdateType() string {
} }
type ModelUpdate struct { type ModelUpdate struct {
Sessions []*SessionType `json:"sessions,omitempty"` Sessions []*SessionType `json:"sessions,omitempty"`
ActiveSessionId string `json:"activesessionid,omitempty"` ActiveSessionId string `json:"activesessionid,omitempty"`
Windows []*WindowType `json:"windows,omitempty"` Screens []*ScreenType `json:"screens,omitempty"`
ScreenWindows []*ScreenWindowType `json:"screenwindows,omitempty"` Line *LineType `json:"line,omitempty"`
Line *LineType `json:"line,omitempty"` Lines []*LineType `json:"lines,omitempty"`
Lines []*LineType `json:"lines,omitempty"` Cmd *CmdType `json:"cmd,omitempty"`
Cmd *CmdType `json:"cmd,omitempty"` CmdLine *CmdLineType `json:"cmdline,omitempty"`
CmdLine *CmdLineType `json:"cmdline,omitempty"` Info *InfoMsgType `json:"info,omitempty"`
Info *InfoMsgType `json:"info,omitempty"` ClearInfo bool `json:"clearinfo,omitempty"`
ClearInfo bool `json:"clearinfo,omitempty"` Remotes []interface{} `json:"remotes,omitempty"` // []*remote.RemoteState
Remotes []interface{} `json:"remotes,omitempty"` // []*remote.RemoteState History *HistoryInfoType `json:"history,omitempty"`
History *HistoryInfoType `json:"history,omitempty"` Interactive bool `json:"interactive"`
Interactive bool `json:"interactive"` Connect bool `json:"connect,omitempty"`
Connect bool `json:"connect,omitempty"` MainView string `json:"mainview,omitempty"`
MainView string `json:"mainview,omitempty"` Bookmarks []*BookmarkType `json:"bookmarks,omitempty"`
Bookmarks []*BookmarkType `json:"bookmarks,omitempty"` HistoryViewData *HistoryViewData `json:"historyviewdata,omitempty"`
HistoryViewData *HistoryViewData `json:"historyviewdata,omitempty"` ClientData *ClientData `json:"clientdata,omitempty"`
ClientData *ClientData `json:"clientdata,omitempty"`
} }
func (ModelUpdate) UpdateType() string { func (ModelUpdate) UpdateType() string {