diff --git a/key/keycode_c.h b/key/keycode_c.h index 13be2a9..75f7c73 100644 --- a/key/keycode_c.h +++ b/key/keycode_c.h @@ -62,74 +62,74 @@ struct XSpecialCharacterMapping XSpecialCharacterTable[] = { #endif -MMKeyCode keyCodeForChar(const char c) -{ -#if defined(IS_MACOSX) - /* OS X does not appear to have a built-in function for this, so instead we - * have to write our own. */ - static CFMutableDictionaryRef charToCodeDict = NULL; - CGKeyCode code; - UniChar character = c; - CFStringRef charStr = NULL; +MMKeyCode keyCodeForChar(const char c){ + #if defined(IS_MACOSX) + /* OS X does not appear to have a built-in function for this, so instead we + * have to write our own. */ + static CFMutableDictionaryRef charToCodeDict = NULL; + CGKeyCode code; + UniChar character = c; + CFStringRef charStr = NULL; - /* Generate table of keycodes and characters. */ - if (charToCodeDict == NULL) { - size_t i; - charToCodeDict = CFDictionaryCreateMutable(kCFAllocatorDefault, - 128, - &kCFCopyStringDictionaryKeyCallBacks, - NULL); - if (charToCodeDict == NULL) return UINT16_MAX; + /* Generate table of keycodes and characters. */ + if (charToCodeDict == NULL) { + size_t i; + charToCodeDict = CFDictionaryCreateMutable(kCFAllocatorDefault, + 128, + &kCFCopyStringDictionaryKeyCallBacks, + NULL); + if (charToCodeDict == NULL) return UINT16_MAX; - /* Loop through every keycode (0 - 127) to find its current mapping. */ - for (i = 0; i < 128; ++i) { - CFStringRef string = createStringForKey((CGKeyCode)i); - if (string != NULL) { - CFDictionaryAddValue(charToCodeDict, string, (const void *)i); - CFRelease(string); + /* Loop through every keycode (0 - 127) to find its current mapping. */ + for (i = 0; i < 128; ++i) { + CFStringRef string = createStringForKey((CGKeyCode)i); + if (string != NULL) { + CFDictionaryAddValue(charToCodeDict, string, (const void *)i); + CFRelease(string); + } } } - } - charStr = CFStringCreateWithCharacters(kCFAllocatorDefault, &character, 1); + charStr = CFStringCreateWithCharacters(kCFAllocatorDefault, &character, 1); - /* Our values may be NULL (0), so we need to use this function. */ - if (!CFDictionaryGetValueIfPresent(charToCodeDict, charStr, - (const void **)&code)) { - code = UINT16_MAX; /* Error */ - } + /* Our values may be NULL (0), so we need to use this function. */ + if (!CFDictionaryGetValueIfPresent(charToCodeDict, charStr, + (const void **)&code)) { + code = UINT16_MAX; /* Error */ + } - CFRelease(charStr); - return (MMKeyCode)code; -#elif defined(IS_WINDOWS) - return VkKeyScan(c); -#elif defined(USE_X11) - MMKeyCode code; + CFRelease(charStr); + return (MMKeyCode)code; + #elif defined(IS_WINDOWS) + return VkKeyScan(c); + #elif defined(USE_X11) + MMKeyCode code; - char buf[2]; - buf[0] = c; - buf[1] = '\0'; + char buf[2]; + buf[0] = c; + buf[1] = '\0'; - code = XStringToKeysym(buf); - if (code == NoSymbol) { - /* Some special keys are apparently not handled properly by - * XStringToKeysym() on some systems, so search for them instead in our - * mapping table. */ - size_t i; - const size_t specialCharacterCount = - sizeof(XSpecialCharacterTable) / sizeof(XSpecialCharacterTable[0]); - for (i = 0; i < specialCharacterCount; ++i) { - if (c == XSpecialCharacterTable[i].name) { - code = XSpecialCharacterTable[i].code; - break; + code = XStringToKeysym(buf); + if (code == NoSymbol) { + /* Some special keys are apparently not handled properly by + * XStringToKeysym() on some systems, so search for them instead in our + * mapping table. */ + size_t i; + const size_t specialCharacterCount = + sizeof(XSpecialCharacterTable) / sizeof(XSpecialCharacterTable[0]); + for (i = 0; i < specialCharacterCount; ++i) { + if (c == XSpecialCharacterTable[i].name) { + code = XSpecialCharacterTable[i].code; + break; + } } } - } - return code; -#endif + return code; + #endif } + #if defined(IS_MACOSX) CFStringRef createStringForKey(CGKeyCode keyCode){ diff --git a/key/keypress_c.h b/key/keypress_c.h index d44e7da..0a62afc 100644 --- a/key/keypress_c.h +++ b/key/keypress_c.h @@ -29,8 +29,7 @@ #endif #if defined(IS_MACOSX) -static io_connect_t _getAuxiliaryKeyDriver(void) -{ +static io_connect_t _getAuxiliaryKeyDriver(void){ static mach_port_t sEventDrvrRef = 0; mach_port_t masterPort, service, iter; kern_return_t kr; @@ -52,13 +51,11 @@ static io_connect_t _getAuxiliaryKeyDriver(void) #endif #if defined(IS_WINDOWS) -void win32KeyEvent(int key, MMKeyFlags flags) -{ +void win32KeyEvent(int key, MMKeyFlags flags){ int scan = MapVirtualKey(key & 0xff, MAPVK_VK_TO_VSC); /* Set the scan code for extended keys */ - switch (key) - { + switch (key){ case VK_RCONTROL: case VK_SNAPSHOT: /* Print Screen */ case VK_RMENU: /* Right Alt / Alt Gr */ @@ -106,8 +103,7 @@ void win32KeyEvent(int key, MMKeyFlags flags) } #endif -void toggleKeyCode(MMKeyCode code, const bool down, MMKeyFlags flags) -{ +void toggleKeyCode(MMKeyCode code, const bool down, MMKeyFlags flags){ #if defined(IS_MACOSX) /* The media keys all have 1000 added to them to help us detect them. */ if (code >= 1000) { @@ -156,14 +152,12 @@ void toggleKeyCode(MMKeyCode code, const bool down, MMKeyFlags flags) #endif } -void tapKeyCode(MMKeyCode code, MMKeyFlags flags) -{ +void tapKeyCode(MMKeyCode code, MMKeyFlags flags){ toggleKeyCode(code, true, flags); toggleKeyCode(code, false, flags); } -void toggleKey(char c, const bool down, MMKeyFlags flags) -{ +void toggleKey(char c, const bool down, MMKeyFlags flags){ MMKeyCode keyCode = keyCodeForChar(c); //Prevent unused variable warning for Mac and Linux. @@ -185,15 +179,13 @@ void toggleKey(char c, const bool down, MMKeyFlags flags) toggleKeyCode(keyCode, down, flags); } -void tapKey(char c, MMKeyFlags flags) -{ +void tapKey(char c, MMKeyFlags flags){ toggleKey(c, true, flags); toggleKey(c, false, flags); } #if defined(IS_MACOSX) -void toggleUnicode(UniChar ch, const bool down) -{ +void toggleUnicode(UniChar ch, const bool down){ /* This function relies on the convenient * CGEventKeyboardSetUnicodeString(), which allows us to not have to * convert characters to a keycode, but does not support adding modifier diff --git a/robotgo.go b/robotgo.go index 240d33d..1705b21 100644 --- a/robotgo.go +++ b/robotgo.go @@ -1398,6 +1398,12 @@ func GetPID() int32 { return int32(pid) } +// internalGetBounds get the window bounds +func internalGetBounds(pid int32, hwnd int) (int, int, int, int) { + bounds := C.get_bounds(C.uintptr(pid), C.uintptr(hwnd)) + return int(bounds.X), int(bounds.Y), int(bounds.W), int(bounds.H) +} + // Pids get the all process id func Pids() ([]int32, error) { var ret []int32 diff --git a/robotgo_mac_win.go b/robotgo_mac_win.go index 81c653d..3acc163 100644 --- a/robotgo_mac_win.go +++ b/robotgo_mac_win.go @@ -12,6 +12,16 @@ package robotgo +// GetBounds get the window bounds +func GetBounds(pid int32, args ...int) (int, int, int, int) { + var hwnd int + if len(args) > 0 { + hwnd = args[0] + } + + return internalGetBounds(pid, hwnd) +} + // ActivePID active the window by PID, // If args[0] > 0 on the Windows platform via a window handle to active func ActivePID(pid int32, args ...int) error { diff --git a/robotgo_unix.go b/robotgo_unix.go index 60fd26f..eadd56f 100644 --- a/robotgo_unix.go +++ b/robotgo_unix.go @@ -23,6 +23,24 @@ import ( var xu *xgbutil.XUtil +// GetBounds get the window bounds +func GetBounds(pid int32, args ...int) (int, int, int, int) { + var hwnd int + if len(args) > 0 { + hwnd = args[0] + + return internalGetBounds(pid, hwnd) + } + + xid, err := GetXId(xu, pid) + if err != nil { + log.Println("GetXidFromPid errors is: ", err) + return 0, 0, 0, 0 + } + + return internalGetBounds(int32(xid), hwnd) +} + // ActivePIDC active the window by PID, // If args[0] > 0 on the unix platform via a xid to active func ActivePIDC(pid int32, args ...int) { @@ -34,18 +52,9 @@ func ActivePIDC(pid int32, args ...int) { return } - if xu == nil { - var err error - xu, err = xgbutil.NewConn() - if err != nil { - log.Println("xgbutil.NewConn errors is: ", err) - return - } - } - - xid, err := getXidFromPid(xu, pid) + xid, err := GetXId(xu, pid) if err != nil { - log.Println("getXidFromPid errors is: ", err) + log.Println("GetXidFromPid errors is: ", err) return } @@ -73,7 +82,8 @@ func ActivePID(pid int32, args ...int) error { return nil } - xid, err := getXidFromPid(xu, pid) + // get xid from pid + xid, err := GetXidFromPid(xu, pid) if err != nil { return err } @@ -86,7 +96,23 @@ func ActivePID(pid int32, args ...int) error { return nil } -func getXidFromPid(xu *xgbutil.XUtil, pid int32) (xproto.Window, error) { +// GetXId get the xid +func GetXId(xu *xgbutil.XUtil, pid int32) (xproto.Window, error) { + if xu == nil { + var err error + xu, err = xgbutil.NewConn() + if err != nil { + // log.Println("xgbutil.NewConn errors is: ", err) + return 0, err + } + } + + xid, err := GetXidFromPid(xu, pid) + return xid, err +} + +// GetXidFromPid get the xide from pid +func GetXidFromPid(xu *xgbutil.XUtil, pid int32) (xproto.Window, error) { windows, err := ewmh.ClientListGet(xu) if err != nil { return 0, err @@ -102,5 +128,5 @@ func getXidFromPid(xu *xgbutil.XUtil, pid int32) (xproto.Window, error) { } } - return 0, errors.New("failed to find a window with a matching pid") + return 0, errors.New("failed to find a window with a matching pid.") } diff --git a/window/goWindow.h b/window/goWindow.h index 5c0d135..c70795b 100644 --- a/window/goWindow.h +++ b/window/goWindow.h @@ -11,6 +11,7 @@ #include "alert_c.h" #include "window.h" #include "win32.h" +#include "win_sys.h" int show_alert(const char *title, const char *msg, const char *defaultButton, const char *cancelButton){ diff --git a/window/pub.h b/window/pub.h index f3a2c0d..2cf99d2 100644 --- a/window/pub.h +++ b/window/pub.h @@ -26,6 +26,15 @@ typedef struct _MData MData; MData mData; +struct _Bounds{ + int32 X; // Top left X coordinate + int32 Y; // Top left Y coordinate + int32 W; // Total bounds width + int32 H; // Total bounds height +}; + +typedef struct _Bounds Bounds; + #if defined(IS_MACOSX) static Boolean(*gAXIsProcessTrustedWithOptions) (CFDictionaryRef); @@ -255,6 +264,30 @@ MData mData; } } + static Bounds GetFrame(MData win){ + Bounds frame; + // Retrieve frame bounds + if (WM_EXTENTS != None) { + long* result; uint32 nItems = 0; + // Get the window extents property + result = (long*) GetWindowProperty(win, WM_EXTENTS, &nItems); + + // Verify the results + if (result != NULL) { + if (nItems == 4) { + frame.X = (int32) result[0]; + frame.Y = (int32) result[2]; + frame.W = (int32) result[0] + (int32) result[1]; + frame.H = (int32) result[2] + (int32) result[3]; + } + + XFree(result); + } + } + + return frame; + } + #elif defined(IS_WINDOWS) // diff --git a/window/win_sys.h b/window/win_sys.h new file mode 100644 index 0000000..de4b020 --- /dev/null +++ b/window/win_sys.h @@ -0,0 +1,162 @@ +// #include "../base/os.h" +Bounds get_client(uintptr pid, uintptr isHwnd); + +Bounds get_bounds(uintptr pid, uintptr isHwnd){ + // Check if the window is valid + Bounds bounds; + if (!IsValid()) { return bounds; } + + #if defined(IS_MACOSX) + + // Bounds bounds; + AXValueRef axp = NULL; + AXValueRef axs = NULL; + AXUIElementRef AxID = AXUIElementCreateApplication(pid); + + // Determine the current point of the window + if (AXUIElementCopyAttributeValue(AxID, + kAXPositionAttribute, (CFTypeRef*) &axp) + != kAXErrorSuccess || axp == NULL){ + goto exit; + } + + // Determine the current size of the window + if (AXUIElementCopyAttributeValue(AxID, + kAXSizeAttribute, (CFTypeRef*) &axs) + != kAXErrorSuccess || axs == NULL){ + goto exit; + } + + CGPoint p; CGSize s; + // Attempt to convert both values into atomic types + if (AXValueGetValue(axp, kAXValueCGPointType, &p) && + AXValueGetValue(axs, kAXValueCGSizeType, &s)){ + bounds.X = p.x; + bounds.Y = p.y; + bounds.W = s.width; + bounds.H = s.height; + } + + exit: + if (axp != NULL) { CFRelease(axp); } + if (axs != NULL) { CFRelease(axs); } + + return bounds; + + #elif defined(USE_X11) + + // Ignore X errors + XDismissErrors(); + MData win; + win.XWin = (Window)pid; + + Bounds client = get_client(pid, isHwnd); + Bounds frame = GetFrame(win); + + bounds.X = client.X - frame.X; + bounds.Y = client.Y - frame.Y; + bounds.W = client.W + frame.W; + bounds.H = client.H + frame.H; + + return bounds; + + #elif defined(IS_WINDOWS) + HWND hwnd; + if (isHwnd == 0) { + hwnd= GetHwndByPId(pid); + } else { + hwnd = (HWND)pid; + } + + RECT rect = { 0 }; + GetWindowRect(hwnd, &rect); + + bounds.X = rect.left; + bounds.Y = rect.top; + bounds.W = rect.right - rect.left; + bounds.H = rect.bottom - rect.top; + + return bounds; + + #endif +} + +Bounds get_client(uintptr pid, uintptr isHwnd){ + // Check if the window is valid + Bounds bounds; + if (!IsValid()) { return bounds; } + + #if defined(IS_MACOSX) + + return get_bounds(pid, isHwnd); + + #elif defined(USE_X11) + + Display *rDisplay = XOpenDisplay(NULL); + + // Ignore X errors + XDismissErrors(); + MData win; + win.XWin = (Window)pid; + + // Property variables + Window root, parent; + Window* children; + unsigned int count; + int32 x = 0, y = 0; + + // Check if the window is the root + XQueryTree(rDisplay, win.XWin, + &root, &parent, &children, &count); + if (children) { XFree(children); } + + // Retrieve window attributes + XWindowAttributes attr = { 0 }; + XGetWindowAttributes(rDisplay, win.XWin, &attr); + + // Coordinates must be translated + if (parent != attr.root){ + XTranslateCoordinates(rDisplay, win.XWin, attr.root, attr.x, + attr.y, &x, &y, &parent); + } + // Coordinates can be left alone + else { + x = attr.x; + y = attr.y; + } + + // Return resulting window bounds + bounds.X = x; + bounds.Y = y; + bounds.W = attr.width; + bounds.H = attr.height; + return bounds; + + #elif defined(IS_WINDOWS) + HWND hwnd; + if (isHwnd == 0) { + hwnd= GetHwndByPId(pid); + } else { + hwnd = (HWND)pid; + } + + + RECT rect = { 0 }; + GetClientRect(hwnd, &rect); + + POINT point; + point.x = rect.left; + point.y = rect.top; + + // Convert the client point to screen + ClientToScreen(hwnd, &point); + + bounds.X = point.x; + bounds.Y = point.y; + bounds.W = rect.right - rect.left; + bounds.H = rect.bottom - rect.top; + + return bounds; + + #endif +} \ No newline at end of file diff --git a/window/window.h b/window/window.h index 7ebfc58..a7569ca 100644 --- a/window/window.h +++ b/window/window.h @@ -170,8 +170,8 @@ bool setHandle(uintptr handle){ mData.AxID = 0; if (handle == 0){ - return 0; - // return true; + // return 0; + return true; } // Retrieve the window element @@ -275,7 +275,7 @@ bool IsMinimized(void){ // Ignore X errors // XDismissErrors(); - // return GetState (mData.XWin, STATE_MINIMIZE); + // return GetState(mData.XWin, STATE_MINIMIZE); #elif defined(IS_WINDOWS) @@ -298,7 +298,7 @@ bool IsMaximized(void){ // Ignore X errors // XDismissErrors(); - // return GetState (mData.XWin, STATE_MAXIMIZE); + // return GetState(mData.XWin, STATE_MAXIMIZE); #elif defined(IS_WINDOWS) @@ -447,7 +447,7 @@ MData GetActive(void){ // Get the current active window result.XWin = XDefaultRootWindow(rDisplay); - void* active = GetWindowProperty(result,WM_ACTIVE,NULL); + void* active = GetWindowProperty(result, WM_ACTIVE, NULL); // Check result value if (active != NULL) { @@ -607,8 +607,9 @@ char *GetTitle(){ #elif defined(IS_WINDOWS) - return GetWindowText - (mData.HWnd, mData.Title, 512) > 0 ? mData.Title : ""; + return GetWindowText( + mData.HWnd, mData.Title, 512) > 0 ? + mData.Title : ""; // return GetWindowText // (mData.HWnd, name, 512) > 0 ? // _UTF8Encode(name) : "null"; @@ -624,10 +625,9 @@ int32 WGetPID(void){ pid_t pid = 0; // Attempt to retrieve the window pid - if (AXUIElementGetPid(mData.AxID, &pid) - == kAXErrorSuccess) { - return pid; - } + if (AXUIElementGetPid(mData.AxID, &pid)== kAXErrorSuccess) { + return pid; + } return 0;