]> git.sur5r.net Git - i3/i3/blob - src/cmdparse.y
handlers.c: remove obsolete code
[i3/i3] / src / cmdparse.y
1 %{
2 /*
3  * vim:ts=4:sw=4:expandtab
4  *
5  * i3 - an improved dynamic tiling window manager
6  * © 2009-2010 Michael Stapelberg and contributors (see also: LICENSE)
7  *
8  * cmdparse.y: the parser for commands you send to i3 (or bind on keys)
9  *
10
11  */
12 #include <sys/types.h>
13 #include <sys/stat.h>
14 #include <unistd.h>
15 #include <fcntl.h>
16
17 #include "all.h"
18
19 typedef struct yy_buffer_state *YY_BUFFER_STATE;
20 extern int cmdyylex(struct context *context);
21 extern int cmdyyparse(void);
22 extern FILE *cmdyyin;
23 YY_BUFFER_STATE cmdyy_scan_string(const char *);
24
25 static struct bindings_head *current_bindings;
26 static struct context *context;
27 static Match current_match;
28
29 /*
30  * Helper data structure for an operation window (window on which the operation
31  * will be performed). Used to build the TAILQ owindows.
32  *
33  */
34 typedef struct owindow {
35     Con *con;
36     TAILQ_ENTRY(owindow) owindows;
37 } owindow;
38 static TAILQ_HEAD(owindows_head, owindow) owindows;
39
40 /* Holds the JSON which will be returned via IPC or NULL for the default return
41  * message */
42 static char *json_output;
43
44 /* We don’t need yydebug for now, as we got decent error messages using
45  * yyerror(). Should you ever want to extend the parser, it might be handy
46  * to just comment it in again, so it stays here. */
47 //int cmdyydebug = 1;
48
49 void cmdyyerror(const char *error_message) {
50     ELOG("\n");
51     ELOG("CMD: %s\n", error_message);
52     ELOG("CMD: in command:\n");
53     ELOG("CMD:   %s\n", context->line_copy);
54     ELOG("CMD:   ");
55     for (int c = 1; c <= context->last_column; c++)
56         if (c >= context->first_column)
57                 printf("^");
58         else printf(" ");
59     printf("\n");
60     ELOG("\n");
61     context->compact_error = sstrdup(error_message);
62 }
63
64 int cmdyywrap() {
65     return 1;
66 }
67
68 char *parse_cmd(const char *new) {
69     cmdyy_scan_string(new);
70
71     match_init(&current_match);
72     context = scalloc(sizeof(struct context));
73     context->filename = "cmd";
74     FREE(json_output);
75     if (cmdyyparse() != 0) {
76         fprintf(stderr, "Could not parse command\n");
77         asprintf(&json_output, "{\"success\":false, \"error\":\"%s at position %d\"}",
78                  context->compact_error, context->first_column);
79         FREE(context->line_copy);
80         FREE(context->compact_error);
81         free(context);
82         return json_output;
83     }
84     printf("done, json output = %s\n", json_output);
85
86     FREE(context->line_copy);
87     FREE(context->compact_error);
88     free(context);
89     return json_output;
90 }
91
92 %}
93
94 %error-verbose
95 %lex-param { struct context *context }
96
97 %union {
98     char *string;
99     char chr;
100     int number;
101 }
102
103 %token TOK_ATTACH "attach"
104 %token TOK_EXEC "exec"
105 %token TOK_EXIT "exit"
106 %token TOK_RELOAD "reload"
107 %token TOK_RESTART "restart"
108 %token TOK_KILL "kill"
109 %token TOK_FULLSCREEN "fullscreen"
110 %token TOK_GLOBAL "global"
111 %token TOK_LAYOUT "layout"
112 %token TOK_DEFAULT "default"
113 %token TOK_STACKED "stacked"
114 %token TOK_TABBED "tabbed"
115 %token TOK_BORDER "border"
116 %token TOK_NORMAL "normal"
117 %token TOK_NONE "none"
118 %token TOK_1PIXEL "1pixel"
119 %token TOK_MODE "mode"
120 %token TOK_TILING "tiling"
121 %token TOK_FLOATING "floating"
122 %token TOK_WORKSPACE "workspace"
123 %token TOK_TOGGLE "toggle"
124 %token TOK_FOCUS "focus"
125 %token TOK_MOVE "move"
126 %token TOK_OPEN "open"
127 %token TOK_NEXT "next"
128 %token TOK_PREV "prev"
129 %token TOK_SPLIT "split"
130 %token TOK_HORIZONTAL "horizontal"
131 %token TOK_VERTICAL "vertical"
132 %token TOK_LEVEL "level"
133 %token TOK_UP "up"
134 %token TOK_DOWN "down"
135 %token TOK_LEFT "left"
136 %token TOK_RIGHT "right"
137 %token TOK_AFTER "after"
138 %token TOK_BEFORE "before"
139 %token TOK_RESTORE "restore"
140 %token TOK_MARK "mark"
141 %token TOK_RESIZE "resize"
142 %token TOK_GROW "grow"
143 %token TOK_SHRINK "shrink"
144 %token TOK_PX "px"
145 %token TOK_OR "or"
146 %token TOK_PPT "ppt"
147 %token TOK_NOP "nop"
148
149 %token TOK_CLASS "class"
150 %token TOK_ID "id"
151 %token TOK_CON_ID "con_id"
152
153 %token WHITESPACE "<whitespace>"
154 %token STR "<string>"
155 %token NUMBER "<number>"
156
157 %%
158
159 commands: /* empty */
160     | commands optwhitespace ';' optwhitespace command
161     | command
162     {
163         owindow *current;
164
165         printf("single command completely parsed, dropping state...\n");
166         while (!TAILQ_EMPTY(&owindows)) {
167             current = TAILQ_FIRST(&owindows);
168             TAILQ_REMOVE(&owindows, current, owindows);
169             free(current);
170         }
171         match_init(&current_match);
172     }
173     ;
174
175 optwhitespace:
176     | WHITESPACE
177     ;
178
179 command:
180     match optwhitespace operations
181     ;
182
183 match:
184     | matchstart optwhitespace criteria optwhitespace matchend
185     {
186         printf("match parsed\n");
187     }
188     ;
189
190 matchstart:
191     '['
192     {
193         printf("start\n");
194         match_init(&current_match);
195         TAILQ_INIT(&owindows);
196         /* copy all_cons */
197         Con *con;
198         TAILQ_FOREACH(con, &all_cons, all_cons) {
199             owindow *ow = smalloc(sizeof(owindow));
200             ow->con = con;
201             TAILQ_INSERT_TAIL(&owindows, ow, owindows);
202         }
203     }
204     ;
205
206 matchend:
207     ']'
208     {
209         owindow *next, *current;
210
211         printf("match specification finished, matching...\n");
212         /* copy the old list head to iterate through it and start with a fresh
213          * list which will contain only matching windows */
214         struct owindows_head old = owindows;
215         TAILQ_INIT(&owindows);
216         for (next = TAILQ_FIRST(&old); next != TAILQ_END(&old);) {
217             /* make a copy of the next pointer and advance the pointer to the
218              * next element as we are going to invalidate the element’s
219              * next/prev pointers by calling TAILQ_INSERT_TAIL later */
220             current = next;
221             next = TAILQ_NEXT(next, owindows);
222
223             printf("checking if con %p / %s matches\n", current->con, current->con->name);
224             if (current_match.con_id != NULL) {
225                 if (current_match.con_id == current->con) {
226                     printf("matches container!\n");
227                     TAILQ_INSERT_TAIL(&owindows, current, owindows);
228
229                 }
230             } else if (current_match.mark != NULL && current->con->mark != NULL &&
231                     strcasecmp(current_match.mark, current->con->mark) == 0) {
232                 printf("match by mark\n");
233                     TAILQ_INSERT_TAIL(&owindows, current, owindows);
234
235             } else {
236                 if (current->con->window == NULL)
237                     continue;
238                 if (match_matches_window(&current_match, current->con->window)) {
239                     printf("matches window!\n");
240                     TAILQ_INSERT_TAIL(&owindows, current, owindows);
241                 } else {
242                     printf("doesnt match\n");
243                     free(current);
244                 }
245             }
246         }
247
248         TAILQ_FOREACH(current, &owindows, owindows) {
249             printf("matching: %p / %s\n", current->con, current->con->name);
250         }
251
252     }
253     ;
254
255 criteria:
256     TOK_CLASS '=' STR
257     {
258         printf("criteria: class = %s\n", $<string>3);
259         current_match.class = $<string>3;
260     }
261     | TOK_CON_ID '=' STR
262     {
263         printf("criteria: id = %s\n", $<string>3);
264         /* TODO: correctly parse number */
265         current_match.con_id = atoi($<string>3);
266         printf("id as int = %d\n", current_match.con_id);
267     }
268     | TOK_ID '=' STR
269     {
270         printf("criteria: window id = %s\n", $<string>3);
271         /* TODO: correctly parse number */
272         current_match.id = atoi($<string>3);
273         printf("window id as int = %d\n", current_match.id);
274     }
275     | TOK_MARK '=' STR
276     {
277         printf("criteria: mark = %s\n", $<string>3);
278         current_match.mark = $<string>3;
279     }
280     ;
281
282 operations:
283     operation
284     | operation optwhitespace
285     | operations ',' optwhitespace operation
286     ;
287
288 operation:
289     exec
290     | exit
291     | restart
292     | reload
293     | border
294     | layout
295     | restore
296     | move
297     | workspace
298     | attach
299     | focus
300     | kill
301     | open
302     | fullscreen
303     | next
304     | prev
305     | split
306     | mode
307     | level
308     | mark
309     | resize
310     | nop
311     ;
312
313 exec:
314     TOK_EXEC WHITESPACE STR
315     {
316         printf("should execute %s\n", $<string>3);
317         start_application($<string>3);
318     }
319     ;
320
321 exit:
322     TOK_EXIT
323     {
324         printf("exit, bye bye\n");
325         exit(0);
326     }
327     ;
328
329 reload:
330     TOK_RELOAD
331     {
332         printf("reloading\n");
333         load_configuration(conn, NULL, true);
334         /* Send an IPC event just in case the ws names have changed */
335         ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"reload\"}");
336     }
337     ;
338
339 restart:
340     TOK_RESTART
341     {
342         printf("restarting i3\n");
343         i3_restart();
344     }
345     ;
346
347 attach:
348     TOK_ATTACH
349     {
350         printf("should attach\n");
351     }
352     ;
353
354 focus:
355     TOK_FOCUS
356     {
357         owindow *current;
358
359         printf("should focus\n");
360         if (match_is_empty(&current_match)) {
361             /* TODO: better error message */
362             LOG("Error: The foucs command requires you to use some criteria.\n");
363             return;
364         }
365
366         /* TODO: warning if the match contains more than one entry. does not
367          * make so much sense when focusing */
368         TAILQ_FOREACH(current, &owindows, owindows) {
369             LOG("focusing %p / %s\n", current->con, current->con->name);
370             con_focus(current->con);
371         }
372     }
373     ;
374
375 kill:
376     TOK_KILL
377     {
378         owindow *current;
379
380         printf("killing!\n");
381         /* check if the match is empty, not if the result is empty */
382         if (match_is_empty(&current_match))
383             tree_close_con();
384         else {
385             TAILQ_FOREACH(current, &owindows, owindows) {
386                 printf("matching: %p / %s\n", current->con, current->con->name);
387                 tree_close(current->con, true, false);
388             }
389         }
390
391     }
392     ;
393
394 workspace:
395     TOK_WORKSPACE WHITESPACE STR
396     {
397         printf("should switch to workspace %s\n", $<string>3);
398         workspace_show($<string>3);
399         free($<string>3);
400     }
401     ;
402
403 open:
404     TOK_OPEN
405     {
406         printf("opening new container\n");
407         Con *con = tree_open_con(NULL);
408         asprintf(&json_output, "{\"success\":true, \"id\":%d}", (long int)con);
409     }
410     ;
411
412 fullscreen:
413     TOK_FULLSCREEN
414     {
415         printf("toggling fullscreen\n");
416         owindow *current;
417
418         /* check if the match is empty, not if the result is empty */
419         if (match_is_empty(&current_match))
420             con_toggle_fullscreen(focused);
421         else {
422             TAILQ_FOREACH(current, &owindows, owindows) {
423                 printf("matching: %p / %s\n", current->con, current->con->name);
424                 con_toggle_fullscreen(current->con);
425             }
426         }
427
428     }
429     ;
430
431 next:
432     TOK_NEXT WHITESPACE direction
433     {
434         /* TODO: use matches */
435         printf("should select next window in direction %c\n", $<chr>3);
436         tree_next('n', ($<chr>3 == 'v' ? VERT : HORIZ));
437     }
438     ;
439
440 prev:
441     TOK_PREV WHITESPACE direction
442     {
443         /* TODO: use matches */
444         printf("should select prev window in direction %c\n", $<chr>3);
445         tree_next('p', ($<chr>3 == 'v' ? VERT : HORIZ));
446     }
447     ;
448
449 split:
450     TOK_SPLIT WHITESPACE direction
451     {
452         /* TODO: use matches */
453         printf("splitting in direction %c\n", $<chr>3);
454         tree_split(focused, ($<chr>3 == 'v' ? VERT : HORIZ));
455     }
456     ;
457
458 direction:
459     TOK_HORIZONTAL  { $<chr>$ = 'h'; }
460     | 'h'           { $<chr>$ = 'h'; }
461     | TOK_VERTICAL  { $<chr>$ = 'v'; }
462     | 'v'           { $<chr>$ = 'v'; }
463     ;
464
465 mode:
466     TOK_MODE WHITESPACE window_mode
467     {
468         if ($<number>3 == TOK_TOGGLE) {
469             printf("should toggle mode\n");
470             toggle_floating_mode(focused, false);
471         } else {
472             printf("should switch mode to %s\n", ($<number>3 == TOK_FLOATING ? "floating" : "tiling"));
473             if ($<number>3 == TOK_FLOATING) {
474                 floating_enable(focused, false);
475             } else {
476                 floating_disable(focused, false);
477             }
478         }
479     }
480     ;
481
482 window_mode:
483     TOK_FLOATING    { $<number>$ = TOK_FLOATING; }
484     | TOK_TILING    { $<number>$ = TOK_TILING; }
485     | TOK_TOGGLE    { $<number>$ = TOK_TOGGLE; }
486     ;
487
488 border:
489     TOK_BORDER WHITESPACE border_style
490     {
491         printf("border style should be changed to %d\n", $<number>3);
492         owindow *current;
493
494         /* check if the match is empty, not if the result is empty */
495         if (match_is_empty(&current_match))
496             focused->border_style = $<number>3;
497         else {
498             TAILQ_FOREACH(current, &owindows, owindows) {
499                 printf("matching: %p / %s\n", current->con, current->con->name);
500                 current->con->border_style = $<number>3;
501             }
502         }
503     }
504     ;
505
506 border_style:
507     TOK_NORMAL      { $<number>$ = BS_NORMAL; }
508     | TOK_NONE      { $<number>$ = BS_NONE; }
509     | TOK_1PIXEL    { $<number>$ = BS_1PIXEL; }
510     ;
511
512
513 level:
514     TOK_LEVEL WHITESPACE level_direction
515     {
516         printf("level %c\n", $<chr>3);
517         if ($<chr>3 == 'u')
518             level_up();
519         else level_down();
520     }
521     ;
522
523 level_direction:
524     TOK_UP     { $<chr>$ = 'u'; }
525     | TOK_DOWN { $<chr>$ = 'd'; }
526     ;
527
528 move:
529     TOK_MOVE WHITESPACE before_after WHITESPACE direction
530     {
531         printf("moving: %s and %c\n", ($<number>3 == TOK_BEFORE ? "before" : "after"), $<chr>5);
532         /* TODO: change API for the next call, we need to convert in both directions while ideally
533          * we should not need any of both */
534         tree_move(($<number>3 == TOK_BEFORE ? 'p' : 'n'), ($<chr>5 == 'v' ? VERT : HORIZ));
535     }
536     | TOK_MOVE WHITESPACE TOK_WORKSPACE WHITESPACE STR
537     {
538         owindow *current;
539
540         printf("should move window to workspace %s\n", $<string>5);
541         /* get the workspace */
542         Con *ws = workspace_get($<string>5);
543
544         /* check if the match is empty, not if the result is empty */
545         if (match_is_empty(&current_match))
546             con_move_to_workspace(focused, ws);
547         else {
548             TAILQ_FOREACH(current, &owindows, owindows) {
549                 printf("matching: %p / %s\n", current->con, current->con->name);
550                 con_move_to_workspace(current->con, ws);
551             }
552         }
553     }
554     ;
555
556 before_after:
557     TOK_BEFORE { $<number>$ = TOK_BEFORE; }
558     | TOK_AFTER { $<number>$ = TOK_AFTER; }
559     ;
560
561 restore:
562     TOK_RESTORE WHITESPACE STR
563     {
564         printf("restoring \"%s\"\n", $<string>3);
565         tree_append_json($<string>3);
566     }
567     ;
568
569 layout:
570     TOK_LAYOUT WHITESPACE layout_mode
571     {
572         printf("changing layout to %d\n", $<number>3);
573         owindow *current;
574
575         /* check if the match is empty, not if the result is empty */
576         if (match_is_empty(&current_match))
577             con_set_layout(focused->parent, $<number>3);
578         else {
579             TAILQ_FOREACH(current, &owindows, owindows) {
580                 printf("matching: %p / %s\n", current->con, current->con->name);
581                 con_set_layout(current->con, $<number>3);
582             }
583         }
584
585     }
586     ;
587
588 layout_mode:
589     TOK_DEFAULT   { $<number>$ = L_DEFAULT; }
590     | TOK_STACKED { $<number>$ = L_STACKED; }
591     | TOK_TABBED  { $<number>$ = L_TABBED; }
592     ;
593
594 mark:
595     TOK_MARK WHITESPACE STR
596     {
597         printf("marking window with str %s\n", $<string>3);
598         owindow *current;
599
600         /* check if the match is empty, not if the result is empty */
601         if (match_is_empty(&current_match))
602             focused->mark = sstrdup($<string>3);
603         else {
604             TAILQ_FOREACH(current, &owindows, owindows) {
605                 printf("matching: %p / %s\n", current->con, current->con->name);
606                 current->con->mark = sstrdup($<string>3);
607             }
608         }
609
610         free($<string>3);
611     }
612     ;
613
614 nop:
615     TOK_NOP WHITESPACE STR
616     {
617         printf("-------------------------------------------------\n");
618         printf("  NOP: %s\n", $<string>3);
619         printf("-------------------------------------------------\n");
620         free($<string>3);
621     }
622     ;
623
624 resize:
625     TOK_RESIZE WHITESPACE resize_way WHITESPACE direction resize_px resize_tiling
626     {
627         /* resize <grow|shrink> <direction> [<px> px] [or <ppt> ppt] */
628         printf("resizing in way %d, direction %d, px %d or ppt %d\n", $<number>3, $<number>5, $<number>6, $<number>7);
629         int direction = $<number>5;
630         int px = $<number>6;
631         int ppt = $<number>7;
632         if ($<number>3 == TOK_SHRINK) {
633             px *= -1;
634             ppt *= -1;
635         }
636
637         if (con_is_floating(focused)) {
638             printf("floating resize\n");
639             if (direction == TOK_UP) {
640                 focused->parent->rect.y -= px;
641                 focused->parent->rect.height += px;
642             } else if (direction == TOK_DOWN) {
643                 focused->rect.height += px;
644             } else if (direction == TOK_LEFT) {
645                 focused->rect.x -= px;
646                 focused->rect.width += px;
647             } else {
648                 focused->rect.width += px;
649             }
650         } else {
651             LOG("tiling resize\n");
652             /* get the default percentage */
653             int children = con_num_children(focused->parent);
654             Con *other;
655             LOG("ins. %d children\n", children);
656             double percentage = 1.0 / children;
657             LOG("default percentage = %f\n", percentage);
658
659             if (direction == TOK_UP || direction == TOK_LEFT) {
660                 other = TAILQ_PREV(focused, nodes_head, nodes);
661             } else {
662                 other = TAILQ_NEXT(focused, nodes);
663             }
664             if (other == TAILQ_END(workspaces)) {
665                 LOG("No other container in this direction found, cannot resize.\n");
666                 return 0;
667             }
668             LOG("other->percent = %f\n", other->percent);
669             LOG("focused->percent before = %f\n", focused->percent);
670             if (focused->percent == 0.0)
671                 focused->percent = percentage;
672             if (other->percent == 0.0)
673                 other->percent = percentage;
674             focused->percent += ((double)ppt / 100.0);
675             other->percent -= ((double)ppt / 100.0);
676             LOG("focused->percent after = %f\n", focused->percent);
677             LOG("other->percent after = %f\n", other->percent);
678         }
679     }
680     ;
681
682 resize_px:
683     /* empty */
684     {
685         $<number>$ = 10;
686     }
687     | WHITESPACE NUMBER WHITESPACE TOK_PX
688     {
689         $<number>$ = $<number>2;
690     }
691     ;
692
693 resize_tiling:
694     /* empty */
695     {
696         $<number>$ = 10;
697     }
698     | WHITESPACE TOK_OR WHITESPACE NUMBER WHITESPACE TOK_PPT
699     {
700         $<number>$ = $<number>4;
701     }
702     ;
703
704 resize_way:
705     TOK_GROW        { $<number>$ = TOK_GROW; }
706     | TOK_SHRINK    { $<number>$ = TOK_SHRINK; }
707     ;
708
709 direction:
710     TOK_UP          { $<number>$ = TOK_UP; }
711     | TOK_DOWN      { $<number>$ = TOK_DOWN; }
712     | TOK_LEFT      { $<number>$ = TOK_LEFT; }
713     | TOK_RIGHT     { $<number>$ = TOK_RIGHT; }
714     ;