diff options
Diffstat (limited to 'sway/tree/view.c')
-rw-r--r-- | sway/tree/view.c | 191 |
1 files changed, 158 insertions, 33 deletions
diff --git a/sway/tree/view.c b/sway/tree/view.c index 24fb6864..a55c8a29 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c | |||
@@ -26,6 +26,7 @@ void view_init(struct sway_view *view, enum sway_view_type type, | |||
26 | view->impl = impl; | 26 | view->impl = impl; |
27 | view->executed_criteria = create_list(); | 27 | view->executed_criteria = create_list(); |
28 | view->marks = create_list(); | 28 | view->marks = create_list(); |
29 | view->allow_request_urgent = true; | ||
29 | wl_signal_init(&view->events.unmap); | 30 | wl_signal_init(&view->events.unmap); |
30 | } | 31 | } |
31 | 32 | ||
@@ -141,6 +142,19 @@ const char *view_get_shell(struct sway_view *view) { | |||
141 | return "unknown"; | 142 | return "unknown"; |
142 | } | 143 | } |
143 | 144 | ||
145 | void view_get_constraints(struct sway_view *view, double *min_width, | ||
146 | double *max_width, double *min_height, double *max_height) { | ||
147 | if (view->impl->get_constraints) { | ||
148 | view->impl->get_constraints(view, | ||
149 | min_width, max_width, min_height, max_height); | ||
150 | } else { | ||
151 | *min_width = DBL_MIN; | ||
152 | *max_width = DBL_MAX; | ||
153 | *min_height = DBL_MIN; | ||
154 | *max_height = DBL_MAX; | ||
155 | } | ||
156 | } | ||
157 | |||
144 | uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, | 158 | uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, |
145 | int height) { | 159 | int height) { |
146 | if (view->impl->configure) { | 160 | if (view->impl->configure) { |
@@ -151,12 +165,43 @@ uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, | |||
151 | 165 | ||
152 | void view_init_floating(struct sway_view *view) { | 166 | void view_init_floating(struct sway_view *view) { |
153 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); | 167 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); |
154 | int max_width = ws->width * 0.6666; | 168 | int min_width, min_height; |
155 | int max_height = ws->height * 0.6666; | 169 | int max_width, max_height; |
156 | view->width = | 170 | |
157 | view->natural_width > max_width ? max_width : view->natural_width; | 171 | if (config->floating_minimum_width == -1) { // no minimum |
158 | view->height = | 172 | min_width = 0; |
159 | view->natural_height > max_height ? max_height : view->natural_height; | 173 | } else if (config->floating_minimum_width == 0) { // automatic |
174 | min_width = 75; | ||
175 | } else { | ||
176 | min_width = config->floating_minimum_width; | ||
177 | } | ||
178 | |||
179 | if (config->floating_minimum_height == -1) { // no minimum | ||
180 | min_height = 0; | ||
181 | } else if (config->floating_minimum_height == 0) { // automatic | ||
182 | min_height = 50; | ||
183 | } else { | ||
184 | min_height = config->floating_minimum_height; | ||
185 | } | ||
186 | |||
187 | if (config->floating_maximum_width == -1) { // no maximum | ||
188 | max_width = INT_MAX; | ||
189 | } else if (config->floating_maximum_width == 0) { // automatic | ||
190 | max_width = ws->width * 0.6666; | ||
191 | } else { | ||
192 | max_width = config->floating_maximum_width; | ||
193 | } | ||
194 | |||
195 | if (config->floating_maximum_height == -1) { // no maximum | ||
196 | max_height = INT_MAX; | ||
197 | } else if (config->floating_maximum_height == 0) { // automatic | ||
198 | max_height = ws->height * 0.6666; | ||
199 | } else { | ||
200 | max_height = config->floating_maximum_height; | ||
201 | } | ||
202 | |||
203 | view->width = fmax(min_width, fmin(view->natural_width, max_width)); | ||
204 | view->height = fmax(min_height, fmin(view->natural_height, max_height)); | ||
160 | view->x = ws->x + (ws->width - view->width) / 2; | 205 | view->x = ws->x + (ws->width - view->width) / 2; |
161 | view->y = ws->y + (ws->height - view->height) / 2; | 206 | view->y = ws->y + (ws->height - view->height) / 2; |
162 | 207 | ||
@@ -165,9 +210,6 @@ void view_init_floating(struct sway_view *view) { | |||
165 | view->border_left = view->border_right = true; | 210 | view->border_left = view->border_right = true; |
166 | 211 | ||
167 | container_set_geometry_from_floating_view(view->swayc); | 212 | container_set_geometry_from_floating_view(view->swayc); |
168 | |||
169 | // Don't maximize floating windows | ||
170 | view_set_tiled(view, false); | ||
171 | } | 213 | } |
172 | 214 | ||
173 | void view_autoconfigure(struct sway_view *view) { | 215 | void view_autoconfigure(struct sway_view *view) { |
@@ -279,7 +321,6 @@ void view_autoconfigure(struct sway_view *view) { | |||
279 | view->y = y; | 321 | view->y = y; |
280 | view->width = width; | 322 | view->width = width; |
281 | view->height = height; | 323 | view->height = height; |
282 | view_set_tiled(view, true); | ||
283 | } | 324 | } |
284 | 325 | ||
285 | void view_set_activated(struct sway_view *view, bool activated) { | 326 | void view_set_activated(struct sway_view *view, bool activated) { |
@@ -289,7 +330,15 @@ void view_set_activated(struct sway_view *view, bool activated) { | |||
289 | } | 330 | } |
290 | 331 | ||
291 | void view_set_tiled(struct sway_view *view, bool tiled) { | 332 | void view_set_tiled(struct sway_view *view, bool tiled) { |
292 | view->border = tiled ? config->border : B_NONE; | 333 | if (!tiled) { |
334 | view->using_csd = true; | ||
335 | if (view->impl->has_client_side_decorations) { | ||
336 | view->using_csd = view->impl->has_client_side_decorations(view); | ||
337 | } | ||
338 | } else { | ||
339 | view->using_csd = false; | ||
340 | } | ||
341 | |||
293 | if (view->impl->set_tiled) { | 342 | if (view->impl->set_tiled) { |
294 | view->impl->set_tiled(view, tiled); | 343 | view->impl->set_tiled(view, tiled); |
295 | } | 344 | } |
@@ -352,6 +401,8 @@ void view_set_fullscreen(struct sway_view *view, bool fullscreen) { | |||
352 | } | 401 | } |
353 | } | 402 | } |
354 | 403 | ||
404 | container_end_mouse_operation(view->swayc); | ||
405 | |||
355 | ipc_event_window(view->swayc, "fullscreen_mode"); | 406 | ipc_event_window(view->swayc, "fullscreen_mode"); |
356 | } | 407 | } |
357 | 408 | ||
@@ -467,27 +518,45 @@ void view_execute_criteria(struct sway_view *view) { | |||
467 | list_t *criterias = criteria_for_view(view, CT_COMMAND); | 518 | list_t *criterias = criteria_for_view(view, CT_COMMAND); |
468 | for (int i = 0; i < criterias->length; i++) { | 519 | for (int i = 0; i < criterias->length; i++) { |
469 | struct criteria *criteria = criterias->items[i]; | 520 | struct criteria *criteria = criterias->items[i]; |
470 | wlr_log(L_DEBUG, "Checking criteria %s", criteria->raw); | 521 | wlr_log(WLR_DEBUG, "Checking criteria %s", criteria->raw); |
471 | if (view_has_executed_criteria(view, criteria)) { | 522 | if (view_has_executed_criteria(view, criteria)) { |
472 | wlr_log(L_DEBUG, "Criteria already executed"); | 523 | wlr_log(WLR_DEBUG, "Criteria already executed"); |
473 | continue; | 524 | continue; |
474 | } | 525 | } |
475 | wlr_log(L_DEBUG, "for_window '%s' matches view %p, cmd: '%s'", | 526 | wlr_log(WLR_DEBUG, "for_window '%s' matches view %p, cmd: '%s'", |
476 | criteria->raw, view, criteria->cmdlist); | 527 | criteria->raw, view, criteria->cmdlist); |
528 | seat_set_focus(seat, view->swayc); | ||
477 | list_add(view->executed_criteria, criteria); | 529 | list_add(view->executed_criteria, criteria); |
478 | struct cmd_results *res = execute_command(criteria->cmdlist, NULL); | 530 | struct cmd_results *res = execute_command(criteria->cmdlist, NULL); |
479 | if (res->status != CMD_SUCCESS) { | 531 | if (res->status != CMD_SUCCESS) { |
480 | wlr_log(L_ERROR, "Command '%s' failed: %s", res->input, res->error); | 532 | wlr_log(WLR_ERROR, "Command '%s' failed: %s", res->input, res->error); |
481 | } | 533 | } |
482 | free_cmd_results(res); | 534 | free_cmd_results(res); |
483 | // view must be focused for commands to affect it, | ||
484 | // so always refocus in-between command lists | ||
485 | seat_set_focus(seat, view->swayc); | ||
486 | } | 535 | } |
487 | list_free(criterias); | 536 | list_free(criterias); |
488 | seat_set_focus(seat, prior_focus); | 537 | seat_set_focus(seat, prior_focus); |
489 | } | 538 | } |
490 | 539 | ||
540 | static bool should_focus(struct sway_view *view) { | ||
541 | // If the view is the only one in the focused workspace, it'll get focus | ||
542 | // regardless of any no_focus criteria. | ||
543 | struct sway_container *parent = view->swayc->parent; | ||
544 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
545 | if (parent->type == C_WORKSPACE && seat_get_focus(seat) == parent) { | ||
546 | size_t num_children = parent->children->length + | ||
547 | parent->sway_workspace->floating->children->length; | ||
548 | if (num_children == 1) { | ||
549 | return true; | ||
550 | } | ||
551 | } | ||
552 | |||
553 | // Check no_focus criteria | ||
554 | list_t *criterias = criteria_for_view(view, CT_NO_FOCUS); | ||
555 | size_t len = criterias->length; | ||
556 | list_free(criterias); | ||
557 | return len == 0; | ||
558 | } | ||
559 | |||
491 | void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { | 560 | void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { |
492 | if (!sway_assert(view->surface == NULL, "cannot map mapped view")) { | 561 | if (!sway_assert(view->surface == NULL, "cannot map mapped view")) { |
493 | return; | 562 | return; |
@@ -524,7 +593,11 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { | |||
524 | prev_focus = target_sibling; | 593 | prev_focus = target_sibling; |
525 | target_sibling = seat_get_focus_inactive(seat, workspace); | 594 | target_sibling = seat_get_focus_inactive(seat, workspace); |
526 | } else { | 595 | } else { |
527 | // TODO: CT_ASSIGN_OUTPUT | 596 | // CT_ASSIGN_OUTPUT |
597 | struct sway_container *output = output_by_name(criteria->target); | ||
598 | if (output) { | ||
599 | prev_focus = seat_get_focus_inactive(seat, output); | ||
600 | } | ||
528 | } | 601 | } |
529 | } | 602 | } |
530 | list_free(criterias); | 603 | list_free(criterias); |
@@ -549,8 +622,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { | |||
549 | 622 | ||
550 | view->surface = wlr_surface; | 623 | view->surface = wlr_surface; |
551 | view->swayc = cont; | 624 | view->swayc = cont; |
552 | view->border = config->border; | ||
553 | view->border_thickness = config->border_thickness; | ||
554 | 625 | ||
555 | view_init_subsurfaces(view, wlr_surface); | 626 | view_init_subsurfaces(view, wlr_surface); |
556 | wl_signal_add(&wlr_surface->events.new_subsurface, | 627 | wl_signal_add(&wlr_surface->events.new_subsurface, |
@@ -561,11 +632,20 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { | |||
561 | view->container_reparent.notify = view_handle_container_reparent; | 632 | view->container_reparent.notify = view_handle_container_reparent; |
562 | 633 | ||
563 | if (view->impl->wants_floating && view->impl->wants_floating(view)) { | 634 | if (view->impl->wants_floating && view->impl->wants_floating(view)) { |
635 | view->border = config->floating_border; | ||
636 | view->border_thickness = config->floating_border_thickness; | ||
564 | container_set_floating(view->swayc, true); | 637 | container_set_floating(view->swayc, true); |
638 | } else { | ||
639 | view->border = config->border; | ||
640 | view->border_thickness = config->border_thickness; | ||
641 | view_set_tiled(view, true); | ||
565 | } | 642 | } |
566 | 643 | ||
567 | if (prev_focus == target_sibling) { | 644 | if (should_focus(view) && prev_focus == target_sibling) { |
568 | input_manager_set_focus(input_manager, cont); | 645 | input_manager_set_focus(input_manager, cont); |
646 | if (workspace) { | ||
647 | workspace_switch(workspace); | ||
648 | } | ||
569 | } | 649 | } |
570 | 650 | ||
571 | view_update_title(view, false); | 651 | view_update_title(view, false); |
@@ -581,16 +661,27 @@ void view_unmap(struct sway_view *view) { | |||
581 | wl_list_remove(&view->surface_new_subsurface.link); | 661 | wl_list_remove(&view->surface_new_subsurface.link); |
582 | wl_list_remove(&view->container_reparent.link); | 662 | wl_list_remove(&view->container_reparent.link); |
583 | 663 | ||
664 | if (view->urgent_timer) { | ||
665 | wl_event_source_remove(view->urgent_timer); | ||
666 | view->urgent_timer = NULL; | ||
667 | } | ||
668 | |||
669 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); | ||
670 | |||
671 | struct sway_container *parent; | ||
584 | if (view->is_fullscreen) { | 672 | if (view->is_fullscreen) { |
585 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); | ||
586 | ws->sway_workspace->fullscreen = NULL; | 673 | ws->sway_workspace->fullscreen = NULL; |
587 | container_destroy(view->swayc); | 674 | parent = container_destroy(view->swayc); |
588 | 675 | ||
589 | arrange_and_commit(ws->parent); | 676 | arrange_windows(ws->parent); |
590 | } else { | 677 | } else { |
591 | struct sway_container *parent = container_destroy(view->swayc); | 678 | parent = container_destroy(view->swayc); |
592 | arrange_and_commit(parent); | 679 | arrange_windows(parent); |
680 | } | ||
681 | if (parent->type >= C_WORKSPACE) { // if the workspace still exists | ||
682 | workspace_detect_urgent(ws); | ||
593 | } | 683 | } |
684 | transaction_commit_dirty(); | ||
594 | view->surface = NULL; | 685 | view->surface = NULL; |
595 | } | 686 | } |
596 | 687 | ||
@@ -601,6 +692,8 @@ void view_update_position(struct sway_view *view, double lx, double ly) { | |||
601 | container_damage_whole(view->swayc); | 692 | container_damage_whole(view->swayc); |
602 | view->x = lx; | 693 | view->x = lx; |
603 | view->y = ly; | 694 | view->y = ly; |
695 | view->swayc->current.view_x = lx; | ||
696 | view->swayc->current.view_y = ly; | ||
604 | if (container_is_floating(view->swayc)) { | 697 | if (container_is_floating(view->swayc)) { |
605 | container_set_geometry_from_floating_view(view->swayc); | 698 | container_set_geometry_from_floating_view(view->swayc); |
606 | } | 699 | } |
@@ -614,6 +707,8 @@ void view_update_size(struct sway_view *view, int width, int height) { | |||
614 | container_damage_whole(view->swayc); | 707 | container_damage_whole(view->swayc); |
615 | view->width = width; | 708 | view->width = width; |
616 | view->height = height; | 709 | view->height = height; |
710 | view->swayc->current.view_width = width; | ||
711 | view->swayc->current.view_height = height; | ||
617 | if (container_is_floating(view->swayc)) { | 712 | if (container_is_floating(view->swayc)) { |
618 | container_set_geometry_from_floating_view(view->swayc); | 713 | container_set_geometry_from_floating_view(view->swayc); |
619 | } | 714 | } |
@@ -624,7 +719,7 @@ static void view_subsurface_create(struct sway_view *view, | |||
624 | struct wlr_subsurface *subsurface) { | 719 | struct wlr_subsurface *subsurface) { |
625 | struct sway_view_child *child = calloc(1, sizeof(struct sway_view_child)); | 720 | struct sway_view_child *child = calloc(1, sizeof(struct sway_view_child)); |
626 | if (child == NULL) { | 721 | if (child == NULL) { |
627 | wlr_log(L_ERROR, "Allocation failed"); | 722 | wlr_log(WLR_ERROR, "Allocation failed"); |
628 | return; | 723 | return; |
629 | } | 724 | } |
630 | view_child_init(child, NULL, view, subsurface->surface); | 725 | view_child_init(child, NULL, view, subsurface->surface); |
@@ -744,8 +839,9 @@ struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { | |||
744 | return NULL; | 839 | return NULL; |
745 | } | 840 | } |
746 | 841 | ||
747 | wlr_log(L_DEBUG, "Surface of unknown type (role %s): %p", | 842 | const char *role = wlr_surface->role ? wlr_surface->role->name : NULL; |
748 | wlr_surface->role, wlr_surface); | 843 | wlr_log(WLR_DEBUG, "Surface of unknown type (role %s): %p", |
844 | role, wlr_surface); | ||
749 | return NULL; | 845 | return NULL; |
750 | } | 846 | } |
751 | 847 | ||
@@ -812,7 +908,7 @@ static char *escape_title(char *buffer) { | |||
812 | char *escaped_title = calloc(length + 1, sizeof(char)); | 908 | char *escaped_title = calloc(length + 1, sizeof(char)); |
813 | int result = escape_markup_text(buffer, escaped_title, length); | 909 | int result = escape_markup_text(buffer, escaped_title, length); |
814 | if (result != length) { | 910 | if (result != length) { |
815 | wlr_log(L_ERROR, "Could not escape title: %s", buffer); | 911 | wlr_log(WLR_ERROR, "Could not escape title: %s", buffer); |
816 | free(escaped_title); | 912 | free(escaped_title); |
817 | return buffer; | 913 | return buffer; |
818 | } | 914 | } |
@@ -946,7 +1042,7 @@ static void update_marks_texture(struct sway_view *view, | |||
946 | 1042 | ||
947 | double scale = output->sway_output->wlr_output->scale; | 1043 | double scale = output->sway_output->wlr_output->scale; |
948 | int width = 0; | 1044 | int width = 0; |
949 | int height = config->font_height * scale; | 1045 | int height = view->swayc->title_height * scale; |
950 | 1046 | ||
951 | cairo_t *c = cairo_create(NULL); | 1047 | cairo_t *c = cairo_create(NULL); |
952 | get_text_size(c, config->font, &width, NULL, scale, false, "%s", buffer); | 1048 | get_text_size(c, config->font, &width, NULL, scale, false, "%s", buffer); |
@@ -995,7 +1091,7 @@ void view_update_marks_textures(struct sway_view *view) { | |||
995 | } | 1091 | } |
996 | 1092 | ||
997 | bool view_is_visible(struct sway_view *view) { | 1093 | bool view_is_visible(struct sway_view *view) { |
998 | if (!view->swayc || view->swayc->destroying) { | 1094 | if (!view->swayc || view->swayc->destroying || !view->swayc->parent) { |
999 | return false; | 1095 | return false; |
1000 | } | 1096 | } |
1001 | struct sway_container *workspace = | 1097 | struct sway_container *workspace = |
@@ -1033,3 +1129,32 @@ bool view_is_visible(struct sway_view *view) { | |||
1033 | } | 1129 | } |
1034 | return true; | 1130 | return true; |
1035 | } | 1131 | } |
1132 | |||
1133 | void view_set_urgent(struct sway_view *view, bool enable) { | ||
1134 | if (view_is_urgent(view) == enable) { | ||
1135 | return; | ||
1136 | } | ||
1137 | if (enable) { | ||
1138 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
1139 | if (seat_get_focus(seat) == view->swayc) { | ||
1140 | return; | ||
1141 | } | ||
1142 | clock_gettime(CLOCK_MONOTONIC, &view->urgent); | ||
1143 | } else { | ||
1144 | view->urgent = (struct timespec){ 0 }; | ||
1145 | if (view->urgent_timer) { | ||
1146 | wl_event_source_remove(view->urgent_timer); | ||
1147 | view->urgent_timer = NULL; | ||
1148 | } | ||
1149 | } | ||
1150 | container_damage_whole(view->swayc); | ||
1151 | |||
1152 | ipc_event_window(view->swayc, "urgent"); | ||
1153 | |||
1154 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); | ||
1155 | workspace_detect_urgent(ws); | ||
1156 | } | ||
1157 | |||
1158 | bool view_is_urgent(struct sway_view *view) { | ||
1159 | return view->urgent.tv_sec || view->urgent.tv_nsec; | ||
1160 | } | ||