1 
2 #include "X11Event.h"
3 
X11EventPoll(JNIEnv * env,jobject obj,Display * dpy,jlong javaObjectAtom,jlong windowDeleteAtom)4 void X11EventPoll(JNIEnv *env, jobject obj, Display *dpy, jlong javaObjectAtom, jlong windowDeleteAtom) {
5     Atom wm_delete_atom = (Atom)windowDeleteAtom;
6     int num_events = 100;
7     int autoRepeatModifiers = 0;
8 
9     if ( NULL == dpy ) {
10         return;
11     }
12 
13     // Periodically take a break
14     while( num_events > 0 ) {
15         JavaWindow *w = NULL;
16         XEvent evt;
17         KeySym keySym = 0;
18         jint modifiers = 0;
19         char keyChar = 0;
20         char text[255];
21 
22         // XEventsQueued(dpy, X):
23         //   QueuedAlready    == XQLength(): No I/O Flush or system call  doesn't work on some cards (eg ATI) ?)
24         //   QueuedAfterFlush == XPending(): I/O Flush only if no already queued events are available
25         //   QueuedAfterReading            : QueuedAlready + if queue==0, attempt to read more ..
26         // if ( 0 >= XPending(dpy) )
27         if ( 0 >= XEventsQueued(dpy, QueuedAfterFlush) )
28         {
29             // DBG_PRINT( "X11: DispatchMessages 0x%X - Leave 1\n", dpy);
30             return;
31         }
32 
33         XNextEvent(dpy, &evt);
34         num_events--;
35 
36         if(dpy!=evt.xany.display) {
37             NewtCommon_throwNewRuntimeException(env, "wrong display, bail out!");
38             return ;
39         }
40 
41         if( 0==evt.xany.window ) {
42             DBG_PRINT( "X11: DispatchMessages dpy %p, Event %d - Window NULL, ignoring\n", (void*)dpy, (int)evt.type);
43             continue;
44         }
45 
46         // DBG_PRINT( "X11: DispatchMessages dpy %p, win %p, Event %d\n", (void*)dpy, (void*)evt.xany.window, (int)evt.type);
47 
48         w = getJavaWindowProperty(env, dpy, evt.xany.window, javaObjectAtom,
49         #ifdef VERBOSE_ON
50                 True
51         #else
52                 False
53         #endif
54             );
55 
56         if(NULL==w) {
57             fprintf(stderr, "Warning: NEWT X11 DisplayDispatch %p, Couldn't handle event %d for X11 window %p\n", (void*)dpy, evt.type, (void*)evt.xany.window);
58             continue;
59         }
60 
61         switch(evt.type) {
62             case KeyRelease:
63                 if (XEventsQueued(dpy, QueuedAfterReading)) {
64                   XEvent nevt;
65                   XPeekEvent(dpy, &nevt);
66 
67                   if (nevt.type == KeyPress && nevt.xkey.time == evt.xkey.time &&
68                       nevt.xkey.keycode == evt.xkey.keycode)
69                   {
70                     autoRepeatModifiers |= EVENT_AUTOREPEAT_MASK;
71                   } else {
72                     autoRepeatModifiers &= ~EVENT_AUTOREPEAT_MASK;
73                   }
74                 }
75                 // fall through intended
76             case KeyPress:
77                 if(XLookupString(&evt.xkey,text,255,&keySym,0)==1) {
78                     KeySym lower_return = 0, upper_return = 0;
79                     keyChar=text[0];
80                     XConvertCase(keySym, &lower_return, &upper_return);
81                     // always return upper case, set modifier masks (SHIFT, ..)
82                     keySym = X11KeySym2NewtVKey(upper_return);
83                 } else {
84                     keyChar=0;
85                     keySym = X11KeySym2NewtVKey(keySym);
86                 }
87                 modifiers |= X11InputState2NewtModifiers(evt.xkey.state) | autoRepeatModifiers;
88                 break;
89 
90             case ButtonPress:
91             case ButtonRelease:
92             case MotionNotify:
93                 modifiers |= X11InputState2NewtModifiers(evt.xbutton.state);
94                 break;
95 
96             default:
97                 break;
98         }
99 
100         switch(evt.type) {
101             case ButtonPress:
102                 (*env)->CallVoidMethod(env, w->jwindow, requestFocusID, JNI_FALSE);
103                 #ifdef USE_SENDIO_DIRECT
104                 (*env)->CallVoidMethod(env, w->jwindow, sendMouseEventID, (jint) EVENT_MOUSE_PRESSED,
105                                       modifiers,
106                                       (jint) evt.xbutton.x, (jint) evt.xbutton.y, (jint) evt.xbutton.button, 0.0f /*rotation*/);
107                 #else
108                 (*env)->CallVoidMethod(env, w->jwindow, enqueueMouseEventID, JNI_FALSE, (jint) EVENT_MOUSE_PRESSED,
109                                       modifiers,
110                                       (jint) evt.xbutton.x, (jint) evt.xbutton.y, (jint) evt.xbutton.button, 0.0f /*rotation*/);
111                 #endif
112                 break;
113             case ButtonRelease:
114                 #ifdef USE_SENDIO_DIRECT
115                 (*env)->CallVoidMethod(env, w->jwindow, sendMouseEventID, (jint) EVENT_MOUSE_RELEASED,
116                                       modifiers,
117                                       (jint) evt.xbutton.x, (jint) evt.xbutton.y, (jint) evt.xbutton.button, 0.0f /*rotation*/);
118                 #else
119                 (*env)->CallVoidMethod(env, w->jwindow, enqueueMouseEventID, JNI_FALSE, (jint) EVENT_MOUSE_RELEASED,
120                                       modifiers,
121                                       (jint) evt.xbutton.x, (jint) evt.xbutton.y, (jint) evt.xbutton.button, 0.0f /*rotation*/);
122                 #endif
123                 break;
124             case MotionNotify:
125                 #ifdef USE_SENDIO_DIRECT
126                 (*env)->CallVoidMethod(env, w->jwindow, sendMouseEventID, (jint) EVENT_MOUSE_MOVED,
127                                       modifiers,
128                                       (jint) evt.xmotion.x, (jint) evt.xmotion.y, (jint) 0, 0.0f /*rotation*/);
129                 #else
130                 (*env)->CallVoidMethod(env, w->jwindow, enqueueMouseEventID, JNI_FALSE, (jint) EVENT_MOUSE_MOVED,
131                                       modifiers,
132                                       (jint) evt.xmotion.x, (jint) evt.xmotion.y, (jint) 0, 0.0f /*rotation*/);
133                 #endif
134                 break;
135             case EnterNotify:
136                 DBG_PRINT( "X11: event . EnterNotify call %p %d/%d\n", (void*)evt.xcrossing.window, evt.xcrossing.x, evt.xcrossing.y);
137                 #ifdef USE_SENDIO_DIRECT
138                 (*env)->CallVoidMethod(env, w->jwindow, sendMouseEventID, (jint) EVENT_MOUSE_ENTERED,
139                                       modifiers,
140                                       (jint) evt.xcrossing.x, (jint) evt.xcrossing.y, (jint) 0, 0.0f /*rotation*/);
141                 #else
142                 (*env)->CallVoidMethod(env, w->jwindow, enqueueMouseEventID, JNI_FALSE, (jint) EVENT_MOUSE_ENTERED,
143                                       modifiers,
144                                       (jint) evt.xcrossing.x, (jint) evt.xcrossing.y, (jint) 0, 0.0f /*rotation*/);
145                 #endif
146                 break;
147             case LeaveNotify:
148                 DBG_PRINT( "X11: event . LeaveNotify call %p %d/%d\n", (void*)evt.xcrossing.window, evt.xcrossing.x, evt.xcrossing.y);
149                 #ifdef USE_SENDIO_DIRECT
150                 (*env)->CallVoidMethod(env, w->jwindow, sendMouseEventID, (jint) EVENT_MOUSE_EXITED,
151                                       modifiers,
152                                       (jint) evt.xcrossing.x, (jint) evt.xcrossing.y, (jint) 0, 0.0f /*rotation*/);
153                 #else
154                 (*env)->CallVoidMethod(env, w->jwindow, enqueueMouseEventID, JNI_FALSE, (jint) EVENT_MOUSE_EXITED,
155                                       modifiers,
156                                       (jint) evt.xcrossing.x, (jint) evt.xcrossing.y, (jint) 0, 0.0f /*rotation*/);
157                 #endif
158                 break;
159             case KeyPress:
160                 #ifdef USE_SENDIO_DIRECT
161                 (*env)->CallVoidMethod(env, w->jwindow, sendKeyEventID, (jint) EVENT_KEY_PRESSED,
162                                       modifiers, keySym, (jchar) -1);
163                 #else
164                 (*env)->CallVoidMethod(env, w->jwindow, enqueueKeyEventID, JNI_FALSE, (jint) EVENT_KEY_PRESSED,
165                                       modifiers, keySym, (jchar) -1);
166                 #endif
167 
168                 break;
169             case KeyRelease:
170                 #ifdef USE_SENDIO_DIRECT
171                 (*env)->CallVoidMethod(env, w->jwindow, sendKeyEventID, (jint) EVENT_KEY_RELEASED,
172                                       modifiers, keySym, (jchar) -1);
173                 #else
174                 (*env)->CallVoidMethod(env, w->jwindow, enqueueKeyEventID, JNI_FALSE, (jint) EVENT_KEY_RELEASED,
175                                       modifiers, keySym, (jchar) -1);
176                 #endif
177 
178                 break;
179             case DestroyNotify:
180                 DBG_PRINT( "X11: event . DestroyNotify call %p, parent %p, child-event: %d\n",
181                     (void*)evt.xdestroywindow.window, (void*)evt.xdestroywindow.event, evt.xdestroywindow.window != evt.xdestroywindow.event);
182                 if ( evt.xdestroywindow.window == evt.xdestroywindow.event ) {
183                     // ignore child destroy notification
184                 }
185                 break;
186             case CreateNotify:
187                 DBG_PRINT( "X11: event . CreateNotify call %p, parent %p, child-event: 1\n",
188                     (void*)evt.xcreatewindow.window, (void*) evt.xcreatewindow.parent);
189                 break;
190             case ConfigureNotify:
191                 DBG_PRINT( "X11: event . ConfigureNotify call %p (parent %p, above %p) %d/%d %dx%d %d, child-event: %d\n",
192                             (void*)evt.xconfigure.window, (void*)evt.xconfigure.event, (void*)evt.xconfigure.above,
193                             evt.xconfigure.x, evt.xconfigure.y, evt.xconfigure.width, evt.xconfigure.height,
194                             evt.xconfigure.override_redirect, evt.xconfigure.window != evt.xconfigure.event);
195                 if ( evt.xconfigure.window == evt.xconfigure.event ) {
196                     // ignore child window change notification
197                     {
198                         // update insets
199                         int left, right, top, bottom;
200                         NewtWindows_updateInsets(env, w->jwindow, dpy, evt.xany.window, &left, &right, &top, &bottom);
201                     }
202                     (*env)->CallVoidMethod(env, w->jwindow, sizeChangedID, JNI_FALSE,
203                                             (jint) evt.xconfigure.width, (jint) evt.xconfigure.height, JNI_FALSE);
204                     (*env)->CallVoidMethod(env, w->jwindow, positionChangedID, JNI_FALSE,
205                                             (jint) evt.xconfigure.x, (jint) evt.xconfigure.y);
206                 }
207                 break;
208             case ClientMessage:
209                 if (evt.xclient.send_event==True && evt.xclient.data.l[0]==wm_delete_atom) { // windowDeleteAtom
210                     jboolean closed;
211                     DBG_PRINT( "X11: event . ClientMessage call %p type 0x%X ..\n",
212                         (void*)evt.xclient.window, (unsigned int)evt.xclient.message_type);
213                     closed = (*env)->CallBooleanMethod(env, w->jwindow, windowDestroyNotifyID, JNI_FALSE);
214                     DBG_PRINT( "X11: event . ClientMessage call %p type 0x%X, closed: %d\n",
215                         (void*)evt.xclient.window, (unsigned int)evt.xclient.message_type, (int)closed);
216                     // Called by Window.java: CloseWindow();
217                     num_events = 0; // end loop in case of destroyed display
218                 }
219                 break;
220 
221             case FocusIn:
222                 DBG_PRINT( "X11: event . FocusIn call %p\n", (void*)evt.xvisibility.window);
223                 (*env)->CallVoidMethod(env, w->jwindow, focusChangedID, JNI_FALSE, JNI_TRUE);
224                 break;
225 
226             case FocusOut:
227                 DBG_PRINT( "X11: event . FocusOut call %p\n", (void*)evt.xvisibility.window);
228                 (*env)->CallVoidMethod(env, w->jwindow, focusChangedID, JNI_FALSE, JNI_FALSE);
229                 break;
230 
231             case Expose:
232                 DBG_PRINT( "X11: event . Expose call %p %d/%d %dx%d count %d\n", (void*)evt.xexpose.window,
233                     evt.xexpose.x, evt.xexpose.y, evt.xexpose.width, evt.xexpose.height, evt.xexpose.count);
234 
235                 if (evt.xexpose.count == 0 && evt.xexpose.width > 0 && evt.xexpose.height > 0) {
236                     (*env)->CallVoidMethod(env, w->jwindow, windowRepaintID, JNI_FALSE,
237                         evt.xexpose.x, evt.xexpose.y, evt.xexpose.width, evt.xexpose.height);
238                 }
239                 break;
240 
241             case MapNotify:
242                 DBG_PRINT( "X11: event . MapNotify call Event %p, Window %p, override_redirect %d, child-event: %d\n",
243                     (void*)evt.xmap.event, (void*)evt.xmap.window, (int)evt.xmap.override_redirect,
244                     evt.xmap.event!=evt.xmap.window);
245                 if( evt.xmap.event == evt.xmap.window ) {
246                     // ignore child window notification
247                     {
248                         // update insets
249                         int left, right, top, bottom;
250                         NewtWindows_updateInsets(env, w->jwindow, dpy, evt.xany.window, &left, &right, &top, &bottom);
251                     }
252                     (*env)->CallVoidMethod(env, w->jwindow, visibleChangedID, JNI_FALSE, JNI_TRUE);
253                 }
254                 break;
255 
256             case UnmapNotify:
257                 DBG_PRINT( "X11: event . UnmapNotify call Event %p, Window %p, from_configure %d, child-event: %d\n",
258                     (void*)evt.xunmap.event, (void*)evt.xunmap.window, (int)evt.xunmap.from_configure,
259                     evt.xunmap.event!=evt.xunmap.window);
260                 if( evt.xunmap.event == evt.xunmap.window ) {
261                     // ignore child window notification
262                     (*env)->CallVoidMethod(env, w->jwindow, visibleChangedID, JNI_FALSE, JNI_FALSE);
263                 }
264                 break;
265 
266             case ReparentNotify:
267                 {
268                     jlong parentResult; // 0 if root, otherwise proper value
269                     Window winRoot, winTopParent;
270                     #ifdef VERBOSE_ON
271                         Window oldParentRoot, oldParentTopParent;
272                         Window parentRoot, parentTopParent;
273                         if( 0 == NewtWindows_getRootAndParent(dpy, evt.xreparent.event, &oldParentRoot, &oldParentTopParent) ) {
274                             oldParentRoot=0; oldParentTopParent = 0;
275                         }
276                         if( 0 == NewtWindows_getRootAndParent(dpy, evt.xreparent.parent, &parentRoot, &parentTopParent) ) {
277                             parentRoot=0; parentTopParent = 0;
278                         }
279                     #endif
280                     if( 0 == NewtWindows_getRootAndParent(dpy, evt.xreparent.window, &winRoot, &winTopParent) ) {
281                         winRoot=0; winTopParent = 0;
282                     }
283                     if(evt.xreparent.parent == winRoot) {
284                         parentResult = 0; // our java indicator for root window
285                     } else {
286                         parentResult = (jlong) (intptr_t) evt.xreparent.parent;
287                     }
288                     #ifdef VERBOSE_ON
289                         DBG_PRINT( "X11: event . ReparentNotify: call %d/%d OldParent %p (root %p, top %p), NewParent %p (root %p, top %p), Window %p (root %p, top %p)\n",
290                             evt.xreparent.x, evt.xreparent.y,
291                             (void*)evt.xreparent.event, (void*)oldParentRoot, (void*)oldParentTopParent,
292                             (void*)evt.xreparent.parent, (void*)parentRoot, (void*)parentTopParent,
293                             (void*)evt.xreparent.window, (void*)winRoot, (void*)winTopParent);
294                     #endif
295                     (*env)->CallVoidMethod(env, w->jwindow, reparentNotifyID, (jlong)evt.xreparent.parent);
296                 }
297                 break;
298 
299             // unhandled events .. yet ..
300 
301             default:
302                 DBG_PRINT("X11: event . unhandled %d 0x%X call %p\n", (int)evt.type, (unsigned int)evt.type, (void*)evt.xunmap.window);
303         }
304     }
305 }
306