oxwm

https://git.tonybtw.com/oxwm.git git://git.tonybtw.com/oxwm.git
40,262 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 Color_Scheme = config_mod.Color_Scheme;
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
    }
482
    c.lua_settop(s, -2);
483
484
    cfg.add_keybind(keybind) catch return 0;
485
486
    return 0;
487
}
488
489
fn lua_gaps_set_enabled(state: ?*c.lua_State) callconv(.c) c_int {
490
    const cfg = config orelse return 0;
491
    const s = state orelse return 0;
492
    cfg.gaps_enabled = c.lua_toboolean(s, 1) != 0;
493
    return 0;
494
}
495
496
fn lua_gaps_enable(state: ?*c.lua_State) callconv(.c) c_int {
497
    _ = state;
498
    const cfg = config orelse return 0;
499
    cfg.gaps_enabled = true;
500
    return 0;
501
}
502
503
fn lua_gaps_disable(state: ?*c.lua_State) callconv(.c) c_int {
504
    _ = state;
505
    const cfg = config orelse return 0;
506
    cfg.gaps_enabled = false;
507
    return 0;
508
}
509
510
fn lua_gaps_set_inner(state: ?*c.lua_State) callconv(.c) c_int {
511
    const cfg = config orelse return 0;
512
    const s = state orelse return 0;
513
    cfg.gap_inner_h = @intCast(c.lua_tointegerx(s, 1, null));
514
    cfg.gap_inner_v = @intCast(c.lua_tointegerx(s, 2, null));
515
    return 0;
516
}
517
518
fn lua_gaps_set_outer(state: ?*c.lua_State) callconv(.c) c_int {
519
    const cfg = config orelse return 0;
520
    const s = state orelse return 0;
521
    cfg.gap_outer_h = @intCast(c.lua_tointegerx(s, 1, null));
522
    cfg.gap_outer_v = @intCast(c.lua_tointegerx(s, 2, null));
523
    return 0;
524
}
525
526
fn lua_gaps_set_smart(state: ?*c.lua_State) callconv(.c) c_int {
527
    const cfg = config orelse return 0;
528
    const s = state orelse return 0;
529
    cfg.smartgaps_enabled = c.lua_toboolean(s, 1) != 0;
530
    return 0;
531
}
532
533
fn lua_border_set_width(state: ?*c.lua_State) callconv(.c) c_int {
534
    const cfg = config orelse return 0;
535
    const s = state orelse return 0;
536
    cfg.border_width = @intCast(c.lua_tointegerx(s, 1, null));
537
    return 0;
538
}
539
540
fn lua_border_set_focused_color(state: ?*c.lua_State) callconv(.c) c_int {
541
    const cfg = config orelse return 0;
542
    const s = state orelse return 0;
543
    cfg.border_focused = parse_color(s, 1);
544
    return 0;
545
}
546
547
fn lua_border_set_unfocused_color(state: ?*c.lua_State) callconv(.c) c_int {
548
    const cfg = config orelse return 0;
549
    const s = state orelse return 0;
550
    cfg.border_unfocused = parse_color(s, 1);
551
    return 0;
552
}
553
554
fn lua_client_kill(state: ?*c.lua_State) callconv(.c) c_int {
555
    const s = state orelse return 0;
556
    create_action_table(s, "KillClient");
557
    return 1;
558
}
559
560
fn lua_client_toggle_fullscreen(state: ?*c.lua_State) callconv(.c) c_int {
561
    const s = state orelse return 0;
562
    create_action_table(s, "ToggleFullScreen");
563
    return 1;
564
}
565
566
fn lua_client_toggle_floating(state: ?*c.lua_State) callconv(.c) c_int {
567
    const s = state orelse return 0;
568
    create_action_table(s, "ToggleFloating");
569
    return 1;
570
}
571
572
fn lua_client_focus_stack(state: ?*c.lua_State) callconv(.c) c_int {
573
    const s = state orelse return 0;
574
    const dir: i32 = @intCast(c.lua_tointegerx(s, 1, null));
575
    create_action_table_with_int(s, "FocusStack", dir);
576
    return 1;
577
}
578
579
fn lua_client_move_stack(state: ?*c.lua_State) callconv(.c) c_int {
580
    const s = state orelse return 0;
581
    const dir: i32 = @intCast(c.lua_tointegerx(s, 1, null));
582
    create_action_table_with_int(s, "MoveStack", dir);
583
    return 1;
584
}
585
586
fn lua_layout_cycle(state: ?*c.lua_State) callconv(.c) c_int {
587
    const s = state orelse return 0;
588
    create_action_table(s, "CycleLayout");
589
    return 1;
590
}
591
592
fn lua_layout_set(state: ?*c.lua_State) callconv(.c) c_int {
593
    const s = state orelse return 0;
594
    create_action_table_with_string(s, "ChangeLayout");
595
    return 1;
596
}
597
598
fn lua_layout_scroll_left(state: ?*c.lua_State) callconv(.c) c_int {
599
    const s = state orelse return 0;
600
    create_action_table(s, "ScrollLeft");
601
    return 1;
602
}
603
604
fn lua_layout_scroll_right(state: ?*c.lua_State) callconv(.c) c_int {
605
    const s = state orelse return 0;
606
    create_action_table(s, "ScrollRight");
607
    return 1;
608
}
609
610
fn lua_tag_view(state: ?*c.lua_State) callconv(.c) c_int {
611
    const s = state orelse return 0;
612
    const idx: i32 = @intCast(c.lua_tointegerx(s, 1, null));
613
    create_action_table_with_int(s, "ViewTag", idx);
614
    return 1;
615
}
616
617
fn lua_tag_view_next(state: ?*c.lua_State) callconv(.c) c_int {
618
    const s = state orelse return 0;
619
    create_action_table(s, "ViewNextTag");
620
    return 1;
621
}
622
623
fn lua_tag_view_previous(state: ?*c.lua_State) callconv(.c) c_int {
624
    const s = state orelse return 0;
625
    create_action_table(s, "ViewPreviousTag");
626
    return 1;
627
}
628
629
fn lua_tag_view_next_nonempty(state: ?*c.lua_State) callconv(.c) c_int {
630
    const s = state orelse return 0;
631
    create_action_table(s, "ViewNextNonEmptyTag");
632
    return 1;
633
}
634
635
fn lua_tag_view_previous_nonempty(state: ?*c.lua_State) callconv(.c) c_int {
636
    const s = state orelse return 0;
637
    create_action_table(s, "ViewPreviousNonEmptyTag");
638
    return 1;
639
}
640
641
fn lua_tag_toggleview(state: ?*c.lua_State) callconv(.c) c_int {
642
    const s = state orelse return 0;
643
    const idx: i32 = @intCast(c.lua_tointegerx(s, 1, null));
644
    create_action_table_with_int(s, "ToggleView", idx);
645
    return 1;
646
}
647
648
fn lua_tag_move_to(state: ?*c.lua_State) callconv(.c) c_int {
649
    const s = state orelse return 0;
650
    const idx: i32 = @intCast(c.lua_tointegerx(s, 1, null));
651
    create_action_table_with_int(s, "MoveToTag", idx);
652
    return 1;
653
}
654
655
fn lua_tag_toggletag(state: ?*c.lua_State) callconv(.c) c_int {
656
    const s = state orelse return 0;
657
    const idx: i32 = @intCast(c.lua_tointegerx(s, 1, null));
658
    create_action_table_with_int(s, "ToggleTag", idx);
659
    return 1;
660
}
661
662
fn lua_tag_set_back_and_forth(state: ?*c.lua_State) callconv(.c) c_int {
663
    const cfg = config orelse return 0;
664
    const s = state orelse return 0;
665
    cfg.tag_back_and_forth = c.lua_toboolean(s, 1) != 0;
666
    return 0;
667
}
668
669
fn lua_monitor_focus(state: ?*c.lua_State) callconv(.c) c_int {
670
    const s = state orelse return 0;
671
    const dir: i32 = @intCast(c.lua_tointegerx(s, 1, null));
672
    create_action_table_with_int(s, "FocusMonitor", dir);
673
    return 1;
674
}
675
676
fn lua_monitor_tag(state: ?*c.lua_State) callconv(.c) c_int {
677
    const s = state orelse return 0;
678
    const dir: i32 = @intCast(c.lua_tointegerx(s, 1, null));
679
    create_action_table_with_int(s, "TagMonitor", dir);
680
    return 1;
681
}
682
683
fn lua_rule_add(state: ?*c.lua_State) callconv(.c) c_int {
684
    const cfg = config orelse return 0;
685
    const s = state orelse return 0;
686
687
    if (c.lua_type(s, 1) != c.LUA_TTABLE) return 0;
688
689
    var rule = Rule{
690
        .class = null,
691
        .instance = null,
692
        .title = null,
693
        .tags = 0,
694
        .is_floating = false,
695
        .monitor = -1,
696
    };
697
698
    _ = c.lua_getfield(s, 1, "class");
699
    if (c.lua_type(s, -1) == c.LUA_TSTRING) {
700
        rule.class = get_lua_string(s, -1);
701
    }
702
    c.lua_settop(s, -2);
703
704
    _ = c.lua_getfield(s, 1, "instance");
705
    if (c.lua_type(s, -1) == c.LUA_TSTRING) {
706
        rule.instance = get_lua_string(s, -1);
707
    }
708
    c.lua_settop(s, -2);
709
710
    _ = c.lua_getfield(s, 1, "title");
711
    if (c.lua_type(s, -1) == c.LUA_TSTRING) {
712
        rule.title = get_lua_string(s, -1);
713
    }
714
    c.lua_settop(s, -2);
715
716
    _ = c.lua_getfield(s, 1, "tag");
717
    if (c.lua_type(s, -1) == c.LUA_TNUMBER) {
718
        const tag_idx: i32 = @intCast(c.lua_tointegerx(s, -1, null));
719
        if (tag_idx > 0) {
720
            rule.tags = @as(u32, 1) << @intCast(tag_idx - 1);
721
        }
722
    }
723
    c.lua_settop(s, -2);
724
725
    _ = c.lua_getfield(s, 1, "floating");
726
    if (c.lua_type(s, -1) == c.LUA_TBOOLEAN) {
727
        rule.is_floating = c.lua_toboolean(s, -1) != 0;
728
    }
729
    c.lua_settop(s, -2);
730
731
    _ = c.lua_getfield(s, 1, "monitor");
732
    if (c.lua_type(s, -1) == c.LUA_TNUMBER) {
733
        rule.monitor = @intCast(c.lua_tointegerx(s, -1, null));
734
    }
735
    c.lua_settop(s, -2);
736
737
    cfg.add_rule(rule) catch return 0;
738
    return 0;
739
}
740
741
fn lua_bar_set_font(state: ?*c.lua_State) callconv(.c) c_int {
742
    const cfg = config orelse return 0;
743
    const s = state orelse return 0;
744
    if (get_string_arg(s, 1)) |font| {
745
        cfg.font = font;
746
    }
747
    return 0;
748
}
749
750
fn lua_bar_set_blocks(state: ?*c.lua_State) callconv(.c) c_int {
751
    const cfg = config orelse return 0;
752
    const s = state orelse return 0;
753
754
    if (c.lua_type(s, 1) != c.LUA_TTABLE) return 0;
755
756
    const len = c.lua_rawlen(s, 1);
757
    var i: usize = 1;
758
    while (i <= len) : (i += 1) {
759
        _ = c.lua_rawgeti(s, 1, @intCast(i));
760
761
        if (c.lua_type(s, -1) != c.LUA_TTABLE) {
762
            c.lua_settop(s, -2);
763
            continue;
764
        }
765
766
        if (parse_block_config(s, -1)) |block| {
767
            cfg.add_block(block) catch {};
768
        }
769
770
        c.lua_settop(s, -2);
771
    }
772
773
    return 0;
774
}
775
776
fn parse_block_config(state: *c.lua_State, idx: c_int) ?Block {
777
    _ = c.lua_getfield(state, idx, "__block_type");
778
    const block_type_str = get_lua_string(state, -1) orelse {
779
        c.lua_settop(state, -2);
780
        return null;
781
    };
782
    c.lua_settop(state, -2);
783
784
    _ = c.lua_getfield(state, idx, "format");
785
    const format = get_lua_string(state, -1) orelse "";
786
    c.lua_settop(state, -2);
787
788
    _ = c.lua_getfield(state, idx, "interval");
789
    const interval: u32 = @intCast(c.lua_tointegerx(state, -1, null));
790
    c.lua_settop(state, -2);
791
792
    _ = c.lua_getfield(state, idx, "color");
793
    const color = parse_color(state, -1);
794
    c.lua_settop(state, -2);
795
796
    _ = c.lua_getfield(state, idx, "underline");
797
    const underline = c.lua_toboolean(state, -1) != 0;
798
    c.lua_settop(state, -2);
799
800
    var block = Block{
801
        .block_type = .static,
802
        .format = format,
803
        .interval = interval,
804
        .color = color,
805
        .underline = underline,
806
    };
807
808
    if (std.mem.eql(u8, block_type_str, "Ram")) {
809
        block.block_type = .ram;
810
    } else if (std.mem.eql(u8, block_type_str, "DateTime")) {
811
        block.block_type = .datetime;
812
        _ = c.lua_getfield(state, idx, "__arg");
813
        block.datetime_format = get_lua_string(state, -1);
814
        c.lua_settop(state, -2);
815
    } else if (std.mem.eql(u8, block_type_str, "Shell")) {
816
        block.block_type = .shell;
817
        _ = c.lua_getfield(state, idx, "__arg");
818
        block.command = get_lua_string(state, -1);
819
        c.lua_settop(state, -2);
820
    } else if (std.mem.eql(u8, block_type_str, "Static")) {
821
        block.block_type = .static;
822
        _ = c.lua_getfield(state, idx, "__arg");
823
        if (get_lua_string(state, -1)) |text| {
824
            block.format = text;
825
        }
826
        c.lua_settop(state, -2);
827
    } else if (std.mem.eql(u8, block_type_str, "Battery")) {
828
        block.block_type = .battery;
829
        _ = c.lua_getfield(state, idx, "__arg");
830
        if (c.lua_type(state, -1) == c.LUA_TTABLE) {
831
            _ = c.lua_getfield(state, -1, "charging");
832
            block.format_charging = get_lua_string(state, -1);
833
            c.lua_settop(state, -2);
834
835
            _ = c.lua_getfield(state, -1, "discharging");
836
            block.format_discharging = get_lua_string(state, -1);
837
            c.lua_settop(state, -2);
838
839
            _ = c.lua_getfield(state, -1, "full");
840
            block.format_full = get_lua_string(state, -1);
841
            c.lua_settop(state, -2);
842
843
            _ = c.lua_getfield(state, -1, "battery_name");
844
            block.battery_name = get_lua_string(state, -1);
845
            c.lua_settop(state, -2);
846
        }
847
        c.lua_settop(state, -2);
848
    } else {
849
        return null;
850
    }
851
852
    return block;
853
}
854
855
fn lua_bar_set_scheme_normal(state: ?*c.lua_State) callconv(.c) c_int {
856
    const cfg = config orelse return 0;
857
    const s = state orelse return 0;
858
    cfg.scheme_normal = parse_scheme(s);
859
    return 0;
860
}
861
862
fn lua_bar_set_scheme_selected(state: ?*c.lua_State) callconv(.c) c_int {
863
    const cfg = config orelse return 0;
864
    const s = state orelse return 0;
865
    cfg.scheme_selected = parse_scheme(s);
866
    return 0;
867
}
868
869
fn lua_bar_set_scheme_occupied(state: ?*c.lua_State) callconv(.c) c_int {
870
    const cfg = config orelse return 0;
871
    const s = state orelse return 0;
872
    cfg.scheme_occupied = parse_scheme(s);
873
    return 0;
874
}
875
876
fn lua_bar_set_scheme_urgent(state: ?*c.lua_State) callconv(.c) c_int {
877
    const cfg = config orelse return 0;
878
    const s = state orelse return 0;
879
    cfg.scheme_urgent = parse_scheme(s);
880
    return 0;
881
}
882
883
fn lua_bar_set_hide_vacant_tags(state: ?*c.lua_State) callconv(.c) c_int {
884
    const cfg = config orelse return 0;
885
    const s = state orelse return 0;
886
    cfg.hide_vacant_tags = c.lua_toboolean(s, 1) != 0;
887
    return 0;
888
}
889
890
fn lua_bar_block_ram(state: ?*c.lua_State) callconv(.c) c_int {
891
    const s = state orelse return 0;
892
    create_block_table(s, "Ram", null);
893
    return 1;
894
}
895
896
fn lua_bar_block_datetime(state: ?*c.lua_State) callconv(.c) c_int {
897
    const s = state orelse return 0;
898
    _ = c.lua_getfield(s, 1, "date_format");
899
    const date_format = get_lua_string(s, -1);
900
    c.lua_settop(s, -2);
901
    create_block_table(s, "DateTime", date_format);
902
    return 1;
903
}
904
905
fn lua_bar_block_shell(state: ?*c.lua_State) callconv(.c) c_int {
906
    const s = state orelse return 0;
907
    _ = c.lua_getfield(s, 1, "command");
908
    const command = get_lua_string(s, -1);
909
    c.lua_settop(s, -2);
910
    create_block_table(s, "Shell", command);
911
    return 1;
912
}
913
914
fn lua_bar_block_static(state: ?*c.lua_State) callconv(.c) c_int {
915
    const s = state orelse return 0;
916
    _ = c.lua_getfield(s, 1, "text");
917
    const text = get_lua_string(s, -1);
918
    c.lua_settop(s, -2);
919
    create_block_table(s, "Static", text);
920
    return 1;
921
}
922
923
fn lua_bar_block_battery(state: ?*c.lua_State) callconv(.c) c_int {
924
    const s = state orelse return 0;
925
926
    c.lua_createtable(s, 0, 6);
927
928
    _ = c.lua_pushstring(s, "Battery");
929
    c.lua_setfield(s, -2, "__block_type");
930
931
    _ = c.lua_getfield(s, 1, "format");
932
    c.lua_setfield(s, -2, "format");
933
934
    _ = c.lua_getfield(s, 1, "interval");
935
    c.lua_setfield(s, -2, "interval");
936
937
    _ = c.lua_getfield(s, 1, "color");
938
    c.lua_setfield(s, -2, "color");
939
940
    _ = c.lua_getfield(s, 1, "underline");
941
    c.lua_setfield(s, -2, "underline");
942
943
    c.lua_createtable(s, 0, 4);
944
    _ = c.lua_getfield(s, 1, "charging");
945
    c.lua_setfield(s, -2, "charging");
946
    _ = c.lua_getfield(s, 1, "discharging");
947
    c.lua_setfield(s, -2, "discharging");
948
    _ = c.lua_getfield(s, 1, "full");
949
    c.lua_setfield(s, -2, "full");
950
    _ = c.lua_getfield(s, 1, "battery_name");
951
    c.lua_setfield(s, -2, "battery_name");
952
    c.lua_setfield(s, -2, "__arg");
953
954
    return 1;
955
}
956
957
fn create_block_table(state: *c.lua_State, block_type: [*:0]const u8, arg: ?[]const u8) void {
958
    c.lua_createtable(state, 0, 6);
959
960
    _ = c.lua_pushstring(state, block_type);
961
    c.lua_setfield(state, -2, "__block_type");
962
963
    _ = c.lua_getfield(state, 1, "format");
964
    c.lua_setfield(state, -2, "format");
965
966
    _ = c.lua_getfield(state, 1, "interval");
967
    c.lua_setfield(state, -2, "interval");
968
969
    _ = c.lua_getfield(state, 1, "color");
970
    c.lua_setfield(state, -2, "color");
971
972
    _ = c.lua_getfield(state, 1, "underline");
973
    c.lua_setfield(state, -2, "underline");
974
975
    if (arg) |a| {
976
        var buf: [256]u8 = undefined;
977
        if (a.len < buf.len) {
978
            @memcpy(buf[0..a.len], a);
979
            buf[a.len] = 0;
980
            _ = c.lua_pushstring(state, &buf);
981
            c.lua_setfield(state, -2, "__arg");
982
        }
983
    }
984
}
985
986
fn lua_set_terminal(state: ?*c.lua_State) callconv(.c) c_int {
987
    const cfg = config orelse return 0;
988
    const s = state orelse return 0;
989
    if (get_string_arg(s, 1)) |term| {
990
        cfg.terminal = term;
991
    }
992
    return 0;
993
}
994
995
fn lua_set_modkey(state: ?*c.lua_State) callconv(.c) c_int {
996
    const cfg = config orelse return 0;
997
    const s = state orelse return 0;
998
    if (get_string_arg(s, 1)) |modkey_str| {
999
        cfg.modkey = parse_single_modifier(modkey_str);
1000
        cfg.add_button(.{
1001
            .click = .client_win,
1002
            .mod_mask = cfg.modkey,
1003
            .button = 1,
1004
            .action = .move_mouse,
1005
        }) catch {};
1006
        cfg.add_button(.{
1007
            .click = .client_win,
1008
            .mod_mask = cfg.modkey,
1009
            .button = 3,
1010
            .action = .resize_mouse,
1011
        }) catch {};
1012
    }
1013
    return 0;
1014
}
1015
1016
fn lua_set_tags(state: ?*c.lua_State) callconv(.c) c_int {
1017
    const cfg = config orelse return 0;
1018
    const s = state orelse return 0;
1019
1020
    if (c.lua_type(s, 1) != c.LUA_TTABLE) return 0;
1021
1022
    const len = c.lua_rawlen(s, 1);
1023
    var i: usize = 0;
1024
    while (i < len and i < 9) : (i += 1) {
1025
        _ = c.lua_rawgeti(s, 1, @intCast(i + 1));
1026
        if (get_lua_string(s, -1)) |tag_str| {
1027
            cfg.tags[i] = tag_str;
1028
        }
1029
        c.lua_settop(s, -2);
1030
    }
1031
1032
    return 0;
1033
}
1034
1035
fn lua_autostart(state: ?*c.lua_State) callconv(.c) c_int {
1036
    const cfg = config orelse return 0;
1037
    const s = state orelse return 0;
1038
    if (get_string_arg(s, 1)) |cmd| {
1039
        cfg.add_autostart(cmd) catch return 0;
1040
    }
1041
    return 0;
1042
}
1043
1044
fn lua_auto_tile(state: ?*c.lua_State) callconv(.c) c_int {
1045
    const cfg = config orelse return 0;
1046
    const s = state orelse return 0;
1047
    cfg.auto_tile = c.lua_toboolean(s, 1) != 0;
1048
    return 0;
1049
}
1050
1051
fn lua_set_layout_symbol(state: ?*c.lua_State) callconv(.c) c_int {
1052
    const cfg = config orelse return 0;
1053
    const s = state orelse return 0;
1054
    const name = get_string_arg(s, 1) orelse return 0;
1055
    const symbol = get_string_arg(s, 2) orelse return 0;
1056
1057
    const layout_map = .{
1058
        .{ "tiling", &cfg.layout_tile_symbol },
1059
        .{ "tile", &cfg.layout_tile_symbol },
1060
        .{ "normie", &cfg.layout_floating_symbol },
1061
        .{ "floating", &cfg.layout_floating_symbol },
1062
        .{ "float", &cfg.layout_floating_symbol },
1063
        .{ "monocle", &cfg.layout_monocle_symbol },
1064
        .{ "scrolling", &cfg.layout_scrolling_symbol },
1065
        .{ "scroll", &cfg.layout_scrolling_symbol },
1066
    };
1067
1068
    inline for (layout_map) |entry| {
1069
        if (std.mem.eql(u8, name, entry[0])) {
1070
            entry[1].* = symbol;
1071
            return 0;
1072
        }
1073
    }
1074
    return 0;
1075
}
1076
1077
fn lua_quit(state: ?*c.lua_State) callconv(.c) c_int {
1078
    const s = state orelse return 0;
1079
    create_action_table(s, "Quit");
1080
    return 1;
1081
}
1082
1083
fn lua_restart(state: ?*c.lua_State) callconv(.c) c_int {
1084
    const s = state orelse return 0;
1085
    create_action_table(s, "Restart");
1086
    return 1;
1087
}
1088
1089
fn lua_toggle_gaps(state: ?*c.lua_State) callconv(.c) c_int {
1090
    const s = state orelse return 0;
1091
    create_action_table(s, "ToggleGaps");
1092
    return 1;
1093
}
1094
1095
fn lua_show_keybinds(state: ?*c.lua_State) callconv(.c) c_int {
1096
    const s = state orelse return 0;
1097
    create_action_table(s, "ShowKeybinds");
1098
    return 1;
1099
}
1100
1101
fn lua_set_master_factor(state: ?*c.lua_State) callconv(.c) c_int {
1102
    const s = state orelse return 0;
1103
    const delta: i32 = @intCast(c.lua_tointegerx(s, 1, null));
1104
    create_action_table_with_int(s, "ResizeMaster", delta);
1105
    return 1;
1106
}
1107
1108
fn lua_inc_num_master(state: ?*c.lua_State) callconv(.c) c_int {
1109
    const s = state orelse return 0;
1110
    const delta: i32 = @intCast(c.lua_tointegerx(s, 1, null));
1111
    if (delta > 0) {
1112
        create_action_table(s, "IncMaster");
1113
    } else {
1114
        create_action_table(s, "DecMaster");
1115
    }
1116
    return 1;
1117
}
1118
1119
fn get_string_arg(state: *c.lua_State, idx: c_int) ?[]const u8 {
1120
    if (c.lua_type(state, idx) != c.LUA_TSTRING) return null;
1121
    return get_lua_string(state, idx);
1122
}
1123
1124
fn extract_spawn_command(state: *c.lua_State, idx: c_int) ?[]const u8 {
1125
    const len = c.lua_rawlen(state, idx);
1126
    if (len == 0) return null;
1127
1128
    if (len >= 3) {
1129
        _ = c.lua_rawgeti(state, idx, 1);
1130
        const first = get_lua_string(state, -1);
1131
        c.lua_settop(state, -2);
1132
1133
        _ = c.lua_rawgeti(state, idx, 2);
1134
        const second = get_lua_string(state, -1);
1135
        c.lua_settop(state, -2);
1136
1137
        if (first != null and second != null and
1138
            std.mem.eql(u8, first.?, "sh") and std.mem.eql(u8, second.?, "-c"))
1139
        {
1140
            _ = c.lua_rawgeti(state, idx, 3);
1141
            const cmd = get_lua_string(state, -1);
1142
            c.lua_settop(state, -2);
1143
            return cmd;
1144
        }
1145
    }
1146
1147
    _ = c.lua_rawgeti(state, idx, 1);
1148
    const first_elem = get_lua_string(state, -1);
1149
    c.lua_settop(state, -2);
1150
    return first_elem;
1151
}
1152
1153
fn get_lua_string(state: *c.lua_State, idx: c_int) ?[]const u8 {
1154
    const cstr = c.lua_tolstring(state, idx, null);
1155
    if (cstr == null) return null;
1156
    return std.mem.span(cstr);
1157
}
1158
1159
fn parse_color(state: *c.lua_State, idx: c_int) u32 {
1160
    const lua_type = c.lua_type(state, idx);
1161
    if (lua_type == c.LUA_TNUMBER) {
1162
        return @intCast(c.lua_tointegerx(state, idx, null));
1163
    }
1164
    if (lua_type == c.LUA_TSTRING) {
1165
        const str = get_lua_string(state, idx) orelse return 0;
1166
        if (str.len > 0 and str[0] == '#') {
1167
            return std.fmt.parseInt(u32, str[1..], 16) catch return 0;
1168
        }
1169
        if (str.len > 2 and str[0] == '0' and str[1] == 'x') {
1170
            return std.fmt.parseInt(u32, str[2..], 16) catch return 0;
1171
        }
1172
        return std.fmt.parseInt(u32, str, 16) catch return 0;
1173
    }
1174
    return 0;
1175
}
1176
1177
fn parse_scheme(state: *c.lua_State) Color_Scheme {
1178
    return Color_Scheme{
1179
        .fg = parse_color(state, 1),
1180
        .bg = parse_color(state, 2),
1181
        .border = parse_color(state, 3),
1182
    };
1183
}
1184
1185
fn parse_modifiers(state: *c.lua_State, idx: c_int) u32 {
1186
    var mod_mask: u32 = 0;
1187
1188
    if (c.lua_type(state, idx) != c.LUA_TTABLE) return mod_mask;
1189
1190
    const len = c.lua_rawlen(state, idx);
1191
    var i: usize = 1;
1192
    while (i <= len) : (i += 1) {
1193
        _ = c.lua_rawgeti(state, idx, @intCast(i));
1194
        if (get_lua_string(state, -1)) |mod_str| {
1195
            const parsed = parse_single_modifier(mod_str);
1196
            mod_mask |= parsed;
1197
        }
1198
        c.lua_settop(state, -2);
1199
    }
1200
1201
    return mod_mask;
1202
}
1203
1204
fn parse_modifiers_at_top(state: *c.lua_State) u32 {
1205
    return parse_modifiers(state, -1);
1206
}
1207
1208
fn parse_single_modifier(name: []const u8) u32 {
1209
    if (std.mem.eql(u8, name, "Mod4") or std.mem.eql(u8, name, "mod4") or std.mem.eql(u8, name, "super")) {
1210
        return (1 << 6);
1211
    } else if (std.mem.eql(u8, name, "Mod1") or std.mem.eql(u8, name, "mod1") or std.mem.eql(u8, name, "alt")) {
1212
        return (1 << 3);
1213
    } else if (std.mem.eql(u8, name, "Shift") or std.mem.eql(u8, name, "shift")) {
1214
        return (1 << 0);
1215
    } else if (std.mem.eql(u8, name, "Control") or std.mem.eql(u8, name, "control") or std.mem.eql(u8, name, "ctrl")) {
1216
        return (1 << 2);
1217
    }
1218
    return 0;
1219
}
1220
1221
fn parse_action(name: []const u8) ?Action {
1222
    const action_map = .{
1223
        .{ "Spawn", Action.spawn },
1224
        .{ "SpawnTerminal", Action.spawn_terminal },
1225
        .{ "KillClient", Action.kill_client },
1226
        .{ "Quit", Action.quit },
1227
        .{ "Restart", Action.restart },
1228
        .{ "ShowKeybinds", Action.show_keybinds },
1229
        .{ "FocusStack", Action.focus_next },
1230
        .{ "MoveStack", Action.move_next },
1231
        .{ "ResizeMaster", Action.resize_master },
1232
        .{ "IncMaster", Action.inc_master },
1233
        .{ "DecMaster", Action.dec_master },
1234
        .{ "ToggleFloating", Action.toggle_floating },
1235
        .{ "ToggleFullScreen", Action.toggle_fullscreen },
1236
        .{ "ToggleGaps", Action.toggle_gaps },
1237
        .{ "CycleLayout", Action.cycle_layout },
1238
        .{ "ChangeLayout", Action.set_layout },
1239
        .{ "ViewTag", Action.view_tag },
1240
        .{ "ViewNextTag", Action.view_next_tag },
1241
        .{ "ViewPreviousTag", Action.view_prev_tag },
1242
        .{ "ViewNextNonEmptyTag", Action.view_next_nonempty_tag },
1243
        .{ "ViewPreviousNonEmptyTag", Action.view_prev_nonempty_tag },
1244
        .{ "MoveToTag", Action.move_to_tag },
1245
        .{ "ToggleView", Action.toggle_view_tag },
1246
        .{ "ToggleTag", Action.toggle_tag },
1247
        .{ "FocusMonitor", Action.focus_monitor },
1248
        .{ "TagMonitor", Action.send_to_monitor },
1249
        .{ "ScrollLeft", Action.scroll_left },
1250
        .{ "ScrollRight", Action.scroll_right },
1251
    };
1252
1253
    inline for (action_map) |entry| {
1254
        if (std.mem.eql(u8, name, entry[0])) {
1255
            return entry[1];
1256
        }
1257
    }
1258
    return null;
1259
}
1260
1261
fn key_name_to_keysym(name: []const u8) ?u64 {
1262
    const key_map = .{
1263
        .{ "Return", 0xff0d },
1264
        .{ "Enter", 0xff0d },
1265
        .{ "Tab", 0xff09 },
1266
        .{ "Escape", 0xff1b },
1267
        .{ "BackSpace", 0xff08 },
1268
        .{ "Delete", 0xffff },
1269
        .{ "space", 0x0020 },
1270
        .{ "Space", 0x0020 },
1271
        .{ "comma", 0x002c },
1272
        .{ "Comma", 0x002c },
1273
        .{ "period", 0x002e },
1274
        .{ "Period", 0x002e },
1275
        .{ "slash", 0x002f },
1276
        .{ "Slash", 0x002f },
1277
        .{ "minus", 0x002d },
1278
        .{ "Minus", 0x002d },
1279
        .{ "equal", 0x003d },
1280
        .{ "Equal", 0x003d },
1281
        .{ "bracketleft", 0x005b },
1282
        .{ "bracketright", 0x005d },
1283
        .{ "backslash", 0x005c },
1284
        .{ "semicolon", 0x003b },
1285
        .{ "apostrophe", 0x0027 },
1286
        .{ "grave", 0x0060 },
1287
        .{ "Left", 0xff51 },
1288
        .{ "Up", 0xff52 },
1289
        .{ "Right", 0xff53 },
1290
        .{ "Down", 0xff54 },
1291
        .{ "F1", 0xffbe },
1292
        .{ "F2", 0xffbf },
1293
        .{ "F3", 0xffc0 },
1294
        .{ "F4", 0xffc1 },
1295
        .{ "F5", 0xffc2 },
1296
        .{ "F6", 0xffc3 },
1297
        .{ "F7", 0xffc4 },
1298
        .{ "F8", 0xffc5 },
1299
        .{ "F9", 0xffc6 },
1300
        .{ "F10", 0xffc7 },
1301
        .{ "F11", 0xffc8 },
1302
        .{ "F12", 0xffc9 },
1303
        .{ "Print", 0xff61 },
1304
        .{ "XF86AudioRaiseVolume", 0x1008ff13 },
1305
        .{ "XF86AudioLowerVolume", 0x1008ff11 },
1306
        .{ "XF86AudioMute", 0x1008ff12 },
1307
        .{ "XF86AudioPlay", 0x1008ff14 },
1308
        .{ "XF86AudioPause", 0x1008ff31 },
1309
        .{ "XF86AudioNext", 0x1008ff17 },
1310
        .{ "XF86AudioPrev", 0x1008ff16 },
1311
        .{ "XF86MonBrightnessUp", 0x1008ff02 },
1312
        .{ "XF86MonBrightnessDown", 0x1008ff03 },
1313
    };
1314
1315
    inline for (key_map) |entry| {
1316
        if (std.mem.eql(u8, name, entry[0])) {
1317
            return entry[1];
1318
        }
1319
    }
1320
1321
    if (name.len == 1) {
1322
        const char = name[0];
1323
        if (char >= 'a' and char <= 'z') {
1324
            return char;
1325
        }
1326
        if (char >= 'A' and char <= 'Z') {
1327
            return char + 32;
1328
        }
1329
        if (char >= '0' and char <= '9') {
1330
            return char;
1331
        }
1332
    }
1333
1334
    return null;
1335
}