event_xlib.c (13941B)
1 #include <stdio.h> 2 3 #include <X11/XKBlib.h> 4 #include <X11/extensions/XKBrules.h> 5 6 #include "util.h" 7 #include "memory.h" 8 #include "graphics.h" 9 #include "xlib_shared.h" 10 #include "config.h" 11 #include "clipboard_xlib.h" 12 13 #define TEXT_INITIAL_SIZE 128 14 15 static char *text = NULL; 16 static size_t text_alloc = 0; 17 static char *cur_kbd = NULL; 18 /* FIXME: support more buttons? 19 -> What even makes sense here? Mice can support a bunch 20 of buttons, but what is sensible here? Just adding 21 support for higher button numbers would cause problems 22 when adding other backends (e.g. SDL) that might do 23 things completely differently. */ 24 /* FIXME: support touch events? */ 25 /* times of last button press/release, 26 used to implement double/triple-click */ 27 static Time last_button_press[] = {0, 0, 0}; 28 static Time last_button_release[] = {0, 0, 0}; 29 /* positions of last press/release so double/triple-click is 30 only generated when the position is near enough */ 31 static struct point { 32 int x; 33 int y; 34 } press_pos[] = {{0, 0}, {0, 0}, {0, 0}}; 35 static struct point release_pos[] = {{0, 0}, {0, 0}, {0, 0}}; 36 /* stores whether the last button press already was 37 a double-click to decide if a triple-click should 38 be generated (same for release) */ 39 static int was_2press[] = {0, 0, 0}; 40 static int was_2release[] = {0, 0, 0}; 41 /* Used to store special next event - currently just 42 used to implement double/triple-click because the 43 actual double/triple-click/release event is 44 generated in addition to the regular press/release */ 45 static int next_event_valid = 0; 46 static ltk_button_event next_event; 47 48 static ltk_keysym get_keysym(KeySym sym); 49 50 void 51 ltk_events_cleanup(void) { 52 ltk_free(text); 53 ltk_free(cur_kbd); 54 cur_kbd = NULL; 55 text = NULL; 56 text_alloc = 0; 57 } 58 59 static ltk_button_type 60 get_button(unsigned int button) { 61 switch (button) { 62 case Button1: return LTK_BUTTONL; 63 case Button2: return LTK_BUTTONM; 64 case Button3: return LTK_BUTTONR; 65 default: return LTK_BUTTONL; /* FIXME: what to do here? */ 66 } 67 } 68 69 /* FIXME: properly implement modifiers - see SDL and GTK */ 70 static ltk_mod_type 71 get_modmask(unsigned int state) { 72 ltk_mod_type t = 0; 73 if (state & ControlMask) 74 t |= LTK_MOD_CTRL; 75 if (state & ShiftMask) 76 t |= LTK_MOD_SHIFT; 77 if (state & Mod1Mask) 78 t |= LTK_MOD_ALT; 79 if (state & Mod4Mask) 80 t |= LTK_MOD_SUPER; 81 return t; 82 } 83 #ifdef X_HAVE_UTF8_STRING 84 #define LOOKUP_STRING_FUNC Xutf8LookupString 85 #else 86 #define LOOKUP_STRING_FUNC XmbLookupString 87 #endif 88 89 static ltk_event 90 process_key(ltk_renderdata *renderdata, size_t lang_index, XEvent *event, ltk_event_type type) { 91 ltk_language_mapping *map = ltk_config_get_language_mapping(lang_index); 92 /* FIXME: see comment in keys.c in ledit repository */ 93 if (!text) { 94 text = ltk_malloc(TEXT_INITIAL_SIZE); 95 text_alloc = TEXT_INITIAL_SIZE; 96 } 97 unsigned int state = event->xkey.state; 98 event->xkey.state &= ~ControlMask; 99 KeySym sym; 100 int len = 0; 101 Status status; 102 if (renderdata->xic && type == LTK_KEYPRESS_EVENT) { 103 len = LOOKUP_STRING_FUNC(renderdata->xic, &event->xkey, text, text_alloc - 1, &sym, &status); 104 if (status == XBufferOverflow) { 105 text_alloc = ideal_array_size(text_alloc, len + 1); 106 text = ltk_realloc(text, text_alloc); 107 len = LOOKUP_STRING_FUNC(renderdata->xic, &event->xkey, text, text_alloc - 1, &sym, &status); 108 } 109 } else { 110 /* FIXME: anything equivalent to XBufferOverflow here? */ 111 len = XLookupString(&event->xkey, text, text_alloc - 1, &sym, NULL); 112 status = XLookupBoth; 113 } 114 text[len >= (int)text_alloc ? (int)text_alloc - 1 : len] = '\0'; 115 char *key_text = (status == XLookupChars || status == XLookupBoth) ? text : NULL; 116 char *mapped = key_text; 117 /* FIXME: BINARY SEARCH! */ 118 if (key_text && map) { 119 for (size_t i = 0; i < map->mappings_len; i++) { 120 if (!strcmp(key_text, map->mappings[i].from)) { 121 mapped = map->mappings[i].to; 122 break; 123 } 124 } 125 } 126 return (ltk_event){.key = { 127 .type = type, 128 .modmask = get_modmask(state), 129 .sym = (status == XLookupKeySym || status == XLookupBoth) ? get_keysym(sym) : LTK_KEY_NONE, 130 .text = key_text, 131 .mapped = mapped 132 }}; 133 } 134 135 void 136 ltk_generate_keyboard_event(ltk_renderdata *renderdata, ltk_event *event) { 137 XkbStateRec s; 138 XkbGetState(renderdata->dpy, XkbUseCoreKbd, &s); 139 XkbDescPtr desc = XkbGetKeyboard( 140 renderdata->dpy, XkbAllComponentsMask, XkbUseCoreKbd 141 ); 142 char *group = XGetAtomName( 143 renderdata->dpy, desc->names->groups[s.group] 144 ); 145 ltk_free(cur_kbd); 146 /* just so the interface is the same for all events and the 147 caller doesn't have to free the contained string(s) */ 148 cur_kbd = ltk_strdup(group); 149 *event = (ltk_event){.keyboard = { 150 .type = LTK_KEYBOARDCHANGE_EVENT, 151 .new_kbd = cur_kbd 152 }}; 153 XFree(group); 154 XkbFreeKeyboard(desc, XkbAllComponentsMask, True); 155 } 156 157 #define DISTSQ(x0, y0, x1, y1) (((x1) - (x0)) * ((x1) - (x0)) + ((y1) - (y0)) * ((y1) - (y0))) 158 /* return value 0 means valid event returned, 159 1 means no events pending, 160 2 means event discarded (need to call again) */ 161 static int 162 next_event_base(ltk_renderdata *renderdata, ltk_clipboard *clip, size_t lang_index, ltk_event *event) { 163 if (next_event_valid) { 164 next_event_valid = 0; 165 *event = (ltk_event){.button = next_event}; 166 return 0; 167 } 168 XEvent xevent; 169 if (!XPending(renderdata->dpy)) 170 return 1; 171 XNextEvent(renderdata->dpy, &xevent); 172 if (renderdata->xkb_supported && xevent.type == renderdata->xkb_event_type) { 173 ltk_generate_keyboard_event(renderdata, event); 174 return 0; 175 } 176 *event = (ltk_event){.type = LTK_UNKNOWN_EVENT}; 177 if (XFilterEvent(&xevent, None)) 178 return 2; 179 if (clip && ltk_clipboard_filter_event(clip, &xevent)) 180 return 2; 181 int button = 0; 182 switch (xevent.type) { 183 case ButtonPress: 184 button = xevent.xbutton.button; 185 /* FIXME: are the buttons really always defined as exactly these values? */ 186 if (button >= 1 && button <= 3) { 187 if (xevent.xbutton.time - last_button_press[button] <= DOUBLECLICK_TIME && 188 DISTSQ(press_pos[button].x, press_pos[button].y, xevent.xbutton.x, xevent.xbutton.y) <= DOUBLECLICK_DISTSQ) { 189 if (was_2press[button]) { 190 /* reset so normal press is sent again next time */ 191 was_2press[button] = 0; 192 last_button_press[button] = 0; 193 next_event = (ltk_button_event){ 194 .type = LTK_3BUTTONPRESS_EVENT, 195 .button = get_button(button), 196 .x = xevent.xbutton.x, 197 .y = xevent.xbutton.y 198 }; 199 } else { 200 was_2press[button] = 1; 201 last_button_press[button] = xevent.xbutton.time; 202 next_event = (ltk_button_event){ 203 .type = LTK_2BUTTONPRESS_EVENT, 204 .button = get_button(button), 205 .x = xevent.xbutton.x, 206 .y = xevent.xbutton.y 207 }; 208 } 209 next_event_valid = 1; 210 } else { 211 last_button_press[button] = xevent.xbutton.time; 212 was_2press[button] = 0; 213 } 214 *event = (ltk_event){.button = { 215 .type = LTK_BUTTONPRESS_EVENT, 216 .button = get_button(button), 217 .x = xevent.xbutton.x, 218 .y = xevent.xbutton.y 219 }}; 220 press_pos[button].x = xevent.xbutton.x; 221 press_pos[button].y = xevent.xbutton.y; 222 } else if (button >= 4 && button <= 7) { 223 /* FIXME: compress multiple scroll events into one */ 224 *event = (ltk_event){.scroll = { 225 .type = LTK_SCROLL_EVENT, 226 .x = xevent.xbutton.x, 227 .y = xevent.xbutton.y, 228 .dx = 0, 229 .dy = 0 230 }}; 231 switch (button) { 232 case 4: 233 event->scroll.dy = 1; 234 break; 235 case 5: 236 event->scroll.dy = -1; 237 break; 238 case 6: 239 event->scroll.dx = -1; 240 break; 241 case 7: 242 event->scroll.dx = 1; 243 break; 244 } 245 } else { 246 return 2; 247 } 248 break; 249 case ButtonRelease: 250 button = xevent.xbutton.button; 251 if (button >= 1 && button <= 3) { 252 if (xevent.xbutton.time - last_button_release[button] <= DOUBLECLICK_TIME && 253 DISTSQ(release_pos[button].x, release_pos[button].y, xevent.xbutton.x, xevent.xbutton.y) <= DOUBLECLICK_DISTSQ) { 254 if (was_2release[button]) { 255 /* reset so normal release is sent again next time */ 256 was_2release[button] = 0; 257 last_button_release[button] = 0; 258 next_event = (ltk_button_event){ 259 .type = LTK_3BUTTONRELEASE_EVENT, 260 .button = get_button(button), 261 .x = xevent.xbutton.x, 262 .y = xevent.xbutton.y 263 }; 264 } else { 265 was_2release[button] = 1; 266 last_button_release[button] = xevent.xbutton.time; 267 next_event = (ltk_button_event){ 268 .type = LTK_2BUTTONRELEASE_EVENT, 269 .button = get_button(button), 270 .x = xevent.xbutton.x, 271 .y = xevent.xbutton.y 272 }; 273 } 274 next_event_valid = 1; 275 } else { 276 last_button_release[button] = xevent.xbutton.time; 277 was_2release[button] = 0; 278 } 279 *event = (ltk_event){.button = { 280 .type = LTK_BUTTONRELEASE_EVENT, 281 .button = get_button(button), 282 .x = xevent.xbutton.x, 283 .y = xevent.xbutton.y 284 }}; 285 release_pos[button].x = xevent.xbutton.x; 286 release_pos[button].y = xevent.xbutton.y; 287 } else { 288 return 2; 289 } 290 break; 291 case MotionNotify: 292 /* FIXME: compress motion events */ 293 *event = (ltk_event){.motion = { 294 .type = LTK_MOTION_EVENT, 295 .x = xevent.xmotion.x, 296 .y = xevent.xmotion.y 297 }}; 298 break; 299 case KeyPress: 300 *event = process_key(renderdata, lang_index, &xevent, LTK_KEYPRESS_EVENT); 301 break; 302 case KeyRelease: 303 *event = process_key(renderdata, lang_index, &xevent, LTK_KEYRELEASE_EVENT); 304 break; 305 case ConfigureNotify: 306 *event = (ltk_event){.configure = { 307 .type = LTK_CONFIGURE_EVENT, 308 .x = xevent.xconfigure.x, 309 .y = xevent.xconfigure.y, 310 .w = xevent.xconfigure.width, 311 .h = xevent.xconfigure.height 312 }}; 313 break; 314 case Expose: 315 /* FIXME: ignoring all of these events would make it not 316 work anymore if the actual rectangle wasn't ignored 317 later anyways */ 318 if (xevent.xexpose.count == 0) { 319 *event = (ltk_event){.expose = { 320 .type = LTK_EXPOSE_EVENT, 321 .x = xevent.xexpose.x, 322 .y = xevent.xexpose.y, 323 .w = xevent.xexpose.width, 324 .h = xevent.xexpose.height 325 }}; 326 } else { 327 return 2; 328 } 329 break; 330 case ClientMessage: 331 if ((Atom)xevent.xclient.data.l[0] == renderdata->wm_delete_msg) 332 *event = (ltk_event){.type = LTK_WINDOWCLOSE_EVENT}; 333 else 334 return 2; 335 break; 336 default: 337 break; 338 } 339 return 0; 340 } 341 342 int 343 ltk_next_event(ltk_renderdata *renderdata, ltk_clipboard *clip, size_t lang_index, ltk_event *event) { 344 int ret = 0; 345 while ((ret = next_event_base(renderdata, clip, lang_index, event)) == 2) { 346 /* NOP */ 347 } 348 return ret; 349 } 350 351 /* FIXME: pre-sort array (current sorting is taken from config.c but doesn't make sense here) */ 352 /* FIXME: can some structure of the X keysyms be abused to make this more efficient */ 353 static struct keysym_mapping { 354 KeySym xkeysym; 355 ltk_keysym keysym; 356 } keysym_map[] = { 357 {XK_BackSpace, LTK_KEY_BACKSPACE}, 358 {XK_Begin, LTK_KEY_BEGIN}, 359 {XK_Break, LTK_KEY_BREAK}, 360 {XK_Cancel, LTK_KEY_CANCEL}, 361 {XK_Clear, LTK_KEY_CLEAR}, 362 {XK_Delete, LTK_KEY_DELETE}, 363 {XK_Down, LTK_KEY_DOWN}, 364 {XK_End, LTK_KEY_END}, 365 {XK_Escape, LTK_KEY_ESCAPE}, 366 {XK_Execute, LTK_KEY_EXECUTE}, 367 368 {XK_F1, LTK_KEY_F1}, 369 {XK_F10, LTK_KEY_F10}, 370 {XK_F11, LTK_KEY_F11}, 371 {XK_F12, LTK_KEY_F12}, 372 {XK_F2, LTK_KEY_F2}, 373 {XK_F3, LTK_KEY_F3}, 374 {XK_F4, LTK_KEY_F4}, 375 {XK_F5, LTK_KEY_F5}, 376 {XK_F6, LTK_KEY_F6}, 377 {XK_F7, LTK_KEY_F7}, 378 {XK_F8, LTK_KEY_F8}, 379 {XK_F9, LTK_KEY_F9}, 380 381 {XK_Find, LTK_KEY_FIND}, 382 {XK_Help, LTK_KEY_HELP}, 383 {XK_Home, LTK_KEY_HOME}, 384 {XK_Insert, LTK_KEY_INSERT}, 385 386 {XK_KP_0, LTK_KEY_KP_0}, 387 {XK_KP_1, LTK_KEY_KP_1}, 388 {XK_KP_2, LTK_KEY_KP_2}, 389 {XK_KP_3, LTK_KEY_KP_3}, 390 {XK_KP_4, LTK_KEY_KP_4}, 391 {XK_KP_5, LTK_KEY_KP_5}, 392 {XK_KP_6, LTK_KEY_KP_6}, 393 {XK_KP_7, LTK_KEY_KP_7}, 394 {XK_KP_8, LTK_KEY_KP_8}, 395 {XK_KP_9, LTK_KEY_KP_9}, 396 {XK_KP_Add, LTK_KEY_KP_ADD}, 397 {XK_KP_Begin, LTK_KEY_KP_BEGIN}, 398 {XK_KP_Decimal, LTK_KEY_KP_DECIMAL}, 399 {XK_KP_Delete, LTK_KEY_KP_DELETE}, 400 {XK_KP_Divide, LTK_KEY_KP_DIVIDE}, 401 {XK_KP_Down, LTK_KEY_KP_DOWN}, 402 {XK_KP_End, LTK_KEY_KP_END}, 403 {XK_KP_Enter, LTK_KEY_KP_ENTER}, 404 {XK_KP_Equal, LTK_KEY_KP_EQUAL}, 405 {XK_KP_Home, LTK_KEY_KP_HOME}, 406 {XK_KP_Insert, LTK_KEY_KP_INSERT}, 407 {XK_KP_Left, LTK_KEY_KP_LEFT}, 408 {XK_KP_Multiply, LTK_KEY_KP_MULTIPLY}, 409 {XK_KP_Next, LTK_KEY_KP_NEXT}, 410 {XK_KP_Page_Down, LTK_KEY_KP_PAGE_DOWN}, 411 {XK_KP_Page_Up, LTK_KEY_KP_PAGE_UP}, 412 {XK_KP_Prior, LTK_KEY_KP_PRIOR}, 413 {XK_KP_Right, LTK_KEY_KP_RIGHT}, 414 {XK_KP_Separator, LTK_KEY_KP_SEPARATOR}, 415 {XK_KP_Space, LTK_KEY_KP_SPACE}, 416 {XK_KP_Subtract, LTK_KEY_KP_SUBTRACT}, 417 {XK_KP_Tab, LTK_KEY_KP_TAB}, 418 {XK_KP_Up, LTK_KEY_KP_UP}, 419 420 {XK_Left, LTK_KEY_LEFT}, 421 {XK_Linefeed, LTK_KEY_LINEFEED}, 422 {XK_Menu, LTK_KEY_MENU}, 423 {XK_Mode_switch, LTK_KEY_MODE_SWITCH}, 424 {XK_Next, LTK_KEY_NEXT}, 425 {XK_Num_Lock, LTK_KEY_NUM_LOCK}, 426 {XK_Page_Down, LTK_KEY_PAGE_DOWN}, 427 {XK_Page_Up, LTK_KEY_PAGE_UP}, 428 {XK_Pause, LTK_KEY_PAUSE}, 429 {XK_Print, LTK_KEY_PRINT}, 430 {XK_Prior, LTK_KEY_PRIOR}, 431 432 {XK_Redo, LTK_KEY_REDO}, 433 {XK_Return, LTK_KEY_RETURN}, 434 {XK_Right, LTK_KEY_RIGHT}, 435 {XK_script_switch, LTK_KEY_SCRIPT_SWITCH}, 436 {XK_Scroll_Lock, LTK_KEY_SCROLL_LOCK}, 437 {XK_Select, LTK_KEY_SELECT}, 438 {XK_space, LTK_KEY_SPACE}, 439 {XK_Sys_Req, LTK_KEY_SYS_REQ}, 440 {XK_Tab, LTK_KEY_TAB}, 441 {XK_Up, LTK_KEY_UP}, 442 {XK_Undo, LTK_KEY_UNDO}, 443 }; 444 445 static int keysym_map_sorted = 0; 446 static int 447 keysym_map_search_helper(const void *keyv, const void *entryv) { 448 KeySym sym = *((KeySym*)keyv); 449 struct keysym_mapping *m = (struct keysym_mapping *)entryv; 450 /* FIXME: branchless compare version? */ 451 return (sym < m->xkeysym) ? -1 : (sym > m->xkeysym); 452 } 453 static int 454 keysym_map_sort_helper(const void *entry1v, const void *entry2v) { 455 struct keysym_mapping *m1 = (struct keysym_mapping *)entry1v; 456 struct keysym_mapping *m2 = (struct keysym_mapping *)entry2v; 457 return (m1->xkeysym < m2->xkeysym) ? -1 : (m1->xkeysym > m2->xkeysym); 458 } 459 460 static ltk_keysym 461 get_keysym(KeySym sym) { 462 if (!keysym_map_sorted) { 463 qsort( 464 keysym_map, LENGTH(keysym_map), 465 sizeof(keysym_map[0]), &keysym_map_sort_helper 466 ); 467 keysym_map_sorted = 1; 468 } 469 struct keysym_mapping *m = bsearch( 470 &sym, keysym_map, LENGTH(keysym_map), 471 sizeof(keysym_map[0]), &keysym_map_search_helper 472 ); 473 return m ? m->keysym : LTK_KEY_NONE; 474 }