input.c (17784B)
1 #define _POSIX_C_SOURCE 200811L 2 #include <signal.h> 3 #include <stdlib.h> 4 #include <sys/wait.h> 5 #include <linux/input-event-codes.h> 6 #include <wlr/types/wlr_cursor.h> 7 #include <wlr/types/wlr_seat.h> 8 #include <wlr/types/wlr_input_device.h> 9 #include <wlr/types/wlr_keyboard.h> 10 #include <wlr/types/wlr_pointer.h> 11 #include <wlr/util/log.h> 12 #include <xkbcommon/xkbcommon.h> 13 #include <unistd.h> 14 #include "server.h" 15 #include "view.h" 16 17 static void keyboard_handle_modifiers( 18 struct wl_listener *listener, void *data) { 19 struct wio_keyboard *keyboard = 20 wl_container_of(listener, keyboard, modifiers); 21 wlr_seat_set_keyboard(keyboard->server->seat, keyboard->device); 22 wlr_seat_keyboard_notify_modifiers(keyboard->server->seat, 23 &keyboard->device->keyboard->modifiers); 24 } 25 26 static void view_end_interactive(struct wio_server *server); 27 static void keyboard_handle_key( 28 struct wl_listener *listener, void *data) { 29 struct wio_keyboard *keyboard = 30 wl_container_of(listener, keyboard, key); 31 struct wio_server *server = keyboard->server; 32 struct wlr_event_keyboard_key *event = data; 33 struct wlr_seat *seat = server->seat; 34 xkb_keycode_t keycode = event->keycode + 8; 35 const xkb_keysym_t *syms; 36 int nsyms = xkb_state_key_get_syms( 37 keyboard->device->keyboard->xkb_state, 38 keycode, &syms); 39 40 for (int i = 0; i < nsyms; i++) { 41 if (syms[i] == XKB_KEY_Escape) { 42 switch (server->input_state) { 43 case INPUT_STATE_NONE: 44 case INPUT_STATE_MENU: 45 break; 46 default: 47 view_end_interactive(server); 48 return; 49 } 50 } 51 } 52 53 wlr_seat_set_keyboard(seat, keyboard->device); 54 wlr_seat_keyboard_notify_key(seat, event->time_msec, 55 event->keycode, event->state); 56 } 57 58 static void server_new_keyboard( 59 struct wio_server *server, struct wlr_input_device *device) { 60 struct wio_keyboard *keyboard = calloc(1, sizeof(struct wio_keyboard)); 61 keyboard->server = server; 62 keyboard->device = device; 63 64 struct xkb_rule_names rules = { 0 }; 65 rules.rules = getenv("XKB_DEFAULT_RULES"); 66 rules.model = getenv("XKB_DEFAULT_MODEL"); 67 rules.layout = getenv("XKB_DEFAULT_LAYOUT"); 68 rules.variant = getenv("XKB_DEFAULT_VARIANT"); 69 rules.options = getenv("XKB_DEFAULT_OPTIONS"); 70 struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); 71 struct xkb_keymap *keymap = xkb_map_new_from_names(context, &rules, 72 XKB_KEYMAP_COMPILE_NO_FLAGS); 73 74 wlr_keyboard_set_keymap(device->keyboard, keymap); 75 xkb_keymap_unref(keymap); 76 xkb_context_unref(context); 77 wlr_keyboard_set_repeat_info(device->keyboard, 25, 600); 78 79 keyboard->modifiers.notify = keyboard_handle_modifiers; 80 wl_signal_add(&device->keyboard->events.modifiers, &keyboard->modifiers); 81 keyboard->key.notify = keyboard_handle_key; 82 wl_signal_add(&device->keyboard->events.key, &keyboard->key); 83 84 wlr_seat_set_keyboard(server->seat, device); 85 wl_list_insert(&server->keyboards, &keyboard->link); 86 } 87 88 static void server_new_pointer( 89 struct wio_server *server, struct wlr_input_device *device) { 90 wlr_cursor_attach_input_device(server->cursor, device); 91 } 92 93 void server_new_input(struct wl_listener *listener, void *data) { 94 struct wio_server *server = wl_container_of(listener, server, new_input); 95 struct wlr_input_device *device = data; 96 switch (device->type) { 97 case WLR_INPUT_DEVICE_KEYBOARD: 98 server_new_keyboard(server, device); 99 break; 100 case WLR_INPUT_DEVICE_POINTER: 101 server_new_pointer(server, device); 102 break; 103 default: 104 break; 105 } 106 uint32_t caps = WL_SEAT_CAPABILITY_POINTER; 107 if (!wl_list_empty(&server->keyboards)) { 108 caps |= WL_SEAT_CAPABILITY_KEYBOARD; 109 } 110 wlr_seat_set_capabilities(server->seat, caps); 111 } 112 113 static void process_cursor_motion(struct wio_server *server, uint32_t time) { 114 double sx, sy; 115 int view_area; 116 struct wlr_seat *seat = server->seat; 117 struct wlr_surface *surface = NULL; 118 struct wio_view *view = wio_view_at( 119 server, server->cursor->x, server->cursor->y, &surface, &sx, &sy, 120 &view_area); 121 if (!view) { 122 switch (server->input_state) { 123 case INPUT_STATE_MOVE_SELECT: 124 case INPUT_STATE_RESIZE_SELECT: 125 case INPUT_STATE_DELETE_SELECT: 126 case INPUT_STATE_HIDE_SELECT: 127 wlr_xcursor_manager_set_cursor_image(server->cursor_mgr, 128 "hand1", server->cursor); 129 break; 130 case INPUT_STATE_MOVE: 131 wlr_xcursor_manager_set_cursor_image(server->cursor_mgr, 132 "grabbing", server->cursor); 133 break; 134 case INPUT_STATE_RESIZE_START: 135 case INPUT_STATE_NEW_START: 136 wlr_xcursor_manager_set_cursor_image(server->cursor_mgr, 137 "top_left_corner", server->cursor); 138 break; 139 case INPUT_STATE_BORDER_DRAG_TOP: 140 wlr_xcursor_manager_set_cursor_image(server->cursor_mgr, 141 "top_side", server->cursor); 142 break; 143 case INPUT_STATE_BORDER_DRAG_RIGHT: 144 wlr_xcursor_manager_set_cursor_image(server->cursor_mgr, 145 "right_side", server->cursor); 146 break; 147 case INPUT_STATE_BORDER_DRAG_BOTTOM: 148 wlr_xcursor_manager_set_cursor_image(server->cursor_mgr, 149 "bottom_side", server->cursor); 150 break; 151 case INPUT_STATE_BORDER_DRAG_LEFT: 152 wlr_xcursor_manager_set_cursor_image(server->cursor_mgr, 153 "left_side", server->cursor); 154 break; 155 case INPUT_STATE_RESIZE_END: 156 case INPUT_STATE_NEW_END: 157 wlr_xcursor_manager_set_cursor_image(server->cursor_mgr, 158 "bottom_right_corner", server->cursor); 159 break; 160 default: 161 wlr_xcursor_manager_set_cursor_image(server->cursor_mgr, 162 "left_ptr", server->cursor); 163 break; 164 } 165 } 166 if (surface) { 167 bool focus_changed = seat->pointer_state.focused_surface != surface; 168 wlr_seat_pointer_notify_enter(seat, surface, sx, sy); 169 if (!focus_changed) { 170 wlr_seat_pointer_notify_motion(seat, time, sx, sy); 171 } 172 } else { 173 wlr_seat_pointer_clear_focus(seat); 174 } 175 } 176 177 void server_cursor_motion(struct wl_listener *listener, void *data) { 178 struct wio_server *server = 179 wl_container_of(listener, server, cursor_motion); 180 struct wlr_event_pointer_motion *event = data; 181 wlr_cursor_move(server->cursor, event->device, 182 event->delta_x, event->delta_y); 183 process_cursor_motion(server, event->time_msec); 184 } 185 186 void server_cursor_motion_absolute( 187 struct wl_listener *listener, void *data) { 188 struct wio_server *server = 189 wl_container_of(listener, server, cursor_motion_absolute); 190 struct wlr_event_pointer_motion_absolute *event = data; 191 wlr_cursor_warp_absolute(server->cursor, event->device, event->x, event->y); 192 process_cursor_motion(server, event->time_msec); 193 } 194 195 static void menu_handle_button( 196 struct wio_server *server, struct wlr_event_pointer_button *event) { 197 server->menu.x = server->menu.y = -1; 198 switch (server->menu.selected) { 199 case 0: 200 server->input_state = INPUT_STATE_NEW_START; 201 wlr_xcursor_manager_set_cursor_image(server->cursor_mgr, 202 "top_left_corner", server->cursor); 203 break; 204 case 1: 205 server->input_state = INPUT_STATE_RESIZE_SELECT; 206 wlr_xcursor_manager_set_cursor_image(server->cursor_mgr, 207 "hand1", server->cursor); 208 break; 209 case 2: 210 server->input_state = INPUT_STATE_MOVE_SELECT; 211 wlr_xcursor_manager_set_cursor_image(server->cursor_mgr, 212 "hand1", server->cursor); 213 break; 214 case 3: 215 server->input_state = INPUT_STATE_DELETE_SELECT; 216 wlr_xcursor_manager_set_cursor_image(server->cursor_mgr, 217 "hand1", server->cursor); 218 break; 219 default: 220 server->input_state = INPUT_STATE_NONE; 221 break; 222 } 223 } 224 225 static void view_begin_interactive(struct wio_view *view, 226 struct wlr_surface *surface, double sx, double sy, 227 const char *cursor, enum wio_input_state state) { 228 wio_view_focus(view, surface); 229 view->server->interactive.view = view; 230 view->server->interactive.sx = (int)sx; 231 view->server->interactive.sy = (int)sy; 232 view->server->input_state = state; 233 wlr_xcursor_manager_set_cursor_image(view->server->cursor_mgr, 234 cursor, view->server->cursor); 235 } 236 237 static void view_end_interactive(struct wio_server *server) { 238 server->input_state = INPUT_STATE_NONE; 239 server->interactive.view = NULL; 240 // TODO: Restore previous pointer? 241 wlr_xcursor_manager_set_cursor_image(server->cursor_mgr, 242 "left_ptr", server->cursor); 243 } 244 245 static void new_view(struct wio_server *server) { 246 int x1 = server->interactive.sx, x2 = server->cursor->x; 247 int y1 = server->interactive.sy, y2 = server->cursor->y; 248 if (x2 < x1) { 249 int _ = x1; 250 x1 = x2; 251 x2 = _; 252 } 253 if (y2 < y1) { 254 int _ = y1; 255 y1 = y2; 256 y2 = _; 257 } 258 struct wio_new_view *view = calloc(1, sizeof(struct wio_new_view)); 259 view->box.x = x1; 260 view->box.y = y1; 261 view->box.width = x2 - x1; 262 view->box.height = y2 - y1; 263 if (view->box.width < 100){ 264 view->box.width = 100; 265 } 266 if (view->box.height < 100){ 267 view->box.height = 100; 268 } 269 int fd[2]; 270 if (pipe(fd) != 0) { 271 wlr_log(WLR_ERROR, "Unable to create pipe for fork"); 272 return; 273 } 274 char cmd[1024]; 275 if (snprintf(cmd, sizeof(cmd), "%s -- %s", 276 server->cage, server->term) >= (int)sizeof(cmd)) { 277 fprintf(stderr, "New view command truncated\n"); 278 return; 279 } 280 pid_t pid, child; 281 if ((pid = fork()) == 0) { 282 setsid(); 283 sigset_t set; 284 sigemptyset(&set); 285 sigprocmask(SIG_SETMASK, &set, NULL); 286 close(fd[0]); 287 if ((child = fork()) == 0) { 288 close(fd[1]); 289 execl("/bin/sh", "/bin/sh", "-c", cmd, (void *)NULL); 290 _exit(0); 291 } 292 ssize_t s = 0; 293 while ((size_t)s < sizeof(pid_t)) { 294 s += write(fd[1], ((uint8_t *)&child) + s, sizeof(pid_t) - s); 295 } 296 close(fd[1]); 297 _exit(0); // Close child process 298 } else if (pid < 0) { 299 close(fd[0]); 300 close(fd[1]); 301 wlr_log(WLR_ERROR, "fork failed"); 302 return; 303 } 304 close(fd[1]); // close write 305 ssize_t s = 0; 306 while ((size_t)s < sizeof(pid_t)) { 307 s += read(fd[0], ((uint8_t *)&child) + s, sizeof(pid_t) - s); 308 } 309 close(fd[0]); 310 waitpid(pid, NULL, 0); 311 if (child > 0) { 312 view->pid = child; 313 wl_list_insert(&server->new_views, &view->link); 314 } 315 } 316 317 static void handle_button_internal( 318 struct wio_server *server, struct wlr_event_pointer_button *event) { 319 // TODO: open menu if the client doesn't handle the button press 320 // will basically involve some serial hacking 321 struct wlr_box menu_box = { 322 .x = server->menu.x, .y = server->menu.y, 323 .width = server->menu.width, .height = server->menu.height, 324 }; 325 int x1, x2, y1, y2; 326 uint32_t width, height; 327 switch (server->input_state) { 328 case INPUT_STATE_NONE: 329 if (event->state == WLR_BUTTON_PRESSED && event->button == BTN_RIGHT) { 330 // TODO: Open over the last-used menu item 331 server->input_state = INPUT_STATE_MENU; 332 server->menu.x = server->cursor->x; 333 server->menu.y = server->cursor->y; 334 } 335 break; 336 case INPUT_STATE_MENU: 337 if (wlr_box_contains_point( 338 &menu_box, server->cursor->x, server->cursor->y)) { 339 menu_handle_button(server, event); 340 } else { 341 if (event->state == WLR_BUTTON_PRESSED) { 342 server->input_state = INPUT_STATE_NONE; 343 server->menu.x = server->menu.y = -1; 344 } 345 } 346 break; 347 case INPUT_STATE_NEW_START: 348 if (event->state == WLR_BUTTON_PRESSED) { 349 server->interactive.sx = server->cursor->x; 350 server->interactive.sy = server->cursor->y; 351 server->input_state = INPUT_STATE_NEW_END; 352 } 353 break; 354 case INPUT_STATE_NEW_END: 355 new_view(server); 356 view_end_interactive(server); 357 break; 358 case INPUT_STATE_RESIZE_SELECT: 359 if (event->state == WLR_BUTTON_PRESSED) { 360 double sx, sy; 361 int view_area; 362 struct wlr_surface *surface = NULL; 363 struct wio_view *view = wio_view_at(server, 364 server->cursor->x, server->cursor->y, &surface, &sx, &sy, 365 &view_area); 366 if (view != NULL) { 367 view_begin_interactive(view, surface, sx, sy, 368 "bottom_right_corner", INPUT_STATE_RESIZE_START); 369 } else { 370 view_end_interactive(server); 371 } 372 } 373 break; 374 case INPUT_STATE_RESIZE_START: 375 if (event->state == WLR_BUTTON_PRESSED) { 376 server->interactive.sx = server->cursor->x; 377 server->interactive.sy = server->cursor->y; 378 server->input_state = INPUT_STATE_RESIZE_END; 379 } 380 break; 381 case INPUT_STATE_BORDER_DRAG_TOP: 382 y1 = server->interactive.view->y + server->interactive.view->xdg_surface->surface->current.height; 383 y2 = server->cursor->y; 384 x1 = server->interactive.view->x; 385 if (y2 < y1) { 386 int _ = y1; 387 y1 = y2; 388 y2 = _; 389 } 390 wio_view_move(server->interactive.view, 391 x1, y1); 392 width = server->interactive.view->xdg_surface->surface->current.width; 393 height = y2 - y1; 394 if (height < 100) { 395 height = 100; 396 } 397 wlr_xdg_toplevel_set_size( 398 server->interactive.view->xdg_surface, width, height); 399 view_end_interactive(server); 400 break; 401 case INPUT_STATE_BORDER_DRAG_LEFT: 402 x1 = server->interactive.view->x + server->interactive.view->xdg_surface->surface->current.width; 403 x2 = server->cursor->x; 404 y1 = server->interactive.view->y; 405 if (x2 < x1) { 406 int _ = x1; 407 x1 = x2; 408 x2 = _; 409 } 410 wio_view_move(server->interactive.view, 411 x1, y1); 412 width = x2 - x1; 413 height = server->interactive.view->xdg_surface->surface->current.height; 414 if (width < 100) { 415 width = 100; 416 } 417 wlr_xdg_toplevel_set_size( 418 server->interactive.view->xdg_surface, width, height); 419 view_end_interactive(server); 420 break; 421 case INPUT_STATE_BORDER_DRAG_BOTTOM: 422 x1 = server->interactive.view->x; 423 y1 = server->interactive.view->y, y2 = server->cursor->y; 424 if (y2 < y1) { 425 int _ = y1; 426 y1 = y2; 427 y2 = _; 428 } 429 wio_view_move(server->interactive.view, 430 x1, y1); 431 width = server->interactive.view->xdg_surface->surface->current.width; 432 height = y2 - y1; 433 if (width < 100) { 434 width = 100; 435 } 436 wlr_xdg_toplevel_set_size( 437 server->interactive.view->xdg_surface, width, height); 438 view_end_interactive(server); 439 break; 440 case INPUT_STATE_BORDER_DRAG_RIGHT: 441 x1 = server->interactive.view->x, x2 = server->cursor->x; 442 y1 = server->interactive.view->y; 443 if (x2 < x1) { 444 int _ = x1; 445 x1 = x2; 446 x2 = _; 447 } 448 wio_view_move(server->interactive.view, 449 x1, y1); 450 width = x2 - x1; 451 height = server->interactive.view->xdg_surface->surface->current.height; 452 if (width < 100) { 453 width = 100; 454 } 455 wlr_xdg_toplevel_set_size( 456 server->interactive.view->xdg_surface, width, height); 457 view_end_interactive(server); 458 break; 459 case INPUT_STATE_RESIZE_END: 460 x1 = server->interactive.sx, x2 = server->cursor->x; 461 y1 = server->interactive.sy, y2 = server->cursor->y; 462 if (x2 < x1) { 463 int _ = x1; 464 x1 = x2; 465 x2 = _; 466 } 467 if (y2 < y1) { 468 int _ = y1; 469 y1 = y2; 470 y2 = _; 471 } 472 wio_view_move(server->interactive.view, 473 x1, y1); 474 width = x2 - x1, height = y2 - y1; 475 if (width < 100) { 476 width = 100; 477 } 478 if (height < 100) { 479 height = 100; 480 } 481 wlr_xdg_toplevel_set_size( 482 server->interactive.view->xdg_surface, width, height); 483 view_end_interactive(server); 484 break; 485 case INPUT_STATE_MOVE_SELECT: 486 if (event->state == WLR_BUTTON_PRESSED) { 487 double sx, sy; 488 int view_area; 489 struct wlr_surface *surface = NULL; 490 struct wio_view *view = wio_view_at(server, 491 server->cursor->x, server->cursor->y, &surface, &sx, &sy, 492 &view_area); 493 if (view != NULL) { 494 view_begin_interactive(view, surface, sx, sy, 495 "grabbing", INPUT_STATE_MOVE); 496 } else { 497 view_end_interactive(server); 498 } 499 } 500 break; 501 case INPUT_STATE_MOVE: 502 wio_view_move(server->interactive.view, 503 server->cursor->x - server->interactive.sx, 504 server->cursor->y - server->interactive.sy); 505 view_end_interactive(server); 506 break; 507 case INPUT_STATE_DELETE_SELECT: 508 if (event->state == WLR_BUTTON_PRESSED) { 509 double sx, sy; 510 int view_area; 511 struct wlr_surface *surface = NULL; 512 struct wio_view *view = wio_view_at(server, 513 server->cursor->x, server->cursor->y, &surface, &sx, &sy, 514 &view_area); 515 if (view != NULL) { 516 wlr_xdg_toplevel_send_close(view->xdg_surface); 517 } 518 view_end_interactive(server); 519 } 520 break; 521 default: 522 // TODO 523 break; 524 } 525 } 526 527 void server_cursor_button(struct wl_listener *listener, void *data) { 528 struct wio_server *server = 529 wl_container_of(listener, server, cursor_button); 530 struct wlr_event_pointer_button *event = data; 531 double sx, sy; 532 struct wlr_surface *surface = NULL; 533 int view_area; 534 struct wio_view *view = wio_view_at( 535 server, server->cursor->x, server->cursor->y, &surface, &sx, &sy, 536 &view_area); 537 if (server->input_state == INPUT_STATE_NONE && view) { 538 wio_view_focus(view, surface); 539 switch (view_area) { 540 case VIEW_AREA_SURFACE: 541 wlr_seat_pointer_notify_button(server->seat, 542 event->time_msec, event->button, event->state); 543 break; 544 case VIEW_AREA_BORDER_TOP: 545 view_begin_interactive(view, surface, view->x, view->y, 546 "top_side", INPUT_STATE_BORDER_DRAG_TOP); 547 break; 548 case VIEW_AREA_BORDER_RIGHT: 549 view_begin_interactive(view, surface, view->x, view->y, 550 "right_side", INPUT_STATE_BORDER_DRAG_RIGHT); 551 break; 552 case VIEW_AREA_BORDER_BOTTOM: 553 view_begin_interactive(view, surface, view->x, view->y, 554 "bottom_side", INPUT_STATE_BORDER_DRAG_BOTTOM); 555 break; 556 case VIEW_AREA_BORDER_LEFT: 557 view_begin_interactive(view, surface, view->x, view->y, 558 "left_side", INPUT_STATE_BORDER_DRAG_LEFT); 559 break; 560 } 561 } else { 562 handle_button_internal(server, event); 563 } 564 } 565 566 void server_cursor_axis(struct wl_listener *listener, void *data) { 567 struct wio_server *server = wl_container_of(listener, server, cursor_axis); 568 struct wlr_event_pointer_axis *event = data; 569 wlr_seat_pointer_notify_axis(server->seat, 570 event->time_msec, event->orientation, event->delta, 571 event->delta_discrete, event->source); 572 } 573 574 void server_cursor_frame(struct wl_listener *listener, void *data) { 575 struct wio_server *server = 576 wl_container_of(listener, server, cursor_frame); 577 wlr_seat_pointer_notify_frame(server->seat); 578 } 579 580 void seat_request_cursor(struct wl_listener *listener, void *data) { 581 struct wio_server *server = wl_container_of( 582 listener, server, request_cursor); 583 struct wlr_seat_pointer_request_set_cursor_event *event = data; 584 struct wlr_seat_client *focused_client = 585 server->seat->pointer_state.focused_client; 586 if (focused_client == event->seat_client 587 && server->input_state == INPUT_STATE_NONE) { 588 wlr_cursor_set_surface(server->cursor, event->surface, 589 event->hotspot_x, event->hotspot_y); 590 } 591 }