diff options
| author | Po Lu | 2022-02-07 16:22:06 +0800 |
|---|---|---|
| committer | Po Lu | 2022-02-07 17:15:53 +0800 |
| commit | b432fb6c86b922bf1e8bfa8ae59e0dc80cb37eb0 (patch) | |
| tree | ebabf964e58c30ac3fd33fe35275b2121a3ef559 /src | |
| parent | dfda7d14631c0c2225317b1c2e0317e2f1ba2630 (diff) | |
| download | emacs-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.c | 122 | ||||
| -rw-r--r-- | src/xterm.c | 2 | ||||
| -rw-r--r-- | src/xterm.h | 4 |
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 (©.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.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 | ||
| 701 | static struct xi_device_t * | 701 | struct xi_device_t * |
| 702 | xi_device_from_id (struct x_display_info *dpyinfo, int deviceid) | 702 | xi_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); | |||
| 1444 | extern struct input_event xg_pending_quit_event; | 1444 | extern struct input_event xg_pending_quit_event; |
| 1445 | #endif | 1445 | #endif |
| 1446 | 1446 | ||
| 1447 | #ifdef HAVE_XINPUT2 | ||
| 1448 | struct 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) |