gimp but for windows: a wayland compositor
git clone https://github.com/m-col/wimp
Files | Refs | Readme

-rw-r--r-- src/action.c


      1 #include <sys/vt.h>
      2 #include <wlr/backend.h>
      3 #include <wlr/backend/session.h>
      4 #include <wlr/types/wlr_output_layout.h>
      5 
      6 #include "action.h"
      7 #include "config.h"
      8 #include "cursor.h"
      9 #include "desk.h"
     10 #include "output.h"
     11 #include "parse.h"
     12 #include "scratchpad.h"
     13 #include "shell.h"
     14 #include "types.h"
     15 
     16 
     17 static void terminate(void *data);
     18 static void exec_command(void *data);
     19 static void close_window(void *data);
     20 static void move_window(void *data);
     21 static void focus_in_direction(void *data);
     22 static void next_desk(void *data);
     23 static void prev_desk(void *data);
     24 static void reset_zoom(void *data);
     25 static void zoom(void *data);
     26 static void set_mark(void *data);
     27 static void go_to_mark(void *data);
     28 static void halfimize(void *data);
     29 static void maximize(void *data);
     30 static void send_to_desk(void *data);
     31 static void toggle_scratchpad(void *data);
     32 static void to_region(void *data);
     33 static void zoom_pinch(void *data);
     34 static void zoom_pinch_begin(void *data);
     35 static void zoom_scroll(void *data);
     36 
     37 
     38 static struct {
     39     const char *name;
     40     const action action;
     41     const action begin;
     42 } pinch_map[] = {
     43     { "zoom", &zoom_pinch, &zoom_pinch_begin },
     44 };
     45 
     46 
     47 static struct {
     48     const char *name;
     49     const action action;
     50 } scroll_map[] = {
     51     { "zoom", &zoom_scroll },
     52     { "pan_desk", &pan_desk },
     53 };
     54 
     55 
     56 static struct {
     57     const char *name;
     58     const action action;
     59     bool (*data_handler)(void **data, char *args);
     60 } action_map[] = {
     61     { "terminate", &terminate, NULL },
     62     { "exec", &exec_command, &str_handler },
     63     { "close_window", &close_window, NULL },
     64     { "move_window", &move_window, NULL },
     65     { "focus", &focus_in_direction, &dir_handler },
     66     { "next_desk", &next_desk, NULL },
     67     { "prev_desk", &prev_desk, NULL },
     68     { "pan_desk", &pan_desk, &motion_handler },
     69     { "reset_zoom", &reset_zoom, NULL },
     70     { "zoom", &zoom, &str_handler },
     71     { "set_mark", &set_mark, NULL },
     72     { "go_to_mark", &go_to_mark, NULL },
     73     { "toggle_fullscreen", &toggle_fullscreen, NULL },
     74     { "halfimize", &halfimize, &dir_handler },
     75     { "maximize", &maximize, NULL },
     76     { "send_to_desk", &send_to_desk, &str_handler },
     77     { "scratchpad", &toggle_scratchpad, &scratchpad_handler },
     78     { "to_region", &to_region, &box_handler },
     79 };
     80 
     81 
     82 bool get_action(
     83     char *name, action *found, char *args, void **data, char *response, int flag
     84 ) {
     85     // flag can be PINCH or SCROLL to access their actions, otherwise neither
     86     *data = NULL;
     87     size_t i;
     88 
     89     switch (flag) {
     90 	case PINCH:
     91 	    for (i = 0; i < sizeof(pinch_map) / sizeof(pinch_map[0]); i++) {
     92 		if (strcmp(pinch_map[i].name, name) == 0) {
     93 		    *found = pinch_map[i].action;
     94 		    action begin = pinch_map[i].begin;
     95 		    *data = calloc(1, sizeof(action));
     96 		    *(action *)(*data) = begin;
     97 		    return true;
     98 		}
     99 	    }
    100 	    break;
    101 
    102 	case SCROLL:
    103 	    for (i = 0; i < sizeof(scroll_map) / sizeof(scroll_map[0]); i++) {
    104 		if (strcmp(scroll_map[i].name, name) == 0) {
    105 		    *found = scroll_map[i].action;
    106 		    return true;
    107 		}
    108 	    }
    109 	    break;
    110 
    111 	default:
    112 	    for (i = 0; i < sizeof(action_map) / sizeof(action_map[0]); i++) {
    113 		if (strcmp(action_map[i].name, name) == 0) {
    114 		    *found = action_map[i].action;
    115 
    116 		    if (action_map[i].data_handler != NULL) {
    117 			if (!action_map[i].data_handler(data, args)) {
    118 			    sprintf(response, "Command malformed/incomplete.");
    119 			    return false;
    120 			}
    121 		    }
    122 
    123 		    return true;
    124 		}
    125 	    }
    126     }
    127 
    128     return false;
    129 }
    130 
    131 
    132 bool do_action(char *message, char *response) {
    133     void *data;
    134     action action;
    135     if (get_action(message, &action, strtok(NULL, "\n\r"), &data, response, 0)) {
    136 	(*action)(data);
    137 	return true;
    138     } else {
    139 	return false;
    140     }
    141 }
    142 
    143 
    144 static void terminate(void *data) {
    145     wl_display_terminate(wimp.display);
    146 }
    147 
    148 
    149 static void exec_command(void *data) {
    150     pid_t pid = fork();
    151     if (pid == 0) {
    152 	if (execl("/bin/sh", "/bin/sh", "-c", data, (void *)NULL) == -1) {
    153 	    exit(EXIT_FAILURE);
    154 	}
    155     } else if (pid < 0) {
    156 	wlr_log(WLR_ERROR, "Failed to fork for exec command.");
    157     } else {
    158 	wlr_log(WLR_DEBUG, "Executing: %s", (char *)data);
    159     }
    160 }
    161 
    162 
    163 void change_vt(void *data) {
    164     unsigned vt = *(unsigned *)data;
    165     struct wlr_session *session = wlr_backend_get_session(wimp.backend);
    166     wlr_session_change_vt(session, vt);
    167 }
    168 
    169 
    170 static void close_window(void *data) {
    171     struct wlr_surface *surface = wimp.seat->keyboard_state.focused_surface;
    172     if (surface) {
    173 	struct wlr_xdg_surface *xdg_surface = surface->role_data;
    174 	if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL && xdg_surface->toplevel) {
    175 	    xdg_toplevel_send_close(xdg_surface->toplevel->resource);
    176 	}
    177     }
    178 }
    179 
    180 
    181 static void move_window(void *data) {
    182     struct view *view;
    183     if (wimp.grabbed_view) {
    184 	view = wimp.grabbed_view;
    185     } else {
    186 	double sx, sy;
    187 	struct wlr_surface *surface;
    188 	bool is_layer;
    189 	view = under_pointer(&surface, &sx, &sy, &is_layer);
    190 	if (!view || is_layer) {
    191 	    return;
    192 	}
    193     }
    194     double zoom = wimp.current_desk->zoom;
    195     struct wlr_box damage = {
    196 	.x = view->x * zoom,
    197 	.y = view->y * zoom,
    198 	.width = view->surface->geometry.width * zoom,
    199 	.height = view->surface->geometry.height * zoom,
    200     };
    201     struct motion motion = *(struct motion*)data;
    202     if (motion.dx > 0) {
    203 	damage.width += motion.dx;
    204     } else {
    205 	damage.x -= motion.dx;
    206 	damage.width -= motion.dx;
    207     }
    208     if (motion.dy > 0) {
    209 	damage.height += motion.dy;
    210     } else {
    211 	damage.y -= motion.dy;
    212 	damage.height -= motion.dy;
    213     }
    214     view->x += motion.dx;
    215     view->y += motion.dy;
    216     damage_box(&damage, true);
    217 }
    218 
    219 
    220 static void focus_in_direction(void *data) {
    221     if (wl_list_empty(&wimp.current_desk->views)) {
    222 	return;
    223     }
    224 
    225     struct view *current = wl_container_of(wimp.current_desk->views.next, current, link);
    226     enum direction dir = *(enum direction*)data;
    227 
    228     struct view *next = NULL;
    229     struct view *view;
    230     double vx, vy, dx, dy, vdist, dist;
    231     double x = current->x + current->surface->geometry.width / 2;
    232     double y = current->y + current->surface->geometry.height / 2;
    233     double c = y - x;  // y = x + c    / slope
    234     double n = y + x;  // y = n - x    \ slope
    235     bool above_c = dir & (DOWN | LEFT);
    236     bool above_n = dir & (DOWN | RIGHT);
    237 
    238     wl_list_for_each(view, &wimp.current_desk->views, link) {
    239 	vx = view->x + view->surface->geometry.width / 2;
    240 	vy = view->y + view->surface->geometry.height / 2;
    241 	dx = x - vx;
    242 	dy = y - vy;
    243 	vdist = dx * dx + dy * dy;
    244 	if (
    245 	    (above_c ^ (vy - vx < c)) &&
    246 	    (above_n ^ (vy + vx < n)) &&
    247 	    (!next || vdist < dist) &&
    248 	    view != current
    249 	) {
    250 	    next = view;
    251 	    dist = vdist;
    252 	}
    253     }
    254 
    255     if (next) {
    256 	unfullscreen();
    257 	pan_to_view(next);
    258 	focus_view(next, NULL);
    259     } else {
    260 	pan_to_view(current);
    261 	focus_view(current, NULL);
    262     }
    263 }
    264 
    265 
    266 static void next_desk(void *data) {
    267     struct desk *desk;
    268     if (wimp.current_desk->index + 1 == wimp.desk_count) {
    269 	desk = wl_container_of(wimp.desks.next, desk, link);
    270     } else {
    271 	desk = wl_container_of(wimp.current_desk->link.next, desk, link);
    272     }
    273     set_desk(desk);
    274 }
    275 
    276 
    277 static void prev_desk(void *data) {
    278     struct desk *desk;
    279     if (wimp.current_desk->index == 0) {
    280 	desk = wl_container_of(wimp.desks.prev, desk, link);
    281     } else {
    282 	desk = wl_container_of(wimp.current_desk->link.prev, desk, link);
    283     }
    284     set_desk(desk);
    285 }
    286 
    287 
    288 void pan_desk(void *data) {
    289     struct desk *desk = wimp.current_desk;
    290     struct motion motion = *(struct motion*)data;
    291     double dx = motion.dx / desk->zoom;
    292     double dy = motion.dy / desk->zoom;
    293     if (motion.is_percentage) {
    294 	struct wlr_box *extents = wlr_output_layout_get_box(wimp.output_layout, NULL);
    295 	dx = extents->width * (dx / 100);
    296 	dy = extents->height * (dy / 100);
    297     }
    298     unfullscreen();
    299     struct view *view;
    300     wl_list_for_each(view, &desk->views, link) {
    301 	view->x -= dx;
    302 	view->y -= dy;
    303     }
    304     desk->panned_x -= dx;
    305     desk->panned_y -= dy;
    306     damage_all_outputs();
    307 }
    308 
    309 
    310 static void zoom(void *data) {
    311     /* Passed value (data) is the percentage step (+ve or -ve) */
    312     struct desk *desk = wimp.current_desk;
    313     double f = 1 + (*(double*)data / 100);
    314 
    315     double next_zoom = desk->zoom * f;
    316     if (next_zoom < wimp.zoom_min) {
    317 	f = wimp.zoom_min / desk->zoom;
    318     } else if (next_zoom > wimp.zoom_max) {
    319 	f = wimp.zoom_max / desk->zoom;
    320     }
    321     unfullscreen();
    322     desk->zoom *= f;
    323     double fx = wimp.cursor->x * (f - 1) / desk->zoom;
    324     double fy = wimp.cursor->y * (f - 1) / desk->zoom;
    325     struct view *view;
    326     wl_list_for_each(view, &desk->views, link) {
    327 	view->x -= fx;
    328 	view->y -= fy;
    329     }
    330     desk->panned_x -= fx;
    331     desk->panned_y -= fy;
    332     damage_all_outputs();
    333 }
    334 
    335 
    336 static void reset_zoom(void *data) {
    337     double dz = 100 * 1 / wimp.current_desk->zoom - 100;
    338     zoom(&dz);
    339 }
    340 
    341 
    342 static void zoom_scroll(void *data) {
    343     struct motion motion = *(struct motion*)data;
    344     double dz = - motion.dx - motion.dy;
    345     zoom(&dz);
    346 }
    347 
    348 
    349 static double zoom_pinch_initial;
    350 
    351 
    352 static void zoom_pinch(void *data) {
    353     double scale = *(double*)data;
    354     double dz = 100 * scale * zoom_pinch_initial / wimp.current_desk->zoom - 100;
    355     zoom(&dz);
    356 }
    357 
    358 
    359 static void zoom_pinch_begin(void *data) {
    360     zoom_pinch_initial = wimp.current_desk->zoom;
    361 }
    362 
    363 
    364 static void set_mark(void *data) {
    365     struct mark *mark = calloc(1, sizeof(struct mark));
    366     struct desk *desk = wimp.current_desk;
    367     mark->desk = desk;
    368     mark->x = desk->panned_x;
    369     mark->y = desk->panned_y;
    370     mark->zoom = desk->zoom;
    371     mark->key = 0;
    372     wl_list_insert(&wimp.marks, &mark->link);
    373     wimp.mark_waiting = true;
    374     damage_mark_indicator();
    375 }
    376 
    377 
    378 void actually_set_mark(const xkb_keysym_t sym) {
    379     struct mark *mark;
    380     struct mark *existing = NULL;
    381 
    382     if (sym == XKB_KEY_Escape) {
    383 	mark = wl_container_of(wimp.marks.next, mark, link);
    384 	wl_list_remove(&mark->link);
    385 	free(mark);
    386 	return;
    387     }
    388 
    389     wl_list_for_each(mark, &wimp.marks, link) {
    390 	if (mark->key == sym) {
    391 	    existing = mark;
    392 	    break;
    393 	}
    394     }
    395     if (existing) {
    396 	wl_list_remove(&existing->link);
    397 	free(existing);
    398     }
    399 
    400     mark = wl_container_of(wimp.marks.next, mark, link);
    401     mark->key = sym;
    402 
    403     if (wimp.bind_marks) {
    404 	struct binding *kb, *tmp;
    405 	wl_list_for_each_safe(kb, tmp, &wimp.key_bindings, link) {
    406 	    if (kb->key == sym && kb->mods == 0) {
    407 		return;
    408 	    }
    409 	}
    410 	kb = calloc(1, sizeof(struct binding));
    411 	kb->mods = 0;
    412 	kb->key = sym;
    413 	kb->data = calloc(1, sizeof(xkb_keysym_t));
    414 	kb->action = &actually_go_to_mark;
    415 	*(xkb_keysym_t *)(kb->data) = sym;
    416 	wl_list_insert(&wimp.key_bindings, &kb->link);
    417     }
    418 }
    419 
    420 
    421 static void go_to_mark(void *data) {
    422     wimp.mark_waiting = true;
    423     damage_mark_indicator();
    424 }
    425 
    426 
    427 void actually_go_to_mark(void *data) {
    428     const xkb_keysym_t sym = *(xkb_keysym_t *)data;
    429     struct mark *mark;
    430 
    431     if (sym == XKB_KEY_Escape) {
    432 	return;
    433     }
    434 
    435     bool found = false;
    436     wl_list_for_each(mark, &wimp.marks, link) {
    437 	if (mark->key == sym) {
    438 	    found = true;
    439 	    break;
    440 	}
    441     }
    442     if (!found) {
    443 	return;
    444     }
    445 
    446     unfullscreen();
    447     struct motion motion = {
    448 	.dx = mark->desk->panned_x - mark->x,
    449 	.dy = mark->desk->panned_y - mark->y,
    450 	.is_percentage = false,
    451     };
    452     mark->desk->zoom = mark->zoom;
    453     set_desk(mark->desk);
    454     pan_desk(&motion);
    455 
    456     struct view *view;
    457     struct wlr_box *extents = wlr_output_layout_get_box(wimp.output_layout, NULL);
    458     wl_list_for_each(view, &wimp.current_desk->views, link) {
    459 	if (
    460 	    view->x + view->surface->geometry.width < 0 || extents->width < view->x ||
    461 	    view->y + view->surface->geometry.height < 0 || extents->height < view->y
    462 	) {
    463 	    continue;
    464 	}
    465 	focus_view(view, NULL);
    466 	return;
    467     }
    468 }
    469 
    470 
    471 void toggle_fullscreen(void *data) {
    472     struct wlr_surface *surface = wimp.seat->keyboard_state.focused_surface;
    473     if (!surface || !wlr_surface_is_xdg_surface(surface)) {
    474 	return;
    475     }
    476     struct wlr_xdg_surface *xdg_surface = wlr_xdg_surface_from_wlr_surface(surface);
    477     struct view *view = wl_container_of(wimp.current_desk->views.next, view, link);
    478     fullscreen_xdg_surface(view, xdg_surface, NULL);
    479 }
    480 
    481 
    482 static void halfimize(void *data) {
    483     struct wlr_surface *surface = wimp.seat->keyboard_state.focused_surface;
    484     if (!surface || !wlr_surface_is_xdg_surface(surface) || wl_list_empty(&wimp.current_desk->views)) {
    485 	return;
    486     }
    487     struct view *view = wl_container_of(wimp.current_desk->views.next, view, link);
    488     enum direction dir = *(enum direction*)data;
    489     double vx = view->x + view->surface->geometry.width / 2;
    490     double vy = view->y + view->surface->geometry.height / 2;
    491     double x, y, width, height;
    492     wlr_output_layout_closest_point(wimp.output_layout, NULL, vx, vy, &x, &y);
    493     struct wlr_output *output = wlr_output_layout_output_at(wimp.output_layout, x, y);
    494     unfullscreen();
    495 
    496     switch (dir) {
    497 	case RIGHT:
    498 	    x = output->width / 2;
    499 	    y = 0;
    500 	    width = output->width / 2;
    501 	    height = output->height;
    502 	    break;
    503 	case LEFT:
    504 	    x = 0;
    505 	    y = 0;
    506 	    width = output->width / 2;
    507 	    height = output->height;
    508 	    break;
    509 	case UP:
    510 	    x = 0;
    511 	    y = 0;
    512 	    width = output->width;
    513 	    height = output->height / 2;
    514 	    break;
    515 	case DOWN:
    516 	    x = 0;
    517 	    y = output->height / 2;
    518 	    width = output->width;
    519 	    height = output->height / 2;
    520 	    break;
    521 	case NO_DIRECTION:
    522 	    return;
    523     }
    524 
    525     double zoom = wimp.current_desk->zoom;
    526     int border_width = wimp.current_desk->border_width;
    527     struct wlr_box new = {
    528 	.x = x / zoom + border_width,
    529 	.y = y / zoom + border_width,
    530 	.width = (width - border_width * 2) / zoom,
    531 	.height = (height - border_width * 2) / zoom,
    532     };
    533     view_apply_geometry(view, &new);
    534 }
    535 
    536 
    537 static void maximize(void *data) {
    538     struct wlr_surface *surface = wimp.seat->keyboard_state.focused_surface;
    539     if (!surface || !wlr_surface_is_xdg_surface(surface) || wl_list_empty(&wimp.current_desk->views)) {
    540 	return;
    541     }
    542 
    543     struct view *view = wl_container_of(wimp.current_desk->views.next, view, link);
    544     double vx = view->x + view->surface->geometry.width / 2;
    545     double vy = view->y + view->surface->geometry.height / 2;
    546     double x, y;
    547     wlr_output_layout_closest_point(wimp.output_layout, NULL, vx, vy, &x, &y);
    548     struct wlr_output *output = wlr_output_layout_output_at(wimp.output_layout, x, y);
    549     unfullscreen();
    550 
    551     double zoom = wimp.current_desk->zoom;
    552     int border_width = wimp.current_desk->border_width;
    553     struct wlr_box new = {
    554 	.x = border_width,
    555 	.y = border_width,
    556 	.width = (output->width - border_width * 2) / zoom,
    557 	.height = (output->height - border_width * 2) / zoom,
    558     };
    559     view_apply_geometry(view, &new);
    560 }
    561 
    562 
    563 static void send_to_desk(void *data) {
    564     struct view *view = wl_container_of(wimp.current_desk->views.next, view, link);
    565     int index = *(double*)data - 1;
    566     unfullscreen();
    567     view_to_desk(view, index);
    568 }
    569 
    570 
    571 static void toggle_scratchpad(void *data) {
    572     struct scratchpad *scratchpad = scratchpad_from_id(*(int *)data);
    573 
    574     if (scratchpad->view) {
    575 	if (scratchpad->is_mapped) {
    576 	    unmap_view(scratchpad->view);
    577 	} else {
    578 	    map_view(scratchpad->view);
    579 	}
    580 	return;
    581     }
    582 
    583     wimp.scratchpad_waiting = true;
    584 
    585     scratchpad->pid = fork();
    586     if (scratchpad->pid == 0) {
    587 	execl("/bin/sh", "/bin/sh", "-c", scratchpad->command, (void *)NULL);
    588 	exit(EXIT_FAILURE);
    589     } else if (scratchpad->pid < 0) {
    590 	wlr_log(WLR_ERROR, "Failed to fork for scratchpad.");
    591 	wimp.scratchpad_waiting = false;
    592     } else {
    593 	wlr_log(WLR_DEBUG, "Launching scratchpad: %s", scratchpad->command);
    594     }
    595 }
    596 
    597 
    598 static void to_region(void *data) {
    599     struct wlr_box *box = (struct wlr_box *)data;
    600     struct wlr_box *extents = wlr_output_layout_get_box(wimp.output_layout, NULL);
    601 
    602     struct motion motion = {
    603 	.dx = - (extents->width / 2) + (box->width / 2) + box->x,
    604 	.dy = - (extents->height / 2) + (box->height / 2) + box->y,
    605 	.is_percentage = false,
    606     };
    607     pan_desk(&motion);
    608 
    609     double f;
    610     if (extents->width / extents->height > box->width / box->height) {
    611 	f = wimp.current_desk->zoom / ((double)box->width / (double)extents->width);
    612     } else {
    613 	f = wimp.current_desk->zoom / ((double)box->height / (double)extents->height);
    614     }
    615     double dz = 100 * f / wimp.current_desk->zoom - 100;
    616     double cx = wimp.cursor->x;
    617     double cy = wimp.cursor->y;
    618     wimp.cursor->x = extents->width / 2;
    619     wimp.cursor->y = extents->height / 2;
    620     zoom(&dz);
    621     wimp.cursor->x = cx;
    622     wimp.cursor->y = cy;
    623 }
    624