66
77//go:build linux
88
9- #include <stdint.h>
10- #include <stdio.h>
119#include <X11/Xlib.h>
1210#include <X11/Xutil.h>
11+ #include <stdint.h>
12+ #include <string.h> // memset
1313
1414extern void hotkeyDown (uintptr_t hkhandle );
1515extern void hotkeyUp (uintptr_t hkhandle );
1616
1717int displayTest () {
18- Display * d = NULL ;
19- for (int i = 0 ; i < 42 ; i ++ ) {
20- d = XOpenDisplay (0 );
21- if (d == NULL ) continue ;
22- break ;
23- }
24- if (d == NULL ) {
25- return -1 ;
26- }
27- return 0 ;
18+ Display * d = NULL ;
19+ for (int i = 0 ; i < 42 ; i ++ ) {
20+ d = XOpenDisplay (0 );
21+ if (d == NULL )
22+ continue ;
23+ break ;
24+ }
25+ if (d == NULL ) {
26+ return -1 ;
27+ }
28+ return 0 ;
2829}
2930
3031// FIXME: handle bad access properly.
@@ -38,48 +39,95 @@ int displayTest() {
3839// pErr->minor_code );
3940// if( pErr->request_code == 33 ){ // 33 (X_GrabKey)
4041// if( pErr->error_code == BadAccess ){
41- // printf("ERROR: key combination already grabbed by another client.\n");
42- // return 0;
42+ // printf("ERROR: key combination already grabbed by another
43+ // client.\n"); return 0;
4344// }
4445// }
4546// return 0;
4647// }
4748
48- // waitHotkey blocks until the hotkey is triggered.
49- // this function crashes the program if the hotkey already grabbed by others.
49+ Display * openDisplay () {
50+ Display * d = NULL ;
51+ for (int i = 0 ; i < 42 ; i ++ ) {
52+ d = XOpenDisplay (0 );
53+ if (d == NULL )
54+ continue ;
55+ break ;
56+ }
57+ return d ;
58+ }
59+
60+ // Creates an invisible window, which can receive ClientMessage events. On
61+ // hotkey cancel a ClientMessageEvent is generated on the window. The event is
62+ // catched and the event loop terminates. x: 0 y: 0 w: 1 h: 1 border_width: 1
63+ // depth: 0
64+ // class: InputOnly (window will not be drawn)
65+ // visual: default visual of display
66+ // no attributes will be set (0, &attr)
67+ Window createInvisWindow (Display * d ) {
68+ XSetWindowAttributes attr ;
69+ return XCreateWindow (d , DefaultRootWindow (d ), 0 , 0 , 1 , 1 , 0 , 0 , InputOnly ,
70+ DefaultVisual (d , 0 ), 0 , & attr );
71+ }
72+
73+ // Sends a custom ClientMessage of type (Atom) "go_hotkey_cancel_hotkey"
74+ // Passed value 'True' of XInternAtom creates the Atom, if it does not exist yet
75+ void sendCancel (Display * d , Window window ) {
76+ Atom atom = XInternAtom (d , "golangdesign_hotkey_cancel_hotkey" , True );
77+ XClientMessageEvent clientEvent ;
78+ memset (& clientEvent , 0 , sizeof (clientEvent ));
79+ clientEvent .type = ClientMessage ;
80+ clientEvent .send_event = True ;
81+ clientEvent .display = d ;
82+ clientEvent .window = window ;
83+ clientEvent .message_type = atom ;
84+ clientEvent .format = 8 ;
85+
86+ XEvent event ;
87+ event .type = ClientMessage ;
88+ event .xclient = clientEvent ;
89+ XSendEvent (d , window , False , 0 , & event );
90+ XFlush (d );
91+ }
92+
93+ // Closes the connection and destroys the invisible 'cancel' window
94+ void cleanupConnection (Display * d , Window w ) {
95+ XDestroyWindow (d , w );
96+ XCloseDisplay (d );
97+ }
98+
99+ // waitHotkey blocks until the hotkey is triggered or canceled.
50100//
51101// mods points to nmods modifier masks: the same hotkey is grabbed once per
52102// mask so that it still fires while NumLock/CapsLock are active (those locks
53103// add bits to the event state that an exact-mask grab would not match).
54- int waitHotkey (uintptr_t hkhandle , unsigned int * mods , int nmods , int key ) {
55- Display * d = NULL ;
56- for (int i = 0 ; i < 42 ; i ++ ) {
57- d = XOpenDisplay (0 );
58- if (d == NULL ) continue ;
59- break ;
60- }
61- if (d == NULL ) {
62- return -1 ;
63- }
64- int keycode = XKeysymToKeycode (d , key );
65- for (int i = 0 ; i < nmods ; i ++ ) {
66- XGrabKey (d , keycode , mods [i ], DefaultRootWindow (d ), False , GrabModeAsync , GrabModeAsync );
67- }
68- XSelectInput (d , DefaultRootWindow (d ), KeyPressMask );
69- XEvent ev ;
70- while (1 ) {
71- XNextEvent (d , & ev );
72- switch (ev .type ) {
73- case KeyPress :
74- hotkeyDown (hkhandle );
75- continue ;
76- case KeyRelease :
77- hotkeyUp (hkhandle );
78- for (int i = 0 ; i < nmods ; i ++ ) {
79- XUngrabKey (d , keycode , mods [i ], DefaultRootWindow (d ));
80- }
81- XCloseDisplay (d );
82- return 0 ;
83- }
84- }
85- }
104+ //
105+ // d is an owned display connection and w an invisible window on it; sending w
106+ // a ClientMessage (see sendCancel) breaks the loop out of XNextEvent so an
107+ // unregister can take effect without waiting for the next keypress.
108+ int waitHotkey (uintptr_t hkhandle , unsigned int * mods , int nmods , int key ,
109+ Display * d , Window w ) {
110+ int keycode = XKeysymToKeycode (d , key );
111+ for (int i = 0 ; i < nmods ; i ++ ) {
112+ XGrabKey (d , keycode , mods [i ], DefaultRootWindow (d ), False , GrabModeAsync ,
113+ GrabModeAsync );
114+ }
115+ XSelectInput (d , DefaultRootWindow (d ), KeyPressMask );
116+ XEvent ev ;
117+ while (1 ) {
118+ XNextEvent (d , & ev );
119+ switch (ev .type ) {
120+ case KeyPress :
121+ hotkeyDown (hkhandle );
122+ continue ;
123+ case KeyRelease :
124+ hotkeyUp (hkhandle );
125+ for (int i = 0 ; i < nmods ; i ++ ) {
126+ XUngrabKey (d , keycode , mods [i ], DefaultRootWindow (d ));
127+ }
128+ return 0 ;
129+ case ClientMessage :
130+ return 0 ;
131+ }
132+ }
133+ }
0 commit comments