Merge remote-tracking branch 'vanilla/next' into gaps-next
Ingo Bürk
4 years ago
3 | 3 | use strict; |
4 | 4 | use warnings; |
5 | 5 | use Pod::Simple::HTML; |
6 | use Getopt::Long; | |
6 | 7 | use v5.10; |
8 | ||
9 | my $stylesurl = ''; | |
10 | ||
11 | GetOptions("stylesurl=s" => \$stylesurl) | |
12 | or die "parsing flags"; | |
7 | 13 | |
8 | 14 | $Pod::Simple::HTML::Tagmap{'Verbatim'} = '<pre><tt>'; |
9 | 15 | $Pod::Simple::HTML::Tagmap{'VerbatimFormatted'} = '<pre><tt>'; |
21 | 27 | my $parser = Pod::Simple::HTML->new(); |
22 | 28 | |
23 | 29 | $parser->index(1); |
24 | $parser->html_header_before_title( | |
25 | <<'EOF' | |
30 | if ($stylesurl ne '') { | |
31 | $parser->html_header_before_title( | |
32 | <<EOF | |
26 | 33 | <!doctype html> |
27 | 34 | <html lang="en"> |
28 | 35 | <head> |
30 | 37 | <meta charset="utf-8"> |
31 | 38 | <meta name="generator" content="Pod::Simple::HTML"> |
32 | 39 | <meta name="description" content="i3 Perl documentation"> |
33 | <link rel="stylesheet" href="https://i3wm.org/css/style.css" type="text/css" /> | |
40 | <link rel="stylesheet" href="$stylesurl/style.css" type="text/css" /> | |
34 | 41 | <style type="text/css"> |
35 | 42 | .pod pre { |
36 | 43 | background: #333; |
62 | 69 | </style> |
63 | 70 | <title> |
64 | 71 | EOF |
65 | ); | |
72 | ); | |
73 | } | |
66 | 74 | $parser->html_header_after_title( |
67 | 75 | <<'EOF' |
68 | 76 | </title> |
1916 | 1916 | to match only the currently focused window. |
1917 | 1917 | floating:: |
1918 | 1918 | Only matches floating windows. This criterion requires no value. |
1919 | floating_from:: | |
1920 | Like +floating+ but this criterion takes two possible values: "auto" | |
1921 | and "user". With "auto", only windows that were automatically opened as | |
1922 | floating are matched. With "user", only windows that the user made | |
1923 | floating are matched. | |
1919 | 1924 | tiling:: |
1920 | 1925 | Only matches tiling windows. This criterion requires no value. |
1926 | tiling_from:: | |
1927 | Like +tiling+ but this criterion takes two possible values: "auto" and | |
1928 | "user". With "auto", only windows that were automatically opened as | |
1929 | tiling are matched. With "user", only windows that the user made tiling | |
1930 | are matched. | |
1921 | 1931 | |
1922 | 1932 | The criteria +class+, +instance+, +role+, +title+, +workspace+ and +mark+ are |
1923 | 1933 | actually regular expressions (PCRE). See +pcresyntax(3)+ or +perldoc perlre+ for |
217 | 217 | # quote of the literal. We can do strdup(literal + 1); then :). |
218 | 218 | $token_name =~ s/'$//; |
219 | 219 | } |
220 | # Escape double quotes: | |
221 | $token_name =~ s,",\\",g; | |
220 | 222 | my $next_state = $token->{next_state}; |
221 | 223 | if ($next_state =~ /^call /) { |
222 | 224 | ($call_identifier) = ($next_state =~ /^call ([0-9]+)$/); |
142 | 142 | $desktops{$relative} = $File::Find::name; |
143 | 143 | }, |
144 | 144 | no_chdir => 1, |
145 | follow_fast => 1, | |
145 | 146 | }, |
146 | 147 | @searchdirs |
147 | 148 | ); |
548 | 548 | } dock; |
549 | 549 | xcb_window_t id; |
550 | 550 | enum { WM_ANY = 0, |
551 | WM_TILING_AUTO, | |
552 | WM_TILING_USER, | |
551 | 553 | WM_TILING, |
554 | WM_FLOATING_AUTO, | |
555 | WM_FLOATING_USER, | |
552 | 556 | WM_FLOATING } window_mode; |
553 | 557 | Con *con_id; |
554 | 558 |
207 | 207 | |
208 | 208 | # Criteria: Used by for_window and assign. |
209 | 209 | state CRITERIA: |
210 | ctype = 'class' -> CRITERION | |
211 | ctype = 'instance' -> CRITERION | |
212 | ctype = 'window_role' -> CRITERION | |
213 | ctype = 'con_id' -> CRITERION | |
214 | ctype = 'id' -> CRITERION | |
215 | ctype = 'window_type' -> CRITERION | |
216 | ctype = 'con_mark' -> CRITERION | |
217 | ctype = 'title' -> CRITERION | |
218 | ctype = 'urgent' -> CRITERION | |
219 | ctype = 'workspace' -> CRITERION | |
210 | ctype = 'class' -> CRITERION | |
211 | ctype = 'instance' -> CRITERION | |
212 | ctype = 'window_role' -> CRITERION | |
213 | ctype = 'con_id' -> CRITERION | |
214 | ctype = 'id' -> CRITERION | |
215 | ctype = 'window_type' -> CRITERION | |
216 | ctype = 'con_mark' -> CRITERION | |
217 | ctype = 'title' -> CRITERION | |
218 | ctype = 'urgent' -> CRITERION | |
219 | ctype = 'workspace' -> CRITERION | |
220 | ctype = 'floating_from' -> CRITERION_FROM | |
221 | ctype = 'tiling_from' -> CRITERION_FROM | |
220 | 222 | ctype = 'tiling', 'floating' |
221 | 223 | -> call cfg_criteria_add($ctype, NULL); CRITERIA |
222 | 224 | ']' |
224 | 226 | |
225 | 227 | state CRITERION: |
226 | 228 | '=' -> CRITERION_STR |
229 | ||
230 | state CRITERION_FROM: | |
231 | '=' -> CRITERION_FROM_STR_START | |
232 | ||
233 | state CRITERION_FROM_STR_START: | |
234 | '"' -> CRITERION_FROM_STR | |
235 | kind = 'auto', 'user' | |
236 | -> call cfg_criteria_add($ctype, $kind); CRITERIA | |
237 | ||
238 | state CRITERION_FROM_STR: | |
239 | kind = 'auto', 'user' | |
240 | -> CRITERION_FROM_STR_END | |
241 | ||
242 | state CRITERION_FROM_STR_END: | |
243 | '"' | |
244 | -> call cfg_criteria_add($ctype, $kind); CRITERIA | |
227 | 245 | |
228 | 246 | state CRITERION_STR: |
229 | 247 | cvalue = word |
841 | 841 | DLOG("The window was requested to be visible on all workspaces, making it sticky and floating.\n"); |
842 | 842 | |
843 | 843 | floating_enable(con, false); |
844 | con->floating = FLOATING_AUTO_ON; | |
844 | 845 | |
845 | 846 | con->sticky = true; |
846 | 847 | ewmh_update_sticky(con->window->id, true); |
1155 | 1156 | return true; |
1156 | 1157 | } |
1157 | 1158 | |
1159 | /* | |
1160 | * Handles the _I3_FLOATING_WINDOW property to properly run assignments for | |
1161 | * floating window changes. | |
1162 | * | |
1163 | * This is needed to correctly run the assignments after changes in floating | |
1164 | * windows which are triggered by user commands (floating enable | disable). In | |
1165 | * that case, we can't call run_assignments because it will modify the parser | |
1166 | * state when it needs to parse the user-specified action, breaking the parser | |
1167 | * state for the original command. | |
1168 | * | |
1169 | */ | |
1170 | static bool handle_i3_floating(Con *con, xcb_get_property_reply_t *prop) { | |
1171 | DLOG("floating change for con %p\n", con); | |
1172 | ||
1173 | remanage_window(con); | |
1174 | ||
1175 | return true; | |
1176 | } | |
1177 | ||
1158 | 1178 | /* Returns false if the event could not be processed (e.g. the window could not |
1159 | 1179 | * be found), true otherwise */ |
1160 | 1180 | typedef bool (*cb_property_handler_t)(Con *con, xcb_get_property_reply_t *property); |
1176 | 1196 | {0, 128, handle_class_change}, |
1177 | 1197 | {0, UINT_MAX, handle_strut_partial_change}, |
1178 | 1198 | {0, UINT_MAX, handle_window_type}, |
1199 | {0, UINT_MAX, handle_i3_floating}, | |
1179 | 1200 | {0, 5 * sizeof(uint64_t), handle_motif_hints_change}}; |
1180 | 1201 | #define NUM_HANDLERS (sizeof(property_handlers) / sizeof(struct property_handler_t)) |
1181 | 1202 | |
1197 | 1218 | property_handlers[7].atom = XCB_ATOM_WM_CLASS; |
1198 | 1219 | property_handlers[8].atom = A__NET_WM_STRUT_PARTIAL; |
1199 | 1220 | property_handlers[9].atom = A__NET_WM_WINDOW_TYPE; |
1200 | property_handlers[10].atom = A__MOTIF_WM_HINTS; | |
1221 | property_handlers[10].atom = A_I3_FLOATING_WINDOW; | |
1222 | property_handlers[11].atom = A__MOTIF_WM_HINTS; | |
1201 | 1223 | } |
1202 | 1224 | |
1203 | 1225 | static void property_notify(uint8_t state, xcb_window_t window, xcb_atom_t atom) { |
170 | 170 | con_attach(json_node, json_node->parent, true); |
171 | 171 | LOG("Creating window\n"); |
172 | 172 | x_con_init(json_node); |
173 | ||
174 | /* Fix erroneous JSON input regarding floating containers to avoid | |
175 | * crashing, see #3901. */ | |
176 | const int old_floating_mode = json_node->floating; | |
177 | if (old_floating_mode >= FLOATING_AUTO_ON && json_node->parent->type != CT_FLOATING_CON) { | |
178 | LOG("Fixing floating node without CT_FLOATING_CON parent\n"); | |
179 | ||
180 | /* Force floating_enable to work */ | |
181 | json_node->floating = FLOATING_AUTO_OFF; | |
182 | floating_enable(json_node, false); | |
183 | json_node->floating = old_floating_mode; | |
184 | } | |
185 | ||
173 | 186 | json_node = json_node->parent; |
174 | 187 | incomplete--; |
175 | 188 | DLOG("incomplete = %d\n", incomplete); |
537 | 537 | bool automatic_border = (motif_border_style == BS_NORMAL); |
538 | 538 | |
539 | 539 | floating_enable(nc, automatic_border); |
540 | nc->floating = FLOATING_AUTO_ON; | |
540 | 541 | } |
541 | 542 | |
542 | 543 | /* explicitly set the border width to the default */ |
214 | 214 | } |
215 | 215 | |
216 | 216 | if (match->window_mode != WM_ANY) { |
217 | if ((con = con_by_window_id(window->id)) == NULL) | |
218 | return false; | |
219 | ||
220 | const bool floating = (con_inside_floating(con) != NULL); | |
221 | ||
222 | if ((match->window_mode == WM_TILING && floating) || | |
223 | (match->window_mode == WM_FLOATING && !floating)) { | |
224 | LOG("window_mode does not match\n"); | |
225 | return false; | |
217 | if ((con = con_by_window_id(window->id)) == NULL) { | |
218 | return false; | |
219 | } | |
220 | ||
221 | switch (match->window_mode) { | |
222 | case WM_TILING_AUTO: | |
223 | if (con->floating != FLOATING_AUTO_OFF) { | |
224 | return false; | |
225 | } | |
226 | break; | |
227 | case WM_TILING_USER: | |
228 | if (con->floating != FLOATING_USER_OFF) { | |
229 | return false; | |
230 | } | |
231 | break; | |
232 | case WM_TILING: | |
233 | if (con_inside_floating(con) != NULL) { | |
234 | return false; | |
235 | } | |
236 | break; | |
237 | case WM_FLOATING_AUTO: | |
238 | if (con->floating != FLOATING_AUTO_ON) { | |
239 | return false; | |
240 | } | |
241 | break; | |
242 | case WM_FLOATING_USER: | |
243 | if (con->floating != FLOATING_USER_ON) { | |
244 | return false; | |
245 | } | |
246 | break; | |
247 | case WM_FLOATING: | |
248 | if (con_inside_floating(con) == NULL) { | |
249 | return false; | |
250 | } | |
251 | break; | |
252 | case WM_ANY: | |
253 | assert(false); | |
226 | 254 | } |
227 | 255 | |
228 | 256 | LOG("window_mode matches\n"); |
366 | 394 | return; |
367 | 395 | } |
368 | 396 | |
397 | if (strcmp(ctype, "tiling_from") == 0 && | |
398 | cvalue != NULL && | |
399 | strcmp(cvalue, "auto") == 0) { | |
400 | match->window_mode = WM_TILING_AUTO; | |
401 | return; | |
402 | } | |
403 | ||
404 | if (strcmp(ctype, "tiling_from") == 0 && | |
405 | cvalue != NULL && | |
406 | strcmp(cvalue, "user") == 0) { | |
407 | match->window_mode = WM_TILING_USER; | |
408 | return; | |
409 | } | |
410 | ||
369 | 411 | if (strcmp(ctype, "floating") == 0) { |
370 | 412 | match->window_mode = WM_FLOATING; |
371 | 413 | return; |
372 | 414 | } |
373 | 415 | |
416 | if (strcmp(ctype, "floating_from") == 0 && | |
417 | cvalue != NULL && | |
418 | strcmp(cvalue, "auto") == 0) { | |
419 | match->window_mode = WM_FLOATING_AUTO; | |
420 | return; | |
421 | } | |
422 | ||
423 | if (strcmp(ctype, "floating_from") == 0 && | |
424 | cvalue != NULL && | |
425 | strcmp(cvalue, "user") == 0) { | |
426 | match->window_mode = WM_FLOATING_USER; | |
427 | return; | |
428 | } | |
429 | ||
374 | 430 | ELOG("Unknown criterion: %s\n", ctype); |
375 | 431 | } |
826 | 826 | } |
827 | 827 | |
828 | 828 | /* |
829 | * Move the content of an outputs container to the first output. | |
830 | * | |
831 | * TODO: Maybe use an on_destroy callback which is implement differently for | |
832 | * different container types (CT_CONTENT vs. CT_DOCKAREA)? | |
833 | * | |
834 | */ | |
835 | static void move_content(Con *con) { | |
836 | Con *first = get_first_output()->con; | |
837 | Con *first_content = output_get_content(first); | |
838 | ||
839 | /* We need to move the workspaces from the disappearing output to the first output */ | |
840 | /* 1: Get the con to focus next */ | |
841 | Con *next = focused; | |
842 | ||
843 | /* 2: iterate through workspaces and re-assign them, fixing the coordinates | |
844 | * of floating containers as we go */ | |
845 | Con *current; | |
846 | Con *old_content = output_get_content(con); | |
847 | while (!TAILQ_EMPTY(&(old_content->nodes_head))) { | |
848 | current = TAILQ_FIRST(&(old_content->nodes_head)); | |
849 | if (current != next && TAILQ_EMPTY(&(current->focus_head))) { | |
850 | /* the workspace is empty and not focused, get rid of it */ | |
851 | DLOG("Getting rid of current = %p / %s (empty, unfocused)\n", current, current->name); | |
852 | tree_close_internal(current, DONT_KILL_WINDOW, false); | |
853 | continue; | |
854 | } | |
855 | DLOG("Detaching current = %p / %s\n", current, current->name); | |
856 | con_detach(current); | |
857 | DLOG("Re-attaching current = %p / %s\n", current, current->name); | |
858 | con_attach(current, first_content, false); | |
859 | DLOG("Fixing the coordinates of floating containers\n"); | |
860 | Con *floating_con; | |
861 | TAILQ_FOREACH (floating_con, &(current->floating_head), floating_windows) { | |
862 | floating_fix_coordinates(floating_con, &(con->rect), &(first->rect)); | |
863 | } | |
864 | } | |
865 | ||
866 | /* Restore focus after con_detach / con_attach. next can be NULL, see #3523. */ | |
867 | if (next) { | |
868 | DLOG("now focusing next = %p\n", next); | |
869 | con_focus(next); | |
870 | workspace_show(con_get_workspace(next)); | |
871 | } | |
872 | ||
873 | /* 3: move the dock clients to the first output */ | |
874 | Con *child; | |
875 | TAILQ_FOREACH (child, &(con->nodes_head), nodes) { | |
876 | if (child->type != CT_DOCKAREA) { | |
877 | continue; | |
878 | } | |
879 | DLOG("Handling dock con %p\n", child); | |
880 | Con *dock; | |
881 | while (!TAILQ_EMPTY(&(child->nodes_head))) { | |
882 | dock = TAILQ_FIRST(&(child->nodes_head)); | |
883 | Con *nc; | |
884 | Match *match; | |
885 | nc = con_for_window(first, dock->window, &match); | |
886 | DLOG("Moving dock client %p to nc %p\n", dock, nc); | |
887 | con_detach(dock); | |
888 | DLOG("Re-attaching\n"); | |
889 | con_attach(dock, nc, false); | |
890 | DLOG("Done\n"); | |
891 | } | |
892 | } | |
893 | ||
894 | DLOG("Destroying disappearing con %p\n", con); | |
895 | tree_close_internal(con, DONT_KILL_WINDOW, true); | |
896 | } | |
897 | ||
898 | /* | |
829 | 899 | * (Re-)queries the outputs via RandR and stores them in the list of outputs. |
830 | 900 | * |
831 | 901 | * If no outputs are found use the root window. |
867 | 937 | other->rect.y != output->rect.y) |
868 | 938 | continue; |
869 | 939 | |
870 | DLOG("output %p has the same position, his mode = %d x %d\n", | |
940 | DLOG("output %p has the same position, its mode = %d x %d\n", | |
871 | 941 | other, other->rect.width, other->rect.height); |
872 | 942 | uint32_t width = min(other->rect.width, output->rect.width); |
873 | 943 | uint32_t height = min(other->rect.height, output->rect.height); |
900 | 970 | } |
901 | 971 | } |
902 | 972 | |
973 | /* Ensure that all containers with type CT_OUTPUT have a valid | |
974 | * corresponding entry in outputs. This can happen in situations related to | |
975 | * those mentioned #3767 e.g. when a CT_OUTPUT is created from an in-place | |
976 | * restart's layout but the output is disabled by a randr query happening | |
977 | * at the same time. */ | |
978 | Con *con; | |
979 | for (con = TAILQ_FIRST(&(croot->nodes_head)); con;) { | |
980 | Con *next = TAILQ_NEXT(con, nodes); | |
981 | if (!con_is_internal(con) && get_output_by_name(con->name, true) == NULL) { | |
982 | DLOG("No output %s found, moving its old content to first output\n", con->name); | |
983 | move_content(con); | |
984 | } | |
985 | con = next; | |
986 | } | |
987 | ||
903 | 988 | /* Handle outputs which have a new mode or are disabled now (either |
904 | 989 | * because the user disabled them or because they are clones) */ |
905 | 990 | TAILQ_FOREACH (output, &outputs, outputs) { |
951 | 1036 | output->active = false; |
952 | 1037 | DLOG("Output %s disabled, re-assigning workspaces/docks\n", output_primary_name(output)); |
953 | 1038 | |
954 | Output *first = get_first_output(); | |
955 | ||
956 | /* TODO: refactor the following code into a nice function. maybe | |
957 | * use an on_destroy callback which is implement differently for | |
958 | * different container types (CT_CONTENT vs. CT_DOCKAREA)? */ | |
959 | Con *first_content = output_get_content(first->con); | |
960 | ||
961 | 1039 | if (output->con != NULL) { |
962 | /* We need to move the workspaces from the disappearing output to the first output */ | |
963 | /* 1: Get the con to focus next */ | |
964 | Con *next = focused; | |
965 | ||
966 | /* 2: iterate through workspaces and re-assign them, fixing the coordinates | |
967 | * of floating containers as we go */ | |
968 | Con *current; | |
969 | Con *old_content = output_get_content(output->con); | |
970 | while (!TAILQ_EMPTY(&(old_content->nodes_head))) { | |
971 | current = TAILQ_FIRST(&(old_content->nodes_head)); | |
972 | if (current != next && TAILQ_EMPTY(&(current->focus_head))) { | |
973 | /* the workspace is empty and not focused, get rid of it */ | |
974 | DLOG("Getting rid of current = %p / %s (empty, unfocused)\n", current, current->name); | |
975 | tree_close_internal(current, DONT_KILL_WINDOW, false); | |
976 | continue; | |
977 | } | |
978 | DLOG("Detaching current = %p / %s\n", current, current->name); | |
979 | con_detach(current); | |
980 | DLOG("Re-attaching current = %p / %s\n", current, current->name); | |
981 | con_attach(current, first_content, false); | |
982 | DLOG("Fixing the coordinates of floating containers\n"); | |
983 | Con *floating_con; | |
984 | TAILQ_FOREACH (floating_con, &(current->floating_head), floating_windows) { | |
985 | floating_fix_coordinates(floating_con, &(output->con->rect), &(first->con->rect)); | |
986 | } | |
987 | } | |
988 | ||
989 | /* Restore focus after con_detach / con_attach. next can be NULL, see #3523. */ | |
990 | if (next) { | |
991 | DLOG("now focusing next = %p\n", next); | |
992 | con_focus(next); | |
993 | workspace_show(con_get_workspace(next)); | |
994 | } | |
995 | ||
996 | /* 3: move the dock clients to the first output */ | |
997 | Con *child; | |
998 | TAILQ_FOREACH (child, &(output->con->nodes_head), nodes) { | |
999 | if (child->type != CT_DOCKAREA) | |
1000 | continue; | |
1001 | DLOG("Handling dock con %p\n", child); | |
1002 | Con *dock; | |
1003 | while (!TAILQ_EMPTY(&(child->nodes_head))) { | |
1004 | dock = TAILQ_FIRST(&(child->nodes_head)); | |
1005 | Con *nc; | |
1006 | Match *match; | |
1007 | nc = con_for_window(first->con, dock->window, &match); | |
1008 | DLOG("Moving dock client %p to nc %p\n", dock, nc); | |
1009 | con_detach(dock); | |
1010 | DLOG("Re-attaching\n"); | |
1011 | con_attach(dock, nc, false); | |
1012 | DLOG("Done\n"); | |
1013 | } | |
1014 | } | |
1015 | ||
1016 | DLOG("destroying disappearing con %p\n", output->con); | |
1040 | /* clear the pointer before move_content calls tree_close_internal in which the memory is freed */ | |
1017 | 1041 | Con *con = output->con; |
1018 | /* clear the pointer before calling tree_close_internal in which the memory is freed */ | |
1019 | 1042 | output->con = NULL; |
1020 | tree_close_internal(con, DONT_KILL_WINDOW, true); | |
1021 | DLOG("Done. Should be fine now\n"); | |
1043 | move_content(con); | |
1022 | 1044 | } |
1023 | 1045 | |
1024 | 1046 | output->to_be_disabled = false; |
97 | 97 | ################################################################################ |
98 | 98 | |
99 | 99 | $config = <<'EOT'; |
100 | for_window [] nop empty | |
100 | 101 | for_window [class="^Chrome"] floating enable |
101 | EOT | |
102 | ||
103 | $expected = <<'EOT'; | |
102 | for_window [class=^Chrome] floating enable | |
103 | for_window [floating_from = "auto" class= ==Class== ] nop floating | |
104 | for_window [tiling_from=auto class="==Class=="]nop floating | |
105 | EOT | |
106 | ||
107 | $expected = <<'EOT'; | |
108 | cfg_for_window(nop empty) | |
104 | 109 | cfg_criteria_add(class, ^Chrome) |
105 | 110 | cfg_for_window(floating enable) |
111 | cfg_criteria_add(class, ^Chrome) | |
112 | cfg_for_window(floating enable) | |
113 | cfg_criteria_add(floating_from, auto) | |
114 | cfg_criteria_add(class, ==Class==) | |
115 | cfg_for_window(nop floating) | |
116 | cfg_criteria_add(tiling_from, auto) | |
117 | cfg_criteria_add(class, ==Class==) | |
118 | cfg_for_window(nop floating) | |
106 | 119 | EOT |
107 | 120 | |
108 | 121 | is(parser_calls($config), |
109 | 122 | $expected, |
110 | 123 | 'for_window okay'); |
124 | ||
125 | $config = <<'EOT'; | |
126 | for_window [tiling_from=typo] nop typo | |
127 | for_window [tiling_from="typo"] nop typo | |
128 | EOT | |
129 | ||
130 | $expected = <<'EOT'; | |
131 | ERROR: CONFIG: Expected one of these tokens: '"', 'auto', 'user' | |
132 | ERROR: CONFIG: (in file <stdin>) | |
133 | ERROR: CONFIG: Line 1: for_window [tiling_from=typo] nop typo | |
134 | ERROR: CONFIG: ^^^^^^^^^^^^^^ | |
135 | ERROR: CONFIG: Line 2: for_window [tiling_from="typo"] nop typo | |
136 | ERROR: CONFIG: Expected one of these tokens: 'auto', 'user' | |
137 | ERROR: CONFIG: (in file <stdin>) | |
138 | ERROR: CONFIG: Line 1: for_window [tiling_from=typo] nop typo | |
139 | ERROR: CONFIG: Line 2: for_window [tiling_from="typo"] nop typo | |
140 | ERROR: CONFIG: ^^^^^^^^^^^^^^^ | |
141 | EOT | |
142 | ||
143 | is(parser_calls($config), | |
144 | $expected, | |
145 | 'for_window errors okay'); | |
111 | 146 | |
112 | 147 | ################################################################################ |
113 | 148 | # assign |
211 | 211 | EOT |
212 | 212 | $fh->flush; |
213 | 213 | $reply = cmd "append_layout $filename"; |
214 | ok(!$reply->[0]->{success}, 'IPC reply indicated success'); | |
214 | ok(!$reply->[0]->{success}, 'IPC reply did not indicate success'); | |
215 | 215 | |
216 | 216 | does_i3_live; |
217 | 217 | |
275 | 275 | |
276 | 276 | close($fh); |
277 | 277 | |
278 | ################################################################################ | |
279 | # Issue with floating key being set, without proper parent | |
280 | # See #3901 | |
281 | ################################################################################ | |
282 | subtest 'issue 3901' => sub { | |
283 | kill_all_windows; | |
284 | $ws = fresh_workspace; | |
285 | is(scalar @{get_ws($ws)->{floating_nodes}}, 0, 'No floating nodes yet'); | |
286 | ||
287 | ($fh, $filename) = tempfile(UNLINK => 1); | |
288 | print $fh <<'EOT'; | |
289 | // vim:ts=4:sw=4:et | |
290 | { | |
291 | "border": "pixel", | |
292 | "current_border_width": 1, | |
293 | "floating": "auto_on", // crashes: user_on, auto_on, no crash: user_off, auto_off | |
294 | "geometry": { | |
295 | "height": 400, | |
296 | "width": 300, | |
297 | "x": 820, | |
298 | "y": 350 | |
299 | }, | |
300 | "name": "Click me to crash", | |
301 | "percent": 0.5, // still crashes if this field is absent | |
302 | "swallows": [ | |
303 | { | |
304 | "class": "^this doesn't matter as long as it doesn't match a new window$" | |
305 | } | |
306 | ], | |
307 | "type": "con" | |
308 | } | |
309 | ||
310 | EOT | |
311 | $fh->flush; | |
312 | $reply = cmd "append_layout $filename"; | |
313 | ok($reply->[0]->{success}, 'IPC reply indicated success'); | |
314 | ||
315 | cmd '[floating] focus'; | |
316 | is(scalar @{get_ws($ws)->{floating_nodes}}, 1, 'one floating node on this ws'); | |
317 | ||
318 | does_i3_live; | |
319 | ||
320 | close($fh); | |
321 | }; | |
278 | 322 | |
279 | 323 | done_testing; |
16 | 16 | use i3test i3_config => <<EOT; |
17 | 17 | # i3 config file (v4) |
18 | 18 | font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1 |
19 | for_window [tiling] mark tiled | |
20 | for_window [floating] mark floated | |
19 | for_window [tiling] mark --add tiling | |
20 | for_window [floating] mark --add floating | |
21 | ||
22 | for_window [tiling_from="auto"] mark --add tiling_auto | |
23 | for_window [floating_from="auto"] mark --add floating_auto | |
24 | ||
25 | for_window [tiling_from="user"] mark --add tiling_user | |
26 | for_window [floating_from="user"] mark --add floating_user | |
21 | 27 | EOT |
22 | 28 | use X11::XCB qw(PROP_MODE_REPLACE); |
23 | 29 | |
24 | 30 | ############################################################## |
25 | # 13: check that the tiling / floating criteria work. | |
31 | # Check that the auto tiling / floating criteria work. | |
26 | 32 | ############################################################## |
27 | 33 | |
28 | 34 | my $tmp = fresh_workspace; |
29 | ||
30 | open_window; | |
31 | open_floating_window; | |
35 | my $A = open_window; | |
36 | my $B = open_floating_window; | |
32 | 37 | |
33 | 38 | my @nodes = @{get_ws($tmp)->{nodes}}; |
34 | 39 | cmp_ok(@nodes, '==', 1, 'one tiling container on this workspace'); |
35 | is_deeply($nodes[0]->{marks}, [ 'tiled' ], "mark set for 'tiling' criterion"); | |
40 | is_deeply($nodes[0]->{marks}, [ 'tiling', 'tiling_auto' ], "mark set for 'tiling' criterion"); | |
36 | 41 | |
37 | 42 | @nodes = @{get_ws($tmp)->{floating_nodes}}; |
38 | 43 | cmp_ok(@nodes, '==', 1, 'one floating container on this workspace'); |
39 | is_deeply($nodes[0]->{nodes}[0]->{marks}, [ 'floated' ], "mark set for 'floating' criterion"); | |
44 | is_deeply($nodes[0]->{nodes}[0]->{marks}, [ 'floating', 'floating_auto' ], "mark set for 'floating' criterion"); | |
45 | ||
46 | ################################################################################ | |
47 | # Check that the user tiling / floating criteria work. | |
48 | # The following rules are triggered here: 'tiling', 'tiling_user', 'floating', | |
49 | # 'floating_user'. Therefore, the old marks 'tiling' and 'floating' are | |
50 | # replaced but the 'tiling_auto' and 'floating_auto' marks are preserved. | |
51 | ################################################################################ | |
52 | ||
53 | cmd '[id=' . $A->{id} . '] floating enable'; | |
54 | cmd '[id=' . $B->{id} . '] floating disable'; | |
55 | ||
56 | @nodes = @{get_ws($tmp)->{nodes}}; | |
57 | cmp_ok(@nodes, '==', 1, 'one tiling container on this workspace'); | |
58 | is_deeply($nodes[0]->{marks}, [ 'floating_auto', 'tiling', 'tiling_user' ], "Marks updated after 'floating_user' criterion"); | |
59 | ||
60 | @nodes = @{get_ws($tmp)->{floating_nodes}}; | |
61 | cmp_ok(@nodes, '==', 1, 'one floating container on this workspace'); | |
62 | is_deeply($nodes[0]->{nodes}[0]->{marks}, [ 'tiling_auto', 'floating', 'floating_user' ], "Marks updated after 'tiling_user' criterion"); | |
63 | ||
64 | ################################################################################ | |
65 | # Check that the default and auto rules do not re-trigger | |
66 | # Here, the windows are returned to their original state but since the rules | |
67 | # `tiling`, `tiling_auto`, `floating` and `floating_auto` where already | |
68 | # triggered, only the `tiling_user` and `floating_user` rules should trigger. | |
69 | ################################################################################ | |
70 | ||
71 | # Use 'mark' to clear old marks | |
72 | cmd '[id=' . $A->{id} . '] mark A, floating disable'; | |
73 | cmd '[id=' . $B->{id} . '] mark B, floating enable'; | |
74 | ||
75 | @nodes = @{get_ws($tmp)->{nodes}}; | |
76 | cmp_ok(@nodes, '==', 1, 'one tiling container on this workspace'); | |
77 | is_deeply($nodes[0]->{marks}, [ 'A', 'tiling_user' ], "Only 'tiling_user' rule triggered"); | |
78 | ||
79 | @nodes = @{get_ws($tmp)->{floating_nodes}}; | |
80 | cmp_ok(@nodes, '==', 1, 'one floating container on this workspace'); | |
81 | is_deeply($nodes[0]->{nodes}[0]->{marks}, [ 'B', 'floating_user' ], "Only 'floating_user' rule triggered"); | |
40 | 82 | |
41 | 83 | ############################################################## |
42 | 84 |
6 | 6 | do |
7 | 7 | asciidoc -a linkcss -a stylesdir=https://i3wm.org/css -a scriptsdir=https://i3wm.org/js --backend=xhtml11 -f docs/asciidoc-git.conf $(dirname $f)/$(basename $f .html) |
8 | 8 | done |
9 | ./docs/i3-pod2html i3-dmenu-desktop man/i3-dmenu-desktop.html | |
10 | ./docs/i3-pod2html i3-save-tree man/i3-save-tree.html | |
11 | ./docs/i3-pod2html build/testcases/lib/i3test.pm docs/lib-i3test.html | |
12 | ./docs/i3-pod2html testcases/lib/i3test/Test.pm docs/lib-i3test-test.html | |
9 | ./docs/i3-pod2html --stylesurl=https://i3wm.org/css i3-dmenu-desktop man/i3-dmenu-desktop.html | |
10 | ./docs/i3-pod2html --stylesurl=https://i3wm.org/css i3-save-tree man/i3-save-tree.html | |
11 | ./docs/i3-pod2html --stylesurl=https://i3wm.org/css build/testcases/lib/i3test.pm docs/lib-i3test.html | |
12 | ./docs/i3-pod2html --stylesurl=https://i3wm.org/css testcases/lib/i3test/Test.pm docs/lib-i3test-test.html | |
13 | 13 | for file in $(sed 's/\.1$/.man/g' debian/i3-wm.manpages) |
14 | 14 | do |
15 | 15 | [ -f "$file" ] && asciidoc -a linkcss -a stylesdir=https://i3wm.org/css -a scriptsdir=https://i3wm.org/js --backend=xhtml11 -f docs/asciidoc-git.conf "$file" |