diff options
-rw-r--r-- | include/swaynagbar/nagbar.h | 27 | ||||
-rw-r--r-- | swaynagbar/main.c | 76 | ||||
-rw-r--r-- | swaynagbar/nagbar.c | 60 | ||||
-rw-r--r-- | swaynagbar/render.c | 160 | ||||
-rw-r--r-- | swaynagbar/swaynagbar.1.scd | 12 |
5 files changed, 321 insertions, 14 deletions
diff --git a/include/swaynagbar/nagbar.h b/include/swaynagbar/nagbar.h index 07a0d51e..8b55e4fa 100644 --- a/include/swaynagbar/nagbar.h +++ b/include/swaynagbar/nagbar.h | |||
@@ -7,17 +7,26 @@ | |||
7 | 7 | ||
8 | #define NAGBAR_BAR_BORDER_THICKNESS 2 | 8 | #define NAGBAR_BAR_BORDER_THICKNESS 2 |
9 | #define NAGBAR_MESSAGE_PADDING 8 | 9 | #define NAGBAR_MESSAGE_PADDING 8 |
10 | #define NAGBAR_DETAILS_BORDER_THICKNESS 3 | ||
10 | #define NAGBAR_BUTTON_BORDER_THICKNESS 3 | 11 | #define NAGBAR_BUTTON_BORDER_THICKNESS 3 |
11 | #define NAGBAR_BUTTON_GAP 20 | 12 | #define NAGBAR_BUTTON_GAP 20 |
12 | #define NAGBAR_BUTTON_GAP_CLOSE 15 | 13 | #define NAGBAR_BUTTON_GAP_CLOSE 15 |
13 | #define NAGBAR_BUTTON_MARGIN_RIGHT 2 | 14 | #define NAGBAR_BUTTON_MARGIN_RIGHT 2 |
14 | #define NAGBAR_BUTTON_PADDING 3 | 15 | #define NAGBAR_BUTTON_PADDING 3 |
15 | 16 | ||
17 | #define NAGBAR_MAX_HEIGHT 500 | ||
18 | |||
16 | enum sway_nagbar_type { | 19 | enum sway_nagbar_type { |
17 | NAGBAR_ERROR, | 20 | NAGBAR_ERROR, |
18 | NAGBAR_WARNING, | 21 | NAGBAR_WARNING, |
19 | }; | 22 | }; |
20 | 23 | ||
24 | enum sway_nagbar_action_type { | ||
25 | NAGBAR_ACTION_DISMISS, | ||
26 | NAGBAR_ACTION_EXPAND, | ||
27 | NAGBAR_ACTION_COMMAND, | ||
28 | }; | ||
29 | |||
21 | struct sway_nagbar_colors { | 30 | struct sway_nagbar_colors { |
22 | uint32_t button_background; | 31 | uint32_t button_background; |
23 | uint32_t background; | 32 | uint32_t background; |
@@ -43,6 +52,7 @@ struct sway_nagbar_output { | |||
43 | 52 | ||
44 | struct sway_nagbar_button { | 53 | struct sway_nagbar_button { |
45 | char *text; | 54 | char *text; |
55 | enum sway_nagbar_action_type type; | ||
46 | char *action; | 56 | char *action; |
47 | int x; | 57 | int x; |
48 | int y; | 58 | int y; |
@@ -50,6 +60,22 @@ struct sway_nagbar_button { | |||
50 | int height; | 60 | int height; |
51 | }; | 61 | }; |
52 | 62 | ||
63 | struct sway_nagbar_details { | ||
64 | bool visible; | ||
65 | char *message; | ||
66 | |||
67 | int x; | ||
68 | int y; | ||
69 | int width; | ||
70 | int height; | ||
71 | |||
72 | int offset; | ||
73 | int visible_lines; | ||
74 | int total_lines; | ||
75 | struct sway_nagbar_button button_up; | ||
76 | struct sway_nagbar_button button_down; | ||
77 | }; | ||
78 | |||
53 | struct sway_nagbar { | 79 | struct sway_nagbar { |
54 | bool run_display; | 80 | bool run_display; |
55 | int querying_outputs; | 81 | int querying_outputs; |
@@ -77,6 +103,7 @@ struct sway_nagbar { | |||
77 | char *message; | 103 | char *message; |
78 | char *font; | 104 | char *font; |
79 | list_t *buttons; | 105 | list_t *buttons; |
106 | struct sway_nagbar_details details; | ||
80 | }; | 107 | }; |
81 | 108 | ||
82 | void nagbar_setup(struct sway_nagbar *nagbar); | 109 | void nagbar_setup(struct sway_nagbar *nagbar); |
diff --git a/swaynagbar/main.c b/swaynagbar/main.c index f5ed064e..389118c6 100644 --- a/swaynagbar/main.c +++ b/swaynagbar/main.c | |||
@@ -3,6 +3,7 @@ | |||
3 | #include <signal.h> | 3 | #include <signal.h> |
4 | #include "log.h" | 4 | #include "log.h" |
5 | #include "list.h" | 5 | #include "list.h" |
6 | #include "readline.h" | ||
6 | #include "swaynagbar/nagbar.h" | 7 | #include "swaynagbar/nagbar.h" |
7 | #include "wlr-layer-shell-unstable-v1-client-protocol.h" | 8 | #include "wlr-layer-shell-unstable-v1-client-protocol.h" |
8 | 9 | ||
@@ -34,6 +35,32 @@ static void set_nagbar_colors() { | |||
34 | } | 35 | } |
35 | } | 36 | } |
36 | 37 | ||
38 | static char *read_from_stdin() { | ||
39 | char *buffer = NULL; | ||
40 | while (!feof(stdin)) { | ||
41 | char *line = read_line(stdin); | ||
42 | if (!line) { | ||
43 | continue; | ||
44 | } | ||
45 | |||
46 | if (!buffer) { | ||
47 | buffer = strdup(line); | ||
48 | } else { | ||
49 | buffer = realloc(buffer, strlen(buffer) + strlen(line) + 2); | ||
50 | strcat(buffer, line); | ||
51 | strcat(buffer, "\n"); | ||
52 | } | ||
53 | |||
54 | free(line); | ||
55 | } | ||
56 | |||
57 | if (buffer && buffer[strlen(buffer) - 1] == '\n') { | ||
58 | buffer[strlen(buffer) - 1] = '\0'; | ||
59 | } | ||
60 | |||
61 | return buffer; | ||
62 | } | ||
63 | |||
37 | int main(int argc, char **argv) { | 64 | int main(int argc, char **argv) { |
38 | int exit_code = EXIT_SUCCESS; | 65 | int exit_code = EXIT_SUCCESS; |
39 | bool debug = false; | 66 | bool debug = false; |
@@ -50,17 +77,25 @@ int main(int argc, char **argv) { | |||
50 | struct sway_nagbar_button *button_close = | 77 | struct sway_nagbar_button *button_close = |
51 | calloc(sizeof(struct sway_nagbar_button), 1); | 78 | calloc(sizeof(struct sway_nagbar_button), 1); |
52 | button_close->text = strdup("X"); | 79 | button_close->text = strdup("X"); |
53 | button_close->action = NULL; | 80 | button_close->type = NAGBAR_ACTION_DISMISS; |
54 | list_add(nagbar.buttons, button_close); | 81 | list_add(nagbar.buttons, button_close); |
55 | 82 | ||
56 | static struct option long_options[] = { | 83 | struct sway_nagbar_button *button_details = |
84 | calloc(sizeof(struct sway_nagbar_button), 1); | ||
85 | button_details->text = strdup("Toggle Details"); | ||
86 | button_details->type = NAGBAR_ACTION_EXPAND; | ||
87 | |||
88 | static struct option opts[] = { | ||
57 | {"button", required_argument, NULL, 'b'}, | 89 | {"button", required_argument, NULL, 'b'}, |
58 | {"debug", no_argument, NULL, 'd'}, | 90 | {"debug", no_argument, NULL, 'd'}, |
59 | {"edge", required_argument, NULL, 'e'}, | 91 | {"edge", required_argument, NULL, 'e'}, |
60 | {"font", required_argument, NULL, 'f'}, | 92 | {"font", required_argument, NULL, 'f'}, |
61 | {"help", no_argument, NULL, 'h'}, | 93 | {"help", no_argument, NULL, 'h'}, |
94 | {"detailed-message", required_argument, NULL, 'l'}, | ||
95 | {"detailed-button", required_argument, NULL, 'L'}, | ||
62 | {"message", required_argument, NULL, 'm'}, | 96 | {"message", required_argument, NULL, 'm'}, |
63 | {"output", required_argument, NULL, 'o'}, | 97 | {"output", required_argument, NULL, 'o'}, |
98 | {"dismiss-button", required_argument, NULL, 's'}, | ||
64 | {"type", required_argument, NULL, 't'}, | 99 | {"type", required_argument, NULL, 't'}, |
65 | {"version", no_argument, NULL, 'v'}, | 100 | {"version", no_argument, NULL, 'v'}, |
66 | {0, 0, 0, 0} | 101 | {0, 0, 0, 0} |
@@ -75,26 +110,30 @@ int main(int argc, char **argv) { | |||
75 | " -e, --edge top|bottom Set the edge to use.\n" | 110 | " -e, --edge top|bottom Set the edge to use.\n" |
76 | " -f, --font <font> Set the font to use.\n" | 111 | " -f, --font <font> Set the font to use.\n" |
77 | " -h, --help Show help message and quit.\n" | 112 | " -h, --help Show help message and quit.\n" |
113 | " -l, --detailed-message <msg> Set a detailed message.\n" | ||
114 | " -L, --detailed-button <text> Set the text of the detail button.\n" | ||
78 | " -m, --message <msg> Set the message text.\n" | 115 | " -m, --message <msg> Set the message text.\n" |
79 | " -o, --output <output> Set the output to use.\n" | 116 | " -o, --output <output> Set the output to use.\n" |
117 | " -s, --dismiss-button <text> Set the dismiss button text.\n" | ||
80 | " -t, --type error|warning Set the message type.\n" | 118 | " -t, --type error|warning Set the message type.\n" |
81 | " -v, --version Show the version number and quit.\n"; | 119 | " -v, --version Show the version number and quit.\n"; |
82 | 120 | ||
83 | while (1) { | 121 | while (1) { |
84 | int c = getopt_long(argc, argv, "b:de:f:hm:o:t:v", long_options, NULL); | 122 | int c = getopt_long(argc, argv, "b:de:f:hl:L:m:o:s:t:v", opts, NULL); |
85 | if (c == -1) { | 123 | if (c == -1) { |
86 | break; | 124 | break; |
87 | } | 125 | } |
88 | switch (c) { | 126 | switch (c) { |
89 | case 'b': // Button | 127 | case 'b': // Button |
90 | if (optind >= argc) { | 128 | if (optind >= argc) { |
91 | fprintf(stderr, "Missing action for button %s", optarg); | 129 | fprintf(stderr, "Missing action for button %s\n", optarg); |
92 | exit_code = EXIT_FAILURE; | 130 | exit_code = EXIT_FAILURE; |
93 | goto cleanup; | 131 | goto cleanup; |
94 | } | 132 | } |
95 | struct sway_nagbar_button *button; | 133 | struct sway_nagbar_button *button; |
96 | button = calloc(sizeof(struct sway_nagbar_button), 1); | 134 | button = calloc(sizeof(struct sway_nagbar_button), 1); |
97 | button->text = strdup(optarg); | 135 | button->text = strdup(optarg); |
136 | button->type = NAGBAR_ACTION_COMMAND; | ||
98 | button->action = strdup(argv[optind]); | 137 | button->action = strdup(argv[optind]); |
99 | optind++; | 138 | optind++; |
100 | list_add(nagbar.buttons, button); | 139 | list_add(nagbar.buttons, button); |
@@ -121,6 +160,20 @@ int main(int argc, char **argv) { | |||
121 | free(nagbar.font); | 160 | free(nagbar.font); |
122 | nagbar.font = strdup(optarg); | 161 | nagbar.font = strdup(optarg); |
123 | break; | 162 | break; |
163 | case 'l': // Detailed Message | ||
164 | free(nagbar.details.message); | ||
165 | if (strcmp(optarg, "-") == 0) { | ||
166 | nagbar.details.message = read_from_stdin(); | ||
167 | } else { | ||
168 | nagbar.details.message = strdup(optarg); | ||
169 | } | ||
170 | nagbar.details.button_up.text = strdup("▲"); | ||
171 | nagbar.details.button_down.text = strdup("▼"); | ||
172 | break; | ||
173 | case 'L': // Detailed Button Text | ||
174 | free(button_details->text); | ||
175 | button_details->text = strdup(optarg); | ||
176 | break; | ||
124 | case 'm': // Message | 177 | case 'm': // Message |
125 | free(nagbar.message); | 178 | free(nagbar.message); |
126 | nagbar.message = strdup(optarg); | 179 | nagbar.message = strdup(optarg); |
@@ -129,13 +182,17 @@ int main(int argc, char **argv) { | |||
129 | free(nagbar.output.name); | 182 | free(nagbar.output.name); |
130 | nagbar.output.name = strdup(optarg); | 183 | nagbar.output.name = strdup(optarg); |
131 | break; | 184 | break; |
185 | case 's': // Dismiss Button Text | ||
186 | free(button_close->text); | ||
187 | button_close->text = strdup(optarg); | ||
188 | break; | ||
132 | case 't': // Type | 189 | case 't': // Type |
133 | if (strcmp(optarg, "error") == 0) { | 190 | if (strcmp(optarg, "error") == 0) { |
134 | nagbar.type = NAGBAR_ERROR; | 191 | nagbar.type = NAGBAR_ERROR; |
135 | } else if (strcmp(optarg, "warning") == 0) { | 192 | } else if (strcmp(optarg, "warning") == 0) { |
136 | nagbar.type = NAGBAR_WARNING; | 193 | nagbar.type = NAGBAR_WARNING; |
137 | } else { | 194 | } else { |
138 | fprintf(stderr, "Type must be either 'error' or 'warning'"); | 195 | fprintf(stderr, "Type must be either 'error' or 'warning'\n"); |
139 | exit_code = EXIT_FAILURE; | 196 | exit_code = EXIT_FAILURE; |
140 | goto cleanup; | 197 | goto cleanup; |
141 | } | 198 | } |
@@ -160,6 +217,13 @@ int main(int argc, char **argv) { | |||
160 | goto cleanup; | 217 | goto cleanup; |
161 | } | 218 | } |
162 | 219 | ||
220 | if (nagbar.details.message) { | ||
221 | list_add(nagbar.buttons, button_details); | ||
222 | } else { | ||
223 | free(button_details->text); | ||
224 | free(button_details); | ||
225 | } | ||
226 | |||
163 | wlr_log(WLR_DEBUG, "Output: %s", nagbar.output.name); | 227 | wlr_log(WLR_DEBUG, "Output: %s", nagbar.output.name); |
164 | wlr_log(WLR_DEBUG, "Anchors: %d", nagbar.anchors); | 228 | wlr_log(WLR_DEBUG, "Anchors: %d", nagbar.anchors); |
165 | wlr_log(WLR_DEBUG, "Type: %d", nagbar.type); | 229 | wlr_log(WLR_DEBUG, "Type: %d", nagbar.type); |
@@ -178,6 +242,8 @@ int main(int argc, char **argv) { | |||
178 | return exit_code; | 242 | return exit_code; |
179 | 243 | ||
180 | cleanup: | 244 | cleanup: |
245 | free(button_details->text); | ||
246 | free(button_details); | ||
181 | nagbar_destroy(&nagbar); | 247 | nagbar_destroy(&nagbar); |
182 | return exit_code; | 248 | return exit_code; |
183 | } | 249 | } |
diff --git a/swaynagbar/nagbar.c b/swaynagbar/nagbar.c index 22e5aff4..9e8bbb4b 100644 --- a/swaynagbar/nagbar.c +++ b/swaynagbar/nagbar.c | |||
@@ -34,8 +34,11 @@ static bool terminal_execute(char *terminal, char *command) { | |||
34 | static void nagbar_button_execute(struct sway_nagbar *nagbar, | 34 | static void nagbar_button_execute(struct sway_nagbar *nagbar, |
35 | struct sway_nagbar_button *button) { | 35 | struct sway_nagbar_button *button) { |
36 | wlr_log(WLR_DEBUG, "Executing [%s]: %s", button->text, button->action); | 36 | wlr_log(WLR_DEBUG, "Executing [%s]: %s", button->text, button->action); |
37 | if (!button->action) { | 37 | if (button->type == NAGBAR_ACTION_DISMISS) { |
38 | nagbar->run_display = false; | 38 | nagbar->run_display = false; |
39 | } else if (button->type == NAGBAR_ACTION_EXPAND) { | ||
40 | nagbar->details.visible = !nagbar->details.visible; | ||
41 | render_frame(nagbar); | ||
39 | } else { | 42 | } else { |
40 | if (fork() == 0) { | 43 | if (fork() == 0) { |
41 | // Child process. Will be used to prevent zombie processes | 44 | // Child process. Will be used to prevent zombie processes |
@@ -119,8 +122,58 @@ static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, | |||
119 | && x < nagbutton->x + nagbutton->width | 122 | && x < nagbutton->x + nagbutton->width |
120 | && y < nagbutton->y + nagbutton->height) { | 123 | && y < nagbutton->y + nagbutton->height) { |
121 | nagbar_button_execute(nagbar, nagbutton); | 124 | nagbar_button_execute(nagbar, nagbutton); |
125 | return; | ||
122 | } | 126 | } |
123 | } | 127 | } |
128 | |||
129 | if (nagbar->details.visible && | ||
130 | nagbar->details.total_lines != nagbar->details.visible_lines) { | ||
131 | struct sway_nagbar_button button_up = nagbar->details.button_up; | ||
132 | if (x >= button_up.x | ||
133 | && y >= button_up.y | ||
134 | && x < button_up.x + button_up.width | ||
135 | && y < button_up.y + button_up.height | ||
136 | && nagbar->details.offset > 0) { | ||
137 | nagbar->details.offset--; | ||
138 | render_frame(nagbar); | ||
139 | return; | ||
140 | } | ||
141 | |||
142 | struct sway_nagbar_button button_down = nagbar->details.button_down; | ||
143 | int bot = nagbar->details.total_lines - nagbar->details.visible_lines; | ||
144 | if (x >= button_down.x | ||
145 | && y >= button_down.y | ||
146 | && x < button_down.x + button_down.width | ||
147 | && y < button_down.y + button_down.height | ||
148 | && nagbar->details.offset < bot) { | ||
149 | nagbar->details.offset++; | ||
150 | render_frame(nagbar); | ||
151 | return; | ||
152 | } | ||
153 | } | ||
154 | } | ||
155 | |||
156 | static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, | ||
157 | uint32_t time, uint32_t axis, wl_fixed_t value) { | ||
158 | struct sway_nagbar *nagbar = data; | ||
159 | if (!nagbar->details.visible | ||
160 | || nagbar->pointer.x < nagbar->details.x | ||
161 | || nagbar->pointer.y < nagbar->details.y | ||
162 | || nagbar->pointer.x >= nagbar->details.x + nagbar->details.width | ||
163 | || nagbar->pointer.y >= nagbar->details.y + nagbar->details.height | ||
164 | || nagbar->details.total_lines == nagbar->details.visible_lines) { | ||
165 | return; | ||
166 | } | ||
167 | |||
168 | int direction = wl_fixed_to_int(value); | ||
169 | int bot = nagbar->details.total_lines - nagbar->details.visible_lines; | ||
170 | if (direction < 0 && nagbar->details.offset > 0) { | ||
171 | nagbar->details.offset--; | ||
172 | } else if (direction > 0 && nagbar->details.offset < bot) { | ||
173 | nagbar->details.offset++; | ||
174 | } | ||
175 | |||
176 | render_frame(nagbar); | ||
124 | } | 177 | } |
125 | 178 | ||
126 | static struct wl_pointer_listener pointer_listener = { | 179 | static struct wl_pointer_listener pointer_listener = { |
@@ -128,7 +181,7 @@ static struct wl_pointer_listener pointer_listener = { | |||
128 | .leave = nop, | 181 | .leave = nop, |
129 | .motion = wl_pointer_motion, | 182 | .motion = wl_pointer_motion, |
130 | .button = wl_pointer_button, | 183 | .button = wl_pointer_button, |
131 | .axis = nop, | 184 | .axis = wl_pointer_axis, |
132 | .frame = nop, | 185 | .frame = nop, |
133 | .axis_source = nop, | 186 | .axis_source = nop, |
134 | .axis_stop = nop, | 187 | .axis_stop = nop, |
@@ -329,6 +382,9 @@ void nagbar_destroy(struct sway_nagbar *nagbar) { | |||
329 | free(button); | 382 | free(button); |
330 | } | 383 | } |
331 | list_free(nagbar->buttons); | 384 | list_free(nagbar->buttons); |
385 | free(nagbar->details.message); | ||
386 | free(nagbar->details.button_up.text); | ||
387 | free(nagbar->details.button_down.text); | ||
332 | 388 | ||
333 | if (nagbar->layer_surface) { | 389 | if (nagbar->layer_surface) { |
334 | zwlr_layer_surface_v1_destroy(nagbar->layer_surface); | 390 | zwlr_layer_surface_v1_destroy(nagbar->layer_surface); |
diff --git a/swaynagbar/render.c b/swaynagbar/render.c index c0f59298..7bc2961e 100644 --- a/swaynagbar/render.c +++ b/swaynagbar/render.c | |||
@@ -23,11 +23,149 @@ static uint32_t render_message(cairo_t *cairo, struct sway_nagbar *nagbar) { | |||
23 | } | 23 | } |
24 | 24 | ||
25 | cairo_set_source_u32(cairo, nagbar->colors.text); | 25 | cairo_set_source_u32(cairo, nagbar->colors.text); |
26 | cairo_move_to(cairo, padding, (int)(height / 2.0 - text_height / 2.0)); | 26 | cairo_move_to(cairo, padding, (int)(ideal_height - text_height) / 2); |
27 | pango_printf(cairo, nagbar->font, nagbar->scale, true, "%s", | 27 | pango_printf(cairo, nagbar->font, nagbar->scale, true, "%s", |
28 | nagbar->message); | 28 | nagbar->message); |
29 | 29 | ||
30 | return nagbar->height; | 30 | return ideal_height; |
31 | } | ||
32 | |||
33 | static void render_details_scroll_button(cairo_t *cairo, | ||
34 | struct sway_nagbar *nagbar, struct sway_nagbar_button *button) { | ||
35 | int text_width, text_height; | ||
36 | get_text_size(cairo, nagbar->font, &text_width, &text_height, | ||
37 | nagbar->scale, true, "%s", button->text); | ||
38 | |||
39 | int border = NAGBAR_BUTTON_BORDER_THICKNESS * nagbar->scale; | ||
40 | int padding = NAGBAR_BUTTON_PADDING * nagbar->scale; | ||
41 | |||
42 | cairo_set_source_u32(cairo, nagbar->colors.border); | ||
43 | cairo_rectangle(cairo, button->x, button->y, | ||
44 | button->width, button->height); | ||
45 | cairo_fill(cairo); | ||
46 | |||
47 | cairo_set_source_u32(cairo, nagbar->colors.button_background); | ||
48 | cairo_rectangle(cairo, button->x + border, button->y + border, | ||
49 | button->width - (border * 2), button->height - (border * 2)); | ||
50 | cairo_fill(cairo); | ||
51 | |||
52 | cairo_set_source_u32(cairo, nagbar->colors.text); | ||
53 | cairo_move_to(cairo, button->x + border + padding, | ||
54 | button->y + border + (button->height - text_height) / 2); | ||
55 | pango_printf(cairo, nagbar->font, nagbar->scale, true, "%s", button->text); | ||
56 | } | ||
57 | |||
58 | static int get_detailed_scroll_button_width(cairo_t *cairo, | ||
59 | struct sway_nagbar *nagbar) { | ||
60 | int up_width, down_width, temp_height; | ||
61 | get_text_size(cairo, nagbar->font, &up_width, &temp_height, | ||
62 | nagbar->scale, true, "%s", nagbar->details.button_up.text); | ||
63 | get_text_size(cairo, nagbar->font, &down_width, &temp_height, | ||
64 | nagbar->scale, true, "%s", nagbar->details.button_down.text); | ||
65 | |||
66 | int text_width = up_width > down_width ? up_width : down_width; | ||
67 | int border = NAGBAR_BUTTON_BORDER_THICKNESS * nagbar->scale; | ||
68 | int padding = NAGBAR_BUTTON_PADDING * nagbar->scale; | ||
69 | |||
70 | return text_width + border * 2 + padding * 2; | ||
71 | } | ||
72 | |||
73 | static uint32_t render_detailed(cairo_t *cairo, struct sway_nagbar *nagbar, | ||
74 | uint32_t y) { | ||
75 | uint32_t width = nagbar->width * nagbar->scale; | ||
76 | uint32_t height = nagbar->height * nagbar->scale; | ||
77 | height -= NAGBAR_BAR_BORDER_THICKNESS * nagbar->scale; | ||
78 | |||
79 | int border = NAGBAR_DETAILS_BORDER_THICKNESS * nagbar->scale; | ||
80 | int padding = NAGBAR_MESSAGE_PADDING * nagbar->scale; | ||
81 | int decor = padding + border; | ||
82 | |||
83 | nagbar->details.x = decor; | ||
84 | nagbar->details.y = y + decor; | ||
85 | nagbar->details.width = width - decor * 2; | ||
86 | |||
87 | PangoLayout *layout = get_pango_layout(cairo, nagbar->font, | ||
88 | nagbar->details.message, nagbar->scale, true); | ||
89 | pango_layout_set_width(layout, | ||
90 | (nagbar->details.width - padding * 2) * PANGO_SCALE); | ||
91 | pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR); | ||
92 | pango_layout_set_single_paragraph_mode(layout, false); | ||
93 | pango_cairo_update_layout(cairo, layout); | ||
94 | nagbar->details.total_lines = pango_layout_get_line_count(layout); | ||
95 | |||
96 | PangoLayoutLine *line; | ||
97 | line = pango_layout_get_line_readonly(layout, nagbar->details.offset); | ||
98 | gint offset = line->start_index; | ||
99 | const char *text = pango_layout_get_text(layout); | ||
100 | pango_layout_set_text(layout, text + offset, strlen(text) - offset); | ||
101 | |||
102 | int text_width, text_height; | ||
103 | pango_cairo_update_layout(cairo, layout); | ||
104 | pango_layout_get_pixel_size(layout, &text_width, &text_height); | ||
105 | |||
106 | bool show_buttons = nagbar->details.offset > 0; | ||
107 | int button_width = get_detailed_scroll_button_width(cairo, nagbar); | ||
108 | if (show_buttons) { | ||
109 | nagbar->details.width -= button_width; | ||
110 | pango_layout_set_width(layout, | ||
111 | (nagbar->details.width - padding * 2) * PANGO_SCALE); | ||
112 | } | ||
113 | |||
114 | uint32_t ideal_height; | ||
115 | do { | ||
116 | ideal_height = nagbar->details.y + text_height + decor + padding * 2; | ||
117 | if (ideal_height > NAGBAR_MAX_HEIGHT) { | ||
118 | ideal_height = NAGBAR_MAX_HEIGHT; | ||
119 | |||
120 | if (!show_buttons) { | ||
121 | show_buttons = true; | ||
122 | nagbar->details.width -= button_width; | ||
123 | pango_layout_set_width(layout, | ||
124 | (nagbar->details.width - padding * 2) * PANGO_SCALE); | ||
125 | } | ||
126 | } | ||
127 | |||
128 | nagbar->details.height = ideal_height - nagbar->details.y - decor; | ||
129 | pango_layout_set_height(layout, | ||
130 | (nagbar->details.height - padding * 2) * PANGO_SCALE); | ||
131 | pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); | ||
132 | pango_cairo_update_layout(cairo, layout); | ||
133 | pango_layout_get_pixel_size(layout, &text_width, &text_height); | ||
134 | } while (text_height != (nagbar->details.height - padding * 2)); | ||
135 | |||
136 | nagbar->details.visible_lines = pango_layout_get_line_count(layout); | ||
137 | |||
138 | if (show_buttons) { | ||
139 | nagbar->details.button_up.x = | ||
140 | nagbar->details.x + nagbar->details.width; | ||
141 | nagbar->details.button_up.y = nagbar->details.y; | ||
142 | nagbar->details.button_up.width = button_width; | ||
143 | nagbar->details.button_up.height = nagbar->details.height / 2; | ||
144 | render_details_scroll_button(cairo, nagbar, | ||
145 | &nagbar->details.button_up); | ||
146 | |||
147 | nagbar->details.button_down.x = | ||
148 | nagbar->details.x + nagbar->details.width; | ||
149 | nagbar->details.button_down.y = | ||
150 | nagbar->details.button_up.y + nagbar->details.button_up.height; | ||
151 | nagbar->details.button_down.width = button_width; | ||
152 | nagbar->details.button_down.height = nagbar->details.height / 2; | ||
153 | render_details_scroll_button(cairo, nagbar, | ||
154 | &nagbar->details.button_down); | ||
155 | } | ||
156 | |||
157 | cairo_set_source_u32(cairo, nagbar->colors.border); | ||
158 | cairo_rectangle(cairo, nagbar->details.x, nagbar->details.y, | ||
159 | nagbar->details.width, nagbar->details.height); | ||
160 | cairo_fill(cairo); | ||
161 | |||
162 | cairo_move_to(cairo, nagbar->details.x + padding, | ||
163 | nagbar->details.y + padding); | ||
164 | cairo_set_source_u32(cairo, nagbar->colors.text); | ||
165 | pango_cairo_show_layout(cairo, layout); | ||
166 | g_object_unref(layout); | ||
167 | |||
168 | return ideal_height; | ||
31 | } | 169 | } |
32 | 170 | ||
33 | static uint32_t render_button(cairo_t *cairo, struct sway_nagbar *nagbar, | 171 | static uint32_t render_button(cairo_t *cairo, struct sway_nagbar *nagbar, |
@@ -50,7 +188,7 @@ static uint32_t render_button(cairo_t *cairo, struct sway_nagbar *nagbar, | |||
50 | } | 188 | } |
51 | 189 | ||
52 | button->x = *x - border - text_width - padding * 2; | 190 | button->x = *x - border - text_width - padding * 2; |
53 | button->y = (int)(height / 2.0 - text_height / 2.0) - padding; | 191 | button->y = (int)(ideal_height - text_height) / 2 - padding; |
54 | button->width = text_width + padding * 2; | 192 | button->width = text_width + padding * 2; |
55 | button->height = text_height + padding * 2; | 193 | button->height = text_height + padding * 2; |
56 | 194 | ||
@@ -70,7 +208,7 @@ static uint32_t render_button(cairo_t *cairo, struct sway_nagbar *nagbar, | |||
70 | 208 | ||
71 | *x = button->x - border; | 209 | *x = button->x - border; |
72 | 210 | ||
73 | return nagbar->height; | 211 | return ideal_height; |
74 | } | 212 | } |
75 | 213 | ||
76 | static uint32_t render_to_cairo(cairo_t *cairo, struct sway_nagbar *nagbar) { | 214 | static uint32_t render_to_cairo(cairo_t *cairo, struct sway_nagbar *nagbar) { |
@@ -93,6 +231,11 @@ static uint32_t render_to_cairo(cairo_t *cairo, struct sway_nagbar *nagbar) { | |||
93 | } | 231 | } |
94 | } | 232 | } |
95 | 233 | ||
234 | if (nagbar->details.visible) { | ||
235 | h = render_detailed(cairo, nagbar, max_height); | ||
236 | max_height = h > max_height ? h : max_height; | ||
237 | } | ||
238 | |||
96 | int border = NAGBAR_BAR_BORDER_THICKNESS * nagbar->scale; | 239 | int border = NAGBAR_BAR_BORDER_THICKNESS * nagbar->scale; |
97 | if (max_height > nagbar->height) { | 240 | if (max_height > nagbar->height) { |
98 | max_height += border; | 241 | max_height += border; |
@@ -102,7 +245,7 @@ static uint32_t render_to_cairo(cairo_t *cairo, struct sway_nagbar *nagbar) { | |||
102 | nagbar->width * nagbar->scale, border); | 245 | nagbar->width * nagbar->scale, border); |
103 | cairo_fill(cairo); | 246 | cairo_fill(cairo); |
104 | 247 | ||
105 | return max_height > nagbar->height ? max_height : nagbar->height; | 248 | return max_height; |
106 | } | 249 | } |
107 | 250 | ||
108 | void render_frame(struct sway_nagbar *nagbar) { | 251 | void render_frame(struct sway_nagbar *nagbar) { |
@@ -129,13 +272,16 @@ void render_frame(struct sway_nagbar *nagbar) { | |||
129 | nagbar->buffers, | 272 | nagbar->buffers, |
130 | nagbar->width * nagbar->scale, | 273 | nagbar->width * nagbar->scale, |
131 | nagbar->height * nagbar->scale); | 274 | nagbar->height * nagbar->scale); |
132 | cairo_t *shm = nagbar->current_buffer->cairo; | 275 | if (!nagbar->current_buffer) { |
276 | wlr_log(WLR_DEBUG, "Failed to get buffer. Skipping frame."); | ||
277 | return; | ||
278 | } | ||
133 | 279 | ||
280 | cairo_t *shm = nagbar->current_buffer->cairo; | ||
134 | cairo_save(shm); | 281 | cairo_save(shm); |
135 | cairo_set_operator(shm, CAIRO_OPERATOR_CLEAR); | 282 | cairo_set_operator(shm, CAIRO_OPERATOR_CLEAR); |
136 | cairo_paint(shm); | 283 | cairo_paint(shm); |
137 | cairo_restore(shm); | 284 | cairo_restore(shm); |
138 | |||
139 | cairo_set_source_surface(shm, recorder, 0.0, 0.0); | 285 | cairo_set_source_surface(shm, recorder, 0.0, 0.0); |
140 | cairo_paint(shm); | 286 | cairo_paint(shm); |
141 | 287 | ||
diff --git a/swaynagbar/swaynagbar.1.scd b/swaynagbar/swaynagbar.1.scd index 4235e2ea..2a34ae68 100644 --- a/swaynagbar/swaynagbar.1.scd +++ b/swaynagbar/swaynagbar.1.scd | |||
@@ -25,6 +25,15 @@ _swaynagbar_ [options...] | |||
25 | *-h, --help* | 25 | *-h, --help* |
26 | Show help message and quit. | 26 | Show help message and quit. |
27 | 27 | ||
28 | *-l, --detailed-message <msg>* | ||
29 | Set the detailed message. A button to toggle details will be added. Details | ||
30 | are shown in a scrollable multi-line text area. If _msg_ is _-_, then the | ||
31 | detailed message will be read from stdin. | ||
32 | |||
33 | *-L, --detailed-button <text>* | ||
34 | Set the text for the button that toggles details. This has no effect if | ||
35 | there is not a detailed message. The default is _Toggle Details_. | ||
36 | |||
28 | *-m, --message <msg>* | 37 | *-m, --message <msg>* |
29 | Set the message text. | 38 | Set the message text. |
30 | 39 | ||
@@ -33,6 +42,9 @@ _swaynagbar_ [options...] | |||
33 | _xdg\_output\_manager_ is not supported, then the first detected output | 42 | _xdg\_output\_manager_ is not supported, then the first detected output |
34 | will be used | 43 | will be used |
35 | 44 | ||
45 | *-s, --dismiss-button <text>* | ||
46 | Sets the text for the dismiss nagbar button. The default is _X_. | ||
47 | |||
36 | *-t, --type error|warning* | 48 | *-t, --type error|warning* |
37 | Set the message type. | 49 | Set the message type. |
38 | 50 | ||