wmstatus: make bindings configurable
This commit is contained in:
		
							parent
							
								
									4a22708f52
								
							
						
					
					
						commit
						9ccdc3430c
					
				
							
								
								
									
										448
									
								
								wmstatus.c
									
									
									
									
									
								
							
							
						
						
									
										448
									
								
								wmstatus.c
									
									
									
									
									
								
							| @ -852,6 +852,7 @@ app_make_config (void) | |||||||
| { | { | ||||||
| 	struct config config = config_make (); | 	struct config config = config_make (); | ||||||
| 	config_register_module (&config, "general", app_load_config_general, NULL); | 	config_register_module (&config, "general", app_load_config_general, NULL); | ||||||
|  | 	config_register_module (&config, "keys",    NULL,                    NULL); | ||||||
| 	config_register_module (&config, "mpd",     app_load_config_mpd,     NULL); | 	config_register_module (&config, "mpd",     app_load_config_mpd,     NULL); | ||||||
| 	config_register_module (&config, "nut",     app_load_config_nut,     NULL); | 	config_register_module (&config, "nut",     app_load_config_nut,     NULL); | ||||||
| 
 | 
 | ||||||
| @ -893,6 +894,65 @@ get_config_boolean (struct config_item *root, const char *key) | |||||||
| 	return &item->value.boolean; | 	return &item->value.boolean; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | ||||||
|  | 
 | ||||||
|  | // This is essentially simplified shell command language syntax,
 | ||||||
|  | // without comments or double quotes, and line feeds are whitespace.
 | ||||||
|  | static bool | ||||||
|  | parse_binding (const char *line, struct strv *out) | ||||||
|  | { | ||||||
|  | 	enum { STA, DEF, ESC, WOR, QUO, STATES }; | ||||||
|  | 	enum { TAKE = 1 << 3, PUSH = 1 << 4, STOP = 1 << 5, ERROR = 1 << 6 }; | ||||||
|  | 	enum { TWOR = TAKE | WOR }; | ||||||
|  | 
 | ||||||
|  | 	// We never transition back to the start state, so it can stay as a no-op
 | ||||||
|  | 	static char table[STATES][7] = | ||||||
|  | 	{ | ||||||
|  | 		// state    NUL          SP, TAB, LF '     \     default
 | ||||||
|  | 		/* STA */ { STOP,        DEF,        QUO,  ESC,  TWOR }, | ||||||
|  | 		/* DEF */ { STOP,        0,          QUO,  ESC,  TWOR }, | ||||||
|  | 		/* ESC */ { ERROR,       TWOR,       TWOR, TWOR, TWOR }, | ||||||
|  | 		/* WOR */ { STOP | PUSH, DEF | PUSH, QUO,  ESC,  TAKE }, | ||||||
|  | 		/* QUO */ { ERROR,       TAKE,       WOR,  TAKE, TAKE }, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	strv_reset (out); | ||||||
|  | 	struct str token = str_make (); | ||||||
|  | 	int state = STA, edge = 0, ch = 0; | ||||||
|  | 	while (true) | ||||||
|  | 	{ | ||||||
|  | 		switch ((ch = (unsigned char) *line++)) | ||||||
|  | 		{ | ||||||
|  | 		case 0:    edge = table[state][0]; break; | ||||||
|  | 		case '\t': | ||||||
|  | 		case '\n': edge = table[state][4]; break; | ||||||
|  | 		case ' ':  edge = table[state][1]; break; | ||||||
|  | 		case '\'': edge = table[state][2]; break; | ||||||
|  | 		case '\\': edge = table[state][3]; break; | ||||||
|  | 		default:   edge = table[state][4]; break; | ||||||
|  | 		} | ||||||
|  | 		if (edge & TAKE) | ||||||
|  | 			str_append_c (&token, ch); | ||||||
|  | 		if (edge & PUSH) | ||||||
|  | 		{ | ||||||
|  | 			strv_append_owned (out, str_steal (&token)); | ||||||
|  | 			token = str_make (); | ||||||
|  | 		} | ||||||
|  | 		if (edge & STOP) | ||||||
|  | 		{ | ||||||
|  | 			str_free (&token); | ||||||
|  | 			return true; | ||||||
|  | 		} | ||||||
|  | 		if (edge & ERROR) | ||||||
|  | 		{ | ||||||
|  | 			str_free (&token); | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  | 		if (edge &= 7) | ||||||
|  | 			state = edge; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // --- Application -------------------------------------------------------------
 | // --- Application -------------------------------------------------------------
 | ||||||
| 
 | 
 | ||||||
| struct app_context | struct app_context | ||||||
| @ -929,6 +989,7 @@ struct app_context | |||||||
| 
 | 
 | ||||||
| 	// Hotkeys:
 | 	// Hotkeys:
 | ||||||
| 
 | 
 | ||||||
|  | 	struct binding *bindings;           ///< Global bindings
 | ||||||
| 	int xkb_base_event_code;            ///< Xkb base event code
 | 	int xkb_base_event_code;            ///< Xkb base event code
 | ||||||
| 	char *layout;                       ///< Keyboard layout
 | 	char *layout;                       ///< Keyboard layout
 | ||||||
| 
 | 
 | ||||||
| @ -2152,8 +2213,15 @@ on_noise_timer (void *user_data) | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void | static void | ||||||
| on_noise_adjust (struct app_context *ctx, int arg) | action_noise_adjust (struct app_context *ctx, const struct strv *args) | ||||||
| { | { | ||||||
|  | 	if (args->len != 1) | ||||||
|  | 	{ | ||||||
|  | 		print_error ("usage: noise-adjust +/-HOURS"); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	long arg = strtol (args->vector[0], NULL, 10); | ||||||
| 	ctx->noise_fadeout_samples = 0; | 	ctx->noise_fadeout_samples = 0; | ||||||
| 	ctx->noise_fadeout_iterator = 0; | 	ctx->noise_fadeout_iterator = 0; | ||||||
| 	if (!ctx->noise_end_time && (arg < 0 || !noise_start (ctx))) | 	if (!ctx->noise_end_time && (arg < 0 || !noise_start (ctx))) | ||||||
| @ -2313,32 +2381,34 @@ spawn (char *argv[]) | |||||||
| 	posix_spawn_file_actions_destroy (&actions); | 	posix_spawn_file_actions_destroy (&actions); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #define MPD_SIMPLE(name, ...)                              \ | static void | ||||||
| 	static void                                            \ | action_exec (struct app_context *ctx, const struct strv *args) | ||||||
| 	on_mpd_ ## name (struct app_context *ctx, int arg)     \ | { | ||||||
| 	{                                                      \ | 	(void) ctx; | ||||||
| 		(void) arg;                                        \ | 	spawn (args->vector); | ||||||
| 		struct mpd_client *c = &ctx->mpd_client;           \ |  | ||||||
| 		if (c->state != MPD_CONNECTED)                     \ |  | ||||||
| 			return;                                        \ |  | ||||||
| 		mpd_client_send_command (c, __VA_ARGS__);          \ |  | ||||||
| 		mpd_client_add_task (c, NULL, NULL);               \ |  | ||||||
| 		mpd_client_idle (c, 0);                            \ |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | static void | ||||||
| MPD_SIMPLE (play,     "play",     NULL) | action_mpd (struct app_context *ctx, const struct strv *args) | ||||||
| MPD_SIMPLE (toggle,   "pause",    NULL) | { | ||||||
| MPD_SIMPLE (stop,     "stop",     NULL) | 	struct mpd_client *c = &ctx->mpd_client; | ||||||
| MPD_SIMPLE (prev,     "previous", NULL) | 	if (c->state != MPD_CONNECTED) | ||||||
| MPD_SIMPLE (next,     "next",     NULL) | 		return; | ||||||
| MPD_SIMPLE (forward,  "seekcur", "+10", NULL) | 	mpd_client_send_commandv (c, args->vector); | ||||||
| MPD_SIMPLE (backward, "seekcur", "-10", NULL) | 	mpd_client_add_task (c, NULL, NULL); | ||||||
|  | 	mpd_client_idle (c, 0); | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| static void | static void | ||||||
| on_mpd_play_toggle (struct app_context *ctx, int arg) | action_mpd_play_toggle (struct app_context *ctx, const struct strv *args) | ||||||
| { | { | ||||||
| 	(ctx->mpd_stopped ? on_mpd_play : on_mpd_toggle) (ctx, arg); | 	(void) args; | ||||||
|  | 	struct mpd_client *c = &ctx->mpd_client; | ||||||
|  | 	if (c->state != MPD_CONNECTED) | ||||||
|  | 		return; | ||||||
|  | 	mpd_client_send_command (c, ctx->mpd_stopped ? "play" : "pause", NULL); | ||||||
|  | 	mpd_client_add_task (c, NULL, NULL); | ||||||
|  | 	mpd_client_idle (c, 0); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void | static void | ||||||
| @ -2352,9 +2422,9 @@ on_volume_finish (pa_context *context, int success, void *userdata) | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void | static void | ||||||
| on_volume_mic_mute (struct app_context *ctx, int arg) | action_audio_mic_mute (struct app_context *ctx, const struct strv *args) | ||||||
| { | { | ||||||
| 	(void) arg; | 	(void) args; | ||||||
| 
 | 
 | ||||||
| 	if (!ctx->context) | 	if (!ctx->context) | ||||||
| 		return; | 		return; | ||||||
| @ -2364,9 +2434,9 @@ on_volume_mic_mute (struct app_context *ctx, int arg) | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void | static void | ||||||
| on_volume_switch (struct app_context *ctx, int arg) | action_audio_switch (struct app_context *ctx, const struct strv *args) | ||||||
| { | { | ||||||
| 	(void) arg; | 	(void) args; | ||||||
| 
 | 
 | ||||||
| 	if (!ctx->context || !ctx->sink_port_active || !ctx->sink_ports.len) | 	if (!ctx->context || !ctx->sink_port_active || !ctx->sink_ports.len) | ||||||
| 		return; | 		return; | ||||||
| @ -2383,9 +2453,9 @@ on_volume_switch (struct app_context *ctx, int arg) | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void | static void | ||||||
| on_volume_mute (struct app_context *ctx, int arg) | action_audio_mute (struct app_context *ctx, const struct strv *args) | ||||||
| { | { | ||||||
| 	(void) arg; | 	(void) args; | ||||||
| 
 | 
 | ||||||
| 	if (!ctx->context) | 	if (!ctx->context) | ||||||
| 		return; | 		return; | ||||||
| @ -2395,65 +2465,26 @@ on_volume_mute (struct app_context *ctx, int arg) | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void | static void | ||||||
| on_volume_set (struct app_context *ctx, int arg) | action_audio_volume (struct app_context *ctx, const struct strv *args) | ||||||
| { | { | ||||||
|  | 	if (args->len != 1) | ||||||
|  | 	{ | ||||||
|  | 		print_error ("usage: audio-volume +/-PERCENT"); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
| 	if (!ctx->context) | 	if (!ctx->context) | ||||||
| 		return; | 		return; | ||||||
| 
 | 
 | ||||||
|  | 	long arg = strtol (args->vector[0], NULL, 10); | ||||||
| 	pa_cvolume volume = ctx->sink_volume; | 	pa_cvolume volume = ctx->sink_volume; | ||||||
| 	if (arg > 0) | 	if (arg > 0) | ||||||
| 		pa_cvolume_inc (&volume, (pa_volume_t)  arg * PA_VOLUME_NORM / 100); | 		pa_cvolume_inc (&volume, (pa_volume_t) +arg * PA_VOLUME_NORM / 100); | ||||||
| 	else | 	else | ||||||
| 		pa_cvolume_dec (&volume, (pa_volume_t) -arg * PA_VOLUME_NORM / 100); | 		pa_cvolume_dec (&volume, (pa_volume_t) -arg * PA_VOLUME_NORM / 100); | ||||||
| 	pa_operation_unref (pa_context_set_sink_volume_by_name (ctx->context, | 	pa_operation_unref (pa_context_set_sink_volume_by_name (ctx->context, | ||||||
| 		DEFAULT_SINK, &volume, on_volume_finish, ctx)); | 		DEFAULT_SINK, &volume, on_volume_finish, ctx)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void |  | ||||||
| on_lock (struct app_context *ctx, int arg) |  | ||||||
| { |  | ||||||
| 	(void) ctx; |  | ||||||
| 	(void) arg; |  | ||||||
| 
 |  | ||||||
| 	// One of these will work
 |  | ||||||
| 	char *argv_gdm[] = { "gdm-switch-user", NULL }; |  | ||||||
| 	spawn (argv_gdm); |  | ||||||
| 	char *argv_ldm[] = { "dm-tool", "lock", NULL }; |  | ||||||
| 	spawn (argv_ldm); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void |  | ||||||
| on_input_switch (struct app_context *ctx, int arg) |  | ||||||
| { |  | ||||||
| 	(void) ctx; |  | ||||||
| 
 |  | ||||||
| 	char *values[] = { "vga", "dvi", "hdmi", "dp" }, |  | ||||||
| 		*numbers[] = { "1", "2" }; |  | ||||||
| 	char *argv[] = { "input-switch", |  | ||||||
| 		values[arg & 0xf], numbers[arg >> 4], NULL }; |  | ||||||
| 	spawn (argv); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void |  | ||||||
| on_brightness (struct app_context *ctx, int arg) |  | ||||||
| { |  | ||||||
| 	(void) ctx; |  | ||||||
| 	char *value = xstrdup_printf ("%d", arg); |  | ||||||
| 	char *argv[] = { "brightness", value, NULL }; |  | ||||||
| 	spawn (argv); |  | ||||||
| 	free (value); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void |  | ||||||
| on_standby (struct app_context *ctx, int arg) |  | ||||||
| { |  | ||||||
| 	(void) ctx; |  | ||||||
| 	(void) arg; |  | ||||||
| 
 |  | ||||||
| 	// We need to wait a little while until user releases the key
 |  | ||||||
| 	spawn ((char *[]) { "sh", "-c", "sleep 1; xset dpms force standby", NULL }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void | static void | ||||||
| go_insomniac (struct app_context *ctx) | go_insomniac (struct app_context *ctx) | ||||||
| { | { | ||||||
| @ -2499,9 +2530,9 @@ go_insomniac (struct app_context *ctx) | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void | static void | ||||||
| on_insomnia (struct app_context *ctx, int arg) | action_insomnia (struct app_context *ctx, const struct strv *args) | ||||||
| { | { | ||||||
| 	(void) arg; | 	(void) args; | ||||||
| 	cstr_set (&ctx->insomnia_info, NULL); | 	cstr_set (&ctx->insomnia_info, NULL); | ||||||
| 
 | 
 | ||||||
| 	// Get rid of the lock if we hold one, establish it otherwise
 | 	// Get rid of the lock if we hold one, establish it otherwise
 | ||||||
| @ -2517,84 +2548,48 @@ on_insomnia (struct app_context *ctx, int arg) | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void | static void | ||||||
| on_lock_group (struct app_context *ctx, int arg) | action_xkb_lock_group (struct app_context *ctx, const struct strv *args) | ||||||
| { | { | ||||||
| 	XkbLockGroup (ctx->dpy, XkbUseCoreKbd, arg); | 	if (args->len != 1) | ||||||
|  | 	{ | ||||||
|  | 		print_error ("usage: xkb-lock-group GROUP"); | ||||||
|  | 		return; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| struct | 	long group = strtol (args->vector[0], NULL, 10) - 1; | ||||||
| { | 	if (group < XkbGroup1Index || group > XkbGroup4Index) | ||||||
| 	unsigned mod; | 		print_warning ("invalid XKB group index: %s", args->vector[0]); | ||||||
| 	KeySym keysym; | 	else | ||||||
| 	void (*handler) (struct app_context *ctx, int arg); | 		XkbLockGroup (ctx->dpy, XkbUseCoreKbd, group); | ||||||
| 	int arg; |  | ||||||
| } | } | ||||||
| g_keys[] = | 
 | ||||||
|  | static const struct action | ||||||
| { | { | ||||||
| 	// This key should be labeled L on normal Qwert[yz] layouts
 | 	const char *name; | ||||||
| 	{ Mod4Mask,             XK_n,         on_lock,              0 }, | 	void (*handler) (struct app_context *ctx, const struct strv *args); | ||||||
|  | } | ||||||
|  | g_handlers[] = | ||||||
|  | { | ||||||
|  | 	{ "exec",            action_exec            }, | ||||||
|  | 	{ "mpd",             action_mpd             }, | ||||||
|  | 	{ "mpd-play-toggle", action_mpd_play_toggle }, | ||||||
|  | 	{ "xkb-lock-group",  action_xkb_lock_group  }, | ||||||
|  | 	{ "insomnia",        action_insomnia        }, | ||||||
|  | 	{ "audio-switch",    action_audio_switch    }, | ||||||
|  | 	{ "audio-mute",      action_audio_mute      }, | ||||||
|  | 	{ "audio-mic-mute",  action_audio_mic_mute  }, | ||||||
|  | 	{ "audio-volume",    action_audio_volume    }, | ||||||
|  | 	{ "noise-adjust",    action_noise_adjust    }, | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| 	// xmodmap | grep -e Alt_R -e Meta_R -e ISO_Level3_Shift -e Mode_switch
 | struct binding | ||||||
| 	// can be used to figure out which modifier is AltGr
 | { | ||||||
|  | 	LIST_HEADER (struct binding) | ||||||
| 
 | 
 | ||||||
| 	// MPD
 | 	unsigned mods;                      ///< Modifiers
 | ||||||
| 	{ Mod4Mask,               XK_Up,        on_mpd_play_toggle,   0 }, | 	KeyCode keycode;                    ///< Key code
 | ||||||
| 	{ Mod4Mask,               XK_Down,      on_mpd_stop,          0 }, | 	struct action handler;              ///< Handling procedure
 | ||||||
| 	{ Mod4Mask,               XK_Left,      on_mpd_prev,          0 }, | 	struct strv args;                   ///< Arguments to the handler
 | ||||||
| 	{ Mod4Mask,               XK_Right,     on_mpd_next,          0 }, |  | ||||||
| 	{ Mod4Mask | ShiftMask,   XK_Left,      on_mpd_backward,      0 }, |  | ||||||
| 	{ Mod4Mask | ShiftMask,   XK_Right,     on_mpd_forward,       0 }, |  | ||||||
| 	{ 0, XF86XK_AudioPlay,                  on_mpd_play_toggle,   0 }, |  | ||||||
| 	{ 0, XF86XK_AudioPrev,                  on_mpd_prev,          0 }, |  | ||||||
| 	{ 0, XF86XK_AudioNext,                  on_mpd_next,          0 }, |  | ||||||
| 
 |  | ||||||
| 	// Keyboard groups
 |  | ||||||
| 	{ Mod4Mask,               XK_F1,        on_lock_group,        0 }, |  | ||||||
| 	{ Mod4Mask,               XK_F2,        on_lock_group,        1 }, |  | ||||||
| 	{ Mod4Mask,               XK_F3,        on_lock_group,        2 }, |  | ||||||
| 	{ Mod4Mask,               XK_F4,        on_lock_group,        3 }, |  | ||||||
| 
 |  | ||||||
| #define CSMask (ControlMask | ShiftMask) |  | ||||||
| 
 |  | ||||||
| 	// Display input sources
 |  | ||||||
| 	{ Mod4Mask | ControlMask, XK_F1,        on_input_switch,      0 }, |  | ||||||
| 	{ Mod4Mask | CSMask,      XK_F1,        on_input_switch, 16 | 0 }, |  | ||||||
| 	{ Mod4Mask | ControlMask, XK_F2,        on_input_switch,      1 }, |  | ||||||
| 	{ Mod4Mask | CSMask,      XK_F2,        on_input_switch, 16 | 1 }, |  | ||||||
| 	{ Mod4Mask | ControlMask, XK_F3,        on_input_switch,      2 }, |  | ||||||
| 	{ Mod4Mask | CSMask,      XK_F3,        on_input_switch, 16 | 2 }, |  | ||||||
| 	{ Mod4Mask | ControlMask, XK_F4,        on_input_switch,      3 }, |  | ||||||
| 	{ Mod4Mask | CSMask,      XK_F4,        on_input_switch, 16 | 3 }, |  | ||||||
| 
 |  | ||||||
| 	// Brightness
 |  | ||||||
| 	{ Mod4Mask,               XK_Home,      on_brightness,       10 }, |  | ||||||
| 	{ Mod4Mask,               XK_End,       on_brightness,      -10 }, |  | ||||||
| 	{ 0, XF86XK_MonBrightnessUp,            on_brightness,       10 }, |  | ||||||
| 	{ 0, XF86XK_MonBrightnessDown,          on_brightness,      -10 }, |  | ||||||
| 
 |  | ||||||
| 	{ Mod4Mask,               XK_F5,        on_standby,           0 }, |  | ||||||
| 	{ Mod4Mask | ShiftMask,   XK_F5,        on_insomnia,          0 }, |  | ||||||
| 	{ Mod4Mask,               XK_Pause,     on_standby,           0 }, |  | ||||||
| 	{ Mod4Mask | ShiftMask,   XK_Pause,     on_insomnia,          0 }, |  | ||||||
| 
 |  | ||||||
| 	// Volume
 |  | ||||||
| 	{ Mod4Mask,               XK_Insert,    on_volume_switch,     0 }, |  | ||||||
| 	{ Mod4Mask,               XK_Delete,    on_volume_mute,       0 }, |  | ||||||
| 	{ Mod4Mask | ShiftMask,   XK_Delete,    on_volume_mic_mute,   0 }, |  | ||||||
| 	{ Mod4Mask,               XK_Page_Up,   on_volume_set,        5 }, |  | ||||||
| 	{ Mod4Mask | ShiftMask,   XK_Page_Up,   on_volume_set,        1 }, |  | ||||||
| 	{ Mod4Mask,               XK_Page_Down, on_volume_set,       -5 }, |  | ||||||
| 	{ Mod4Mask | ShiftMask,   XK_Page_Down, on_volume_set,       -1 }, |  | ||||||
| 	{ 0, XF86XK_AudioRaiseVolume,           on_volume_set,        5 }, |  | ||||||
| 	{ ShiftMask, XF86XK_AudioRaiseVolume,   on_volume_set,        1 }, |  | ||||||
| 	{ 0, XF86XK_AudioLowerVolume,           on_volume_set,       -5 }, |  | ||||||
| 	{ ShiftMask, XF86XK_AudioLowerVolume,   on_volume_set,       -1 }, |  | ||||||
| 	{ 0, XF86XK_AudioMute,                  on_volume_mute,       0 }, |  | ||||||
| 	{ 0, XF86XK_AudioMicMute,               on_volume_mic_mute,   0 }, |  | ||||||
| 
 |  | ||||||
| 	// Noise playback
 |  | ||||||
| 	{ ControlMask, XF86XK_AudioRaiseVolume, on_noise_adjust,      1 }, |  | ||||||
| 	{ ControlMask, XF86XK_AudioLowerVolume, on_noise_adjust,     -1 }, |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | ||||||
| @ -2603,16 +2598,11 @@ static void | |||||||
| on_x_keypress (struct app_context *ctx, XEvent *e) | on_x_keypress (struct app_context *ctx, XEvent *e) | ||||||
| { | { | ||||||
| 	XKeyEvent *ev = &e->xkey; | 	XKeyEvent *ev = &e->xkey; | ||||||
| 	unsigned unconsumed_mods; | 	LIST_FOR_EACH (struct binding, iter, ctx->bindings) | ||||||
| 	KeySym keysym; | 		if (iter->keycode == ev->keycode | ||||||
| 	if (!XkbLookupKeySym (ctx->dpy, | 		 && iter->mods == ev->state | ||||||
| 		(KeyCode) ev->keycode, ev->state, &unconsumed_mods, &keysym)) | 		 && iter->handler.handler) | ||||||
| 		return; | 			iter->handler.handler (ctx, &iter->args); | ||||||
| 	for (size_t i = 0; i < N_ELEMENTS (g_keys); i++) |  | ||||||
| 		if (g_keys[i].keysym == keysym |  | ||||||
| 		 && g_keys[i].mod == ev->state |  | ||||||
| 		 && g_keys[i].handler) |  | ||||||
| 			g_keys[i].handler (ctx, g_keys[i].arg); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void | static void | ||||||
| @ -2667,6 +2657,134 @@ on_x_ready (const struct pollfd *pfd, void *user_data) | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static bool | ||||||
|  | parse_key_modifier (const char *modifier, unsigned *mods) | ||||||
|  | { | ||||||
|  | 	static const struct | ||||||
|  | 	{ | ||||||
|  | 		const char *name; | ||||||
|  | 		unsigned mask; | ||||||
|  | 	} | ||||||
|  | 	modifiers[] = | ||||||
|  | 	{ | ||||||
|  | 		{"Shift",   ShiftMask}, | ||||||
|  | 		{"Lock",    LockMask}, | ||||||
|  | 		{"Control", ControlMask}, | ||||||
|  | 		{"Mod1",    Mod1Mask}, | ||||||
|  | 		{"Mod2",    Mod2Mask}, | ||||||
|  | 		{"Mod3",    Mod3Mask}, | ||||||
|  | 		{"Mod4",    Mod4Mask}, | ||||||
|  | 		{"Mod5",    Mod5Mask}, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	for (size_t k = 0; k < N_ELEMENTS (modifiers); k++) | ||||||
|  | 		if (!strcasecmp_ascii (modifiers[k].name, modifier)) | ||||||
|  | 		{ | ||||||
|  | 			*mods |= modifiers[k].mask; | ||||||
|  | 			return true; | ||||||
|  | 		} | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool | ||||||
|  | parse_key_vector (const struct strv *keys, unsigned *mods, KeySym *keysym) | ||||||
|  | { | ||||||
|  | 	for (size_t i = 0; i < keys->len; i++) | ||||||
|  | 	{ | ||||||
|  | 		if (parse_key_modifier (keys->vector[i], mods)) | ||||||
|  | 			continue; | ||||||
|  | 		if (*keysym) | ||||||
|  | 			return false; | ||||||
|  | 		*keysym = XStringToKeysym (keys->vector[i]); | ||||||
|  | 	} | ||||||
|  | 	return *keysym != 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool | ||||||
|  | parse_key_combination (const char *combination, unsigned *mods, KeySym *keysym) | ||||||
|  | { | ||||||
|  | 	struct strv keys = strv_make (); | ||||||
|  | 	bool result = parse_binding (combination, &keys) | ||||||
|  | 		&& parse_key_vector (&keys, mods, keysym); | ||||||
|  | 	strv_free (&keys); | ||||||
|  | 	return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const char * | ||||||
|  | init_grab (struct app_context *ctx, const char *combination, const char *action) | ||||||
|  | { | ||||||
|  | 	unsigned mods = 0; | ||||||
|  | 	KeySym keysym = 0; | ||||||
|  | 	if (!parse_key_combination (combination, &mods, &keysym)) | ||||||
|  | 		return "parsing key combination failed"; | ||||||
|  | 
 | ||||||
|  | 	KeyCode keycode = XKeysymToKeycode (ctx->dpy, keysym); | ||||||
|  | 	if (!keycode) | ||||||
|  | 		return "no keycode found"; | ||||||
|  | 
 | ||||||
|  | 	struct strv args = strv_make (); | ||||||
|  | 	if (!parse_binding (action, &args) || !args.len) | ||||||
|  | 	{ | ||||||
|  | 		strv_free (&args); | ||||||
|  | 		return "parsing the binding failed"; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	struct action handler = {}; | ||||||
|  | 	for (size_t i = 0; i < N_ELEMENTS (g_handlers); i++) | ||||||
|  | 		if (!strcmp (g_handlers[i].name, args.vector[0])) | ||||||
|  | 		{ | ||||||
|  | 			handler = g_handlers[i]; | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 	free (strv_steal (&args, 0)); | ||||||
|  | 	if (!handler.name) | ||||||
|  | 	{ | ||||||
|  | 		strv_free (&args); | ||||||
|  | 		return "unknown action"; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	XGrabKey (ctx->dpy, keycode, mods, DefaultRootWindow (ctx->dpy), | ||||||
|  | 		 False /* ? */, GrabModeAsync, GrabModeAsync); | ||||||
|  | 
 | ||||||
|  | 	struct binding *binding = xcalloc (1, sizeof *binding); | ||||||
|  | 	binding->mods = mods; | ||||||
|  | 	binding->keycode = keycode; | ||||||
|  | 	binding->handler = handler; | ||||||
|  | 	binding->args = args; | ||||||
|  | 	LIST_PREPEND (ctx->bindings, binding); | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | init_bindings (struct app_context *ctx) | ||||||
|  | { | ||||||
|  | 	unsigned ignored_locks = | ||||||
|  | 		LockMask | XkbKeysymToModifiers (ctx->dpy, XK_Num_Lock); | ||||||
|  | 	hard_assert (XkbSetIgnoreLockMods | ||||||
|  | 		(ctx->dpy, XkbUseCoreKbd, ignored_locks, ignored_locks, 0, 0)); | ||||||
|  | 
 | ||||||
|  | 	struct str_map *keys = | ||||||
|  | 		&config_item_get (ctx->config.root, "keys", NULL)->value.object; | ||||||
|  | 	struct str_map_iter iter = str_map_iter_make (keys); | ||||||
|  | 
 | ||||||
|  | 	struct config_item *action; | ||||||
|  | 	while ((action = str_map_iter_next (&iter))) | ||||||
|  | 	{ | ||||||
|  | 		const char *combination = iter.link->key, *err = NULL; | ||||||
|  | 		if (action->type != CONFIG_ITEM_NULL) | ||||||
|  | 		{ | ||||||
|  | 			if (action->type != CONFIG_ITEM_STRING) | ||||||
|  | 				err = "expected a string"; | ||||||
|  | 			else | ||||||
|  | 				err = init_grab (ctx, combination, action->value.string.str); | ||||||
|  | 		} | ||||||
|  | 		if (err) | ||||||
|  | 			print_warning ("configuration: key `%s': %s", combination, err); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	XSelectInput (ctx->dpy, DefaultRootWindow (ctx->dpy), KeyPressMask); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static void | static void | ||||||
| init_xlib_events (struct app_context *ctx) | init_xlib_events (struct app_context *ctx) | ||||||
| { | { | ||||||
| @ -2681,19 +2799,7 @@ init_xlib_events (struct app_context *ctx) | |||||||
| 			XSyncPositiveComparison, ctx->idle_timeout); | 			XSyncPositiveComparison, ctx->idle_timeout); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	unsigned ignored_locks = | 	init_bindings (ctx); | ||||||
| 		LockMask | XkbKeysymToModifiers (ctx->dpy, XK_Num_Lock); |  | ||||||
| 	hard_assert (XkbSetIgnoreLockMods |  | ||||||
| 		(ctx->dpy, XkbUseCoreKbd, ignored_locks, ignored_locks, 0, 0)); |  | ||||||
| 
 |  | ||||||
| 	KeyCode code; |  | ||||||
| 	Window root = DefaultRootWindow (ctx->dpy); |  | ||||||
| 	for (size_t i = 0; i < N_ELEMENTS (g_keys); i++) |  | ||||||
| 		if ((code = XKeysymToKeycode (ctx->dpy, g_keys[i].keysym))) |  | ||||||
| 			XGrabKey (ctx->dpy, code, g_keys[i].mod, root, |  | ||||||
| 				 False /* ? */, GrabModeAsync, GrabModeAsync); |  | ||||||
| 
 |  | ||||||
| 	XSelectInput (ctx->dpy, root, KeyPressMask); |  | ||||||
| 	XSync (ctx->dpy, False); | 	XSync (ctx->dpy, False); | ||||||
| 
 | 
 | ||||||
| 	ctx->x_event.dispatcher = on_x_ready; | 	ctx->x_event.dispatcher = on_x_ready; | ||||||
|  | |||||||
							
								
								
									
										60
									
								
								wmstatus.conf.example
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								wmstatus.conf.example
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,60 @@ | |||||||
|  | # vim: set ft=libertyconf: | ||||||
|  | keys = { | ||||||
|  | 	# This key should be labeled L on normal Qwert[yz] layouts | ||||||
|  | 	"Mod4 n"                       = "exec dm-tool lock" # gdm-switch-user | ||||||
|  | 
 | ||||||
|  | 	# xmodmap grep -e Alt_R -e Meta_R -e ISO_Level3_Shift -e Mode_switch | ||||||
|  | 	# can be used to figure out which modifier is AltGr | ||||||
|  | 
 | ||||||
|  | 	"Mod4       Up"                = "mpd-play-toggle" | ||||||
|  | 	"Mod4       Down"              = "mpd stop" | ||||||
|  | 	"Mod4       Left"              = "mpd previous" | ||||||
|  | 	"Mod4       Right"             = "mpd next" | ||||||
|  | 	"Mod4 Shift Left"              = "mpd seekcur -10" | ||||||
|  | 	"Mod4 Shift Right"             = "mpd seekcur +10" | ||||||
|  | 	"XF86AudioPlay"                = "mpd-play-toggle" | ||||||
|  | 	"XF86AudioPrev"                = "mpd previous" | ||||||
|  | 	"XF86AudioNext"                = "mpd next" | ||||||
|  | 
 | ||||||
|  | 	"Mod4 F1"                      = "xkb-lock-group 1" | ||||||
|  | 	"Mod4 F2"                      = "xkb-lock-group 2" | ||||||
|  | 	"Mod4 F3"                      = "xkb-lock-group 3" | ||||||
|  | 	"Mod4 F4"                      = "xkb-lock-group 4" | ||||||
|  | 
 | ||||||
|  | 	"Mod4 Control       F1"        = "exec input-switch vga 1" | ||||||
|  | 	"Mod4 Control Shift F1"        = "exec input-switch vga 2" | ||||||
|  | 	"Mod4 Control       F2"        = "exec input-switch dvi 1" | ||||||
|  | 	"Mod4 Control Shift F2"        = "exec input-switch dvi 2" | ||||||
|  | 	"Mod4 Control       F3"        = "exec input-switch hdmi 1" | ||||||
|  | 	"Mod4 Control Shift F3"        = "exec input-switch hdmi 2" | ||||||
|  | 	"Mod4 Control       F4"        = "exec input-switch dp 1" | ||||||
|  | 	"Mod4 Control Shift F4"        = "exec input-switch dp 2" | ||||||
|  | 
 | ||||||
|  | 	"Mod4 Home"                    = "exec brightness +10" | ||||||
|  | 	"Mod4 End"                     = "exec brightness -10" | ||||||
|  | 	"XF86MonBrightnessUp"          = "exec brightness +10" | ||||||
|  | 	"XF86MonBrightnessDown"        = "exec brightness -10" | ||||||
|  | 
 | ||||||
|  | 	# We need to wait a little while until user releases the key | ||||||
|  | 	"Mod4       F5"    = "exec sh -c 'sleep 1; xset dpms force standby'" | ||||||
|  | 	"Mod4 Shift F5"    = "insomnia" | ||||||
|  | 	"Mod4       Pause" = "exec sh -c 'sleep 1; xset dpms force standby'" | ||||||
|  | 	"Mod4 Shift Pause" = "insomnia" | ||||||
|  | 
 | ||||||
|  | 	"Mod4       Insert"            = "audio-switch" | ||||||
|  | 	"Mod4       Delete"            = "audio-mute" | ||||||
|  | 	"Mod4 Shift Delete"            = "audio-mic-mute" | ||||||
|  | 	"Mod4       Page_Up"           = "audio-volume +5" | ||||||
|  | 	"Mod4 Shift Page_Up"           = "audio-volume +1" | ||||||
|  | 	"Mod4       Page_Down"         = "audio-volume -5" | ||||||
|  | 	"Mod4 Shift Page_Down"         = "audio-volume -1" | ||||||
|  | 	"        XF86AudioRaiseVolume" = "audio-volume +5" | ||||||
|  | 	"Shift   XF86AudioRaiseVolume" = "audio-volume +1" | ||||||
|  | 	"        XF86AudioLowerVolume" = "audio-volume -5" | ||||||
|  | 	"Shift   XF86AudioLowerVolume" = "audio-volume -1" | ||||||
|  | 	"        XF86AudioMute"        = "audio-mute" | ||||||
|  | 	"        XF86AudioMicMute"     = "audio-mic-mute" | ||||||
|  | 
 | ||||||
|  | 	"Control XF86AudioRaiseVolume" = "noise-adjust +1" | ||||||
|  | 	"Control XF86AudioLowerVolume" = "noise-adjust -1" | ||||||
|  | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user