oxwm

https://git.tonybtw.com/oxwm.git git://git.tonybtw.com/oxwm.git
41,020 bytes raw
1
const std = @import("std");
2
pub const config_mod = @import("config.zig");
3
const Config = config_mod.Config;
4
const Keybind = config_mod.Keybind;
5
const Action = config_mod.Action;
6
const Rule = config_mod.Rule;
7
const Block = config_mod.Block;
8
const Block_Type = config_mod.Block_Type;
9
const Mouse_Button = config_mod.Mouse_Button;
10
const Click_Target = config_mod.Click_Target;
11
const Mouse_Action = config_mod.Mouse_Action;
12
const ColorScheme = config_mod.ColorScheme;
13
14
const c = @cImport({
15
    @cInclude("lua.h");
16
    @cInclude("lauxlib.h");
17
    @cInclude("lualib.h");
18
});
19
20
var L: ?*c.lua_State = null;
21
var config: ?*Config = null;
22
23
pub fn init(cfg: *Config) bool {
24
    config = cfg;
25
    L = c.luaL_newstate();
26
    if (L == null) return false;
27
    c.luaL_openlibs(L);
28
    register_api();
29
    return true;
30
}
31
32
pub fn deinit() void {
33
    if (L) |state| {
34
        c.lua_close(state);
35
    }
36
    L = null;
37
    config = null;
38
}
39
40
pub fn load_file(path: []const u8) bool {
41
    const state = L orelse return false;
42
    var path_buf: [512]u8 = undefined;
43
    if (path.len >= path_buf.len) return false;
44
    @memcpy(path_buf[0..path.len], path);
45
    path_buf[path.len] = 0;
46
47
    if (std.mem.lastIndexOfScalar(u8, path, '/')) |last_slash| {
48
        const dir = path[0..last_slash];
49
        var setup_buf: [600]u8 = undefined;
50
        const setup_code = std.fmt.bufPrint(&setup_buf, "package.path = '{s}/?.lua;' .. package.path\x00", .{dir}) catch return false;
51
        if (c.luaL_loadstring(state, setup_code.ptr) != 0 or c.lua_pcallk(state, 0, 0, 0, 0, null) != 0) {
52
            c.lua_settop(state, -2);
53
            return false;
54
        }
55
    }
56
57
    if (c.luaL_loadfilex(state, &path_buf, null) != 0) {
58
        const err = c.lua_tolstring(state, -1, null);
59
        if (err != null) {
60
            std.debug.print("lua load error: {s}\n", .{std.mem.span(err)});
61
        }
62
        c.lua_settop(state, -2);
63
        return false;
64
    }
65
66
    if (c.lua_pcallk(state, 0, 0, 0, 0, null) != 0) {
67
        const err = c.lua_tolstring(state, -1, null);
68
        if (err != null) {
69
            std.debug.print("lua runtime error: {s}\n", .{std.mem.span(err)});
70
        }
71
        c.lua_settop(state, -2);
72
        return false;
73
    }
74
75
    return true;
76
}
77
78
pub fn load_config() bool {
79
    const home = std.posix.getenv("HOME") orelse return false;
80
    var path_buf: [512]u8 = undefined;
81
    const path = std.fmt.bufPrint(&path_buf, "{s}/.config/oxwm/config.lua", .{home}) catch return false;
82
    return load_file(path);
83
}
84
85
fn register_api() void {
86
    const state = L orelse return;
87
88
    c.lua_createtable(state, 0, 16);
89
90
    register_spawn_functions(state);
91
    register_key_module(state);
92
    register_gaps_module(state);
93
    register_border_module(state);
94
    register_client_module(state);
95
    register_layout_module(state);
96
    register_tag_module(state);
97
    register_monitor_module(state);
98
    register_rule_module(state);
99
    register_bar_module(state);
100
    register_misc_functions(state);
101
102
    c.lua_setglobal(state, "oxwm");
103
}
104
105
fn register_spawn_functions(state: *c.lua_State) void {
106
    c.lua_pushcfunction(state, lua_spawn);
107
    c.lua_setfield(state, -2, "spawn");
108
109
    c.lua_pushcfunction(state, lua_spawn_terminal);
110
    c.lua_setfield(state, -2, "spawn_terminal");
111
}
112
113
fn register_key_module(state: *c.lua_State) void {
114
    c.lua_createtable(state, 0, 2);
115
116
    c.lua_pushcfunction(state, lua_key_bind);
117
    c.lua_setfield(state, -2, "bind");
118
119
    c.lua_pushcfunction(state, lua_key_chord);
120
    c.lua_setfield(state, -2, "chord");
121
122
    c.lua_setfield(state, -2, "key");
123
}
124
125
fn register_gaps_module(state: *c.lua_State) void {
126
    c.lua_createtable(state, 0, 6);
127
128
    c.lua_pushcfunction(state, lua_gaps_set_enabled);
129
    c.lua_setfield(state, -2, "set_enabled");
130
131
    c.lua_pushcfunction(state, lua_gaps_enable);
132
    c.lua_setfield(state, -2, "enable");
133
134
    c.lua_pushcfunction(state, lua_gaps_disable);
135
    c.lua_setfield(state, -2, "disable");
136
137
    c.lua_pushcfunction(state, lua_gaps_set_inner);
138
    c.lua_setfield(state, -2, "set_inner");
139
140
    c.lua_pushcfunction(state, lua_gaps_set_outer);
141
    c.lua_setfield(state, -2, "set_outer");
142
143
    c.lua_pushcfunction(state, lua_gaps_set_smart);
144
    c.lua_setfield(state, -2, "set_smart");
145
146
    c.lua_setfield(state, -2, "gaps");
147
}
148
149
fn register_border_module(state: *c.lua_State) void {
150
    c.lua_createtable(state, 0, 3);
151
152
    c.lua_pushcfunction(state, lua_border_set_width);
153
    c.lua_setfield(state, -2, "set_width");
154
155
    c.lua_pushcfunction(state, lua_border_set_focused_color);
156
    c.lua_setfield(state, -2, "set_focused_color");
157
158
    c.lua_pushcfunction(state, lua_border_set_unfocused_color);
159
    c.lua_setfield(state, -2, "set_unfocused_color");
160
161
    c.lua_setfield(state, -2, "border");
162
}
163
164
fn register_client_module(state: *c.lua_State) void {
165
    c.lua_createtable(state, 0, 5);
166
167
    c.lua_pushcfunction(state, lua_client_kill);
168
    c.lua_setfield(state, -2, "kill");
169
170
    c.lua_pushcfunction(state, lua_client_toggle_fullscreen);
171
    c.lua_setfield(state, -2, "toggle_fullscreen");
172
173
    c.lua_pushcfunction(state, lua_client_toggle_floating);
174
    c.lua_setfield(state, -2, "toggle_floating");
175
176
    c.lua_pushcfunction(state, lua_client_focus_stack);
177
    c.lua_setfield(state, -2, "focus_stack");
178
179
    c.lua_pushcfunction(state, lua_client_move_stack);
180
    c.lua_setfield(state, -2, "move_stack");
181
182
    c.lua_setfield(state, -2, "client");
183
}
184
185
fn register_layout_module(state: *c.lua_State) void {
186
    c.lua_createtable(state, 0, 4);
187
188
    c.lua_pushcfunction(state, lua_layout_cycle);
189
    c.lua_setfield(state, -2, "cycle");
190
191
    c.lua_pushcfunction(state, lua_layout_set);
192
    c.lua_setfield(state, -2, "set");
193
194
    c.lua_pushcfunction(state, lua_layout_scroll_left);
195
    c.lua_setfield(state, -2, "scroll_left");
196
197
    c.lua_pushcfunction(state, lua_layout_scroll_right);
198
    c.lua_setfield(state, -2, "scroll_right");
199
200
    c.lua_setfield(state, -2, "layout");
201
}
202
203
fn register_tag_module(state: *c.lua_State) void {
204
    c.lua_createtable(state, 0, 10);
205
206
    c.lua_pushcfunction(state, lua_tag_view);
207
    c.lua_setfield(state, -2, "view");
208
209
    c.lua_pushcfunction(state, lua_tag_view_next);
210
    c.lua_setfield(state, -2, "view_next");
211
212
    c.lua_pushcfunction(state, lua_tag_view_previous);
213
    c.lua_setfield(state, -2, "view_previous");
214
215
    c.lua_pushcfunction(state, lua_tag_view_next_nonempty);
216
    c.lua_setfield(state, -2, "view_next_nonempty");
217
218
    c.lua_pushcfunction(state, lua_tag_view_previous_nonempty);
219
    c.lua_setfield(state, -2, "view_previous_nonempty");
220
221
    c.lua_pushcfunction(state, lua_tag_toggleview);
222
    c.lua_setfield(state, -2, "toggleview");
223
224
    c.lua_pushcfunction(state, lua_tag_move_to);
225
    c.lua_setfield(state, -2, "move_to");
226
227
    c.lua_pushcfunction(state, lua_tag_toggletag);
228
    c.lua_setfield(state, -2, "toggletag");
229
230
    c.lua_pushcfunction(state, lua_tag_set_back_and_forth);
231
    c.lua_setfield(state, -2, "set_back_and_forth");
232
233
    c.lua_setfield(state, -2, "tag");
234
}
235
236
fn register_monitor_module(state: *c.lua_State) void {
237
    c.lua_createtable(state, 0, 2);
238
239
    c.lua_pushcfunction(state, lua_monitor_focus);
240
    c.lua_setfield(state, -2, "focus");
241
242
    c.lua_pushcfunction(state, lua_monitor_tag);
243
    c.lua_setfield(state, -2, "tag");
244
245
    c.lua_setfield(state, -2, "monitor");
246
}
247
248
fn register_rule_module(state: *c.lua_State) void {
249
    c.lua_createtable(state, 0, 1);
250
251
    c.lua_pushcfunction(state, lua_rule_add);
252
    c.lua_setfield(state, -2, "add");
253
254
    c.lua_setfield(state, -2, "rule");
255
}
256
257
fn register_bar_module(state: *c.lua_State) void {
258
    c.lua_createtable(state, 0, 10);
259
260
    c.lua_pushcfunction(state, lua_bar_set_font);
261
    c.lua_setfield(state, -2, "set_font");
262
263
    c.lua_pushcfunction(state, lua_bar_set_blocks);
264
    c.lua_setfield(state, -2, "set_blocks");
265
266
    c.lua_pushcfunction(state, lua_bar_set_scheme_normal);
267
    c.lua_setfield(state, -2, "set_scheme_normal");
268
269
    c.lua_pushcfunction(state, lua_bar_set_scheme_selected);
270
    c.lua_setfield(state, -2, "set_scheme_selected");
271
272
    c.lua_pushcfunction(state, lua_bar_set_scheme_occupied);
273
    c.lua_setfield(state, -2, "set_scheme_occupied");
274
275
    c.lua_pushcfunction(state, lua_bar_set_scheme_urgent);
276
    c.lua_setfield(state, -2, "set_scheme_urgent");
277
278
    c.lua_pushcfunction(state, lua_bar_set_hide_vacant_tags);
279
    c.lua_setfield(state, -2, "set_hide_vacant_tags");
280
281
    c.lua_createtable(state, 0, 6);
282
283
    c.lua_pushcfunction(state, lua_bar_block_ram);
284
    c.lua_setfield(state, -2, "ram");
285
286
    c.lua_pushcfunction(state, lua_bar_block_datetime);
287
    c.lua_setfield(state, -2, "datetime");
288
289
    c.lua_pushcfunction(state, lua_bar_block_shell);
290
    c.lua_setfield(state, -2, "shell");
291
292
    c.lua_pushcfunction(state, lua_bar_block_static);
293
    c.lua_setfield(state, -2, "static");
294
295
    c.lua_pushcfunction(state, lua_bar_block_battery);
296
    c.lua_setfield(state, -2, "battery");
297
298
    c.lua_setfield(state, -2, "block");
299
300
    c.lua_setfield(state, -2, "bar");
301
}
302
303
fn register_misc_functions(state: *c.lua_State) void {
304
    c.lua_pushcfunction(state, lua_set_terminal);
305
    c.lua_setfield(state, -2, "set_terminal");
306
307
    c.lua_pushcfunction(state, lua_set_modkey);
308
    c.lua_setfield(state, -2, "set_modkey");
309
310
    c.lua_pushcfunction(state, lua_set_tags);
311
    c.lua_setfield(state, -2, "set_tags");
312
313
    c.lua_pushcfunction(state, lua_set_layout_symbol);
314
    c.lua_setfield(state, -2, "set_layout_symbol");
315
316
    c.lua_pushcfunction(state, lua_autostart);
317
    c.lua_setfield(state, -2, "autostart");
318
319
    c.lua_pushcfunction(state, lua_auto_tile);
320
    c.lua_setfield(state, -2, "auto_tile");
321
322
    c.lua_pushcfunction(state, lua_quit);
323
    c.lua_setfield(state, -2, "quit");
324
325
    c.lua_pushcfunction(state, lua_restart);
326
    c.lua_setfield(state, -2, "restart");
327
328
    c.lua_pushcfunction(state, lua_toggle_gaps);
329
    c.lua_setfield(state, -2, "toggle_gaps");
330
331
    c.lua_pushcfunction(state, lua_show_keybinds);
332
    c.lua_setfield(state, -2, "show_keybinds");
333
334
    c.lua_pushcfunction(state, lua_set_master_factor);
335
    c.lua_setfield(state, -2, "set_master_factor");
336
337
    c.lua_pushcfunction(state, lua_inc_num_master);
338
    c.lua_setfield(state, -2, "inc_num_master");
339
}
340
341
fn create_action_table(state: *c.lua_State, action_name: [*:0]const u8) void {
342
    c.lua_createtable(state, 0, 2);
343
    _ = c.lua_pushstring(state, action_name);
344
    c.lua_setfield(state, -2, "__action");
345
}
346
347
fn create_action_table_with_int(state: *c.lua_State, action_name: [*:0]const u8, arg: i32) void {
348
    c.lua_createtable(state, 0, 2);
349
    _ = c.lua_pushstring(state, action_name);
350
    c.lua_setfield(state, -2, "__action");
351
    c.lua_pushinteger(state, arg);
352
    c.lua_setfield(state, -2, "__arg");
353
}
354
355
fn create_action_table_with_string(state: *c.lua_State, action_name: [*:0]const u8) void {
356
    c.lua_createtable(state, 0, 2);
357
    _ = c.lua_pushstring(state, action_name);
358
    c.lua_setfield(state, -2, "__action");
359
    c.lua_pushvalue(state, 1);
360
    c.lua_setfield(state, -2, "__arg");
361
}
362
363
fn lua_spawn(state: ?*c.lua_State) callconv(.c) c_int {
364
    const s = state orelse return 0;
365
    create_action_table_with_string(s, "Spawn");
366
    return 1;
367
}
368
369
fn lua_spawn_terminal(state: ?*c.lua_State) callconv(.c) c_int {
370
    const s = state orelse return 0;
371
    create_action_table(s, "SpawnTerminal");
372
    return 1;
373
}
374
375
fn lua_key_bind(state: ?*c.lua_State) callconv(.c) c_int {
376
    const s = state orelse return 0;
377
    const cfg = config orelse return 0;
378
379
    const mod_mask = parse_modifiers(s, 1);
380
    const key_str = get_string_arg(s, 2) orelse return 0;
381
    const keysym = key_name_to_keysym(key_str) orelse return 0;
382
383
    if (c.lua_type(s, 3) != c.LUA_TTABLE) return 0;
384
385
    _ = c.lua_getfield(s, 3, "__action");
386
    const action_str = get_lua_string(s, -1) orelse {
387
        c.lua_settop(s, -2);
388
        return 0;
389
    };
390
    c.lua_settop(s, -2);
391
392
    const action = parse_action(action_str) orelse return 0;
393
394
    var int_arg: i32 = 0;
395
    var str_arg: ?[]const u8 = null;
396
397
    _ = c.lua_getfield(s, 3, "__arg");
398
    if (c.lua_type(s, -1) == c.LUA_TNUMBER) {
399
        int_arg = @intCast(c.lua_tointegerx(s, -1, null));
400
    } else if (c.lua_type(s, -1) == c.LUA_TSTRING) {
401
        str_arg = get_lua_string(s, -1);
402
    } else if (c.lua_type(s, -1) == c.LUA_TTABLE) {
403
        str_arg = extract_spawn_command(s, -1);
404
    }
405
    c.lua_settop(s, -2);
406
407
    var keybind: config_mod.Keybind = .{
408
        .action = action,
409
        .int_arg = int_arg,
410
        .str_arg = str_arg,
411
    };
412
    keybind.keys[0] = .{ .mod_mask = mod_mask, .keysym = keysym };
413
    keybind.key_count = 1;
414
415
    cfg.add_keybind(keybind) catch return 0;
416
417
    return 0;
418
}
419
420
fn lua_key_chord(state: ?*c.lua_State) callconv(.c) c_int {
421
    const s = state orelse return 0;
422
    const cfg = config orelse return 0;
423
424
    if (c.lua_type(s, 1) != c.LUA_TTABLE) return 0;
425
    if (c.lua_type(s, 2) != c.LUA_TTABLE) return 0;
426
427
    var keybind: config_mod.Keybind = .{
428
        .action = .quit,
429
        .int_arg = 0,
430
        .str_arg = null,
431
    };
432
    keybind.key_count = 0;
433
434
    const num_keys = c.lua_rawlen(s, 1);
435
    if (num_keys == 0 or num_keys > 4) return 0;
436
437
    var i: usize = 1;
438
    while (i <= num_keys) : (i += 1) {
439
        _ = c.lua_rawgeti(s, 1, @intCast(i));
440
        if (c.lua_type(s, -1) != c.LUA_TTABLE) {
441
            c.lua_settop(s, -2);
442
            return 0;
443
        }
444
445
        _ = c.lua_rawgeti(s, -1, 1);
446
        const mod_mask = parse_modifiers_at_top(s);
447
        c.lua_settop(s, -2);
448
449
        _ = c.lua_rawgeti(s, -1, 2);
450
        const key_str = get_lua_string(s, -1) orelse {
451
            c.lua_settop(s, -3);
452
            return 0;
453
        };
454
        c.lua_settop(s, -2);
455
456
        const keysym = key_name_to_keysym(key_str) orelse {
457
            c.lua_settop(s, -2);
458
            return 0;
459
        };
460
461
        keybind.keys[keybind.key_count] = .{ .mod_mask = mod_mask, .keysym = keysym };
462
        keybind.key_count += 1;
463
464
        c.lua_settop(s, -2);
465
    }
466
467
    _ = c.lua_getfield(s, 2, "__action");
468
    const action_str = get_lua_string(s, -1) orelse {
469
        c.lua_settop(s, -2);
470
        return 0;
471
    };
472
    c.lua_settop(s, -2);
473
474
    keybind.action = parse_action(action_str) orelse return 0;
475
476
    _ = c.lua_getfield(s, 2, "__arg");
477
    if (c.lua_type(s, -1) == c.LUA_TNUMBER) {
478
        keybind.int_arg = @intCast(c.lua_tointegerx(s, -1, null));
479
    } else if (c.lua_type(s, -1) == c.LUA_TSTRING) {
480
        keybind.str_arg = get_lua_string(s, -1);
481
    } else if (c.lua_type(s, -1) == c.LUA_TTABLE) {
482
        keybind.str_arg = extract_spawn_command(s, -1);
483
    }
484
485
    c.lua_settop(s, -2);
486
    cfg.add_keybind(keybind) catch return 0;
487
488
    return 0;
489
}
490
491
fn lua_gaps_set_enabled(state: ?*c.lua_State) callconv(.c) c_int {
492
    const cfg = config orelse return 0;
493
    const s = state orelse return 0;
494
    cfg.gaps_enabled = c.lua_toboolean(s, 1) != 0;
495
    return 0;
496
}
497
498
fn lua_gaps_enable(state: ?*c.lua_State) callconv(.c) c_int {
499
    _ = state;
500
    const cfg = config orelse return 0;
501
    cfg.gaps_enabled = true;
502
    return 0;
503
}
504
505
fn lua_gaps_disable(state: ?*c.lua_State) callconv(.c) c_int {
506
    _ = state;
507
    const cfg = config orelse return 0;
508
    cfg.gaps_enabled = false;
509
    return 0;
510
}
511
512
fn lua_gaps_set_inner(state: ?*c.lua_State) callconv(.c) c_int {
513
    const cfg = config orelse return 0;
514
    const s = state orelse return 0;
515
    cfg.gap_inner_h = @intCast(c.lua_tointegerx(s, 1, null));
516
    cfg.gap_inner_v = @intCast(c.lua_tointegerx(s, 2, null));
517
    return 0;
518
}
519
520
fn lua_gaps_set_outer(state: ?*c.lua_State) callconv(.c) c_int {
521
    const cfg = config orelse return 0;
522
    const s = state orelse return 0;
523
    cfg.gap_outer_h = @intCast(c.lua_tointegerx(s, 1, null));
524
    cfg.gap_outer_v = @intCast(c.lua_tointegerx(s, 2, null));
525
    return 0;
526
}
527
528
fn lua_gaps_set_smart(state: ?*c.lua_State) callconv(.c) c_int {
529
    const cfg = config orelse return 0;
530
    const s = state orelse return 0;
531
    cfg.smartgaps_enabled = c.lua_toboolean(s, 1) != 0;
532
    return 0;
533
}
534
535
fn lua_border_set_width(state: ?*c.lua_State) callconv(.c) c_int {
536
    const cfg = config orelse return 0;
537
    const s = state orelse return 0;
538
    cfg.border_width = @intCast(c.lua_tointegerx(s, 1, null));
539
    return 0;
540
}
541
542
fn lua_border_set_focused_color(state: ?*c.lua_State) callconv(.c) c_int {
543
    const cfg = config orelse return 0;
544
    const s = state orelse return 0;
545
    cfg.border_focused = parse_color(s, 1);
546
    return 0;
547
}
548
549
fn lua_border_set_unfocused_color(state: ?*c.lua_State) callconv(.c) c_int {
550
    const cfg = config orelse return 0;
551
    const s = state orelse return 0;
552
    cfg.border_unfocused = parse_color(s, 1);
553
    return 0;
554
}
555
556
fn lua_client_kill(state: ?*c.lua_State) callconv(.c) c_int {
557
    const s = state orelse return 0;
558
    create_action_table(s, "KillClient");
559
    return 1;
560
}
561
562
fn lua_client_toggle_fullscreen(state: ?*c.lua_State) callconv(.c) c_int {
563
    const s = state orelse return 0;
564
    create_action_table(s, "ToggleFullScreen");
565
    return 1;
566
}
567
568
fn lua_client_toggle_floating(state: ?*c.lua_State) callconv(.c) c_int {
569
    const s = state orelse return 0;
570
    create_action_table(s, "ToggleFloating");
571
    return 1;
572
}
573
574
fn lua_client_focus_stack(state: ?*c.lua_State) callconv(.c) c_int {
575
    const s = state orelse return 0;
576
    const dir: i32 = @intCast(c.lua_tointegerx(s, 1, null));
577
    create_action_table_with_int(s, "FocusStack", dir);
578
    return 1;
579
}
580
581
fn lua_client_move_stack(state: ?*c.lua_State) callconv(.c) c_int {
582
    const s = state orelse return 0;
583
    const dir: i32 = @intCast(c.lua_tointegerx(s, 1, null));
584
    create_action_table_with_int(s, "MoveStack", dir);
585
    return 1;
586
}
587
588
fn lua_layout_cycle(state: ?*c.lua_State) callconv(.c) c_int {
589
    const s = state orelse return 0;
590
    create_action_table(s, "CycleLayout");
591
    return 1;
592
}
593
594
fn lua_layout_set(state: ?*c.lua_State) callconv(.c) c_int {
595
    const s = state orelse return 0;
596
    create_action_table_with_string(s, "ChangeLayout");
597
    return 1;
598
}
599
600
fn lua_layout_scroll_left(state: ?*c.lua_State) callconv(.c) c_int {
601
    const s = state orelse return 0;
602
    create_action_table(s, "ScrollLeft");
603
    return 1;
604
}
605
606
fn lua_layout_scroll_right(state: ?*c.lua_State) callconv(.c) c_int {
607
    const s = state orelse return 0;
608
    create_action_table(s, "ScrollRight");
609
    return 1;
610
}
611
612
fn lua_tag_view(state: ?*c.lua_State) callconv(.c) c_int {
613
    const s = state orelse return 0;
614
    const idx: i32 = @intCast(c.lua_tointegerx(s, 1, null));
615
    create_action_table_with_int(s, "ViewTag", idx);
616
    return 1;
617
}
618
619
fn lua_tag_view_next(state: ?*c.lua_State) callconv(.c) c_int {
620
    const s = state orelse return 0;
621
    create_action_table(s, "ViewNextTag");
622
    return 1;
623
}
624
625
fn lua_tag_view_previous(state: ?*c.lua_State) callconv(.c) c_int {
626
    const s = state orelse return 0;
627
    create_action_table(s, "ViewPreviousTag");
628
    return 1;
629
}
630
631
fn lua_tag_view_next_nonempty(state: ?*c.lua_State) callconv(.c) c_int {
632
    const s = state orelse return 0;
633
    create_action_table(s, "ViewNextNonEmptyTag");
634
    return 1;
635
}
636
637
fn lua_tag_view_previous_nonempty(state: ?*c.lua_State) callconv(.c) c_int {
638
    const s = state orelse return 0;
639
    create_action_table(s, "ViewPreviousNonEmptyTag");
640
    return 1;
641
}
642
643
fn lua_tag_toggleview(state: ?*c.lua_State) callconv(.c) c_int {
644
    const s = state orelse return 0;
645
    const idx: i32 = @intCast(c.lua_tointegerx(s, 1, null));
646
    create_action_table_with_int(s, "ToggleView", idx);
647
    return 1;
648
}
649
650
fn lua_tag_move_to(state: ?*c.lua_State) callconv(.c) c_int {
651
    const s = state orelse return 0;
652
    const idx: i32 = @intCast(c.lua_tointegerx(s, 1, null));
653
    create_action_table_with_int(s, "MoveToTag", idx);
654
    return 1;
655
}
656
657
fn lua_tag_toggletag(state: ?*c.lua_State) callconv(.c) c_int {
658
    const s = state orelse return 0;
659
    const idx: i32 = @intCast(c.lua_tointegerx(s, 1, null));
660
    create_action_table_with_int(s, "ToggleTag", idx);
661
    return 1;
662
}
663
664
fn lua_tag_set_back_and_forth(state: ?*c.lua_State) callconv(.c) c_int {
665
    const cfg = config orelse return 0;
666
    const s = state orelse return 0;
667
    cfg.tag_back_and_forth = c.lua_toboolean(s, 1) != 0;
668
    return 0;
669
}
670
671
fn lua_monitor_focus(state: ?*c.lua_State) callconv(.c) c_int {
672
    const s = state orelse return 0;
673
    const dir: i32 = @intCast(c.lua_tointegerx(s, 1, null));
674
    create_action_table_with_int(s, "FocusMonitor", dir);
675
    return 1;
676
}
677
678
fn lua_monitor_tag(state: ?*c.lua_State) callconv(.c) c_int {
679
    const s = state orelse return 0;
680
    const dir: i32 = @intCast(c.lua_tointegerx(s, 1, null));
681
    create_action_table_with_int(s, "TagMonitor", dir);
682
    return 1;
683
}
684
685
fn lua_rule_add(state: ?*c.lua_State) callconv(.c) c_int {
686
    const cfg = config orelse return 0;
687
    const s = state orelse return 0;
688
689
    if (c.lua_type(s, 1) != c.LUA_TTABLE) return 0;
690
691
    var rule = Rule{
692
        .class = null,
693
        .instance = null,
694
        .title = null,
695
        .tags = 0,
696
        .is_floating = false,
697
        .monitor = -1,
698
        .focus = false,
699
    };
700
701
    _ = c.lua_getfield(s, 1, "class");
702
    if (c.lua_type(s, -1) == c.LUA_TSTRING) {
703
        rule.class = get_lua_string(s, -1);
704
    }
705
    c.lua_settop(s, -2);
706
707
    _ = c.lua_getfield(s, 1, "instance");
708
    if (c.lua_type(s, -1) == c.LUA_TSTRING) {
709
        rule.instance = get_lua_string(s, -1);
710
    }
711
    c.lua_settop(s, -2);
712
713
    _ = c.lua_getfield(s, 1, "title");
714
    if (c.lua_type(s, -1) == c.LUA_TSTRING) {
715
        rule.title = get_lua_string(s, -1);
716
    }
717
    c.lua_settop(s, -2);
718
719
    _ = c.lua_getfield(s, 1, "tag");
720
    if (c.lua_type(s, -1) == c.LUA_TNUMBER) {
721
        const tag_idx: i32 = @intCast(c.lua_tointegerx(s, -1, null));
722
        if (tag_idx > 0) {
723
            rule.tags = @as(u32, 1) << @intCast(tag_idx - 1);
724
        }
725
    }
726
    c.lua_settop(s, -2);
727
728
    _ = c.lua_getfield(s, 1, "floating");
729
    if (c.lua_type(s, -1) == c.LUA_TBOOLEAN) {
730
        rule.is_floating = c.lua_toboolean(s, -1) != 0;
731
    }
732
    c.lua_settop(s, -2);
733
734
    _ = c.lua_getfield(s, 1, "monitor");
735
    if (c.lua_type(s, -1) == c.LUA_TNUMBER) {
736
        rule.monitor = @intCast(c.lua_tointegerx(s, -1, null));
737
    }
738
    c.lua_settop(s, -2);
739
740
    _ = c.lua_getfield(s, 1, "focus");
741
    if (c.lua_type(s, -1) == c.LUA_TBOOLEAN) {
742
        rule.focus = c.lua_toboolean(s, -1) != 0;
743
    }
744
    c.lua_settop(s, -2);
745
746
    cfg.add_rule(rule) catch return 0;
747
    return 0;
748
}
749
750
fn lua_bar_set_font(state: ?*c.lua_State) callconv(.c) c_int {
751
    const cfg = config orelse return 0;
752
    const s = state orelse return 0;
753
    if (dupe_lua_string(s, 1)) |font| {
754
        cfg.font = font;
755
    }
756
    return 0;
757
}
758
759
fn lua_bar_set_blocks(state: ?*c.lua_State) callconv(.c) c_int {
760
    const cfg = config orelse return 0;
761
    const s = state orelse return 0;
762
763
    if (c.lua_type(s, 1) != c.LUA_TTABLE) return 0;
764
765
    const len = c.lua_rawlen(s, 1);
766
    var i: usize = 1;
767
    while (i <= len) : (i += 1) {
768
        _ = c.lua_rawgeti(s, 1, @intCast(i));
769
770
        if (c.lua_type(s, -1) != c.LUA_TTABLE) {
771
            c.lua_settop(s, -2);
772
            continue;
773
        }
774
775
        if (parse_block_config(s, -1)) |block| {
776
            cfg.add_block(block) catch {};
777
        }
778
779
        c.lua_settop(s, -2);
780
    }
781
782
    return 0;
783
}
784
785
fn parse_block_config(state: *c.lua_State, idx: c_int) ?Block {
786
    _ = c.lua_getfield(state, idx, "__block_type");
787
    const block_type_str = get_lua_string(state, -1) orelse {
788
        c.lua_settop(state, -2);
789
        return null;
790
    };
791
    c.lua_settop(state, -2);
792
793
    _ = c.lua_getfield(state, idx, "format");
794
    const format = dupe_lua_string(state, -1) orelse "";
795
    c.lua_settop(state, -2);
796
797
    _ = c.lua_getfield(state, idx, "interval");
798
    const interval: u32 = @intCast(c.lua_tointegerx(state, -1, null));
799
    c.lua_settop(state, -2);
800
801
    _ = c.lua_getfield(state, idx, "color");
802
    const color = parse_color(state, -1);
803
    c.lua_settop(state, -2);
804
805
    _ = c.lua_getfield(state, idx, "underline");
806
    const underline = c.lua_toboolean(state, -1) != 0;
807
    c.lua_settop(state, -2);
808
809
    var block = Block{
810
        .block_type = .static,
811
        .format = format,
812
        .interval = interval,
813
        .color = color,
814
        .underline = underline,
815
    };
816
817
    if (std.mem.eql(u8, block_type_str, "Ram")) {
818
        block.block_type = .ram;
819
    } else if (std.mem.eql(u8, block_type_str, "DateTime")) {
820
        block.block_type = .datetime;
821
        _ = c.lua_getfield(state, idx, "__arg");
822
        block.datetime_format = dupe_lua_string(state, -1);
823
        c.lua_settop(state, -2);
824
    } else if (std.mem.eql(u8, block_type_str, "Shell")) {
825
        block.block_type = .shell;
826
        _ = c.lua_getfield(state, idx, "__arg");
827
        block.command = dupe_lua_string(state, -1);
828
        c.lua_settop(state, -2);
829
    } else if (std.mem.eql(u8, block_type_str, "Static")) {
830
        block.block_type = .static;
831
        _ = c.lua_getfield(state, idx, "__arg");
832
        if (dupe_lua_string(state, -1)) |text| {
833
            block.format = text;
834
        }
835
        c.lua_settop(state, -2);
836
    } else if (std.mem.eql(u8, block_type_str, "Battery")) {
837
        block.block_type = .battery;
838
        _ = c.lua_getfield(state, idx, "__arg");
839
        if (c.lua_type(state, -1) == c.LUA_TTABLE) {
840
            _ = c.lua_getfield(state, -1, "charging");
841
            block.format_charging = dupe_lua_string(state, -1);
842
            c.lua_settop(state, -2);
843
844
            _ = c.lua_getfield(state, -1, "discharging");
845
            block.format_discharging = dupe_lua_string(state, -1);
846
            c.lua_settop(state, -2);
847
848
            _ = c.lua_getfield(state, -1, "full");
849
            block.format_full = dupe_lua_string(state, -1);
850
            c.lua_settop(state, -2);
851
852
            _ = c.lua_getfield(state, -1, "battery_name");
853
            block.battery_name = dupe_lua_string(state, -1);
854
            c.lua_settop(state, -2);
855
        }
856
        c.lua_settop(state, -2);
857
    } else {
858
        return null;
859
    }
860
861
    return block;
862
}
863
864
fn lua_bar_set_scheme_normal(state: ?*c.lua_State) callconv(.c) c_int {
865
    const cfg = config orelse return 0;
866
    const s = state orelse return 0;
867
    cfg.scheme_normal = parse_scheme(s);
868
    return 0;
869
}
870
871
fn lua_bar_set_scheme_selected(state: ?*c.lua_State) callconv(.c) c_int {
872
    const cfg = config orelse return 0;
873
    const s = state orelse return 0;
874
    cfg.scheme_selected = parse_scheme(s);
875
    return 0;
876
}
877
878
fn lua_bar_set_scheme_occupied(state: ?*c.lua_State) callconv(.c) c_int {
879
    const cfg = config orelse return 0;
880
    const s = state orelse return 0;
881
    cfg.scheme_occupied = parse_scheme(s);
882
    return 0;
883
}
884
885
fn lua_bar_set_scheme_urgent(state: ?*c.lua_State) callconv(.c) c_int {
886
    const cfg = config orelse return 0;
887
    const s = state orelse return 0;
888
    cfg.scheme_urgent = parse_scheme(s);
889
    return 0;
890
}
891
892
fn lua_bar_set_hide_vacant_tags(state: ?*c.lua_State) callconv(.c) c_int {
893
    const cfg = config orelse return 0;
894
    const s = state orelse return 0;
895
    cfg.hide_vacant_tags = c.lua_toboolean(s, 1) != 0;
896
    return 0;
897
}
898
899
fn lua_bar_block_ram(state: ?*c.lua_State) callconv(.c) c_int {
900
    const s = state orelse return 0;
901
    create_block_table(s, "Ram", null);
902
    return 1;
903
}
904
905
fn lua_bar_block_datetime(state: ?*c.lua_State) callconv(.c) c_int {
906
    const s = state orelse return 0;
907
    _ = c.lua_getfield(s, 1, "date_format");
908
    const date_format = get_lua_string(s, -1);
909
    c.lua_settop(s, -2);
910
    create_block_table(s, "DateTime", date_format);
911
    return 1;
912
}
913
914
fn lua_bar_block_shell(state: ?*c.lua_State) callconv(.c) c_int {
915
    const s = state orelse return 0;
916
    _ = c.lua_getfield(s, 1, "command");
917
    const command = get_lua_string(s, -1);
918
    c.lua_settop(s, -2);
919
    create_block_table(s, "Shell", command);
920
    return 1;
921
}
922
923
fn lua_bar_block_static(state: ?*c.lua_State) callconv(.c) c_int {
924
    const s = state orelse return 0;
925
    _ = c.lua_getfield(s, 1, "text");
926
    const text = get_lua_string(s, -1);
927
    c.lua_settop(s, -2);
928
    create_block_table(s, "Static", text);
929
    return 1;
930
}
931
932
fn lua_bar_block_battery(state: ?*c.lua_State) callconv(.c) c_int {
933
    const s = state orelse return 0;
934
935
    c.lua_createtable(s, 0, 6);
936
937
    _ = c.lua_pushstring(s, "Battery");
938
    c.lua_setfield(s, -2, "__block_type");
939
940
    _ = c.lua_getfield(s, 1, "format");
941
    c.lua_setfield(s, -2, "format");
942
943
    _ = c.lua_getfield(s, 1, "interval");
944
    c.lua_setfield(s, -2, "interval");
945
946
    _ = c.lua_getfield(s, 1, "color");
947
    c.lua_setfield(s, -2, "color");
948
949
    _ = c.lua_getfield(s, 1, "underline");
950
    c.lua_setfield(s, -2, "underline");
951
952
    c.lua_createtable(s, 0, 4);
953
    _ = c.lua_getfield(s, 1, "charging");
954
    c.lua_setfield(s, -2, "charging");
955
    _ = c.lua_getfield(s, 1, "discharging");
956
    c.lua_setfield(s, -2, "discharging");
957
    _ = c.lua_getfield(s, 1, "full");
958
    c.lua_setfield(s, -2, "full");
959
    _ = c.lua_getfield(s, 1, "battery_name");
960
    c.lua_setfield(s, -2, "battery_name");
961
    c.lua_setfield(s, -2, "__arg");
962
963
    return 1;
964
}
965
966
fn create_block_table(state: *c.lua_State, block_type: [*:0]const u8, arg: ?[]const u8) void {
967
    c.lua_createtable(state, 0, 6);
968
969
    _ = c.lua_pushstring(state, block_type);
970
    c.lua_setfield(state, -2, "__block_type");
971
972
    _ = c.lua_getfield(state, 1, "format");
973
    c.lua_setfield(state, -2, "format");
974
975
    _ = c.lua_getfield(state, 1, "interval");
976
    c.lua_setfield(state, -2, "interval");
977
978
    _ = c.lua_getfield(state, 1, "color");
979
    c.lua_setfield(state, -2, "color");
980
981
    _ = c.lua_getfield(state, 1, "underline");
982
    c.lua_setfield(state, -2, "underline");
983
984
    if (arg) |a| {
985
        var buf: [256]u8 = undefined;
986
        if (a.len < buf.len) {
987
            @memcpy(buf[0..a.len], a);
988
            buf[a.len] = 0;
989
            _ = c.lua_pushstring(state, &buf);
990
            c.lua_setfield(state, -2, "__arg");
991
        }
992
    }
993
}
994
995
fn lua_set_terminal(state: ?*c.lua_State) callconv(.c) c_int {
996
    const cfg = config orelse return 0;
997
    const s = state orelse return 0;
998
    if (dupe_lua_string(s, 1)) |term| {
999
        cfg.terminal = term;
1000
    }
1001
    return 0;
1002
}
1003
1004
fn lua_set_modkey(state: ?*c.lua_State) callconv(.c) c_int {
1005
    const cfg = config orelse return 0;
1006
    const s = state orelse return 0;
1007
    if (get_string_arg(s, 1)) |modkey_str| {
1008
        cfg.modkey = parse_single_modifier(modkey_str);
1009
        cfg.add_button(.{
1010
            .click = .client_win,
1011
            .mod_mask = cfg.modkey,
1012
            .button = 1,
1013
            .action = .move_mouse,
1014
        }) catch {};
1015
        cfg.add_button(.{
1016
            .click = .client_win,
1017
            .mod_mask = cfg.modkey,
1018
            .button = 3,
1019
            .action = .resize_mouse,
1020
        }) catch {};
1021
    }
1022
    return 0;
1023
}
1024
1025
fn lua_set_tags(state: ?*c.lua_State) callconv(.c) c_int {
1026
    const cfg = config orelse return 0;
1027
    const s = state orelse return 0;
1028
1029
    if (c.lua_type(s, 1) != c.LUA_TTABLE) return 0;
1030
1031
    const len = c.lua_rawlen(s, 1);
1032
    const count = @min(len, 9);
1033
    var i: usize = 0;
1034
    while (i < count) : (i += 1) {
1035
        _ = c.lua_rawgeti(s, 1, @intCast(i + 1));
1036
        if (dupe_lua_string(s, -1)) |tag_str| {
1037
            cfg.tags[i] = tag_str;
1038
        }
1039
        c.lua_settop(s, -2);
1040
    }
1041
    cfg.tag_count = @intCast(count);
1042
1043
    return 0;
1044
}
1045
1046
fn lua_autostart(state: ?*c.lua_State) callconv(.c) c_int {
1047
    const cfg = config orelse return 0;
1048
    const s = state orelse return 0;
1049
    if (dupe_lua_string(s, 1)) |cmd| {
1050
        cfg.add_autostart(cmd) catch return 0;
1051
    }
1052
    return 0;
1053
}
1054
1055
fn lua_auto_tile(state: ?*c.lua_State) callconv(.c) c_int {
1056
    const cfg = config orelse return 0;
1057
    const s = state orelse return 0;
1058
    cfg.auto_tile = c.lua_toboolean(s, 1) != 0;
1059
    return 0;
1060
}
1061
1062
fn lua_set_layout_symbol(state: ?*c.lua_State) callconv(.c) c_int {
1063
    const cfg = config orelse return 0;
1064
    const s = state orelse return 0;
1065
    const name = get_string_arg(s, 1) orelse return 0;
1066
    const symbol = dupe_lua_string(s, 2) orelse return 0;
1067
1068
    const layout_map = .{
1069
        .{ "tiling", &cfg.layout_tile_symbol },
1070
        .{ "tile", &cfg.layout_tile_symbol },
1071
        .{ "normie", &cfg.layout_floating_symbol },
1072
        .{ "floating", &cfg.layout_floating_symbol },
1073
        .{ "float", &cfg.layout_floating_symbol },
1074
        .{ "monocle", &cfg.layout_monocle_symbol },
1075
        .{ "scrolling", &cfg.layout_scrolling_symbol },
1076
        .{ "scroll", &cfg.layout_scrolling_symbol },
1077
        .{ "grid", &cfg.layout_grid_symbol },
1078
    };
1079
1080
    inline for (layout_map) |entry| {
1081
        if (std.mem.eql(u8, name, entry[0])) {
1082
            entry[1].* = symbol;
1083
            return 0;
1084
        }
1085
    }
1086
    return 0;
1087
}
1088
1089
fn lua_quit(state: ?*c.lua_State) callconv(.c) c_int {
1090
    const s = state orelse return 0;
1091
    create_action_table(s, "Quit");
1092
    return 1;
1093
}
1094
1095
fn lua_restart(state: ?*c.lua_State) callconv(.c) c_int {
1096
    const s = state orelse return 0;
1097
    create_action_table(s, "Restart");
1098
    return 1;
1099
}
1100
1101
fn lua_toggle_gaps(state: ?*c.lua_State) callconv(.c) c_int {
1102
    const s = state orelse return 0;
1103
    create_action_table(s, "ToggleGaps");
1104
    return 1;
1105
}
1106
1107
fn lua_show_keybinds(state: ?*c.lua_State) callconv(.c) c_int {
1108
    const s = state orelse return 0;
1109
    create_action_table(s, "ShowKeybinds");
1110
    return 1;
1111
}
1112
1113
fn lua_set_master_factor(state: ?*c.lua_State) callconv(.c) c_int {
1114
    const s = state orelse return 0;
1115
    const delta: i32 = @intCast(c.lua_tointegerx(s, 1, null));
1116
    create_action_table_with_int(s, "ResizeMaster", delta);
1117
    return 1;
1118
}
1119
1120
fn lua_inc_num_master(state: ?*c.lua_State) callconv(.c) c_int {
1121
    const s = state orelse return 0;
1122
    const delta: i32 = @intCast(c.lua_tointegerx(s, 1, null));
1123
    if (delta > 0) {
1124
        create_action_table(s, "IncMaster");
1125
    } else {
1126
        create_action_table(s, "DecMaster");
1127
    }
1128
    return 1;
1129
}
1130
1131
fn get_string_arg(state: *c.lua_State, idx: c_int) ?[]const u8 {
1132
    if (c.lua_type(state, idx) != c.LUA_TSTRING) return null;
1133
    return get_lua_string(state, idx);
1134
}
1135
1136
fn extract_spawn_command(state: *c.lua_State, idx: c_int) ?[]const u8 {
1137
    const len = c.lua_rawlen(state, idx);
1138
    if (len == 0) return null;
1139
1140
    if (len >= 3) {
1141
        _ = c.lua_rawgeti(state, idx, 1);
1142
        const first = get_lua_string(state, -1);
1143
        c.lua_settop(state, -2);
1144
1145
        _ = c.lua_rawgeti(state, idx, 2);
1146
        const second = get_lua_string(state, -1);
1147
        c.lua_settop(state, -2);
1148
1149
        if (first != null and second != null and
1150
            std.mem.eql(u8, first.?, "sh") and std.mem.eql(u8, second.?, "-c"))
1151
        {
1152
            _ = c.lua_rawgeti(state, idx, 3);
1153
            const cmd = get_lua_string(state, -1);
1154
            c.lua_settop(state, -2);
1155
            return cmd;
1156
        }
1157
    }
1158
1159
    _ = c.lua_rawgeti(state, idx, 1);
1160
    const first_elem = get_lua_string(state, -1);
1161
    c.lua_settop(state, -2);
1162
    return first_elem;
1163
}
1164
1165
fn get_lua_string(state: *c.lua_State, idx: c_int) ?[]const u8 {
1166
    const cstr = c.lua_tolstring(state, idx, null);
1167
    if (cstr == null) return null;
1168
    return std.mem.span(cstr);
1169
}
1170
1171
fn dupe_lua_string(state: *c.lua_State, idx: c_int) ?[]const u8 {
1172
    const cfg = config orelse return null;
1173
    const lua_str = get_lua_string(state, idx) orelse return null;
1174
    const arena_allocator = cfg.string_arena.allocator();
1175
    const duped = arena_allocator.dupe(u8, lua_str) catch return null;
1176
    return duped;
1177
}
1178
1179
fn parse_color(state: *c.lua_State, idx: c_int) u32 {
1180
    const lua_type = c.lua_type(state, idx);
1181
    if (lua_type == c.LUA_TNUMBER) {
1182
        return @intCast(c.lua_tointegerx(state, idx, null));
1183
    }
1184
    if (lua_type == c.LUA_TSTRING) {
1185
        const str = get_lua_string(state, idx) orelse return 0;
1186
        if (str.len > 0 and str[0] == '#') {
1187
            return std.fmt.parseInt(u32, str[1..], 16) catch return 0;
1188
        }
1189
        if (str.len > 2 and str[0] == '0' and str[1] == 'x') {
1190
            return std.fmt.parseInt(u32, str[2..], 16) catch return 0;
1191
        }
1192
        return std.fmt.parseInt(u32, str, 16) catch return 0;
1193
    }
1194
    return 0;
1195
}
1196
1197
fn parse_scheme(state: *c.lua_State) ColorScheme {
1198
    return ColorScheme{
1199
        .foreground = parse_color(state, 1),
1200
        .background = parse_color(state, 2),
1201
        .border = parse_color(state, 3),
1202
    };
1203
}
1204
1205
fn parse_modifiers(state: *c.lua_State, idx: c_int) u32 {
1206
    var mod_mask: u32 = 0;
1207
1208
    if (c.lua_type(state, idx) != c.LUA_TTABLE) return mod_mask;
1209
1210
    const len = c.lua_rawlen(state, idx);
1211
    var i: usize = 1;
1212
    while (i <= len) : (i += 1) {
1213
        _ = c.lua_rawgeti(state, idx, @intCast(i));
1214
        if (get_lua_string(state, -1)) |mod_str| {
1215
            const parsed = parse_single_modifier(mod_str);
1216
            mod_mask |= parsed;
1217
        }
1218
        c.lua_settop(state, -2);
1219
    }
1220
1221
    return mod_mask;
1222
}
1223
1224
fn parse_modifiers_at_top(state: *c.lua_State) u32 {
1225
    return parse_modifiers(state, -1);
1226
}
1227
1228
fn parse_single_modifier(name: []const u8) u32 {
1229
    if (std.mem.eql(u8, name, "Mod4") or std.mem.eql(u8, name, "mod4") or std.mem.eql(u8, name, "super")) {
1230
        return (1 << 6);
1231
    } else if (std.mem.eql(u8, name, "Mod1") or std.mem.eql(u8, name, "mod1") or std.mem.eql(u8, name, "alt")) {
1232
        return (1 << 3);
1233
    } else if (std.mem.eql(u8, name, "Shift") or std.mem.eql(u8, name, "shift")) {
1234
        return (1 << 0);
1235
    } else if (std.mem.eql(u8, name, "Control") or std.mem.eql(u8, name, "control") or std.mem.eql(u8, name, "ctrl")) {
1236
        return (1 << 2);
1237
    }
1238
    return 0;
1239
}
1240
1241
fn parse_action(name: []const u8) ?Action {
1242
    const action_map = .{
1243
        .{ "Spawn", Action.spawn },
1244
        .{ "SpawnTerminal", Action.spawn_terminal },
1245
        .{ "KillClient", Action.kill_client },
1246
        .{ "Quit", Action.quit },
1247
        .{ "Restart", Action.restart },
1248
        .{ "ShowKeybinds", Action.show_keybinds },
1249
        .{ "FocusStack", Action.focus_next },
1250
        .{ "MoveStack", Action.move_next },
1251
        .{ "ResizeMaster", Action.resize_master },
1252
        .{ "IncMaster", Action.inc_master },
1253
        .{ "DecMaster", Action.dec_master },
1254
        .{ "ToggleFloating", Action.toggle_floating },
1255
        .{ "ToggleFullScreen", Action.toggle_fullscreen },
1256
        .{ "ToggleGaps", Action.toggle_gaps },
1257
        .{ "CycleLayout", Action.cycle_layout },
1258
        .{ "ChangeLayout", Action.set_layout },
1259
        .{ "ViewTag", Action.view_tag },
1260
        .{ "ViewNextTag", Action.view_next_tag },
1261
        .{ "ViewPreviousTag", Action.view_prev_tag },
1262
        .{ "ViewNextNonEmptyTag", Action.view_next_nonempty_tag },
1263
        .{ "ViewPreviousNonEmptyTag", Action.view_prev_nonempty_tag },
1264
        .{ "MoveToTag", Action.move_to_tag },
1265
        .{ "ToggleView", Action.toggle_view_tag },
1266
        .{ "ToggleTag", Action.toggle_tag },
1267
        .{ "FocusMonitor", Action.focus_monitor },
1268
        .{ "TagMonitor", Action.send_to_monitor },
1269
        .{ "ScrollLeft", Action.scroll_left },
1270
        .{ "ScrollRight", Action.scroll_right },
1271
    };
1272
1273
    inline for (action_map) |entry| {
1274
        if (std.mem.eql(u8, name, entry[0])) {
1275
            return entry[1];
1276
        }
1277
    }
1278
    return null;
1279
}
1280
1281
fn key_name_to_keysym(name: []const u8) ?u64 {
1282
    const key_map = .{
1283
        .{ "Return", 0xff0d },
1284
        .{ "Enter", 0xff0d },
1285
        .{ "Tab", 0xff09 },
1286
        .{ "Escape", 0xff1b },
1287
        .{ "BackSpace", 0xff08 },
1288
        .{ "Delete", 0xffff },
1289
        .{ "space", 0x0020 },
1290
        .{ "Space", 0x0020 },
1291
        .{ "comma", 0x002c },
1292
        .{ "Comma", 0x002c },
1293
        .{ "period", 0x002e },
1294
        .{ "Period", 0x002e },
1295
        .{ "slash", 0x002f },
1296
        .{ "Slash", 0x002f },
1297
        .{ "minus", 0x002d },
1298
        .{ "Minus", 0x002d },
1299
        .{ "equal", 0x003d },
1300
        .{ "Equal", 0x003d },
1301
        .{ "bracketleft", 0x005b },
1302
        .{ "bracketright", 0x005d },
1303
        .{ "backslash", 0x005c },
1304
        .{ "semicolon", 0x003b },
1305
        .{ "apostrophe", 0x0027 },
1306
        .{ "grave", 0x0060 },
1307
        .{ "Left", 0xff51 },
1308
        .{ "Up", 0xff52 },
1309
        .{ "Right", 0xff53 },
1310
        .{ "Down", 0xff54 },
1311
        .{ "F1", 0xffbe },
1312
        .{ "F2", 0xffbf },
1313
        .{ "F3", 0xffc0 },
1314
        .{ "F4", 0xffc1 },
1315
        .{ "F5", 0xffc2 },
1316
        .{ "F6", 0xffc3 },
1317
        .{ "F7", 0xffc4 },
1318
        .{ "F8", 0xffc5 },
1319
        .{ "F9", 0xffc6 },
1320
        .{ "F10", 0xffc7 },
1321
        .{ "F11", 0xffc8 },
1322
        .{ "F12", 0xffc9 },
1323
        .{ "Print", 0xff61 },
1324
        .{ "XF86AudioRaiseVolume", 0x1008ff13 },
1325
        .{ "XF86AudioLowerVolume", 0x1008ff11 },
1326
        .{ "XF86AudioMute", 0x1008ff12 },
1327
        .{ "XF86AudioPlay", 0x1008ff14 },
1328
        .{ "XF86AudioPause", 0x1008ff31 },
1329
        .{ "XF86AudioNext", 0x1008ff17 },
1330
        .{ "XF86AudioPrev", 0x1008ff16 },
1331
        .{ "XF86MonBrightnessUp", 0x1008ff02 },
1332
        .{ "XF86MonBrightnessDown", 0x1008ff03 },
1333
    };
1334
1335
    inline for (key_map) |entry| {
1336
        if (std.mem.eql(u8, name, entry[0])) {
1337
            return entry[1];
1338
        }
1339
    }
1340
1341
    if (name.len == 1) {
1342
        const char = name[0];
1343
        if (char >= 'a' and char <= 'z') {
1344
            return char;
1345
        }
1346
        if (char >= 'A' and char <= 'Z') {
1347
            return char + 32;
1348
        }
1349
        if (char >= '0' and char <= '9') {
1350
            return char;
1351
        }
1352
    }
1353
1354
    return null;
1355
}