home · contact · privacy
Strongly overhauled keybinding managemment. Window-specific keybindings and a window...
[plomrogue] / src / wincontrol.c
1 /* wincontrol.c */
2
3 #include "wincontrol.h"
4 #include <stdlib.h> /* for free() */
5 #include <string.h> /* for strlen(), strchr(), strstr() */
6 #include <stdint.h> /* for uint8_t, uint16_t */
7 #include <unistd.h> /* for access(), unlink() */
8 #include "windows.h" /* for suspend_win(), append_win(), reset_pad_offset(),
9                       * resize_active_win(), init_win(), free_win(),
10                       * structs Win, WinMeta
11                       */
12 #include "yx_uint16.h" /* for yx_uint16 struct */
13 #include "main.h" /* for Wins struct */
14 #include "readwrite.h" /* for get_linemax(), try_fopen(), try_fclose(),
15                         * try_fgets(), try_fclose_unlink_rename(), try_fwrite()
16                         */
17 #include "rexit.h" /* for exit_err() */
18 #include "main.h" /* for World struct */
19 #include "draw_wins.h" /* for draw_win_map(), draw_win_info(), draw_win_og(),
20                         * draw_win_keybindings_global(),
21                         * draw_win_keybindings_winconf_geometry(),
22                         * draw_win_keybindings_winconf_keybindings(),
23                         * draw_winconf_geometry(), draw_winconf_keybindings()
24                         */
25 #include "misc.h" /* for try_malloc() */
26 #include "dirent.h" /* for opendir(), closedir(), readdir() */
27 #include "errno.h" /* for errno */
28 #include "keybindings.h" /* for KeyBinding struct, free_keybindings() */
29
30
31
32 /* Return string "prefix" + "id"; malloc()'s string, remember to call free()! */
33 static char * string_prefixed_id(struct World * world, char * prefix, char id);
34
35
36
37 /* Create Winconf, init ->view/height_type/width_type to 0, ->id to "id". */
38 static void create_winconf(struct World * world, char id, struct WinConf * wcp);
39
40 /* Initialize Winconf of "id" from appropriate config file.*/
41 static void init_winconf_from_file(struct World * world, char id);
42
43 /* Wrapper around init_win() called with values from Winconf of "id". */
44 static void init_win_from_winconf(struct World * world, char id);
45
46 /* Save title, draw function, size of window identified by "id" to conffile. */
47 static void save_win_config(struct World * world, char id);
48
49 /* Free data pointed to inside WinConf struct. */
50 static void free_winconf_data(struct World * world, char id);
51
52
53
54 /* Write geometry of a window to its WinConf, as positive or negative values
55  * (dependent on state ofWinConf->height_type / WinConf->width_type).
56  */
57 static void set_winconf_geometry(struct World * world, char id);
58
59
60
61 /* Get WinConf by "id"; get id of WinConf mothering "win". */
62 static struct WinConf * get_winconf_by_id(struct World * world, char id);
63
64 /* Get (Win->draw) function identified by "c"; NULL if c not mapped to one. */
65 static void * get_drawfunc_by_char(char c);
66
67 /* Iterate over bytes of world->winconf_ids array. Re-start after null byte. */
68 static char get_next_winconf_id(struct World * world);
69
70
71
72 static char * string_prefixed_id(struct World * world, char * prefix, char id)
73 {
74     uint8_t size = strlen(prefix) + 2;
75     char * path = try_malloc(size, world, "string_prefixed_id()");
76     sprintf(path, "%s_", prefix);
77     path[size - 2] = id;
78     return path;
79 }
80
81
82
83 static void create_winconf(struct World * world, char id, struct WinConf * wcp)
84 {
85     wcp->id = id;
86     wcp->view = 0;
87     wcp->height_type = 0;
88     wcp->width_type = 0;
89     wcp->kb.edit = 0;
90     wcp->kb.select = 0;
91 }
92
93
94
95 static void init_winconf_from_file(struct World * world, char id)
96 {
97     char * tmp = "init_winconf_from_file() on window id '_'";
98     char * context = try_malloc(strlen(tmp) + 1, world,
99                                "init_winconf_from_file()");
100     memcpy(context, tmp, strlen(tmp) + 1);
101     context[strlen(tmp) - 2] = id;
102
103     char * path = string_prefixed_id(world, "config/windows/Win_", id);
104     FILE * file = try_fopen(path, "r", world, context);
105     free(path);
106     uint16_t linemax = get_linemax(file, world, context);
107     char line[linemax + 1];
108
109     struct WinConf * winconf = get_winconf_by_id(world, id);
110     try_fgets(line, linemax + 1, file, world, context);
111     winconf->title = try_malloc(strlen(line), world, context);
112     memcpy(winconf->title, line, strlen(line) - 1); /* Eliminate newline char */
113     winconf->title[strlen(line) - 1] = '\0';        /* char at end of string. */
114
115     try_fgets(line, linemax + 1, file, world, context);
116     winconf->draw = line[0];
117
118     try_fgets(line, linemax + 1, file, world, context);
119     winconf->height = atoi(line);
120     if (0 >= winconf->height)
121     {
122         winconf->height_type = 1;
123     }
124     try_fgets(line, linemax + 1, file, world, context);
125     winconf->width = atoi(line);
126     if (0 >= winconf->width)
127     {
128         winconf->width_type = 1;
129     }
130
131     char command[linemax + 1];
132     char * cmdptr;
133     struct KeyBinding ** loc_last_ptr = &winconf->kb.kbs;
134     * loc_last_ptr = 0;
135     while (fgets(command, linemax + 1, file))
136     {
137         if ('\n' == command[0] || 0 == command[0])
138         {
139             break;
140         }
141         * loc_last_ptr = try_malloc(sizeof(struct KeyBinding), world, context);
142         struct KeyBinding * kb_p = * loc_last_ptr;
143         kb_p->next = 0;
144         kb_p->key = atoi(command);
145         cmdptr = strchr(command, ' ') + 1;
146         kb_p->name = try_malloc(strlen(cmdptr), world, context);
147         memcpy(kb_p->name, cmdptr, strlen(cmdptr) - 1);
148         kb_p->name[strlen(cmdptr) - 1] = '\0';
149         loc_last_ptr = & kb_p->next;
150     }
151
152     try_fclose(file, world, context);
153     free(context);
154 }
155
156
157
158 static void init_win_from_winconf(struct World * world, char id)
159 {
160     char * tmp = "Trouble in init_win_from_file() with init_win() (win id: _).";
161     char * err = try_malloc(strlen(tmp) + 1, world, "init_win_from_file()");
162     memcpy(err, tmp, strlen(tmp) + 1);
163     err[strlen(tmp) - 3] = id;
164     struct WinConf * winconf = get_winconf_by_id(world, id);
165     void * f = get_drawfunc_by_char(winconf->draw);
166     exit_err(NULL == f, world, err);
167     exit_err(init_win(world->wmeta, &winconf->win, winconf->title,
168                       winconf->height, winconf->width, world, f),
169              world, err);
170     free(err);
171 }
172
173
174
175 extern void save_win_config(struct World * world, char id)
176 {
177     char * f_name = "save_win_config()";
178
179     char * path_tmp = string_prefixed_id(world, "config/windows/Win_tmp_", id);
180     FILE * file = try_fopen(path_tmp, "w", world, f_name);
181
182     struct WinConf * wc = get_winconf_by_id(world, id);
183     uint8_t size = strlen(wc->title) + 2;
184     if (size < 7)
185     {
186         size = 7;
187     }
188     char line[size];
189     sprintf(line, "%s\n", wc->title);
190     try_fwrite(line, sizeof(char), strlen(line), file, world, f_name);
191     sprintf(line, "%c\n", wc->draw);
192     try_fwrite(line, sizeof(char), strlen(line), file, world, f_name);
193     sprintf(line, "%d\n", wc->height);
194     try_fwrite(line, sizeof(char), strlen(line), file, world, f_name);
195     sprintf(line, "%d\n", wc->width);
196     try_fwrite(line, sizeof(char), strlen(line), file, world, f_name);
197
198     uint16_t linemax = 0;
199     struct KeyBinding * kb_p = wc->kb.kbs;
200     while (0 != kb_p)
201     {
202         if (strlen(kb_p->name) > linemax)
203         {
204             linemax = strlen(kb_p->name);
205         }
206         kb_p = kb_p->next;
207     }
208     linemax = linemax + 6;         /* + 6 = + 3 digits + whitespace + \n + \0 */
209
210     char keyb_line[linemax];
211     kb_p = wc->kb.kbs;
212     while (0 != kb_p)
213     {
214         snprintf(keyb_line, linemax, "%d %s\n", kb_p->key, kb_p->name);
215         try_fwrite(keyb_line, sizeof(char), strlen(keyb_line), file, world, f_name);
216         kb_p = kb_p->next;
217     }
218
219     char * path = string_prefixed_id(world, "config/windows/Win_", id);
220     try_fclose_unlink_rename(file, path_tmp, path, world, f_name);
221     free(path);
222     free(path_tmp);
223 }
224
225
226
227 static void free_winconf_data(struct World * world, char id)
228 {
229     struct WinConf * wc = get_winconf_by_id(world, id);
230     free(wc->title);
231     free_keybindings(wc->kb.kbs);
232     free_win(wc->win);
233 }
234
235
236
237 static void set_winconf_geometry(struct World * world, char id)
238 {
239     struct WinConf * wcp = get_winconf_by_id(world, id);
240     if      (0 == wcp->height_type)
241     {
242         wcp->height = wcp->win->frame.size.y;
243     }
244     else if (1 == wcp->height_type)
245     {
246         wcp->height = wcp->win->frame.size.y - world->wmeta->padframe.size.y
247                       + 1;
248     }
249     if      (0 == wcp->width_type)
250     {
251         wcp->width = wcp->win->frame.size.x;
252     }
253     else if (1 == wcp->width_type)
254     {
255         wcp->width = wcp->win->frame.size.x - world->wmeta->padframe.size.x;
256     }
257 }
258
259
260
261 static struct WinConf * get_winconf_by_id(struct World * world, char id)
262 {
263     uint8_t i = 0;
264     while (1)
265     {
266         if (id == world->winconfs[i].id)
267         {
268             return &world->winconfs[i];
269         }
270         i++;
271     }
272 }
273
274
275
276 static void * get_drawfunc_by_char(char c)
277 {
278     if      ('i' == c)
279     {
280         return draw_win_info;
281     }
282     else if ('l' == c)
283     {
284         return draw_win_log;
285     }
286     else if ('m' == c)
287     {
288         return draw_win_map;
289     }
290     else if ('0' == c)
291     {
292         return draw_win_keybindings_global;
293     }
294     else if ('1' == c)
295     {
296         return draw_win_keybindings_winconf_geometry;
297     }
298     else if ('2' == c)
299     {
300         return draw_win_keybindings_winconf_keybindings;
301     }
302     return NULL;
303 }
304
305
306
307 static char get_next_winconf_id(struct World * world)
308 {
309     static uint8_t i = 0;
310     char c = world->winconf_ids[i];
311     if (0 == c)
312     {
313         i = 0;
314     }
315     else
316     {
317         i++;
318     }
319     return c;
320 }
321
322
323
324 extern struct WinConf * get_winconf_by_win(struct World * world,
325                                            struct Win * win)
326 {
327     uint8_t i = 0;
328     while (1)
329     {
330         if (win == world->winconfs[i].win)
331         {
332             return &world->winconfs[i];
333         }
334         i++;
335     }
336 }
337
338
339
340 extern struct Win * get_win_by_id(struct World * world, char id)
341 {
342     struct WinConf * wc = get_winconf_by_id(world, id);
343     return wc->win;
344 }
345
346
347
348 extern void init_winconfs(struct World * world)
349 {
350     char * f_name = "init_winconfs()";
351     char * err_o = "Trouble in init_winconfs() with opendir().";
352     char * err_r = "Trouble in init_winconfs() with readdir().";
353     char * err_c = "Trouble in init_winconfs() with closedir().";
354
355     DIR * dp = opendir("config/windows");
356     exit_err(NULL == dp, world, err_o);
357     struct dirent * fn;
358     errno = 0;
359     char * winconf_ids = try_malloc(256, world, f_name);
360     uint8_t i = 0;
361     char id;
362     while (NULL != (fn = readdir(dp)))
363     {
364         if (   5 == strlen(fn->d_name)
365             && fn->d_name == strstr(fn->d_name, "Win_"))
366         {
367             id = fn->d_name[4];
368             winconf_ids[i] = id;
369             i++;
370         }
371     }
372     winconf_ids[i] = '\0';
373     exit_err(errno, world, err_r);
374     exit_err(closedir(dp), world, err_c);
375     world->winconf_ids = try_malloc(strlen(winconf_ids) + 1, world, f_name);
376     memcpy(world->winconf_ids, winconf_ids, strlen(winconf_ids) + 1);
377     free(winconf_ids);
378
379     struct WinConf * winconfs;
380     winconfs = try_malloc(strlen(world->winconf_ids) * sizeof(struct WinConf),
381                                  world, f_name);
382     i = 0;
383     while (0 != (id = get_next_winconf_id(world)))
384     {
385         create_winconf(world, id, &winconfs[i]);
386         i++;
387     }
388     world->winconfs = winconfs;
389     while (0 != (id = get_next_winconf_id(world)))
390     {
391         init_winconf_from_file(world, id);
392         i++;
393     }
394 }
395
396
397
398 extern void free_winconfs(struct World * world)
399 {
400     char id;
401     while (0 != (id = get_next_winconf_id(world)))
402     {
403         free_winconf_data(world, id);
404     }
405     free(world->winconf_ids);
406     free(world->winconfs);
407 }
408
409
410
411 extern void init_wins(struct World * world)
412 {
413     char id;
414     while (0 != (id = get_next_winconf_id(world)))
415     {
416         init_win_from_winconf(world, id);
417     }
418 }
419
420
421
422 extern void sorted_wintoggle(struct World * world)
423 {
424     char * f_name = "sorted_wintoggle()";
425     char * path = "config/windows/toggle_order";
426     FILE * file = try_fopen(path, "r", world, f_name);
427     uint16_t linemax = get_linemax(file, world, f_name);
428     char win_order[linemax + 1];
429     try_fgets(win_order, linemax + 1, file, world, f_name);
430     try_fclose(file, world, f_name);
431     uint8_t i = 0;
432     for (; i < linemax - 1; i++)
433     {
434         if (NULL == strchr(world->winconf_ids, win_order[i]))
435         {
436             continue;
437         }
438         toggle_window(world->wmeta, get_win_by_id(world, win_order[i]));
439     }
440 }
441
442
443
444 extern void reload_win_config(struct World * world)
445 {
446     while (0 != world->wmeta->active)
447     {
448         suspend_win(world->wmeta, world->wmeta->active);
449     }
450     free_winconfs(world);
451     init_winconfs(world);
452     init_wins(world);
453     sorted_wintoggle(world);
454 }
455
456
457
458 extern void save_win_configs(struct World * world)
459 {
460     char * f_name = "save_win_configs()";
461
462     char id;
463     while (0 != (id = get_next_winconf_id(world)))
464     {
465         save_win_config(world, id);
466     }
467
468     char * path     = "config/windows/toggle_order";
469     char * path_tmp = "config/windows/toggle_order_tmp";
470     FILE * file = try_fopen(path_tmp, "w", world, f_name);
471
472     char line[6];
473     struct Win * w_p = world->wmeta->chain_start;
474     uint8_t i = 0;
475     while (0 != w_p)
476     {
477         struct WinConf * wc = get_winconf_by_win(world, w_p);
478         line[i] = wc->id;
479         w_p = w_p->next;
480         i++;
481     }
482     line[i] = '\n';
483     try_fwrite(line, sizeof(char), strlen(line), file, world, f_name);
484
485     try_fclose_unlink_rename(file, path_tmp, path, world, f_name);
486 }
487
488
489
490 extern uint8_t toggle_window(struct WinMeta * win_meta, struct Win * win)
491 {
492     if (0 != win->frame.curses_win)
493     {
494         return suspend_win(win_meta, win);
495     }
496     else
497     {
498         return append_win(win_meta, win);
499     }
500 }
501
502
503
504 extern void toggle_winconfig(struct World * world, struct Win * win)
505 {
506     struct WinConf * wcp = get_winconf_by_win(world, win);
507     if      (0 == wcp->view)
508     {
509         win->draw = draw_winconf_geometry;
510         wcp->view = 1;
511     }
512     else if (1 == wcp->view)
513     {
514         win->draw = draw_winconf_keybindings;
515         wcp->view = 2;
516     }
517     else
518     {
519         win->draw = get_drawfunc_by_char(wcp->draw);
520         wcp->view = 0;
521     }
522 }
523
524
525
526 extern void toggle_win_height_type(struct World * world, struct Win * win)
527 {
528     struct WinConf * wcp = get_winconf_by_win(world, win);
529     if (0 == wcp->height_type)
530     {
531         wcp->height_type = 1;
532     }
533     else
534     {
535         wcp->height_type = 0;
536     }
537     set_winconf_geometry(world, wcp->id);
538 }
539
540
541
542 extern void toggle_win_width_type(struct World * world, struct Win * win)
543 {
544     struct WinConf * wcp = get_winconf_by_win(world, win);
545     if (   0 == wcp->width_type
546         && win->frame.size.x <= world->wmeta->padframe.size.x)
547     {
548         wcp->width_type = 1;
549     }
550     else
551     {
552         wcp->width_type = 0;
553     }
554     set_winconf_geometry(world, wcp->id);
555 }
556
557
558
559 extern void scroll_pad(struct WinMeta * win_meta, char dir)
560 {
561     if      ('+' == dir)
562     {
563         reset_pad_offset(win_meta, win_meta->pad_offset + 1);
564     }
565     else if ('-' == dir)
566     {
567         reset_pad_offset(win_meta, win_meta->pad_offset - 1);
568     }
569 }
570
571
572
573 extern uint8_t growshrink_active_window(struct World * world, char change)
574 {
575     if (0 != world->wmeta->active)
576     {
577         struct yx_uint16 size = world->wmeta->active->frame.size;
578         if      (change == '-')
579         {
580             size.y--;
581         }
582         else if (change == '+')
583         {
584             size.y++;
585         }
586         else if (change == '_')
587         {
588             size.x--;
589         }
590         else if (change == '*')
591         {
592             size.x++;
593         }
594         uint8_t x = resize_active_win(world->wmeta, size);
595         struct WinConf * wcp = get_winconf_by_win(world, world->wmeta->active);
596         if (   1 == wcp->width_type
597             && world->wmeta->active->frame.size.x
598                > world->wmeta->padframe.size.x)
599         {
600             wcp->width_type = 0;
601         }
602         set_winconf_geometry(world, wcp->id);
603         return x;
604     }
605     return 0;
606 }