diff options
Diffstat (limited to 'sway')
-rw-r--r-- | sway/commands/seat.c | 2 | ||||
-rw-r--r-- | sway/commands/seat/idle.c | 73 | ||||
-rw-r--r-- | sway/config/seat.c | 11 | ||||
-rw-r--r-- | sway/input/cursor.c | 16 | ||||
-rw-r--r-- | sway/input/keyboard.c | 2 | ||||
-rw-r--r-- | sway/input/seat.c | 31 | ||||
-rw-r--r-- | sway/input/switch.c | 4 | ||||
-rw-r--r-- | sway/meson.build | 1 | ||||
-rw-r--r-- | sway/sway-input.5.scd | 12 |
9 files changed, 141 insertions, 11 deletions
diff --git a/sway/commands/seat.c b/sway/commands/seat.c index a2a3fbc4..eba28cac 100644 --- a/sway/commands/seat.c +++ b/sway/commands/seat.c | |||
@@ -18,6 +18,8 @@ static struct cmd_handler seat_handlers[] = { | |||
18 | { "attach", seat_cmd_attach }, | 18 | { "attach", seat_cmd_attach }, |
19 | { "fallback", seat_cmd_fallback }, | 19 | { "fallback", seat_cmd_fallback }, |
20 | { "hide_cursor", seat_cmd_hide_cursor }, | 20 | { "hide_cursor", seat_cmd_hide_cursor }, |
21 | { "idle_inhibit", seat_cmd_idle_inhibit }, | ||
22 | { "idle_wake", seat_cmd_idle_wake }, | ||
21 | { "keyboard_grouping", seat_cmd_keyboard_grouping }, | 23 | { "keyboard_grouping", seat_cmd_keyboard_grouping }, |
22 | { "pointer_constraint", seat_cmd_pointer_constraint }, | 24 | { "pointer_constraint", seat_cmd_pointer_constraint }, |
23 | { "xcursor_theme", seat_cmd_xcursor_theme }, | 25 | { "xcursor_theme", seat_cmd_xcursor_theme }, |
diff --git a/sway/commands/seat/idle.c b/sway/commands/seat/idle.c new file mode 100644 index 00000000..82428f2c --- /dev/null +++ b/sway/commands/seat/idle.c | |||
@@ -0,0 +1,73 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <limits.h> | ||
3 | #include <string.h> | ||
4 | #include <strings.h> | ||
5 | #include <stdint.h> | ||
6 | #include "sway/commands.h" | ||
7 | #include "sway/config.h" | ||
8 | #include "sway/input/seat.h" | ||
9 | |||
10 | static const struct { | ||
11 | const char *name; | ||
12 | uint32_t value; | ||
13 | } idle_source_strings[] = { | ||
14 | { "keyboard", IDLE_SOURCE_KEYBOARD }, | ||
15 | { "pointer", IDLE_SOURCE_POINTER }, | ||
16 | { "touch", IDLE_SOURCE_TOUCH }, | ||
17 | { "tablet_pad", IDLE_SOURCE_TABLET_PAD }, | ||
18 | { "tablet_tool", IDLE_SOURCE_TABLET_TOOL }, | ||
19 | { "switch", IDLE_SOURCE_SWITCH }, | ||
20 | }; | ||
21 | |||
22 | static uint32_t parse_sources(int argc, char **argv) { | ||
23 | uint32_t sources = 0; | ||
24 | for (int i = 0; i < argc; ++i) { | ||
25 | uint32_t value = 0; | ||
26 | for (size_t j = 0; j < sizeof(idle_source_strings) | ||
27 | / sizeof(idle_source_strings[0]); ++j) { | ||
28 | if (strcasecmp(idle_source_strings[j].name, argv[i]) == 0) { | ||
29 | value = idle_source_strings[j].value; | ||
30 | break; | ||
31 | } | ||
32 | } | ||
33 | if (value == 0) { | ||
34 | return UINT32_MAX; | ||
35 | } | ||
36 | sources |= value; | ||
37 | } | ||
38 | return sources; | ||
39 | } | ||
40 | |||
41 | struct cmd_results *seat_cmd_idle_inhibit(int argc, char **argv) { | ||
42 | struct cmd_results *error = NULL; | ||
43 | if ((error = checkarg(argc, "idle_inhibit", EXPECTED_AT_LEAST, 1))) { | ||
44 | return error; | ||
45 | } | ||
46 | if (!config->handler_context.seat_config) { | ||
47 | return cmd_results_new(CMD_FAILURE, "No seat defined"); | ||
48 | } | ||
49 | |||
50 | uint32_t sources = parse_sources(argc, argv); | ||
51 | if (sources == UINT32_MAX) { | ||
52 | return cmd_results_new(CMD_FAILURE, "Invalid idle source"); | ||
53 | } | ||
54 | config->handler_context.seat_config->idle_inhibit_sources = sources; | ||
55 | return cmd_results_new(CMD_SUCCESS, NULL); | ||
56 | } | ||
57 | |||
58 | struct cmd_results *seat_cmd_idle_wake(int argc, char **argv) { | ||
59 | struct cmd_results *error = NULL; | ||
60 | if ((error = checkarg(argc, "idle_wake", EXPECTED_AT_LEAST, 1))) { | ||
61 | return error; | ||
62 | } | ||
63 | if (!config->handler_context.seat_config) { | ||
64 | return cmd_results_new(CMD_FAILURE, "No seat defined"); | ||
65 | } | ||
66 | |||
67 | uint32_t sources = parse_sources(argc, argv); | ||
68 | if (sources == UINT32_MAX) { | ||
69 | return cmd_results_new(CMD_FAILURE, "Invalid idle source"); | ||
70 | } | ||
71 | config->handler_context.seat_config->idle_wake_sources = sources; | ||
72 | return cmd_results_new(CMD_SUCCESS, NULL); | ||
73 | } | ||
diff --git a/sway/config/seat.c b/sway/config/seat.c index d2401162..6c916727 100644 --- a/sway/config/seat.c +++ b/sway/config/seat.c | |||
@@ -1,4 +1,5 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | 1 | #define _POSIX_C_SOURCE 200809L |
2 | #include <limits.h> | ||
2 | #include <stdlib.h> | 3 | #include <stdlib.h> |
3 | #include <string.h> | 4 | #include <string.h> |
4 | #include "sway/config.h" | 5 | #include "sway/config.h" |
@@ -17,6 +18,8 @@ struct seat_config *new_seat_config(const char* name) { | |||
17 | return NULL; | 18 | return NULL; |
18 | } | 19 | } |
19 | 20 | ||
21 | seat->idle_inhibit_sources = seat->idle_wake_sources = UINT32_MAX; | ||
22 | |||
20 | seat->fallback = -1; | 23 | seat->fallback = -1; |
21 | seat->attachments = create_list(); | 24 | seat->attachments = create_list(); |
22 | if (!sway_assert(seat->attachments, | 25 | if (!sway_assert(seat->attachments, |
@@ -160,6 +163,14 @@ void merge_seat_config(struct seat_config *dest, struct seat_config *source) { | |||
160 | dest->xcursor_theme.name = strdup(source->xcursor_theme.name); | 163 | dest->xcursor_theme.name = strdup(source->xcursor_theme.name); |
161 | dest->xcursor_theme.size = source->xcursor_theme.size; | 164 | dest->xcursor_theme.size = source->xcursor_theme.size; |
162 | } | 165 | } |
166 | |||
167 | if (source->idle_inhibit_sources != UINT32_MAX) { | ||
168 | dest->idle_inhibit_sources = source->idle_inhibit_sources; | ||
169 | } | ||
170 | |||
171 | if (source->idle_wake_sources != UINT32_MAX) { | ||
172 | dest->idle_wake_sources = source->idle_wake_sources; | ||
173 | } | ||
163 | } | 174 | } |
164 | 175 | ||
165 | struct seat_config *copy_seat_config(struct seat_config *seat) { | 176 | struct seat_config *copy_seat_config(struct seat_config *seat) { |
diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 83b5212d..680fe39e 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c | |||
@@ -206,7 +206,7 @@ void cursor_handle_activity(struct sway_cursor *cursor) { | |||
206 | wl_event_source_timer_update( | 206 | wl_event_source_timer_update( |
207 | cursor->hide_source, cursor_get_timeout(cursor)); | 207 | cursor->hide_source, cursor_get_timeout(cursor)); |
208 | 208 | ||
209 | wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat); | 209 | seat_idle_notify_activity(cursor->seat, IDLE_SOURCE_POINTER); |
210 | if (cursor->hidden) { | 210 | if (cursor->hidden) { |
211 | cursor_unhide(cursor); | 211 | cursor_unhide(cursor); |
212 | } | 212 | } |
@@ -341,7 +341,7 @@ static void handle_cursor_frame(struct wl_listener *listener, void *data) { | |||
341 | 341 | ||
342 | static void handle_touch_down(struct wl_listener *listener, void *data) { | 342 | static void handle_touch_down(struct wl_listener *listener, void *data) { |
343 | struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_down); | 343 | struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_down); |
344 | wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat); | 344 | seat_idle_notify_activity(cursor->seat, IDLE_SOURCE_TOUCH); |
345 | struct wlr_event_touch_down *event = data; | 345 | struct wlr_event_touch_down *event = data; |
346 | 346 | ||
347 | struct sway_seat *seat = cursor->seat; | 347 | struct sway_seat *seat = cursor->seat; |
@@ -372,7 +372,7 @@ static void handle_touch_down(struct wl_listener *listener, void *data) { | |||
372 | 372 | ||
373 | static void handle_touch_up(struct wl_listener *listener, void *data) { | 373 | static void handle_touch_up(struct wl_listener *listener, void *data) { |
374 | struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_up); | 374 | struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_up); |
375 | wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat); | 375 | seat_idle_notify_activity(cursor->seat, IDLE_SOURCE_TOUCH); |
376 | struct wlr_event_touch_up *event = data; | 376 | struct wlr_event_touch_up *event = data; |
377 | struct wlr_seat *seat = cursor->seat->wlr_seat; | 377 | struct wlr_seat *seat = cursor->seat->wlr_seat; |
378 | // TODO: fall back to cursor simulation if client has not bound to touch | 378 | // TODO: fall back to cursor simulation if client has not bound to touch |
@@ -382,7 +382,7 @@ static void handle_touch_up(struct wl_listener *listener, void *data) { | |||
382 | static void handle_touch_motion(struct wl_listener *listener, void *data) { | 382 | static void handle_touch_motion(struct wl_listener *listener, void *data) { |
383 | struct sway_cursor *cursor = | 383 | struct sway_cursor *cursor = |
384 | wl_container_of(listener, cursor, touch_motion); | 384 | wl_container_of(listener, cursor, touch_motion); |
385 | wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat); | 385 | seat_idle_notify_activity(cursor->seat, IDLE_SOURCE_TOUCH); |
386 | struct wlr_event_touch_motion *event = data; | 386 | struct wlr_event_touch_motion *event = data; |
387 | 387 | ||
388 | struct sway_seat *seat = cursor->seat; | 388 | struct sway_seat *seat = cursor->seat; |
@@ -492,7 +492,7 @@ static void handle_tablet_tool_position(struct sway_cursor *cursor, | |||
492 | 492 | ||
493 | static void handle_tool_axis(struct wl_listener *listener, void *data) { | 493 | static void handle_tool_axis(struct wl_listener *listener, void *data) { |
494 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_axis); | 494 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_axis); |
495 | wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat); | 495 | seat_idle_notify_activity(cursor->seat, IDLE_SOURCE_TABLET_TOOL); |
496 | struct wlr_event_tablet_tool_axis *event = data; | 496 | struct wlr_event_tablet_tool_axis *event = data; |
497 | struct sway_tablet_tool *sway_tool = event->tool->data; | 497 | struct sway_tablet_tool *sway_tool = event->tool->data; |
498 | 498 | ||
@@ -548,7 +548,7 @@ static void handle_tool_axis(struct wl_listener *listener, void *data) { | |||
548 | 548 | ||
549 | static void handle_tool_tip(struct wl_listener *listener, void *data) { | 549 | static void handle_tool_tip(struct wl_listener *listener, void *data) { |
550 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip); | 550 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip); |
551 | wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat); | 551 | seat_idle_notify_activity(cursor->seat, IDLE_SOURCE_TABLET_TOOL); |
552 | struct wlr_event_tablet_tool_tip *event = data; | 552 | struct wlr_event_tablet_tool_tip *event = data; |
553 | struct sway_tablet_tool *sway_tool = event->tool->data; | 553 | struct sway_tablet_tool *sway_tool = event->tool->data; |
554 | struct wlr_tablet_v2_tablet *tablet_v2 = sway_tool->tablet->tablet_v2; | 554 | struct wlr_tablet_v2_tablet *tablet_v2 = sway_tool->tablet->tablet_v2; |
@@ -589,7 +589,7 @@ static struct sway_tablet *get_tablet_for_device(struct sway_cursor *cursor, | |||
589 | 589 | ||
590 | static void handle_tool_proximity(struct wl_listener *listener, void *data) { | 590 | static void handle_tool_proximity(struct wl_listener *listener, void *data) { |
591 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_proximity); | 591 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_proximity); |
592 | wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat); | 592 | seat_idle_notify_activity(cursor->seat, IDLE_SOURCE_TABLET_TOOL); |
593 | struct wlr_event_tablet_tool_proximity *event = data; | 593 | struct wlr_event_tablet_tool_proximity *event = data; |
594 | 594 | ||
595 | struct wlr_tablet_tool *tool = event->tool; | 595 | struct wlr_tablet_tool *tool = event->tool; |
@@ -619,7 +619,7 @@ static void handle_tool_proximity(struct wl_listener *listener, void *data) { | |||
619 | 619 | ||
620 | static void handle_tool_button(struct wl_listener *listener, void *data) { | 620 | static void handle_tool_button(struct wl_listener *listener, void *data) { |
621 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_button); | 621 | struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_button); |
622 | wlr_idle_notify_activity(server.idle, cursor->seat->wlr_seat); | 622 | seat_idle_notify_activity(cursor->seat, IDLE_SOURCE_TABLET_TOOL); |
623 | struct wlr_event_tablet_tool_button *event = data; | 623 | struct wlr_event_tablet_tool_button *event = data; |
624 | struct sway_tablet_tool *sway_tool = event->tool->data; | 624 | struct sway_tablet_tool *sway_tool = event->tool->data; |
625 | struct wlr_tablet_v2_tablet *tablet_v2 = sway_tool->tablet->tablet_v2; | 625 | struct wlr_tablet_v2_tablet *tablet_v2 = sway_tool->tablet->tablet_v2; |
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index c4ce8246..1d55c165 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c | |||
@@ -326,7 +326,7 @@ static void handle_key_event(struct sway_keyboard *keyboard, | |||
326 | keyboard->seat_device->input_device->wlr_device; | 326 | keyboard->seat_device->input_device->wlr_device; |
327 | char *device_identifier = input_device_get_identifier(wlr_device); | 327 | char *device_identifier = input_device_get_identifier(wlr_device); |
328 | bool exact_identifier = wlr_device->keyboard->group != NULL; | 328 | bool exact_identifier = wlr_device->keyboard->group != NULL; |
329 | wlr_idle_notify_activity(server.idle, wlr_seat); | 329 | seat_idle_notify_activity(seat, IDLE_SOURCE_KEYBOARD); |
330 | bool input_inhibited = seat->exclusive_client != NULL; | 330 | bool input_inhibited = seat->exclusive_client != NULL; |
331 | 331 | ||
332 | // Identify new keycode, raw keysym(s), and translated keysym(s) | 332 | // Identify new keycode, raw keysym(s), and translated keysym(s) |
diff --git a/sway/input/seat.c b/sway/input/seat.c index bc72ff0c..371de56e 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c | |||
@@ -6,6 +6,7 @@ | |||
6 | #include <time.h> | 6 | #include <time.h> |
7 | #include <wlr/types/wlr_cursor.h> | 7 | #include <wlr/types/wlr_cursor.h> |
8 | #include <wlr/types/wlr_data_device.h> | 8 | #include <wlr/types/wlr_data_device.h> |
9 | #include <wlr/types/wlr_idle.h> | ||
9 | #include <wlr/types/wlr_output_layout.h> | 10 | #include <wlr/types/wlr_output_layout.h> |
10 | #include <wlr/types/wlr_primary_selection.h> | 11 | #include <wlr/types/wlr_primary_selection.h> |
11 | #include <wlr/types/wlr_tablet_v2.h> | 12 | #include <wlr/types/wlr_tablet_v2.h> |
@@ -71,6 +72,25 @@ static void seat_node_destroy(struct sway_seat_node *seat_node) { | |||
71 | free(seat_node); | 72 | free(seat_node); |
72 | } | 73 | } |
73 | 74 | ||
75 | void seat_idle_notify_activity(struct sway_seat *seat, | ||
76 | enum sway_input_idle_source source) { | ||
77 | uint32_t mask = seat->idle_inhibit_sources; | ||
78 | struct wlr_idle_timeout *timeout; | ||
79 | int ntimers = 0, nidle = 0; | ||
80 | wl_list_for_each(timeout, &server.idle->idle_timers, link) { | ||
81 | ++ntimers; | ||
82 | if (timeout->idle_state) { | ||
83 | ++nidle; | ||
84 | } | ||
85 | } | ||
86 | if (nidle == ntimers) { | ||
87 | mask = seat->idle_wake_sources; | ||
88 | } | ||
89 | if ((source & mask) > 0) { | ||
90 | wlr_idle_notify_activity(server.idle, seat->wlr_seat); | ||
91 | } | ||
92 | } | ||
93 | |||
74 | /** | 94 | /** |
75 | * Activate all views within this container recursively. | 95 | * Activate all views within this container recursively. |
76 | */ | 96 | */ |
@@ -491,6 +511,14 @@ struct sway_seat *seat_create(const char *seat_name) { | |||
491 | return NULL; | 511 | return NULL; |
492 | } | 512 | } |
493 | 513 | ||
514 | seat->idle_inhibit_sources = seat->idle_wake_sources = | ||
515 | IDLE_SOURCE_KEYBOARD | | ||
516 | IDLE_SOURCE_POINTER | | ||
517 | IDLE_SOURCE_TOUCH | | ||
518 | IDLE_SOURCE_TABLET_PAD | | ||
519 | IDLE_SOURCE_TABLET_TOOL | | ||
520 | IDLE_SOURCE_SWITCH; | ||
521 | |||
494 | // init the focus stack | 522 | // init the focus stack |
495 | wl_list_init(&seat->focus_stack); | 523 | wl_list_init(&seat->focus_stack); |
496 | 524 | ||
@@ -1325,6 +1353,9 @@ void seat_apply_config(struct sway_seat *seat, | |||
1325 | return; | 1353 | return; |
1326 | } | 1354 | } |
1327 | 1355 | ||
1356 | seat->idle_inhibit_sources = seat_config->idle_inhibit_sources; | ||
1357 | seat->idle_wake_sources = seat_config->idle_wake_sources; | ||
1358 | |||
1328 | wl_list_for_each(seat_device, &seat->devices, link) { | 1359 | wl_list_for_each(seat_device, &seat->devices, link) { |
1329 | seat_configure_device(seat, seat_device->input_device); | 1360 | seat_configure_device(seat, seat_device->input_device); |
1330 | } | 1361 | } |
diff --git a/sway/input/switch.c b/sway/input/switch.c index 72d1245f..b7c28df1 100644 --- a/sway/input/switch.c +++ b/sway/input/switch.c | |||
@@ -70,8 +70,8 @@ static void handle_switch_toggle(struct wl_listener *listener, void *data) { | |||
70 | struct sway_switch *sway_switch = | 70 | struct sway_switch *sway_switch = |
71 | wl_container_of(listener, sway_switch, switch_toggle); | 71 | wl_container_of(listener, sway_switch, switch_toggle); |
72 | struct wlr_event_switch_toggle *event = data; | 72 | struct wlr_event_switch_toggle *event = data; |
73 | struct wlr_seat* wlr_seat = sway_switch->seat_device->sway_seat->wlr_seat; | 73 | struct sway_seat *seat = sway_switch->seat_device->sway_seat; |
74 | wlr_idle_notify_activity(server.idle, wlr_seat); | 74 | seat_idle_notify_activity(seat, IDLE_SOURCE_SWITCH); |
75 | 75 | ||
76 | struct wlr_input_device *wlr_device = | 76 | struct wlr_input_device *wlr_device = |
77 | sway_switch->seat_device->input_device->wlr_device; | 77 | sway_switch->seat_device->input_device->wlr_device; |
diff --git a/sway/meson.build b/sway/meson.build index 3e6e4da6..20fe02fb 100644 --- a/sway/meson.build +++ b/sway/meson.build | |||
@@ -91,6 +91,7 @@ sway_sources = files( | |||
91 | 'commands/seat/cursor.c', | 91 | 'commands/seat/cursor.c', |
92 | 'commands/seat/fallback.c', | 92 | 'commands/seat/fallback.c', |
93 | 'commands/seat/hide_cursor.c', | 93 | 'commands/seat/hide_cursor.c', |
94 | 'commands/seat/idle.c', | ||
94 | 'commands/seat/keyboard_grouping.c', | 95 | 'commands/seat/keyboard_grouping.c', |
95 | 'commands/seat/pointer_constraint.c', | 96 | 'commands/seat/pointer_constraint.c', |
96 | 'commands/seat/xcursor_theme.c', | 97 | 'commands/seat/xcursor_theme.c', |
diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd index 5631293c..e1ae6781 100644 --- a/sway/sway-input.5.scd +++ b/sway/sway-input.5.scd | |||
@@ -218,6 +218,18 @@ correct seat. | |||
218 | disables hiding the cursor. The minimal timeout is 100 and any value less | 218 | disables hiding the cursor. The minimal timeout is 100 and any value less |
219 | than that (aside from 0), will be increased to 100. | 219 | than that (aside from 0), will be increased to 100. |
220 | 220 | ||
221 | *seat* <name> idle_inhibit <sources...> | ||
222 | Sets the set of input event sources which can prevent the seat from | ||
223 | becoming idle, as a space separated list of source names. Valid names are | ||
224 | "keyboard", "pointer", "touchpad", "touch", "tablet_pad", "tablet_tool", | ||
225 | and "switch". The default behavior is to prevent idle on any event. | ||
226 | |||
227 | *seat* <name> idle_wake <sources...> | ||
228 | Sets the set of input event sources which can wake the seat from | ||
229 | its idle state, as a space separated list of source names. Valid names are | ||
230 | "keyboard", "pointer", "touchpad", "touch", "tablet pad", "tablet tool", | ||
231 | and "switch". The default behavior is to wake from idle on any event. | ||
232 | |||
221 | *seat* <name> keyboard_grouping none|keymap | 233 | *seat* <name> keyboard_grouping none|keymap |
222 | Set how the keyboards in the seat are grouped together. Currently, there | 234 | Set how the keyboards in the seat are grouped together. Currently, there |
223 | are two options. _none_ will disable all keyboard grouping. This will make | 235 | are two options. _none_ will disable all keyboard grouping. This will make |