946 lines
24 KiB
C
Raw Normal View History

2025-12-28 03:14:31 +01:00
/*
* DWN - Desktop Window Manager
* retoor <retoor@molodetz.nl>
2025-12-28 03:14:31 +01:00
* Keyboard shortcut handling implementation
*/
#include "keys.h"
#include "client.h"
#include "workspace.h"
#include "config.h"
#include "util.h"
#include "ai.h"
#include "notifications.h"
#include "news.h"
#include "applauncher.h"
#include "decorations.h"
#include "demo.h"
2025-12-28 03:14:31 +01:00
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <X11/XKBlib.h>
static bool super_pressed = false;
static bool super_used_in_combo = false;
2025-12-28 03:14:31 +01:00
void key_spawn_terminal(void);
void key_spawn_launcher(void);
void key_spawn_file_manager(void);
void key_spawn_browser(void);
void key_close_window(void);
void key_quit_dwn(void);
void key_cycle_layout(void);
void key_toggle_floating(void);
void key_toggle_fullscreen(void);
void key_toggle_maximize(void);
void key_focus_next(void);
void key_focus_prev(void);
void key_workspace_next(void);
void key_workspace_prev(void);
void key_workspace_1(void);
void key_workspace_2(void);
void key_workspace_3(void);
void key_workspace_4(void);
void key_workspace_5(void);
void key_workspace_6(void);
void key_workspace_7(void);
void key_workspace_8(void);
void key_workspace_9(void);
void key_move_to_workspace_1(void);
void key_move_to_workspace_2(void);
void key_move_to_workspace_3(void);
void key_move_to_workspace_4(void);
void key_move_to_workspace_5(void);
void key_move_to_workspace_6(void);
void key_move_to_workspace_7(void);
void key_move_to_workspace_8(void);
void key_move_to_workspace_9(void);
void key_increase_master(void);
void key_decrease_master(void);
void key_increase_master_count(void);
void key_decrease_master_count(void);
void key_toggle_ai(void);
void key_ai_command(void);
void key_exa_search(void);
void key_news_next(void);
void key_news_prev(void);
void key_news_open(void);
void key_show_shortcuts(void);
void key_start_tutorial(void);
void key_screenshot(void);
static KeyBinding bindings[MAX_KEYBINDINGS];
static int binding_count = 0;
typedef struct {
const char *title;
const char *instruction;
const char *hint;
unsigned int modifiers;
KeySym keysym;
} TutorialStep;
static const TutorialStep tutorial_steps[] = {
{
"Welcome to DWN!",
"This tutorial teaches all keyboard shortcuts.\n\n"
"Press Super+T to continue...",
"(Super = Windows/Meta key)",
MOD_SUPER, XK_t
},
{
"1/20: Open Terminal",
"The terminal is your command center.\n\n"
"Press: Ctrl + Alt + T",
"Hold Ctrl and Alt, then press T",
MOD_CTRL | MOD_ALT, XK_t
},
{
"2/20: App Launcher",
"Launch any application by name.\n\n"
"Press: Alt + F2",
"Type app name and press Enter",
MOD_ALT, XK_F2
},
{
"3/20: File Manager",
"Open the file manager.\n\n"
"Press: Super + E",
"",
MOD_SUPER, XK_e
},
{
"4/20: Web Browser",
"Open your default web browser.\n\n"
"Press: Super + B",
"",
MOD_SUPER, XK_b
},
{
"5/20: Switch Windows",
"Cycle through open windows.\n\n"
"Press: Alt + Tab",
"Hold Alt, press Tab repeatedly",
MOD_ALT, XK_Tab
},
{
"6/20: Close Window",
"Close the focused window.\n\n"
"Press: Alt + F4",
"",
MOD_ALT, XK_F4
},
{
"7/20: Toggle Maximize",
"Maximize or restore a window.\n\n"
"Press: Alt + F10",
"",
MOD_ALT, XK_F10
},
{
"8/20: Toggle Fullscreen",
"Make a window fullscreen.\n\n"
"Press: Alt + F11",
"Press again to exit",
MOD_ALT, XK_F11
},
{
"9/20: Toggle Floating",
"Make window float above tiled windows.\n\n"
"Press: Super + F9",
"",
MOD_SUPER, XK_F9
},
{
"10/20: Next Workspace",
"Switch to the next virtual desktop.\n\n"
"Press: Ctrl + Alt + Right",
"",
MOD_CTRL | MOD_ALT, XK_Right
},
{
"11/20: Previous Workspace",
"Switch to the previous workspace.\n\n"
"Press: Ctrl + Alt + Left",
"",
MOD_CTRL | MOD_ALT, XK_Left
},
{
"12/20: Go to Workspace",
"Jump directly to workspace 1.\n\n"
"Press: F1",
"Use F1-F9 for workspaces 1-9",
0, XK_F1
},
{
"13/20: Cycle Layout",
"Switch between tiling, floating, monocle.\n\n"
"Press: Super + Space",
"Try it multiple times!",
MOD_SUPER, XK_space
},
{
"14/20: Expand Master",
"Make the master area larger.\n\n"
"Press: Super + L",
"",
MOD_SUPER, XK_l
},
{
"15/20: Shrink Master",
"Make the master area smaller.\n\n"
"Press: Super + H",
"",
MOD_SUPER, XK_h
},
{
"16/20: Add to Master",
"Increase windows in master area.\n\n"
"Press: Super + I",
"",
MOD_SUPER, XK_i
},
{
"17/20: AI Context",
"Show AI analysis of your current task.\n\n"
"Press: Super + A",
"Requires OPENROUTER_API_KEY",
MOD_SUPER, XK_a
},
{
"18/20: AI Command",
"Ask AI to run commands for you.\n\n"
"Press: Super + Shift + A",
"Requires OPENROUTER_API_KEY",
MOD_SUPER | MOD_SHIFT, XK_a
},
{
"19/20: Exa Search",
"Semantic web search.\n\n"
"Press: Super + Shift + E",
"Requires EXA_API_KEY",
MOD_SUPER | MOD_SHIFT, XK_e
},
{
"20/20: Show Shortcuts",
"Display all keyboard shortcuts.\n\n"
"Press: Super + S",
"Shows complete reference",
MOD_SUPER, XK_s
},
{
"Tutorial Complete!",
"You've learned all DWN shortcuts!\n\n"
"Super+S: Show all shortcuts\n"
"Super+T: Restart this tutorial\n\n"
"Press: Super + Backspace to quit DWN",
"Congratulations!",
MOD_SUPER, XK_BackSpace
}
};
#define TUTORIAL_STEPS (sizeof(tutorial_steps) / sizeof(tutorial_steps[0]))
static bool tutorial_active = false;
static int tutorial_current_step = 0;
bool tutorial_is_active(void)
{
return tutorial_active;
}
void tutorial_start(void)
{
tutorial_active = true;
tutorial_current_step = 0;
const TutorialStep *step = &tutorial_steps[0];
char msg[512];
snprintf(msg, sizeof(msg), "%s\n\n%s", step->instruction, step->hint);
notification_show("DWN Tutorial", step->title, msg, NULL, 0);
2025-12-28 03:14:31 +01:00
}
void tutorial_stop(void)
{
tutorial_active = false;
tutorial_current_step = 0;
notification_show("DWN Tutorial", "Tutorial Stopped",
"Press Super+T to restart anytime.", NULL, 3000);
}
void tutorial_next_step(void)
{
tutorial_current_step++;
if (tutorial_current_step >= (int)TUTORIAL_STEPS) {
tutorial_active = false;
tutorial_current_step = 0;
return;
}
const TutorialStep *step = &tutorial_steps[tutorial_current_step];
char msg[512];
snprintf(msg, sizeof(msg), "%s\n\n%s", step->instruction, step->hint);
notification_show("DWN Tutorial", step->title, msg, NULL, 0);
}
void tutorial_check_key(unsigned int modifiers, KeySym keysym)
{
if (!tutorial_active) {
return;
}
const TutorialStep *step = &tutorial_steps[tutorial_current_step];
if (modifiers == step->modifiers && keysym == step->keysym) {
tutorial_next_step();
}
}
void keys_init(void)
{
binding_count = 0;
memset(bindings, 0, sizeof(bindings));
keys_setup_defaults();
keys_grab_all();
LOG_INFO("Keyboard shortcuts initialized (%d bindings)", binding_count);
}
void keys_cleanup(void)
{
keys_ungrab_all();
keys_clear_all();
}
void keys_grab_all(void)
{
if (dwn == NULL || dwn->display == NULL) {
return;
}
Display *dpy = dwn->display;
Window root = dwn->root;
XUngrabKey(dpy, AnyKey, AnyModifier, root);
for (int i = 0; i < binding_count; i++) {
KeyCode code = XKeysymToKeycode(dpy, bindings[i].keysym);
if (code == 0) {
LOG_WARN("Could not get keycode for keysym %lu", bindings[i].keysym);
continue;
}
unsigned int modifiers[] = {
bindings[i].modifiers,
bindings[i].modifiers | Mod2Mask,
bindings[i].modifiers | LockMask,
2025-12-28 03:14:31 +01:00
bindings[i].modifiers | Mod2Mask | LockMask
};
for (size_t j = 0; j < sizeof(modifiers) / sizeof(modifiers[0]); j++) {
XGrabKey(dpy, code, modifiers[j], root, True,
GrabModeAsync, GrabModeAsync);
}
}
KeyCode super_l = XKeysymToKeycode(dpy, XK_Super_L);
KeyCode super_r = XKeysymToKeycode(dpy, XK_Super_R);
unsigned int lock_mods[] = { 0, Mod2Mask, LockMask, Mod2Mask | LockMask };
for (size_t i = 0; i < sizeof(lock_mods) / sizeof(lock_mods[0]); i++) {
if (super_l) XGrabKey(dpy, super_l, lock_mods[i], root, True,
GrabModeAsync, GrabModeAsync);
if (super_r) XGrabKey(dpy, super_r, lock_mods[i], root, True,
GrabModeAsync, GrabModeAsync);
}
2025-12-28 03:14:31 +01:00
}
void keys_ungrab_all(void)
{
if (dwn == NULL || dwn->display == NULL) {
return;
}
XUngrabKey(dwn->display, AnyKey, AnyModifier, dwn->root);
}
void keys_bind(unsigned int modifiers, KeySym keysym,
KeyCallback callback, const char *description)
{
if (binding_count >= MAX_KEYBINDINGS) {
LOG_WARN("Maximum key bindings reached");
return;
}
bindings[binding_count].modifiers = modifiers;
bindings[binding_count].keysym = keysym;
bindings[binding_count].callback = callback;
bindings[binding_count].description = description;
binding_count++;
}
void keys_unbind(unsigned int modifiers, KeySym keysym)
{
for (int i = 0; i < binding_count; i++) {
if (bindings[i].modifiers == modifiers &&
bindings[i].keysym == keysym) {
memmove(&bindings[i], &bindings[i + 1],
(binding_count - i - 1) * sizeof(KeyBinding));
binding_count--;
return;
}
}
}
void keys_clear_all(void)
{
binding_count = 0;
memset(bindings, 0, sizeof(bindings));
}
void keys_handle_press(XKeyEvent *ev)
{
if (ev == NULL || dwn == NULL || dwn->display == NULL) {
return;
}
KeySym keysym = XLookupKeysym(ev, 0);
if (keysym == XK_Super_L || keysym == XK_Super_R) {
super_pressed = true;
super_used_in_combo = false;
return;
}
if (super_pressed && (ev->state & Mod4Mask)) {
super_used_in_combo = true;
}
2025-12-28 03:14:31 +01:00
unsigned int clean_mask = ev->state & ~(Mod2Mask | LockMask);
if (tutorial_is_active()) {
tutorial_check_key(clean_mask, keysym);
}
for (int i = 0; i < binding_count; i++) {
if (bindings[i].keysym == keysym &&
bindings[i].modifiers == clean_mask) {
if (bindings[i].callback != NULL) {
LOG_DEBUG("Key binding triggered: %s", bindings[i].description);
bindings[i].callback();
}
return;
}
}
}
void keys_handle_release(XKeyEvent *ev)
{
if (ev == NULL || dwn == NULL || dwn->display == NULL) {
return;
}
KeySym keysym = XLookupKeysym(ev, 0);
if (keysym == XK_Super_L || keysym == XK_Super_R) {
if (super_pressed && !super_used_in_combo) {
key_spawn_launcher();
}
super_pressed = false;
super_used_in_combo = false;
}
2025-12-28 03:14:31 +01:00
}
void keys_setup_defaults(void)
{
keys_bind(MOD_CTRL | MOD_ALT, XK_t, key_spawn_terminal, "Spawn terminal");
keys_bind(MOD_ALT, XK_F2, key_spawn_launcher, "Application finder");
keys_bind(MOD_SUPER, XK_e, key_spawn_file_manager, "File manager");
keys_bind(MOD_SUPER, XK_b, key_spawn_browser, "Web browser");
keys_bind(MOD_ALT, XK_F4, key_close_window, "Close window");
keys_bind(MOD_ALT, XK_F9, key_toggle_minimize, "Toggle minimize");
2025-12-28 03:14:31 +01:00
keys_bind(MOD_ALT, XK_F10, key_toggle_maximize, "Toggle maximize");
keys_bind(MOD_ALT, XK_F11, key_toggle_fullscreen, "Toggle fullscreen");
keys_bind(MOD_ALT, XK_Tab, key_focus_next, "Cycle windows");
keys_bind(MOD_ALT | MOD_SHIFT, XK_Tab, key_focus_prev, "Cycle windows reverse");
keys_bind(MOD_SUPER, XK_F9, key_toggle_floating, "Toggle floating");
keys_bind(MOD_CTRL | MOD_ALT, XK_Right, key_workspace_next, "Next workspace");
keys_bind(MOD_CTRL | MOD_ALT, XK_Left, key_workspace_prev, "Previous workspace");
keys_bind(0, XK_F1, key_workspace_1, "Switch to workspace 1");
keys_bind(0, XK_F2, key_workspace_2, "Switch to workspace 2");
keys_bind(0, XK_F3, key_workspace_3, "Switch to workspace 3");
keys_bind(0, XK_F4, key_workspace_4, "Switch to workspace 4");
keys_bind(0, XK_F5, key_workspace_5, "Switch to workspace 5");
keys_bind(0, XK_F6, key_workspace_6, "Switch to workspace 6");
keys_bind(0, XK_F7, key_workspace_7, "Switch to workspace 7");
keys_bind(0, XK_F8, key_workspace_8, "Switch to workspace 8");
keys_bind(0, XK_F9, key_workspace_9, "Switch to workspace 9");
keys_bind(MOD_SHIFT, XK_F1, key_move_to_workspace_1, "Move to workspace 1");
keys_bind(MOD_SHIFT, XK_F2, key_move_to_workspace_2, "Move to workspace 2");
keys_bind(MOD_SHIFT, XK_F3, key_move_to_workspace_3, "Move to workspace 3");
keys_bind(MOD_SHIFT, XK_F4, key_move_to_workspace_4, "Move to workspace 4");
keys_bind(MOD_SHIFT, XK_F5, key_move_to_workspace_5, "Move to workspace 5");
keys_bind(MOD_SHIFT, XK_F6, key_move_to_workspace_6, "Move to workspace 6");
keys_bind(MOD_SHIFT, XK_F7, key_move_to_workspace_7, "Move to workspace 7");
keys_bind(MOD_SHIFT, XK_F8, key_move_to_workspace_8, "Move to workspace 8");
keys_bind(MOD_SHIFT, XK_F9, key_move_to_workspace_9, "Move to workspace 9");
keys_bind(MOD_SUPER, XK_BackSpace, key_quit_dwn, "Quit DWN");
keys_bind(MOD_SUPER, XK_space, key_cycle_layout, "Cycle layout");
keys_bind(MOD_SUPER, XK_l, key_increase_master, "Increase master ratio");
keys_bind(MOD_SUPER, XK_h, key_decrease_master, "Decrease master ratio");
keys_bind(MOD_SUPER, XK_i, key_increase_master_count, "Increase master count");
keys_bind(MOD_SUPER, XK_d, key_decrease_master_count, "Decrease master count");
keys_bind(MOD_SUPER, XK_Left, key_snap_left, "Snap window left");
keys_bind(MOD_SUPER, XK_Right, key_snap_right, "Snap window right");
2025-12-28 03:14:31 +01:00
keys_bind(MOD_SUPER, XK_a, key_toggle_ai, "Toggle AI context");
keys_bind(MOD_SUPER | MOD_SHIFT, XK_a, key_ai_command, "AI command");
keys_bind(MOD_SUPER | MOD_SHIFT, XK_e, key_exa_search, "Exa web search");
keys_bind(MOD_SUPER | MOD_SHIFT, XK_d, key_start_demo, "Start/stop demo mode");
2025-12-28 03:14:31 +01:00
keys_bind(MOD_SUPER, XK_s, key_show_shortcuts, "Show shortcuts");
keys_bind(MOD_SUPER, XK_t, key_start_tutorial, "Start tutorial");
keys_bind(MOD_SUPER, XK_Down, key_news_next, "Next news article");
keys_bind(MOD_SUPER, XK_Up, key_news_prev, "Previous news article");
keys_bind(MOD_SUPER, XK_Return, key_news_open, "Open news article");
keys_bind(0, XK_Print, key_screenshot, "Take screenshot");
}
void key_spawn_terminal(void)
{
const char *terminal = config_get_terminal();
spawn_async(terminal);
}
void key_spawn_launcher(void)
{
applauncher_show();
}
void key_spawn_file_manager(void)
{
if (dwn != NULL && dwn->config != NULL) {
spawn_async(dwn->config->file_manager);
} else {
spawn_async("thunar");
}
}
void key_spawn_browser(void)
{
spawn_async("xdg-open http://");
}
void key_close_window(void)
{
Workspace *ws = workspace_get_current();
if (ws != NULL && ws->focused != NULL) {
client_close(ws->focused);
}
}
void key_quit_dwn(void)
{
LOG_INFO("Quit requested via keyboard shortcut");
dwn_quit();
}
void key_cycle_layout(void)
{
if (dwn == NULL) {
return;
}
workspace_cycle_layout(dwn->current_workspace);
}
void key_toggle_floating(void)
{
Workspace *ws = workspace_get_current();
if (ws != NULL && ws->focused != NULL) {
client_toggle_floating(ws->focused);
workspace_arrange_current();
}
}
void key_toggle_fullscreen(void)
{
Workspace *ws = workspace_get_current();
if (ws != NULL && ws->focused != NULL) {
client_toggle_fullscreen(ws->focused);
}
}
void key_toggle_maximize(void)
{
Workspace *ws = workspace_get_current();
if (ws != NULL && ws->focused != NULL) {
client_toggle_maximize(ws->focused);
2025-12-28 03:14:31 +01:00
}
}
void key_toggle_minimize(void)
{
Workspace *ws = workspace_get_current();
if (ws == NULL || ws->focused == NULL) {
return;
}
if (client_is_minimized(ws->focused)) {
client_restore(ws->focused);
} else {
client_minimize(ws->focused);
}
}
2025-12-28 03:14:31 +01:00
void key_focus_next(void)
{
workspace_focus_next();
}
void key_focus_prev(void)
{
workspace_focus_prev();
}
void key_workspace_next(void)
{
if (dwn == NULL) {
return;
}
int next = dwn->current_workspace + 1;
if (next >= MAX_WORKSPACES) {
next = 0;
2025-12-28 03:14:31 +01:00
}
workspace_switch(next);
}
void key_workspace_prev(void)
{
if (dwn == NULL) {
return;
}
int prev = dwn->current_workspace - 1;
if (prev < 0) {
prev = MAX_WORKSPACES - 1;
2025-12-28 03:14:31 +01:00
}
workspace_switch(prev);
}
void key_workspace_1(void) { workspace_switch(0); }
void key_workspace_2(void) { workspace_switch(1); }
void key_workspace_3(void) { workspace_switch(2); }
void key_workspace_4(void) { workspace_switch(3); }
void key_workspace_5(void) { workspace_switch(4); }
void key_workspace_6(void) { workspace_switch(5); }
void key_workspace_7(void) { workspace_switch(6); }
void key_workspace_8(void) { workspace_switch(7); }
void key_workspace_9(void) { workspace_switch(8); }
static void move_focused_to_workspace(int ws)
{
Workspace *current = workspace_get_current();
if (current != NULL && current->focused != NULL) {
workspace_move_client(current->focused, ws);
}
}
void key_move_to_workspace_1(void) { move_focused_to_workspace(0); }
void key_move_to_workspace_2(void) { move_focused_to_workspace(1); }
void key_move_to_workspace_3(void) { move_focused_to_workspace(2); }
void key_move_to_workspace_4(void) { move_focused_to_workspace(3); }
void key_move_to_workspace_5(void) { move_focused_to_workspace(4); }
void key_move_to_workspace_6(void) { move_focused_to_workspace(5); }
void key_move_to_workspace_7(void) { move_focused_to_workspace(6); }
void key_move_to_workspace_8(void) { move_focused_to_workspace(7); }
void key_move_to_workspace_9(void) { move_focused_to_workspace(8); }
void key_increase_master(void)
{
if (dwn == NULL) {
return;
}
workspace_adjust_master_ratio(dwn->current_workspace, 0.05f);
}
void key_decrease_master(void)
{
if (dwn == NULL) {
return;
}
workspace_adjust_master_ratio(dwn->current_workspace, -0.05f);
}
void key_increase_master_count(void)
{
if (dwn == NULL) {
return;
}
workspace_adjust_master_count(dwn->current_workspace, 1);
}
void key_decrease_master_count(void)
{
if (dwn == NULL) {
return;
}
workspace_adjust_master_count(dwn->current_workspace, -1);
}
void key_toggle_ai(void)
{
if (dwn == NULL) {
return;
}
if (!dwn->ai_enabled) {
notification_show("DWN AI", "AI Disabled",
"Set OPENROUTER_API_KEY to enable AI features",
NULL, 3000);
return;
}
ai_update_context();
const char *task = ai_analyze_task();
const char *suggestion = ai_suggest_window();
char body[512];
Workspace *ws = workspace_get_current();
if (ws != NULL && ws->focused != NULL) {
snprintf(body, sizeof(body),
"Current task: %s\nFocused: %s\n%s",
task ? task : "Unknown",
ws->focused->title[0] ? ws->focused->title : "(unnamed)",
suggestion ? suggestion : "Press Alt+A to ask AI for help");
} else {
snprintf(body, sizeof(body),
"No window focused.\nPress Alt+A to ask AI for help");
}
notification_show("DWN AI", "Context Analysis", body, NULL, 4000);
}
void key_ai_command(void)
{
if (dwn == NULL) {
return;
}
if (dwn->ai_enabled) {
ai_show_command_palette();
} else {
LOG_INFO("AI not enabled (no API key)");
}
}
void key_exa_search(void)
{
exa_show_app_launcher();
}
void key_show_shortcuts(void)
{
const char *shortcuts =
"=== Applications ===\n"
"Ctrl+Alt+T Terminal\n"
"Super / Alt+F2 App launcher\n"
2025-12-28 03:14:31 +01:00
"Super+E File manager\n"
"Super+B Web browser\n"
"Print Screenshot\n"
"\n"
"=== Window Management ===\n"
"Alt+F4 Close window\n"
"Alt+Tab Next window\n"
"Alt+Shift+Tab Previous window\n"
"Alt+F9 Toggle minimize\n"
2025-12-28 03:14:31 +01:00
"Alt+F10 Toggle maximize\n"
"Alt+F11 Toggle fullscreen\n"
"Super+F9 Toggle floating\n"
"\n"
"=== Workspaces ===\n"
"F1-F9 Switch to workspace\n"
"Shift+F1-F9 Move window to workspace\n"
"Ctrl+Alt+Right Next workspace\n"
"Ctrl+Alt+Left Previous workspace\n"
"\n"
"=== Layout Control ===\n"
"Super+Space Cycle layout (tile/float/mono)\n"
"Super+H Shrink master area\n"
"Super+L Expand master area\n"
"Super+I Add to master\n"
"Super+D Remove from master\n"
"Super+Left Snap window left (50%)\n"
"Super+Right Snap window right (50%)\n"
2025-12-28 03:14:31 +01:00
"\n"
"=== AI Features ===\n"
"Super+A Show AI context\n"
"Super+Shift+A AI command palette\n"
"Super+Shift+E Exa semantic search\n"
"\n"
"=== Help & System ===\n"
"Super+S Show shortcuts (this)\n"
"Super+T Interactive tutorial\n"
"Super+Shift+D Demo mode\n"
2025-12-28 03:14:31 +01:00
"Super+Backspace Quit DWN";
notification_show("DWN Shortcuts", "Complete Reference", shortcuts, NULL, 0);
}
void key_start_tutorial(void)
{
if (tutorial_is_active()) {
tutorial_next_step();
} else {
tutorial_start();
}
}
void key_news_next(void)
{
news_next_article();
}
void key_news_prev(void)
{
news_prev_article();
}
void key_news_open(void)
{
news_open_current();
}
void key_screenshot(void)
{
spawn_async("xfce4-screenshooter");
}
void key_snap_left(void)
{
if (dwn == NULL) {
return;
}
Workspace *ws = workspace_get_current();
if (ws == NULL || ws->focused == NULL) {
return;
}
Client *c = ws->focused;
int work_x = 0;
int work_y = 0;
int work_width = dwn->screen_width;
int work_height = dwn->screen_height;
if (dwn->config != NULL) {
if (dwn->config->top_panel_enabled) {
work_y += config_get_panel_height();
work_height -= config_get_panel_height();
}
if (dwn->config->bottom_panel_enabled) {
work_height -= config_get_panel_height();
}
}
int gap = config_get_gap();
if (!client_is_floating(c)) {
client_set_floating(c, true);
}
c->x = work_x + gap;
c->y = work_y + gap;
c->width = (work_width / 2) - (gap * 2);
c->height = work_height - (gap * 2);
client_configure(c);
decorations_render(c, true);
}
void key_snap_right(void)
{
if (dwn == NULL) {
return;
}
Workspace *ws = workspace_get_current();
if (ws == NULL || ws->focused == NULL) {
return;
}
Client *c = ws->focused;
int work_x = 0;
int work_y = 0;
int work_width = dwn->screen_width;
int work_height = dwn->screen_height;
if (dwn->config != NULL) {
if (dwn->config->top_panel_enabled) {
work_y += config_get_panel_height();
work_height -= config_get_panel_height();
}
if (dwn->config->bottom_panel_enabled) {
work_height -= config_get_panel_height();
}
}
int gap = config_get_gap();
if (!client_is_floating(c)) {
client_set_floating(c, true);
}
c->x = work_x + (work_width / 2) + gap;
c->y = work_y + gap;
c->width = (work_width / 2) - (gap * 2);
c->height = work_height - (gap * 2);
client_configure(c);
decorations_render(c, true);
}
void key_start_demo(void)
{
if (demo_is_active()) {
demo_stop();
} else {
demo_start();
}
}