aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPo Lu2022-02-07 16:22:06 +0800
committerPo Lu2022-02-07 17:15:53 +0800
commitb432fb6c86b922bf1e8bfa8ae59e0dc80cb37eb0 (patch)
treeebabf964e58c30ac3fd33fe35275b2121a3ef559 /src
parentdfda7d14631c0c2225317b1c2e0317e2f1ba2630 (diff)
downloademacs-b432fb6c86b922bf1e8bfa8ae59e0dc80cb37eb0.tar.gz
emacs-b432fb6c86b922bf1e8bfa8ae59e0dc80cb37eb0.zip
Make menus work better on X toolkit builds with XInput 2
* src/xmenu.c (popup_get_selection): Translate some important XI2 events into events the toolkit can understand. (x_activate_menubar): (create_and_show_popup_menu): Clear grab regardless of reported status on Motif. * src/xterm.c (xi_device_from_id): Export function. * src/xterm.h: Update prototypes.
Diffstat (limited to 'src')
-rw-r--r--src/xmenu.c122
-rw-r--r--src/xterm.c2
-rw-r--r--src/xterm.h4
3 files changed, 124 insertions, 4 deletions
diff --git a/src/xmenu.c b/src/xmenu.c
index 9e4e6b62fce..745a80ade1b 100644
--- a/src/xmenu.c
+++ b/src/xmenu.c
@@ -52,6 +52,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
52#endif 52#endif
53 53
54#ifdef HAVE_XINPUT2 54#ifdef HAVE_XINPUT2
55#include <math.h>
55#include <X11/extensions/XInput2.h> 56#include <X11/extensions/XInput2.h>
56#endif 57#endif
57 58
@@ -240,18 +241,25 @@ popup_get_selection (XEvent *initial_event, struct x_display_info *dpyinfo,
240 LWLIB_ID id, bool do_timers) 241 LWLIB_ID id, bool do_timers)
241{ 242{
242 XEvent event; 243 XEvent event;
244 XEvent copy;
245#ifdef HAVE_XINPUT2
246 bool cookie_claimed_p = false;
247 XIDeviceEvent *xev;
248 struct xi_device_t *device;
249#endif
243 250
244 while (popup_activated_flag) 251 while (popup_activated_flag)
245 { 252 {
246 if (initial_event) 253 if (initial_event)
247 { 254 {
248 event = *initial_event; 255 copy = event = *initial_event;
249 initial_event = 0; 256 initial_event = 0;
250 } 257 }
251 else 258 else
252 { 259 {
253 if (do_timers) x_menu_wait_for_event (0); 260 if (do_timers) x_menu_wait_for_event (0);
254 XtAppNextEvent (Xt_app_con, &event); 261 XtAppNextEvent (Xt_app_con, &event);
262 copy = event;
255 } 263 }
256 264
257 /* Make sure we don't consider buttons grabbed after menu goes. 265 /* Make sure we don't consider buttons grabbed after menu goes.
@@ -271,6 +279,7 @@ popup_get_selection (XEvent *initial_event, struct x_display_info *dpyinfo,
271 so Motif thinks this is the case. */ 279 so Motif thinks this is the case. */
272 event.xbutton.state = 0; 280 event.xbutton.state = 0;
273#endif 281#endif
282 copy = event;
274 } 283 }
275 /* Pop down on C-g and Escape. */ 284 /* Pop down on C-g and Escape. */
276 else if (event.type == KeyPress 285 else if (event.type == KeyPress
@@ -281,9 +290,110 @@ popup_get_selection (XEvent *initial_event, struct x_display_info *dpyinfo,
281 if ((keysym == XK_g && (event.xkey.state & ControlMask) != 0) 290 if ((keysym == XK_g && (event.xkey.state & ControlMask) != 0)
282 || keysym == XK_Escape) /* Any escape, ignore modifiers. */ 291 || keysym == XK_Escape) /* Any escape, ignore modifiers. */
283 popup_activated_flag = 0; 292 popup_activated_flag = 0;
293
294 copy = event;
284 } 295 }
296#ifdef HAVE_XINPUT2
297 else if (event.type == GenericEvent
298 && dpyinfo->supports_xi2
299 && event.xgeneric.display == dpyinfo->display
300 && event.xgeneric.extension == dpyinfo->xi2_opcode)
301 {
302 if (!event.xcookie.data
303 && XGetEventData (dpyinfo->display, &event.xcookie))
304 cookie_claimed_p = true;
305
306 if (event.xcookie.data)
307 {
308 switch (event.xgeneric.evtype)
309 {
310 case XI_ButtonRelease:
311 {
312 xev = (XIDeviceEvent *) event.xcookie.data;
313 device = xi_device_from_id (dpyinfo, xev->deviceid);
314
315 dpyinfo->grabbed &= ~(1 << xev->detail);
316 device->grab &= ~(1 << xev->detail);
317
318 copy.xbutton.type = ButtonRelease;
319 copy.xbutton.serial = xev->serial;
320 copy.xbutton.send_event = xev->send_event;
321 copy.xbutton.display = xev->display;
322 copy.xbutton.window = xev->event;
323 copy.xbutton.root = xev->root;
324 copy.xbutton.subwindow = xev->child;
325 copy.xbutton.time = xev->time;
326 copy.xbutton.x = lrint (xev->event_x);
327 copy.xbutton.y = lrint (xev->event_y);
328 copy.xbutton.x_root = lrint (xev->root_x);
329 copy.xbutton.y_root = lrint (xev->root_y);
330 copy.xbutton.state = xev->mods.effective;
331 copy.xbutton.button = xev->detail;
332 copy.xbutton.same_screen = True;
333
334#ifdef USE_MOTIF /* Pretending that the event came from a
335 Btn1Down seems the only way to convince Motif to
336 activate its callbacks; setting the XmNmenuPost
337 isn't working. --marcus@sysc.pdx.edu. */
338 copy.xbutton.button = 1;
339 /* Motif only pops down menus when no Ctrl, Alt or Mod
340 key is pressed and the button is released. So reset key state
341 so Motif thinks this is the case. */
342 copy.xbutton.state = 0;
343#endif
344
345 if (xev->buttons.mask_len)
346 {
347 if (XIMaskIsSet (xev->buttons.mask, 1))
348 copy.xbutton.state |= Button1Mask;
349 if (XIMaskIsSet (xev->buttons.mask, 2))
350 copy.xbutton.state |= Button2Mask;
351 if (XIMaskIsSet (xev->buttons.mask, 3))
352 copy.xbutton.state |= Button3Mask;
353 }
354
355 break;
356 }
357 case XI_KeyPress:
358 {
359 KeySym keysym;
360
361 xev = (XIDeviceEvent *) event.xcookie.data;
362
363 copy.xkey.type = KeyPress;
364 copy.xkey.serial = xev->serial;
365 copy.xkey.send_event = xev->send_event;
366 copy.xkey.display = xev->display;
367 copy.xkey.window = xev->event;
368 copy.xkey.root = xev->root;
369 copy.xkey.subwindow = xev->child;
370 copy.xkey.time = xev->time;
371 copy.xkey.x = lrint (xev->event_x);
372 copy.xkey.y = lrint (xev->event_y);
373 copy.xkey.x_root = lrint (xev->root_x);
374 copy.xkey.y_root = lrint (xev->root_y);
375 copy.xkey.state = xev->mods.effective;
376 copy.xkey.keycode = xev->detail;
377 copy.xkey.same_screen = True;
378
379 keysym = XLookupKeysym (&copy.xkey, 0);
380
381 if ((keysym == XK_g
382 && (copy.xkey.state & ControlMask) != 0)
383 || keysym == XK_Escape) /* Any escape, ignore modifiers. */
384 popup_activated_flag = 0;
385
386 break;
387 }
388 }
389 }
390 }
285 391
286 x_dispatch_event (&event, event.xany.display); 392 if (cookie_claimed_p)
393 XFreeEventData (dpyinfo->display, &event.xcookie);
394#endif
395
396 x_dispatch_event (&copy, copy.xany.display);
287 } 397 }
288} 398}
289 399
@@ -458,7 +568,9 @@ x_activate_menubar (struct frame *f)
458 { 568 {
459 for (int i = 0; i < dpyinfo->num_devices; ++i) 569 for (int i = 0; i < dpyinfo->num_devices; ++i)
460 { 570 {
571#ifndef USE_MOTIF
461 if (dpyinfo->devices[i].grab) 572 if (dpyinfo->devices[i].grab)
573#endif
462 XIUngrabDevice (dpyinfo->display, dpyinfo->devices[i].device_id, 574 XIUngrabDevice (dpyinfo->display, dpyinfo->devices[i].device_id,
463 CurrentTime); 575 CurrentTime);
464 } 576 }
@@ -1465,7 +1577,8 @@ create_and_show_popup_menu (struct frame *f, widget_value *first_wv,
1465 /* Don't allow any geometry request from the user. */ 1577 /* Don't allow any geometry request from the user. */
1466 XtSetArg (av[ac], (char *) XtNgeometry, 0); ac++; 1578 XtSetArg (av[ac], (char *) XtNgeometry, 0); ac++;
1467 XtSetValues (menu, av, ac); 1579 XtSetValues (menu, av, ac);
1468#if defined HAVE_XINPUT2 && defined USE_LUCID 1580
1581#if defined HAVE_XINPUT2 && defined USE_X_TOOLKIT
1469 struct x_display_info *dpyinfo = FRAME_DISPLAY_INFO (f); 1582 struct x_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
1470 /* Clear the XI2 grab so lwlib can set a core grab. */ 1583 /* Clear the XI2 grab so lwlib can set a core grab. */
1471 1584
@@ -1473,12 +1586,15 @@ create_and_show_popup_menu (struct frame *f, widget_value *first_wv,
1473 { 1586 {
1474 for (int i = 0; i < dpyinfo->num_devices; ++i) 1587 for (int i = 0; i < dpyinfo->num_devices; ++i)
1475 { 1588 {
1589#ifndef USE_MOTIF
1476 if (dpyinfo->devices[i].grab) 1590 if (dpyinfo->devices[i].grab)
1591#endif
1477 XIUngrabDevice (dpyinfo->display, dpyinfo->devices[i].device_id, 1592 XIUngrabDevice (dpyinfo->display, dpyinfo->devices[i].device_id,
1478 CurrentTime); 1593 CurrentTime);
1479 } 1594 }
1480 } 1595 }
1481#endif 1596#endif
1597
1482 /* Display the menu. */ 1598 /* Display the menu. */
1483 lw_popup_menu (menu, &dummy); 1599 lw_popup_menu (menu, &dummy);
1484 popup_activated_flag = 1; 1600 popup_activated_flag = 1;
diff --git a/src/xterm.c b/src/xterm.c
index 49fc2b1bb74..940ee347d5a 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -698,7 +698,7 @@ x_get_scroll_valuator_delta (struct x_display_info *dpyinfo, int device_id,
698 return DBL_MAX; 698 return DBL_MAX;
699} 699}
700 700
701static struct xi_device_t * 701struct xi_device_t *
702xi_device_from_id (struct x_display_info *dpyinfo, int deviceid) 702xi_device_from_id (struct x_display_info *dpyinfo, int deviceid)
703{ 703{
704 for (int i = 0; i < dpyinfo->num_devices; ++i) 704 for (int i = 0; i < dpyinfo->num_devices; ++i)
diff --git a/src/xterm.h b/src/xterm.h
index d3678054a82..63956fd6434 100644
--- a/src/xterm.h
+++ b/src/xterm.h
@@ -1444,6 +1444,10 @@ extern void x_session_close (void);
1444extern struct input_event xg_pending_quit_event; 1444extern struct input_event xg_pending_quit_event;
1445#endif 1445#endif
1446 1446
1447#ifdef HAVE_XINPUT2
1448struct xi_device_t *xi_device_from_id (struct x_display_info *, int);
1449#endif
1450
1447/* Is the frame embedded into another application? */ 1451/* Is the frame embedded into another application? */
1448 1452
1449#define FRAME_X_EMBEDDED_P(f) (FRAME_X_OUTPUT(f)->explicit_parent != 0) 1453#define FRAME_X_EMBEDDED_P(f) (FRAME_X_OUTPUT(f)->explicit_parent != 0)