|
|
|
|
@@ -45,7 +45,8 @@ var runtimeTimePattern = regexp.MustCompile(`^\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\
|
|
|
|
|
var vrcLogTimePattern = regexp.MustCompile(`^(\d{4}\.\d{2}\.\d{2} \d{2}:\d{2}:\d{2})`)
|
|
|
|
|
var vrcJoinPattern = regexp.MustCompile(`OnPlayerJoined\s+(.+?)\s+\(usr_[0-9a-fA-F-]+\)`)
|
|
|
|
|
var vrcLeftPattern = regexp.MustCompile(`OnPlayerLeft\s+(.+?)\s+\(usr_[0-9a-fA-F-]+\)`)
|
|
|
|
|
var hwndTopMostFlag uintptr = ^uintptr(0) - 1
|
|
|
|
|
var hwndTopMostFlag uintptr = ^uintptr(0)
|
|
|
|
|
var hwndNotTopMostFlag uintptr = ^uintptr(0) - 1
|
|
|
|
|
var hwndNoMove uintptr = 0x0002
|
|
|
|
|
var hwndNoSize uintptr = 0x0001
|
|
|
|
|
var hwndNoActivate uintptr = 0x0010
|
|
|
|
|
@@ -74,13 +75,10 @@ type guiApp struct {
|
|
|
|
|
tabJoin uintptr
|
|
|
|
|
tabTranslate uintptr
|
|
|
|
|
tabSettings uintptr
|
|
|
|
|
btnActiveWindow uintptr
|
|
|
|
|
btnTopMost uintptr
|
|
|
|
|
btnFontDown uintptr
|
|
|
|
|
btnFontUp uintptr
|
|
|
|
|
activeTab string
|
|
|
|
|
activeWindow bool
|
|
|
|
|
lastActiveWindow bool
|
|
|
|
|
topMost bool
|
|
|
|
|
lastTopMost bool
|
|
|
|
|
fontSize int
|
|
|
|
|
@@ -125,15 +123,12 @@ func runNativeGUI() error {
|
|
|
|
|
translateMessage := user32.NewProc("TranslateMessage")
|
|
|
|
|
dispatchMessage := user32.NewProc("DispatchMessageW")
|
|
|
|
|
postQuitMessage := user32.NewProc("PostQuitMessage")
|
|
|
|
|
setForegroundWindow := user32.NewProc("SetForegroundWindow")
|
|
|
|
|
bringWindowToTop := user32.NewProc("BringWindowToTop")
|
|
|
|
|
setTimer := user32.NewProc("SetTimer")
|
|
|
|
|
loadCursor := user32.NewProc("LoadCursorW")
|
|
|
|
|
setWindowText := user32.NewProc("SetWindowTextW")
|
|
|
|
|
getWindowRect := user32.NewProc("GetWindowRect")
|
|
|
|
|
isWindowVisible := user32.NewProc("IsWindowVisible")
|
|
|
|
|
isIconic := user32.NewProc("IsIconic")
|
|
|
|
|
getForegroundWindow := user32.NewProc("GetForegroundWindow")
|
|
|
|
|
getSystemMetrics := user32.NewProc("GetSystemMetrics")
|
|
|
|
|
getModuleHandle := kernel32.NewProc("GetModuleHandleW")
|
|
|
|
|
loadIcon := user32.NewProc("LoadIconW")
|
|
|
|
|
@@ -168,7 +163,6 @@ func runNativeGUI() error {
|
|
|
|
|
|
|
|
|
|
var app guiApp
|
|
|
|
|
guiCfg := loadGUISettings()
|
|
|
|
|
app.activeWindow = guiCfg.ActiveWindow
|
|
|
|
|
app.topMost = guiCfg.TopMost
|
|
|
|
|
if guiCfg.FontSize > 0 {
|
|
|
|
|
app.fontSize = guiCfg.FontSize
|
|
|
|
|
@@ -187,7 +181,6 @@ func runNativeGUI() error {
|
|
|
|
|
joinTitle, _ := syscall.UTF16PtrFromString("Join Log")
|
|
|
|
|
translateTitle, _ := syscall.UTF16PtrFromString("Translate")
|
|
|
|
|
settingsTitle, _ := syscall.UTF16PtrFromString("Settings")
|
|
|
|
|
activeWindowTitle, _ := syscall.UTF16PtrFromString("Active window")
|
|
|
|
|
topMostTitle, _ := syscall.UTF16PtrFromString("Top most")
|
|
|
|
|
fontDownTitle, _ := syscall.UTF16PtrFromString("A-")
|
|
|
|
|
fontUpTitle, _ := syscall.UTF16PtrFromString("A+")
|
|
|
|
|
@@ -198,35 +191,28 @@ func runNativeGUI() error {
|
|
|
|
|
app.rightHwnd, _, _ = createWindowEx.Call(0, uintptr(unsafe.Pointer(rightClass)), uintptr(unsafe.Pointer(rightTitle)), wsVisible|wsChild|wsClipChildren, 370, 60, 330, 260, hwnd, idLog, hInstance, 0)
|
|
|
|
|
settingsPaneTitle, _ := syscall.UTF16PtrFromString("")
|
|
|
|
|
app.settingsPaneHwnd, _, _ = createWindowEx.Call(0, uintptr(unsafe.Pointer(rightClass)), uintptr(unsafe.Pointer(settingsPaneTitle)), wsVisible|wsChild|wsClipChildren, 370, 60, 330, 260, hwnd, idLog+1, hInstance, 0)
|
|
|
|
|
app.btnActiveWindow, _, _ = createWindowEx.Call(0, uintptr(unsafe.Pointer(buttonClass)), uintptr(unsafe.Pointer(activeWindowTitle)), wsVisible|wsChild, 390, 90, 130, 30, hwnd, 3004, hInstance, 0)
|
|
|
|
|
app.btnTopMost, _, _ = createWindowEx.Call(0, uintptr(unsafe.Pointer(buttonClass)), uintptr(unsafe.Pointer(topMostTitle)), wsVisible|wsChild, 390, 130, 120, 30, hwnd, 3005, hInstance, 0)
|
|
|
|
|
app.btnFontDown, _, _ = createWindowEx.Call(0, uintptr(unsafe.Pointer(buttonClass)), uintptr(unsafe.Pointer(fontDownTitle)), wsVisible|wsChild, 390, 175, 50, 30, hwnd, 3006, hInstance, 0)
|
|
|
|
|
app.btnFontUp, _, _ = createWindowEx.Call(0, uintptr(unsafe.Pointer(buttonClass)), uintptr(unsafe.Pointer(fontUpTitle)), wsVisible|wsChild, 445, 175, 50, 30, hwnd, 3007, hInstance, 0)
|
|
|
|
|
app.btnTopMost, _, _ = createWindowEx.Call(0, uintptr(unsafe.Pointer(buttonClass)), uintptr(unsafe.Pointer(topMostTitle)), wsVisible|wsChild, 390, 90, 120, 30, hwnd, 3005, hInstance, 0)
|
|
|
|
|
app.btnFontDown, _, _ = createWindowEx.Call(0, uintptr(unsafe.Pointer(buttonClass)), uintptr(unsafe.Pointer(fontDownTitle)), wsVisible|wsChild, 390, 135, 50, 30, hwnd, 3006, hInstance, 0)
|
|
|
|
|
app.btnFontUp, _, _ = createWindowEx.Call(0, uintptr(unsafe.Pointer(buttonClass)), uintptr(unsafe.Pointer(fontUpTitle)), wsVisible|wsChild, 445, 135, 50, 30, hwnd, 3007, hInstance, 0)
|
|
|
|
|
app.activeTab = "join"
|
|
|
|
|
app.activeWindow = true
|
|
|
|
|
app.lastActiveWindow = false
|
|
|
|
|
setTimer.Call(hwnd, timerRefresh, 2000, 0)
|
|
|
|
|
applyFont(&app)
|
|
|
|
|
refreshGUI(setWindowText, &app)
|
|
|
|
|
applyActiveWindow(hwnd, &app, showWindowProc, setForegroundWindow, bringWindowToTop, setWindowPosProc, getForegroundWindow, getSystemMetrics, getWindowRect, isWindowVisible, isIconic)
|
|
|
|
|
applyTopMost(hwnd, &app)
|
|
|
|
|
ensureWindowVisible("startup", hwnd, showWindowProc, setWindowPosProc, getWindowRect, isWindowVisible, isIconic, getSystemMetrics)
|
|
|
|
|
logWindowState("after-ensure", hwnd, getWindowRect, isWindowVisible, isIconic)
|
|
|
|
|
return 0
|
|
|
|
|
case wmCommandGUI:
|
|
|
|
|
needsFullRefresh := false
|
|
|
|
|
switch uint16(wParam & 0xffff) {
|
|
|
|
|
case 3001:
|
|
|
|
|
app.activeTab = "join"
|
|
|
|
|
needsFullRefresh = true
|
|
|
|
|
case 3002:
|
|
|
|
|
app.activeTab = "translate"
|
|
|
|
|
needsFullRefresh = true
|
|
|
|
|
case 3003:
|
|
|
|
|
app.activeTab = "settings"
|
|
|
|
|
case 3004:
|
|
|
|
|
if !allowRapidSettingsAction(&app) {
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
app.activeWindow = !app.activeWindow
|
|
|
|
|
saveGUISettings(&app)
|
|
|
|
|
applyActiveWindow(hwnd, &app, showWindowProc, setForegroundWindow, bringWindowToTop, setWindowPosProc, getForegroundWindow, getSystemMetrics, getWindowRect, isWindowVisible, isIconic)
|
|
|
|
|
case 3005:
|
|
|
|
|
if !allowRapidSettingsAction(&app) {
|
|
|
|
|
return 0
|
|
|
|
|
@@ -263,8 +249,13 @@ func runNativeGUI() error {
|
|
|
|
|
saveGUISettings(&app)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
refreshGUI(setWindowText, &app)
|
|
|
|
|
refreshSettingsControls(showWindowProc, setWindowPosProc, &app)
|
|
|
|
|
if needsFullRefresh {
|
|
|
|
|
refreshGUI(setWindowText, &app)
|
|
|
|
|
refreshSettingsControls(showWindowProc, setWindowPosProc, &app)
|
|
|
|
|
} else {
|
|
|
|
|
refreshCommandUI(setWindowText, &app)
|
|
|
|
|
refreshSettingsControls(showWindowProc, setWindowPosProc, &app)
|
|
|
|
|
}
|
|
|
|
|
return 0
|
|
|
|
|
case wmTimerGUI:
|
|
|
|
|
refreshGUI(setWindowText, &app)
|
|
|
|
|
@@ -364,14 +355,6 @@ func refreshGUI(setWindowText *syscall.LazyProc, app *guiApp) {
|
|
|
|
|
t, _ := syscall.UTF16PtrFromString(formatGuestPane("Join Log", current))
|
|
|
|
|
setWindowText.Call(app.leftHwnd, uintptr(unsafe.Pointer(t)))
|
|
|
|
|
}
|
|
|
|
|
if app.btnActiveWindow != 0 {
|
|
|
|
|
label := "Active window: OFF"
|
|
|
|
|
if app.activeWindow {
|
|
|
|
|
label = "Active window: ON"
|
|
|
|
|
}
|
|
|
|
|
t, _ := syscall.UTF16PtrFromString(label)
|
|
|
|
|
setWindowText.Call(app.btnActiveWindow, uintptr(unsafe.Pointer(t)))
|
|
|
|
|
}
|
|
|
|
|
if app.btnTopMost != 0 {
|
|
|
|
|
label := "Top most: OFF"
|
|
|
|
|
if app.topMost {
|
|
|
|
|
@@ -388,7 +371,7 @@ func refreshGUI(setWindowText *syscall.LazyProc, app *guiApp) {
|
|
|
|
|
showWindowProc.Call(app.settingsPaneHwnd, swShowControl)
|
|
|
|
|
setWindowPosProc.Call(app.settingsPaneHwnd, 0, 370, 60, 330, 260, swpVisibleFlags)
|
|
|
|
|
}
|
|
|
|
|
for _, hwnd := range []uintptr{app.btnActiveWindow, app.btnTopMost, app.btnFontDown, app.btnFontUp} {
|
|
|
|
|
for _, hwnd := range []uintptr{app.btnTopMost, app.btnFontDown, app.btnFontUp} {
|
|
|
|
|
if hwnd != 0 {
|
|
|
|
|
showWindowProc.Call(hwnd, swShowControl)
|
|
|
|
|
}
|
|
|
|
|
@@ -397,7 +380,6 @@ func refreshGUI(setWindowText *syscall.LazyProc, app *guiApp) {
|
|
|
|
|
}
|
|
|
|
|
if app.rightHwnd != 0 {
|
|
|
|
|
state := readRuntimeSnapshot()
|
|
|
|
|
state["active_window"] = app.activeWindow
|
|
|
|
|
state["top_most"] = app.topMost
|
|
|
|
|
state["font_size"] = app.fontSize
|
|
|
|
|
rightText := formatRightPane(app.activeTab, state, currentWorldLabel(), instanceCount)
|
|
|
|
|
@@ -408,13 +390,27 @@ func refreshGUI(setWindowText *syscall.LazyProc, app *guiApp) {
|
|
|
|
|
if app.settingsPaneHwnd != 0 {
|
|
|
|
|
showWindowProc.Call(app.settingsPaneHwnd, swHideControl)
|
|
|
|
|
}
|
|
|
|
|
for _, hwnd := range []uintptr{app.btnActiveWindow, app.btnTopMost, app.btnFontDown, app.btnFontUp} {
|
|
|
|
|
for _, hwnd := range []uintptr{app.btnTopMost, app.btnFontDown, app.btnFontUp} {
|
|
|
|
|
if hwnd != 0 {
|
|
|
|
|
showWindowProc.Call(hwnd, swHideControl)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func refreshCommandUI(setWindowText *syscall.LazyProc, app *guiApp) {
|
|
|
|
|
if app == nil || setWindowText == nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if app.btnTopMost != 0 {
|
|
|
|
|
label := "Top most: OFF"
|
|
|
|
|
if app.topMost {
|
|
|
|
|
label = "Top most: ON"
|
|
|
|
|
}
|
|
|
|
|
t, _ := syscall.UTF16PtrFromString(label)
|
|
|
|
|
setWindowText.Call(app.btnTopMost, uintptr(unsafe.Pointer(t)))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func refreshSettingsControls(showWindowProc, setWindowPosProc *syscall.LazyProc, app *guiApp) {
|
|
|
|
|
if showWindowProc == nil || setWindowPosProc == nil || app == nil {
|
|
|
|
|
return
|
|
|
|
|
@@ -424,7 +420,7 @@ func refreshSettingsControls(showWindowProc, setWindowPosProc *syscall.LazyProc,
|
|
|
|
|
showWindowProc.Call(app.settingsPaneHwnd, swShowControl)
|
|
|
|
|
setWindowPosProc.Call(app.settingsPaneHwnd, 0, 370, 60, 330, 260, swpVisibleFlags)
|
|
|
|
|
}
|
|
|
|
|
for _, hwnd := range []uintptr{app.btnActiveWindow, app.btnTopMost, app.btnFontDown, app.btnFontUp} {
|
|
|
|
|
for _, hwnd := range []uintptr{app.btnTopMost, app.btnFontDown, app.btnFontUp} {
|
|
|
|
|
if hwnd != 0 {
|
|
|
|
|
showWindowProc.Call(hwnd, swShowControl)
|
|
|
|
|
}
|
|
|
|
|
@@ -435,7 +431,7 @@ func refreshSettingsControls(showWindowProc, setWindowPosProc *syscall.LazyProc,
|
|
|
|
|
if app.settingsPaneHwnd != 0 {
|
|
|
|
|
showWindowProc.Call(app.settingsPaneHwnd, swHideControl)
|
|
|
|
|
}
|
|
|
|
|
for _, hwnd := range []uintptr{app.btnActiveWindow, app.btnTopMost, app.btnFontDown, app.btnFontUp} {
|
|
|
|
|
for _, hwnd := range []uintptr{app.btnTopMost, app.btnFontDown, app.btnFontUp} {
|
|
|
|
|
if hwnd != 0 {
|
|
|
|
|
showWindowProc.Call(hwnd, swHideControl)
|
|
|
|
|
}
|
|
|
|
|
@@ -443,24 +439,6 @@ func refreshSettingsControls(showWindowProc, setWindowPosProc *syscall.LazyProc,
|
|
|
|
|
guiLog("refreshSettingsControls hidden")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func applyActiveWindow(hwnd uintptr, app *guiApp, showWindowProc, setForegroundWindow, bringWindowToTop, setWindowPosProc, getForegroundWindow, getSystemMetrics, getWindowRect, isWindowVisible, isIconic *syscall.LazyProc) {
|
|
|
|
|
if hwnd == 0 || app.activeWindow == app.lastActiveWindow {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if app.activeWindow {
|
|
|
|
|
showWindowProc.Call(hwnd, swRestore)
|
|
|
|
|
setWindowPosProc.Call(hwnd, 0, 0, 0, 0, 0, swpShowFlags)
|
|
|
|
|
bringWindowToTop.Call(hwnd)
|
|
|
|
|
setForegroundWindow.Call(hwnd)
|
|
|
|
|
foreground, _, _ := getForegroundWindow.Call()
|
|
|
|
|
guiLog(fmt.Sprintf("applyActiveWindow on foreground=%d target=%d", foreground, hwnd))
|
|
|
|
|
ensureWindowVisible("active-window", hwnd, showWindowProc, setWindowPosProc, getWindowRect, isWindowVisible, isIconic, getSystemMetrics)
|
|
|
|
|
} else {
|
|
|
|
|
guiLog("applyActiveWindow off")
|
|
|
|
|
}
|
|
|
|
|
app.lastActiveWindow = app.activeWindow
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func ensureWindowVisible(stage string, hwnd uintptr, showWindowProc, setWindowPosProc, getWindowRect, isWindowVisible, isIconic, getSystemMetrics *syscall.LazyProc) {
|
|
|
|
|
if hwnd == 0 || getSystemMetrics == nil {
|
|
|
|
|
return
|
|
|
|
|
@@ -524,7 +502,7 @@ func applyTopMost(hwnd uintptr, app *guiApp) {
|
|
|
|
|
if app.topMost {
|
|
|
|
|
setWindowPosProc.Call(hwnd, hwndTopMostFlag, 0, 0, 0, 0, swpFlags)
|
|
|
|
|
} else {
|
|
|
|
|
setWindowPosProc.Call(hwnd, 0, 0, 0, 0, 0, swpFlags)
|
|
|
|
|
setWindowPosProc.Call(hwnd, hwndNotTopMostFlag, 0, 0, 0, 0, swpFlags)
|
|
|
|
|
}
|
|
|
|
|
app.lastTopMost = app.topMost
|
|
|
|
|
}
|
|
|
|
|
@@ -814,7 +792,6 @@ func formatGuestPane(title string, items []userState) string {
|
|
|
|
|
for _, item := range present {
|
|
|
|
|
b.WriteString("・ ")
|
|
|
|
|
b.WriteString(item.Name)
|
|
|
|
|
b.WriteString(" [present]")
|
|
|
|
|
if !item.LastJoin.IsZero() {
|
|
|
|
|
b.WriteString(" ")
|
|
|
|
|
b.WriteString(item.LastJoin.Format("15:04:05"))
|
|
|
|
|
@@ -835,7 +812,6 @@ func formatGuestPane(title string, items []userState) string {
|
|
|
|
|
for _, item := range left {
|
|
|
|
|
b.WriteString("・ ")
|
|
|
|
|
b.WriteString(item.Name)
|
|
|
|
|
b.WriteString(" [gray]")
|
|
|
|
|
if !item.LastLeave.IsZero() {
|
|
|
|
|
b.WriteString(" ")
|
|
|
|
|
b.WriteString(timeAgo(item.LastLeave))
|
|
|
|
|
@@ -873,7 +849,7 @@ func currentSelfName() string {
|
|
|
|
|
func loadGUISettings() config.GUIConfig {
|
|
|
|
|
cfg, err := config.Load("")
|
|
|
|
|
if err != nil || cfg == nil {
|
|
|
|
|
return config.GUIConfig{ActiveWindow: true, FontSize: 18}
|
|
|
|
|
return config.GUIConfig{TopMost: false, FontSize: 18}
|
|
|
|
|
}
|
|
|
|
|
if cfg.GUI.FontSize <= 0 {
|
|
|
|
|
cfg.GUI.FontSize = 18
|
|
|
|
|
@@ -886,9 +862,8 @@ func saveGUISettings(app *guiApp) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
guiCfg := config.GUIConfig{
|
|
|
|
|
ActiveWindow: app.activeWindow,
|
|
|
|
|
TopMost: app.topMost,
|
|
|
|
|
FontSize: app.fontSize,
|
|
|
|
|
TopMost: app.topMost,
|
|
|
|
|
FontSize: app.fontSize,
|
|
|
|
|
}
|
|
|
|
|
if err := config.SaveGUI("", guiCfg); err != nil {
|
|
|
|
|
guiLog("saveGUISettings failed: " + err.Error())
|
|
|
|
|
@@ -941,13 +916,7 @@ func formatRightPane(tab string, state map[string]any, worldLabel string, curren
|
|
|
|
|
b.WriteString("\r\nWorld: ")
|
|
|
|
|
b.WriteString(worldLabel)
|
|
|
|
|
case "settings":
|
|
|
|
|
b.WriteString("Active window: ")
|
|
|
|
|
if stateBool(state, "active_window") {
|
|
|
|
|
b.WriteString("ON")
|
|
|
|
|
} else {
|
|
|
|
|
b.WriteString("OFF")
|
|
|
|
|
}
|
|
|
|
|
b.WriteString("\r\nTop most: ")
|
|
|
|
|
b.WriteString("Top most: ")
|
|
|
|
|
if stateBool(state, "top_most") {
|
|
|
|
|
b.WriteString("ON")
|
|
|
|
|
} else {
|
|
|
|
|
|