Skip to content

Commit

Permalink
chore: try to support multi-monitor
Browse files Browse the repository at this point in the history
  • Loading branch information
amorphobia committed Nov 20, 2023
1 parent d37bd27 commit c6198a1
Show file tree
Hide file tree
Showing 5 changed files with 337 additions and 9 deletions.
2 changes: 1 addition & 1 deletion Lib/RabbitCommon.ahk
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,6 @@ OnMessage(context_object, session_id, message_type, message_value) {
TrayTip(msg_type . ": " . msg_value . " (" . session_id . ")", RABBIT_IME_NAME)
}
} else {
; TrayTip(msg_type . ": " . msg_value . " (" . session_id . ")", RABBIT_IME_NAME)
TrayTip(msg_type . ": " . msg_value . " (" . session_id . ")", RABBIT_IME_NAME)
}
}
175 changes: 175 additions & 0 deletions Lib/RabbitMonitors.ahk
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
/*
* Copyright (c) 2023 Xuesong Peng <pengxuesong.cn@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

WCHAR_SIZE() {
return 2
}

global CCHDEVICENAME := 32
global MONITOR_DEFAULTTONULL := 0
global MONITOR_DEFAULTTOPRIMARY := 1
global MONITOR_DEFAULTTONEAREST := 2

class Point extends Class {
__New(x := 0, y := 0) {
this.buff := Buffer(Point.struct_size(), 0)
this.x := x
this.y := y
}

static x_offset := (*) => 0
static y_offset := (*) => Point.x_offset() + INT_SIZE()
static struct_size := (*) => Point.y_offset() + INT_SIZE()

struct_ptr := (*) => this.buff.Ptr

x {
get => NumGet(this.struct_ptr(), Point.x_offset(), "Int")
set => NumPut("Int", Value, this.struct_ptr(), Point.x_offset())
}
y {
get => NumGet(this.struct_ptr(), Point.y_offset(), "Int")
set => NumPut("Int", Value, this.struct_ptr(), Point.y_offset())
}
}

class Rect extends Class {
__New(left := 0, top := 0, right := 0, bottom := 0) {
this.buff := Buffer(Rect.struct_size(), 0)
this.left := left
this.top := top
this.right := right
this.bottom := bottom
}

static left_offset := (*) => 0
static top_offset := (*) => Rect.left_offset() + INT_SIZE()
static right_offset := (*) => Rect.top_offset() + INT_SIZE()
static bottom_offset := (*) => Rect.right_offset() + INT_SIZE()
static struct_size := (*) => Rect.bottom_offset() + INT_SIZE()

struct_ptr := (*) => this.buff.Ptr

left {
get => NumGet(this.struct_ptr(), Rect.left_offset(), "Int")
set => NumPut("Int", Value, this.struct_ptr(), Rect.left_offset())
}
top {
get => NumGet(this.struct_ptr(), Rect.top_offset(), "Int")
set => NumPut("Int", Value, this.struct_ptr(), Rect.top_offset())
}
right {
get => NumGet(this.struct_ptr(), Rect.right_offset(), "Int")
set => NumPut("Int", Value, this.struct_ptr(), Rect.right_offset())
}
bottom {
get => NumGet(this.struct_ptr(), Rect.bottom_offset(), "Int")
set => NumPut("Int", Value, this.struct_ptr(), Rect.bottom_offset())
}

width() {
return this.right - this.left
}
height() {
return this.bottom - this.top
}
} ; Rect

class MonitorInfo extends Class {
__New() {
this.buff := Buffer(MonitorInfo.struct_size(), 0)
NumPut("Int", MonitorInfo.struct_size(), this.struct_ptr())
}

static size_offset := (*) => 0
static monitor_offset := (*) => MonitorInfo.size_offset() + INT_SIZE()
static work_offset := (*) => MonitorInfo.monitor_offset() + Rect.struct_size()
static flags_offset := (*) => MonitorInfo.work_offset() + Rect.struct_size()
static struct_size := (*) => MonitorInfo.flags_offset() + INT_SIZE()

struct_ptr := (*) => this.buff.Ptr

size {
get => NumGet(this.struct_ptr(), MonitorInfo.size_offset(), "Int")
}
monitor {
get => Rect(
NumGet(this.struct_ptr(), MonitorInfo.monitor_offset(), "Int"),
NumGet(this.struct_ptr(), MonitorInfo.monitor_offset() + INT_SIZE(), "Int"),
NumGet(this.struct_ptr(), MonitorInfo.monitor_offset() + INT_SIZE() * 2, "Int"),
NumGet(this.struct_ptr(), MonitorInfo.monitor_offset() + INT_SIZE() * 3, "Int")
)
}
work {
get => Rect(
NumGet(this.struct_ptr(), MonitorInfo.work_offset(), "Int"),
NumGet(this.struct_ptr(), MonitorInfo.work_offset() + INT_SIZE(), "Int"),
NumGet(this.struct_ptr(), MonitorInfo.work_offset() + INT_SIZE() * 2, "Int"),
NumGet(this.struct_ptr(), MonitorInfo.work_offset() + INT_SIZE() * 3, "Int")
)
}
flags {
get => NumGet(this.struct_ptr(), MonitorInfo.flags_offset(), "Int")
}
} ; MonitorInfo

class MonitorInfoEx extends MonitorInfo {
__New() {
this.buff := Buffer(MonitorInfoEx.struct_size(), 0)
NumPut("Int", MonitorInfoEx.struct_size(), this.struct_ptr())
}

static device_offset := (*) => MonitorInfoEx.flags_offset() + INT_SIZE()
static struct_size := (*) => MonitorInfoEx.device_offset() + WCHAR_SIZE() * CCHDEVICENAME

device {
get => StrGet(this.struct_ptr() + MonitorInfoEx.device_offset(), CCHDEVICENAME)
}
} ; MonitorInfoEx

class MonitorManage extends Class {
static monitors := Map()

static MonitorEnumProc(hMon, hDC, rect, data) {
MonitorManage.monitors[hMon] := MonitorManage.GetMonitorInfo(hMon)
return true
}

static EnumDisplayMonitors() {
MonitorManage.monitors := Map()
return DllCall("EnumDisplayMonitors", "Ptr", 0, "Ptr", 0, "Ptr", CallbackCreate(ObjBindMethod(MonitorManage, "MonitorEnumProc"), , 4), "Ptr", 0)
}

static MonitorFromWindow(hWnd, flags := MONITOR_DEFAULTTONULL) {
return DllCall("MonitorFromWindow", "Ptr", hWnd, "UInt", flags)
}

static MonitorFromPoint(point, flags := MONITOR_DEFAULTTONULL) {
return DllCall("MonitorFromPoint", "Int64", (point.x & 0xFFFFFFFF) | (point.y << 32), "UInt", flags)
}

static MonitorFromRect(rect, flags := MONITOR_DEFAULTTONULL) {
return DllCall("MonitorFromRect", "Ptr", rect.struct_ptr(), "UInt", flags)
}

static GetMonitorInfo(hMon) {
info := MonitorInfoEx()
res := DllCall("GetMonitorInfo", "Ptr", hMon, "Ptr", info.struct_ptr())
return res ? info : 0
}
} ; MonitorManage
12 changes: 10 additions & 2 deletions Lib/RabbitTrayMenu.ahk
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,18 @@

TraySetIcon("Rabbit.ico") ; https://www.freepik.com/icon/rabbit_4905239
A_TrayMenu.Delete()
A_TrayMenu.add("打开用户文件夹", (*) => Run(A_ScriptDir . "\Rime"))
A_TrayMenu.add("打开脚本文件夹", (*) => Run(A_ScriptDir))
; A_TrayMenu.add("输入法设定")
; A_TrayMenu.add("用户词典管理")
A_TrayMenu.add("用户资料同步", (*) => false)
A_TrayMenu.add()
A_TrayMenu.add("用户文件夹", (*) => Run(A_ScriptDir . "\Rime"))
A_TrayMenu.add("脚本文件夹", (*) => Run(A_ScriptDir))
A_TrayMenu.add()
A_TrayMenu.add("仓库主页", (*) => Run("https://github.com/amorphobia/rabbit"))
A_TrayMenu.add()
A_TrayMenu.add("重新部署", (*) => Reload())
A_TrayMenu.add("退出玉兔毫", (*) => ExitApp())

; A_TrayMenu.Disable("输入法设定")
; A_TrayMenu.Disable("用户词典管理")
A_TrayMenu.Disable("用户资料同步")
24 changes: 18 additions & 6 deletions Rabbit.ahk
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#Include <RabbitCandidateBox>
#Include <RabbitCaret>
#Include <RabbitTrayMenu>
#Include <RabbitMonitors>

global session_id := 0
global box := Gui()
Expand Down Expand Up @@ -236,8 +237,6 @@ ProcessKey(key, mask, this_hotkey) {
max_width := 150

if caret {
workspace_width := SysGet(16) ; SM_CXFULLSCREEN
workspace_height := SysGet(17) ; SM_CYFULLSCREEN
box["Preedit"].Value := preedit_text
box["Candidates"].Value := menu_text
box["Preedit"].Move(, , max_width)
Expand All @@ -246,10 +245,23 @@ ProcessKey(key, mask, this_hotkey) {
box.GetPos(, , &box_width, &box_height)
new_x := caret_x + caret_w
new_y := caret_y + caret_h + 4
if new_x + box_width > workspace_width
new_x := workspace_width - box_width
if new_y + box_height > workspace_height
new_y := caret_y - 4 - box_height

hWnd := WinExist("A")
hMon := MonitorManage.MonitorFromWindow(hWnd)
info := MonitorManage.GetMonitorInfo(hMon)
if info {
if new_x + box_width > info.work.right
new_x := info.work.right - box_width
if new_y + box_height > info.work.bottom
new_y := caret_y - 4 - box_height
} else {
workspace_width := SysGet(16) ; SM_CXFULLSCREEN
workspace_height := SysGet(17) ; SM_CYFULLSCREEN
if new_x + box_width > workspace_width
new_x := workspace_width - box_width
if new_y + box_height > workspace_height
new_y := caret_y - 4 - box_height
}
box.Show("AutoSize NA x" . new_x . " y" . new_y)
WinSetAlwaysOnTop(1, box)
} else {
Expand Down
133 changes: 133 additions & 0 deletions RabbitDeployer.ahk
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/*
* Copyright (c) 2023 Xuesong Peng <pengxuesong.cn@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#Requires AutoHotkey v2.0 32-bit
; #NoTrayIcon

#Include <RabbitCommon>
#Include <librime-ahk\rime_levers_api>

global rime
global ERROR_ALREADY_EXISTS := 183
global INVALID_FILE_ATTRIBUTES := -1
global FILE_ATTRIBUTE_DIRECTORY := 0x00000010

arg := A_Args.Length > 0 ? A_Args[1] : ""
RunDeployer(arg)

RunDeployer(command) {
conf := Configurator()
conf.Initialize()
deployment_scheduled := command == "deploy"
if deployment_scheduled
return conf.UpdateWorkspace()
dict_management := command == "dict"
if dict_management
return 0 ; return conf.DictManagement()
sync_user_dict := command == "sync"
if sync_user_dict
return conf.SyncUserData()
installing := command == "install"
return conf.Run(installing)
}

CreateFileIfNotExist(filename) {
user_data_dir := A_ScriptDir . "\Rime\"
if not InStr(DirExist(user_data_dir), "D")
DirCreate(user_data_dir)
filepath := user_data_dir . filename
if not InStr(FileExist(filepath), "N")
FileAppend("", filepath)
}

ConfigureSwitcher(levers, switcher_settings, reconfigured) {
if not levers.load_settings(switcher_settings)
return false
;
}

class Configurator extends Class {
__New() {
CreateFileIfNotExist("default.custom.yaml")
CreateFileIfNotExist("rabbit.custom.yaml")
}

Initialize() {
rabbit_traits := CreateTraits()
rime.setup(rabbit_traits)
rime.deployer_initialize(0)
}

Run(installing) {
levers := RimeLeversApi()
if not levers
return 1

switcher_settings := levers.switcher_settings_init()
skip_switcher_settings := installing && !levers.is_first_run(switcher_settings)

if installing
this.UpdateWorkspace()

return 0
}

UpdateWorkspace(report_errors := false) {
hMutex := DllCall("CreateMutex", "Ptr", 0, "Int", true, "Str", "RabbitDeployerMutex")
if not hMutex {
return 1
}
if DllCall("GetLastError") == ERROR_ALREADY_EXISTS {
DllCall("CloseHandle", "Ptr", hMutex)
return 1
}

{
rime.deploy()
; rime.deploy_config_file("rabbit.yaml", "config_version")
}

DllCall("CloseHandle", "Ptr", hMutex)

return 0
}

; DictManagement()

SyncUserData() {
hMutex := DllCall("CreateMutex", "Ptr", 0, "Int", true, "Str", "RabbitDeployerMutex")
if not hMutex {
return 1
}
if DllCall("GetLastError") == ERROR_ALREADY_EXISTS {
DllCall("CloseHandle", "Ptr", hMutex)
return 1
}

{
if not rime.sync_user_data() {
DllCall("CloseHandle", "Ptr", hMutex)
return 1
}
rime.join_maintenance_thread()
}

DllCall("CloseHandle", "Ptr", hMutex)

return 0
}
}

0 comments on commit c6198a1

Please sign in to comment.