2019-06-19 10:04:47 +02:00
|
|
|
|
#include "input.h"
|
|
|
|
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
#include <signal.h>
|
|
|
|
|
|
#include <threads.h>
|
2019-07-09 10:00:54 +02:00
|
|
|
|
#include <locale.h>
|
2020-07-15 13:34:32 +02:00
|
|
|
|
#include <errno.h>
|
2021-11-20 19:24:00 +01:00
|
|
|
|
#include <wctype.h>
|
2019-06-19 10:04:47 +02:00
|
|
|
|
#include <sys/mman.h>
|
2019-07-17 21:30:57 +02:00
|
|
|
|
#include <sys/time.h>
|
2019-08-05 19:33:01 +02:00
|
|
|
|
#include <sys/timerfd.h>
|
2020-02-29 12:52:55 +01:00
|
|
|
|
#include <sys/epoll.h>
|
2020-07-15 13:34:32 +02:00
|
|
|
|
#include <fcntl.h>
|
2019-06-19 10:04:47 +02:00
|
|
|
|
|
2019-07-10 14:17:44 +02:00
|
|
|
|
#include <linux/input-event-codes.h>
|
|
|
|
|
|
|
2019-06-19 10:04:47 +02:00
|
|
|
|
#include <xkbcommon/xkbcommon.h>
|
|
|
|
|
|
#include <xkbcommon/xkbcommon-keysyms.h>
|
2019-07-09 10:00:54 +02:00
|
|
|
|
#include <xkbcommon/xkbcommon-compose.h>
|
2019-06-19 10:04:47 +02:00
|
|
|
|
|
2020-02-25 20:31:13 +01:00
|
|
|
|
#include <xdg-shell.h>
|
|
|
|
|
|
|
2019-06-19 10:04:47 +02:00
|
|
|
|
#define LOG_MODULE "input"
|
2020-12-02 18:52:50 +01:00
|
|
|
|
#define LOG_ENABLE_DBG 0
|
2019-06-19 10:04:47 +02:00
|
|
|
|
#include "log.h"
|
2020-03-02 18:42:49 +01:00
|
|
|
|
#include "config.h"
|
2019-07-09 16:26:36 +02:00
|
|
|
|
#include "commands.h"
|
2019-08-27 17:23:28 +02:00
|
|
|
|
#include "keymap.h"
|
2020-08-07 20:42:34 +01:00
|
|
|
|
#include "macros.h"
|
2020-03-02 20:29:28 +01:00
|
|
|
|
#include "quirks.h"
|
2019-08-27 17:23:28 +02:00
|
|
|
|
#include "render.h"
|
|
|
|
|
|
#include "search.h"
|
2019-07-11 09:51:51 +02:00
|
|
|
|
#include "selection.h"
|
2020-07-15 13:34:32 +02:00
|
|
|
|
#include "spawn.h"
|
2019-08-27 17:23:28 +02:00
|
|
|
|
#include "terminal.h"
|
2020-07-15 13:34:32 +02:00
|
|
|
|
#include "tokenize.h"
|
2021-01-31 11:12:07 +01:00
|
|
|
|
#include "url-mode.h"
|
2020-05-01 11:46:24 +02:00
|
|
|
|
#include "util.h"
|
2019-07-15 15:42:21 +02:00
|
|
|
|
#include "vt.h"
|
2020-08-08 20:34:30 +01:00
|
|
|
|
#include "xmalloc.h"
|
2021-01-14 21:30:06 +00:00
|
|
|
|
#include "xsnprintf.h"
|
2019-06-19 10:04:47 +02:00
|
|
|
|
|
2020-07-15 13:34:32 +02:00
|
|
|
|
struct pipe_context {
|
|
|
|
|
|
char *text;
|
|
|
|
|
|
size_t idx;
|
|
|
|
|
|
size_t left;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
|
fdm_write_pipe(struct fdm *fdm, int fd, int events, void *data)
|
|
|
|
|
|
{
|
|
|
|
|
|
struct pipe_context *ctx = data;
|
|
|
|
|
|
|
|
|
|
|
|
if (events & EPOLLHUP)
|
|
|
|
|
|
goto pipe_closed;
|
|
|
|
|
|
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(events & EPOLLOUT);
|
2020-07-15 13:34:32 +02:00
|
|
|
|
ssize_t written = write(fd, &ctx->text[ctx->idx], ctx->left);
|
|
|
|
|
|
|
|
|
|
|
|
if (written < 0) {
|
|
|
|
|
|
LOG_WARN("failed to write to pipe: %s", strerror(errno));
|
|
|
|
|
|
goto pipe_closed;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(written <= ctx->left);
|
2020-07-15 13:34:32 +02:00
|
|
|
|
ctx->idx += written;
|
|
|
|
|
|
ctx->left -= written;
|
|
|
|
|
|
|
|
|
|
|
|
if (ctx->left == 0)
|
|
|
|
|
|
goto pipe_closed;
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
|
|
pipe_closed:
|
|
|
|
|
|
free(ctx->text);
|
|
|
|
|
|
free(ctx);
|
|
|
|
|
|
fdm_del(fdm, fd);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-08-22 13:35:36 +02:00
|
|
|
|
static bool
|
2020-07-08 18:41:09 +02:00
|
|
|
|
execute_binding(struct seat *seat, struct terminal *term,
|
2020-07-30 18:53:51 +02:00
|
|
|
|
enum bind_action_normal action, char *const *pipe_argv,
|
2020-07-15 13:34:32 +02:00
|
|
|
|
uint32_t serial)
|
2020-03-08 12:08:46 +01:00
|
|
|
|
{
|
|
|
|
|
|
switch (action) {
|
2020-03-12 10:46:27 +01:00
|
|
|
|
case BIND_ACTION_NONE:
|
2020-08-22 13:35:36 +02:00
|
|
|
|
return true;
|
2020-03-12 10:46:27 +01:00
|
|
|
|
|
2021-10-24 11:49:37 +02:00
|
|
|
|
case BIND_ACTION_NOOP:
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
2020-09-10 18:17:47 +02:00
|
|
|
|
case BIND_ACTION_SCROLLBACK_UP_PAGE:
|
2021-06-04 23:25:45 +02:00
|
|
|
|
if (term->grid == &term->normal) {
|
|
|
|
|
|
cmd_scrollback_up(term, term->rows);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
2020-03-08 15:17:29 +01:00
|
|
|
|
|
2020-09-10 18:17:47 +02:00
|
|
|
|
case BIND_ACTION_SCROLLBACK_UP_HALF_PAGE:
|
2021-06-04 23:25:45 +02:00
|
|
|
|
if (term->grid == &term->normal) {
|
|
|
|
|
|
cmd_scrollback_up(term, max(term->rows / 2, 1));
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
2020-09-09 19:40:48 +02:00
|
|
|
|
|
2020-09-10 18:17:47 +02:00
|
|
|
|
case BIND_ACTION_SCROLLBACK_UP_LINE:
|
2021-06-04 23:25:45 +02:00
|
|
|
|
if (term->grid == &term->normal) {
|
|
|
|
|
|
cmd_scrollback_up(term, 1);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
2020-09-10 18:17:47 +02:00
|
|
|
|
|
|
|
|
|
|
case BIND_ACTION_SCROLLBACK_DOWN_PAGE:
|
2021-06-04 23:25:45 +02:00
|
|
|
|
if (term->grid == &term->normal) {
|
|
|
|
|
|
cmd_scrollback_down(term, term->rows);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
2020-03-08 15:17:29 +01:00
|
|
|
|
|
2020-09-10 18:17:47 +02:00
|
|
|
|
case BIND_ACTION_SCROLLBACK_DOWN_HALF_PAGE:
|
2021-06-04 23:25:45 +02:00
|
|
|
|
if (term->grid == &term->normal) {
|
|
|
|
|
|
cmd_scrollback_down(term, max(term->rows / 2, 1));
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
2020-09-09 19:40:48 +02:00
|
|
|
|
|
2020-09-10 18:17:47 +02:00
|
|
|
|
case BIND_ACTION_SCROLLBACK_DOWN_LINE:
|
2021-06-04 23:25:45 +02:00
|
|
|
|
if (term->grid == &term->normal) {
|
|
|
|
|
|
cmd_scrollback_down(term, 1);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
2020-09-10 18:17:47 +02:00
|
|
|
|
|
2020-03-08 12:08:46 +01:00
|
|
|
|
case BIND_ACTION_CLIPBOARD_COPY:
|
2020-07-08 18:41:09 +02:00
|
|
|
|
selection_to_clipboard(seat, term, serial);
|
2020-08-22 13:35:36 +02:00
|
|
|
|
return true;
|
2020-03-08 12:08:46 +01:00
|
|
|
|
|
|
|
|
|
|
case BIND_ACTION_CLIPBOARD_PASTE:
|
2020-07-08 18:41:09 +02:00
|
|
|
|
selection_from_clipboard(seat, term, serial);
|
2020-03-08 12:08:46 +01:00
|
|
|
|
term_reset_view(term);
|
2020-08-22 13:35:36 +02:00
|
|
|
|
return true;
|
2020-03-08 12:08:46 +01:00
|
|
|
|
|
|
|
|
|
|
case BIND_ACTION_PRIMARY_PASTE:
|
2020-07-08 18:41:09 +02:00
|
|
|
|
selection_from_primary(seat, term);
|
2020-08-22 13:35:36 +02:00
|
|
|
|
return true;
|
2020-03-08 12:08:46 +01:00
|
|
|
|
|
|
|
|
|
|
case BIND_ACTION_SEARCH_START:
|
|
|
|
|
|
search_begin(term);
|
2020-08-22 13:35:36 +02:00
|
|
|
|
return true;
|
2020-03-08 12:08:46 +01:00
|
|
|
|
|
2020-03-08 15:17:29 +01:00
|
|
|
|
case BIND_ACTION_FONT_SIZE_UP:
|
|
|
|
|
|
term_font_size_increase(term);
|
2020-08-22 13:35:36 +02:00
|
|
|
|
return true;
|
2020-03-08 15:17:29 +01:00
|
|
|
|
|
|
|
|
|
|
case BIND_ACTION_FONT_SIZE_DOWN:
|
|
|
|
|
|
term_font_size_decrease(term);
|
2020-08-22 13:35:36 +02:00
|
|
|
|
return true;
|
2020-03-08 15:17:29 +01:00
|
|
|
|
|
|
|
|
|
|
case BIND_ACTION_FONT_SIZE_RESET:
|
|
|
|
|
|
term_font_size_reset(term);
|
2020-08-22 13:35:36 +02:00
|
|
|
|
return true;
|
2020-03-08 15:17:29 +01:00
|
|
|
|
|
|
|
|
|
|
case BIND_ACTION_SPAWN_TERMINAL:
|
|
|
|
|
|
term_spawn_new(term);
|
2020-08-22 13:35:36 +02:00
|
|
|
|
return true;
|
2020-03-08 15:17:29 +01:00
|
|
|
|
|
2020-03-12 09:34:09 +01:00
|
|
|
|
case BIND_ACTION_MINIMIZE:
|
|
|
|
|
|
xdg_toplevel_set_minimized(term->window->xdg_toplevel);
|
2020-08-22 13:35:36 +02:00
|
|
|
|
return true;
|
2020-03-12 09:34:09 +01:00
|
|
|
|
|
|
|
|
|
|
case BIND_ACTION_MAXIMIZE:
|
2020-03-12 17:47:53 +01:00
|
|
|
|
if (term->window->is_fullscreen)
|
|
|
|
|
|
xdg_toplevel_unset_fullscreen(term->window->xdg_toplevel);
|
2020-03-12 09:34:09 +01:00
|
|
|
|
if (term->window->is_maximized)
|
|
|
|
|
|
xdg_toplevel_unset_maximized(term->window->xdg_toplevel);
|
|
|
|
|
|
else
|
|
|
|
|
|
xdg_toplevel_set_maximized(term->window->xdg_toplevel);
|
2020-08-22 13:35:36 +02:00
|
|
|
|
return true;
|
2020-03-12 09:34:09 +01:00
|
|
|
|
|
|
|
|
|
|
case BIND_ACTION_FULLSCREEN:
|
|
|
|
|
|
if (term->window->is_fullscreen)
|
|
|
|
|
|
xdg_toplevel_unset_fullscreen(term->window->xdg_toplevel);
|
|
|
|
|
|
else
|
|
|
|
|
|
xdg_toplevel_set_fullscreen(term->window->xdg_toplevel, NULL);
|
2020-08-22 13:35:36 +02:00
|
|
|
|
return true;
|
2020-03-12 09:34:09 +01:00
|
|
|
|
|
2020-07-15 09:46:13 +02:00
|
|
|
|
case BIND_ACTION_PIPE_SCROLLBACK:
|
2021-06-04 23:25:45 +02:00
|
|
|
|
if (term->grid == &term->alt)
|
|
|
|
|
|
break;
|
|
|
|
|
|
/* FALLTHROUGH */
|
2020-07-31 17:02:53 +02:00
|
|
|
|
case BIND_ACTION_PIPE_VIEW:
|
|
|
|
|
|
case BIND_ACTION_PIPE_SELECTED: {
|
2020-07-30 18:53:51 +02:00
|
|
|
|
if (pipe_argv == NULL)
|
2020-08-22 13:35:36 +02:00
|
|
|
|
return true;
|
2020-07-15 13:34:32 +02:00
|
|
|
|
|
|
|
|
|
|
struct pipe_context *ctx = NULL;
|
|
|
|
|
|
|
2020-07-22 19:34:07 +02:00
|
|
|
|
int pipe_fd[2] = {-1, -1};
|
|
|
|
|
|
int stdout_fd = -1;
|
|
|
|
|
|
int stderr_fd = -1;
|
|
|
|
|
|
|
|
|
|
|
|
char *text = NULL;
|
|
|
|
|
|
size_t len = 0;
|
|
|
|
|
|
|
2020-07-15 13:34:32 +02:00
|
|
|
|
if (pipe(pipe_fd) < 0) {
|
|
|
|
|
|
LOG_ERRNO("failed to create pipe");
|
|
|
|
|
|
goto pipe_err;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-22 19:34:07 +02:00
|
|
|
|
stdout_fd = open("/dev/null", O_WRONLY);
|
|
|
|
|
|
stderr_fd = open("/dev/null", O_WRONLY);
|
2020-07-15 18:17:28 +02:00
|
|
|
|
|
|
|
|
|
|
if (stdout_fd < 0 || stderr_fd < 0) {
|
|
|
|
|
|
LOG_ERRNO("failed to open /dev/null");
|
|
|
|
|
|
goto pipe_err;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-31 17:02:53 +02:00
|
|
|
|
bool success;
|
|
|
|
|
|
switch (action) {
|
|
|
|
|
|
case BIND_ACTION_PIPE_SCROLLBACK:
|
|
|
|
|
|
success = term_scrollback_to_text(term, &text, &len);
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case BIND_ACTION_PIPE_VIEW:
|
|
|
|
|
|
success = term_view_to_text(term, &text, &len);
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case BIND_ACTION_PIPE_SELECTED:
|
|
|
|
|
|
text = selection_to_text(term);
|
|
|
|
|
|
success = text != NULL;
|
|
|
|
|
|
len = text != NULL ? strlen(text) : 0;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
2021-02-09 13:52:33 +00:00
|
|
|
|
BUG("Unhandled action type");
|
2020-07-31 17:02:53 +02:00
|
|
|
|
success = false;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
2020-07-15 13:34:32 +02:00
|
|
|
|
|
|
|
|
|
|
if (!success)
|
|
|
|
|
|
goto pipe_err;
|
|
|
|
|
|
|
|
|
|
|
|
/* Make write-end non-blocking; required by the FDM */
|
|
|
|
|
|
{
|
|
|
|
|
|
int flags = fcntl(pipe_fd[1], F_GETFL);
|
|
|
|
|
|
if (flags < 0 ||
|
|
|
|
|
|
fcntl(pipe_fd[1], F_SETFL, flags | O_NONBLOCK) < 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
LOG_ERRNO("failed to make write-end of pipe non-blocking");
|
|
|
|
|
|
goto pipe_err;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-15 16:47:01 +02:00
|
|
|
|
/* Make sure write-end is closed on exec() - or the spawned
|
|
|
|
|
|
* program may not terminate*/
|
2020-07-15 13:34:32 +02:00
|
|
|
|
{
|
|
|
|
|
|
int flags = fcntl(pipe_fd[1], F_GETFD);
|
|
|
|
|
|
if (flags < 0 ||
|
|
|
|
|
|
fcntl(pipe_fd[1], F_SETFD, flags | FD_CLOEXEC) < 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
LOG_ERRNO("failed to set FD_CLOEXEC on writeend of pipe");
|
|
|
|
|
|
goto pipe_err;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-30 18:53:51 +02:00
|
|
|
|
if (!spawn(term->reaper, NULL, pipe_argv, pipe_fd[0], stdout_fd, stderr_fd))
|
2020-07-15 13:34:32 +02:00
|
|
|
|
goto pipe_err;
|
|
|
|
|
|
|
|
|
|
|
|
/* Close read end */
|
|
|
|
|
|
close(pipe_fd[0]);
|
|
|
|
|
|
|
2020-08-08 20:34:30 +01:00
|
|
|
|
ctx = xmalloc(sizeof(*ctx));
|
2020-07-15 13:34:32 +02:00
|
|
|
|
*ctx = (struct pipe_context){
|
|
|
|
|
|
.text = text,
|
|
|
|
|
|
.left = len,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2020-07-15 16:47:01 +02:00
|
|
|
|
/* Asynchronously write the output to the pipe */
|
2020-07-15 13:34:32 +02:00
|
|
|
|
if (!fdm_add(term->fdm, pipe_fd[1], EPOLLOUT, &fdm_write_pipe, ctx))
|
|
|
|
|
|
goto pipe_err;
|
|
|
|
|
|
|
2020-08-22 13:35:36 +02:00
|
|
|
|
return true;
|
2020-07-15 09:46:13 +02:00
|
|
|
|
|
2020-08-22 13:35:36 +02:00
|
|
|
|
pipe_err:
|
2020-07-15 18:17:28 +02:00
|
|
|
|
if (stdout_fd >= 0)
|
|
|
|
|
|
close(stdout_fd);
|
|
|
|
|
|
if (stderr_fd >= 0)
|
|
|
|
|
|
close(stderr_fd);
|
2020-07-15 13:34:32 +02:00
|
|
|
|
if (pipe_fd[0] >= 0)
|
|
|
|
|
|
close(pipe_fd[0]);
|
|
|
|
|
|
if (pipe_fd[1] >= 0)
|
|
|
|
|
|
close(pipe_fd[1]);
|
|
|
|
|
|
free(text);
|
|
|
|
|
|
free(ctx);
|
2020-08-22 13:35:36 +02:00
|
|
|
|
return true;
|
2020-07-15 13:34:32 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-02-04 20:55:08 +01:00
|
|
|
|
case BIND_ACTION_SHOW_URLS_COPY:
|
|
|
|
|
|
case BIND_ACTION_SHOW_URLS_LAUNCH: {
|
2021-01-31 11:12:07 +01:00
|
|
|
|
xassert(!urls_mode_is_active(term));
|
|
|
|
|
|
|
2021-02-04 20:55:08 +01:00
|
|
|
|
enum url_action url_action = action == BIND_ACTION_SHOW_URLS_COPY
|
|
|
|
|
|
? URL_ACTION_COPY
|
|
|
|
|
|
: URL_ACTION_LAUNCH;
|
|
|
|
|
|
|
2021-02-06 20:01:52 +01:00
|
|
|
|
urls_collect(term, url_action, &term->urls);
|
2021-02-13 11:43:28 +01:00
|
|
|
|
urls_assign_key_combos(term->conf, &term->urls);
|
2021-02-06 20:01:52 +01:00
|
|
|
|
urls_render(term);
|
2021-01-31 11:12:07 +01:00
|
|
|
|
return true;
|
2021-02-04 20:55:08 +01:00
|
|
|
|
}
|
2021-01-30 12:43:59 +01:00
|
|
|
|
|
2020-08-11 10:53:21 +02:00
|
|
|
|
case BIND_ACTION_SELECT_BEGIN:
|
2021-02-02 09:52:05 +01:00
|
|
|
|
selection_start(
|
|
|
|
|
|
term, seat->mouse.col, seat->mouse.row, SELECTION_CHAR_WISE, false);
|
|
|
|
|
|
return true;
|
2020-08-11 09:55:33 +02:00
|
|
|
|
|
2020-08-11 10:53:21 +02:00
|
|
|
|
case BIND_ACTION_SELECT_BEGIN_BLOCK:
|
2021-02-02 09:52:05 +01:00
|
|
|
|
selection_start(
|
|
|
|
|
|
term, seat->mouse.col, seat->mouse.row, SELECTION_BLOCK, false);
|
|
|
|
|
|
return true;
|
2020-08-11 10:17:19 +02:00
|
|
|
|
|
2020-08-11 10:53:21 +02:00
|
|
|
|
case BIND_ACTION_SELECT_EXTEND:
|
2021-02-02 09:52:05 +01:00
|
|
|
|
selection_extend(
|
|
|
|
|
|
seat, term, seat->mouse.col, seat->mouse.row, term->selection.kind);
|
|
|
|
|
|
return true;
|
2021-01-06 11:11:46 +01:00
|
|
|
|
|
|
|
|
|
|
case BIND_ACTION_SELECT_EXTEND_CHAR_WISE:
|
2021-02-02 09:52:05 +01:00
|
|
|
|
if (term->selection.kind != SELECTION_BLOCK) {
|
2021-01-06 11:11:46 +01:00
|
|
|
|
selection_extend(
|
|
|
|
|
|
seat, term, seat->mouse.col, seat->mouse.row, SELECTION_CHAR_WISE);
|
2020-08-22 13:35:36 +02:00
|
|
|
|
return true;
|
2020-08-11 10:17:19 +02:00
|
|
|
|
}
|
2020-08-22 13:35:36 +02:00
|
|
|
|
return false;
|
2020-08-11 10:17:19 +02:00
|
|
|
|
|
2020-08-11 10:53:21 +02:00
|
|
|
|
case BIND_ACTION_SELECT_WORD:
|
2021-02-02 09:52:05 +01:00
|
|
|
|
selection_start(
|
|
|
|
|
|
term, seat->mouse.col, seat->mouse.row, SELECTION_WORD_WISE, false);
|
|
|
|
|
|
return true;
|
2020-08-11 09:55:33 +02:00
|
|
|
|
|
2020-08-11 10:53:21 +02:00
|
|
|
|
case BIND_ACTION_SELECT_WORD_WS:
|
2021-02-02 09:52:05 +01:00
|
|
|
|
selection_start(
|
|
|
|
|
|
term, seat->mouse.col, seat->mouse.row, SELECTION_WORD_WISE, true);
|
|
|
|
|
|
return true;
|
2020-08-11 09:55:33 +02:00
|
|
|
|
|
2020-08-11 10:53:21 +02:00
|
|
|
|
case BIND_ACTION_SELECT_ROW:
|
2021-02-02 09:52:05 +01:00
|
|
|
|
selection_start(
|
|
|
|
|
|
term, seat->mouse.col, seat->mouse.row, SELECTION_LINE_WISE, false);
|
|
|
|
|
|
return true;
|
2020-08-11 09:55:33 +02:00
|
|
|
|
|
2020-03-08 12:08:46 +01:00
|
|
|
|
case BIND_ACTION_COUNT:
|
2021-02-09 15:16:19 +00:00
|
|
|
|
BUG("Invalid action type");
|
2020-08-22 13:35:36 +02:00
|
|
|
|
return false;
|
2020-03-08 12:08:46 +01:00
|
|
|
|
}
|
2020-08-22 13:35:36 +02:00
|
|
|
|
|
|
|
|
|
|
return false;
|
2020-03-08 12:08:46 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
static xkb_mod_mask_t
|
|
|
|
|
|
conf_modifiers_to_mask(const struct seat *seat,
|
|
|
|
|
|
const struct config_key_modifiers *modifiers)
|
2020-03-08 15:17:29 +01:00
|
|
|
|
{
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
xkb_mod_mask_t mods = 0;
|
2021-11-21 11:53:22 +01:00
|
|
|
|
if (seat->kbd.mod_shift != XKB_MOD_INVALID)
|
|
|
|
|
|
mods |= modifiers->shift << seat->kbd.mod_shift;
|
|
|
|
|
|
if (seat->kbd.mod_ctrl != XKB_MOD_INVALID)
|
|
|
|
|
|
mods |= modifiers->ctrl << seat->kbd.mod_ctrl;
|
|
|
|
|
|
if (seat->kbd.mod_alt != XKB_MOD_INVALID)
|
|
|
|
|
|
mods |= modifiers->alt << seat->kbd.mod_alt;
|
2021-11-21 12:01:16 +01:00
|
|
|
|
if (seat->kbd.mod_super != XKB_MOD_INVALID)
|
|
|
|
|
|
mods |= modifiers->meta << seat->kbd.mod_super;
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
return mods;
|
|
|
|
|
|
}
|
2020-03-08 15:17:29 +01:00
|
|
|
|
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
static xkb_keycode_list_t
|
|
|
|
|
|
key_codes_for_xkb_sym(struct xkb_keymap *keymap, xkb_keysym_t sym)
|
|
|
|
|
|
{
|
|
|
|
|
|
xkb_keycode_list_t key_codes = tll_init();
|
2020-03-08 15:17:29 +01:00
|
|
|
|
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
/*
|
input: rewrite of how we match foot’s own key bindings
Bindings are matched in one out of three ways:
* By translated (by XKB) symbols
* By untranslated symbols
* By raw key codes
A translated symbol is affected by pressed modifiers, some of which
can be “consumed”. Consumed modifiers to not partake in the comparison
with the binding’s modifiers. In this mode, ctrl+shift+2 maps to
ctrl+@ on a US layout.
Untranslated symbols, or un-shifted symbols refer to the “base” symbol
of the pressed key, i.e. it’s unaffected by modifiers. In this mode,
consumed modifiers *do* partake in the comparison with the binding’s
modifiers, and ctrl+shift+2 maps to ctrl+shift+2 on a US layout.
More examples: ctrl+shift+u maps to ctrl+U in the translated lookup,
while ctrl+shift+u maps to ctrl+shift+u in the untranslated lookup.
Finally, we also match raw key codes. This allows our bindings to work
using the same physical keys when the user switches between latin and
non-latin layouts.
This means key bindings in foot.ini *must* not include both +shift+
and a *shifted* key. I.e. ctrl+shift+U is not a valid combo as it
cannot be triggered. Unfortunately, this was how you were supposed to
write bindings up until now... so, we try to detect such bindings, log
a deprecation warning and then “fix” the binding for the user.
When specifying bindings in foot.ini, both ctrl+U and ctrl+shift+u are
valid, and will work. The latter is preferred though, since we cannot
detect the raw key code for the former variant. Personally, I also
prefer the latter one because it is more explicit; it’s more obvious
which keys are involved.
However, in some cases it makes more sense to use the other
variant. Typically for non-letter combos.
2021-02-27 20:42:31 +01:00
|
|
|
|
* Find all key codes that map to this symbol.
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
*
|
|
|
|
|
|
* This allows us to match bindings in other layouts
|
|
|
|
|
|
* too.
|
|
|
|
|
|
*/
|
|
|
|
|
|
struct xkb_state *state = xkb_state_new(keymap);
|
2020-03-08 15:17:29 +01:00
|
|
|
|
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
for (xkb_keycode_t code = xkb_keymap_min_keycode(keymap);
|
|
|
|
|
|
code <= xkb_keymap_max_keycode(keymap);
|
|
|
|
|
|
code++)
|
2020-03-08 15:17:29 +01:00
|
|
|
|
{
|
input: rewrite of how we match foot’s own key bindings
Bindings are matched in one out of three ways:
* By translated (by XKB) symbols
* By untranslated symbols
* By raw key codes
A translated symbol is affected by pressed modifiers, some of which
can be “consumed”. Consumed modifiers to not partake in the comparison
with the binding’s modifiers. In this mode, ctrl+shift+2 maps to
ctrl+@ on a US layout.
Untranslated symbols, or un-shifted symbols refer to the “base” symbol
of the pressed key, i.e. it’s unaffected by modifiers. In this mode,
consumed modifiers *do* partake in the comparison with the binding’s
modifiers, and ctrl+shift+2 maps to ctrl+shift+2 on a US layout.
More examples: ctrl+shift+u maps to ctrl+U in the translated lookup,
while ctrl+shift+u maps to ctrl+shift+u in the untranslated lookup.
Finally, we also match raw key codes. This allows our bindings to work
using the same physical keys when the user switches between latin and
non-latin layouts.
This means key bindings in foot.ini *must* not include both +shift+
and a *shifted* key. I.e. ctrl+shift+U is not a valid combo as it
cannot be triggered. Unfortunately, this was how you were supposed to
write bindings up until now... so, we try to detect such bindings, log
a deprecation warning and then “fix” the binding for the user.
When specifying bindings in foot.ini, both ctrl+U and ctrl+shift+u are
valid, and will work. The latter is preferred though, since we cannot
detect the raw key code for the former variant. Personally, I also
prefer the latter one because it is more explicit; it’s more obvious
which keys are involved.
However, in some cases it makes more sense to use the other
variant. Typically for non-letter combos.
2021-02-27 20:42:31 +01:00
|
|
|
|
if (xkb_state_key_get_one_sym(state, code) == sym)
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
tll_push_back(key_codes, code);
|
2020-03-08 15:17:29 +01:00
|
|
|
|
}
|
2020-03-18 14:29:34 +01:00
|
|
|
|
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
xkb_state_unref(state);
|
|
|
|
|
|
return key_codes;
|
2020-03-08 15:17:29 +01:00
|
|
|
|
}
|
2020-03-18 14:29:34 +01:00
|
|
|
|
|
input: repair key combos containing both explicit modifier and shifted symbol
If a user-provided key combo contains both an explicit modifier, and
the shifted symbol it produces, replace the symbol with its un-shifted
version.
Example: Control+Shift+U contains both ‘Shift’, and ‘U’, where ‘Shift’
is the modifier that shifts ‘u’ to ‘U’.
Such modifiers are “consumed”, i.e. filtered out, when matching key
combos. As such, Control+Shift+U will never trigger since we’ll never
be able to match the consumed modifier ‘Shift’.
The above combo can be written in two ways:
- Control+U
- Control+Shift+u
To be able to detect that Control+Shift+U is an invalid combination,
we need to check all *shifted* symbols for all key *codes*.
Once we’ve detected a shifted symbol (and where it differs from the
un-shifted symbol), we loop all modifier sets that produce the shift
level where our shifted symbol is. For each such set, check if it
intersects with the user-provided key combo’s modifier set.
If there is an intersection, it means the user provided a key combo
containing a modifier and symbol, such that the modifier is consumed
when the symbol is produced.
In this case, we replace the symbol with its un-shifted version.
2021-03-02 17:50:06 +01:00
|
|
|
|
static xkb_keysym_t
|
|
|
|
|
|
maybe_repair_key_combo(const struct seat *seat,
|
|
|
|
|
|
xkb_keysym_t sym, xkb_mod_mask_t mods)
|
|
|
|
|
|
{
|
|
|
|
|
|
/*
|
|
|
|
|
|
* Detect combos containing a shifted symbol and the corresponding
|
|
|
|
|
|
* modifier, and replace the shifted symbol with its unshifted
|
|
|
|
|
|
* variant.
|
|
|
|
|
|
*
|
|
|
|
|
|
* For example, the combo is “Control+Shift+U”. In this case,
|
|
|
|
|
|
* Shift is the modifier used to “shift” ‘u’ to ‘U’, after which
|
|
|
|
|
|
* ‘Shift’ will have been “consumed”. Since we filter out consumed
|
|
|
|
|
|
* modifiers when matching key combos, this key combo will never
|
|
|
|
|
|
* trigger (we will never be able to match the ‘Shift’ modifier).
|
|
|
|
|
|
*
|
|
|
|
|
|
* There are two correct variants of the above key combo:
|
|
|
|
|
|
* - “Control+U” (upper case ‘U’)
|
|
|
|
|
|
* - “Control+Shift+u” (lower case ‘u’)
|
|
|
|
|
|
*
|
|
|
|
|
|
* What we do here is, for each key *code*, check if there are any
|
|
|
|
|
|
* (shifted) levels where it produces ‘sym’. If there are, check
|
|
|
|
|
|
* *which* sets of modifiers are needed to produce it, and compare
|
|
|
|
|
|
* with ‘mods’.
|
|
|
|
|
|
*
|
|
|
|
|
|
* If there is at least one common modifier, it means ‘sym’ is a
|
|
|
|
|
|
* “shifted” symbol, with the corresponding shifting modifier
|
|
|
|
|
|
* explicitly included in the key combo. I.e. the key combo will
|
|
|
|
|
|
* never trigger.
|
|
|
|
|
|
*
|
|
|
|
|
|
* We then proceed and “repair” the key combo by replacing ‘sym’
|
|
|
|
|
|
* with the corresponding unshifted symbol.
|
|
|
|
|
|
*
|
|
|
|
|
|
* To reduce the noise, we ignore all key codes where the shifted
|
|
|
|
|
|
* symbol is the same as the unshifted symbol.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
for (xkb_keycode_t code = xkb_keymap_min_keycode(seat->kbd.xkb_keymap);
|
|
|
|
|
|
code <= xkb_keymap_max_keycode(seat->kbd.xkb_keymap);
|
|
|
|
|
|
code++)
|
|
|
|
|
|
{
|
|
|
|
|
|
xkb_layout_index_t layout_idx =
|
|
|
|
|
|
xkb_state_key_get_layout(seat->kbd.xkb_state, code);
|
|
|
|
|
|
|
|
|
|
|
|
/* Get all unshifted symbols for this key */
|
|
|
|
|
|
const xkb_keysym_t *base_syms = NULL;
|
|
|
|
|
|
size_t base_count = xkb_keymap_key_get_syms_by_level(
|
|
|
|
|
|
seat->kbd.xkb_keymap, code, layout_idx, 0, &base_syms);
|
|
|
|
|
|
|
|
|
|
|
|
if (base_count == 0 || sym == base_syms[0]) {
|
|
|
|
|
|
/* No unshifted symbols, or unshifted symbol is same as ‘sym’ */
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Name of the unshifted symbol, for logging */
|
|
|
|
|
|
char base_name[100];
|
|
|
|
|
|
xkb_keysym_get_name(base_syms[0], base_name, sizeof(base_name));
|
|
|
|
|
|
|
|
|
|
|
|
/* Iterate all shift levels */
|
|
|
|
|
|
for (xkb_level_index_t level_idx = 1;
|
|
|
|
|
|
level_idx < xkb_keymap_num_levels_for_key(
|
|
|
|
|
|
seat->kbd.xkb_keymap, code, layout_idx);
|
|
|
|
|
|
level_idx++) {
|
|
|
|
|
|
|
|
|
|
|
|
/* Get all symbols for current shift level */
|
|
|
|
|
|
const xkb_keysym_t *shifted_syms = NULL;
|
|
|
|
|
|
size_t shifted_count = xkb_keymap_key_get_syms_by_level(
|
|
|
|
|
|
seat->kbd.xkb_keymap, code,
|
|
|
|
|
|
layout_idx, level_idx, &shifted_syms);
|
|
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < shifted_count; i++) {
|
|
|
|
|
|
if (shifted_syms[i] != sym)
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
/* Get modifier sets that produces the current shift level */
|
|
|
|
|
|
xkb_mod_mask_t mod_masks[16];
|
|
|
|
|
|
size_t mod_mask_count = xkb_keymap_key_get_mods_for_level(
|
|
|
|
|
|
seat->kbd.xkb_keymap, code, layout_idx, level_idx,
|
|
|
|
|
|
mod_masks, ALEN(mod_masks));
|
|
|
|
|
|
|
|
|
|
|
|
/* Check if key combo’s modifier set intersects */
|
|
|
|
|
|
for (size_t j = 0; j < mod_mask_count; j++) {
|
input: must have all required modifiers to un-shift a symbol
When detecting, and repairing, “broken” key bindings (where the key
binding itself explicitly lists a modifier that is consumed by the
final symbol - e.g “Shift+W”), don’t just look for an intersection
between the set of modifiers needed to produce the final symbol, and
the modifiers listed in the key combo. Instead, check if the key combo
has *all* the required modifiers.
Example: Shift+AltGr+w produces Page_Down. I.e. Page_Down is the
_shifted_ symbol, ‘w’ is the un-shifted symbol, and Shift+AltGr are
the modifiers required to shift ‘w’ to Page_Down.
If we have the key combo Shift+Page_Down, foot would, correctly,
determine that Page_Down is a shifted symbol. It would find the
Shift+AltGr modifier set, and since the intersection of “Shift+AltGr”
and “Shift” (from our key combo) is non-empty, foot
would (incorrectly) determine that we can, and should, replace
Page_Down with its un-shifted symbol ‘w’.
This is completely wrong, since Shift+w does _not_ produce Page_Down.
Closes #407
2021-03-21 11:55:03 +01:00
|
|
|
|
if ((mod_masks[j] & mods) != mod_masks[j])
|
input: repair key combos containing both explicit modifier and shifted symbol
If a user-provided key combo contains both an explicit modifier, and
the shifted symbol it produces, replace the symbol with its un-shifted
version.
Example: Control+Shift+U contains both ‘Shift’, and ‘U’, where ‘Shift’
is the modifier that shifts ‘u’ to ‘U’.
Such modifiers are “consumed”, i.e. filtered out, when matching key
combos. As such, Control+Shift+U will never trigger since we’ll never
be able to match the consumed modifier ‘Shift’.
The above combo can be written in two ways:
- Control+U
- Control+Shift+u
To be able to detect that Control+Shift+U is an invalid combination,
we need to check all *shifted* symbols for all key *codes*.
Once we’ve detected a shifted symbol (and where it differs from the
un-shifted symbol), we loop all modifier sets that produce the shift
level where our shifted symbol is. For each such set, check if it
intersects with the user-provided key combo’s modifier set.
If there is an intersection, it means the user provided a key combo
containing a modifier and symbol, such that the modifier is consumed
when the symbol is produced.
In this case, we replace the symbol with its un-shifted version.
2021-03-02 17:50:06 +01:00
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
char combo[64] = {0};
|
|
|
|
|
|
|
|
|
|
|
|
for (int k = 0; k < sizeof(xkb_mod_mask_t) * 8; k++) {
|
|
|
|
|
|
if (!(mods & (1u << k)))
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
const char *mod_name = xkb_keymap_mod_get_name(
|
|
|
|
|
|
seat->kbd.xkb_keymap, k);
|
|
|
|
|
|
strcat(combo, mod_name);
|
|
|
|
|
|
strcat(combo, "+");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
size_t len = strlen(combo);
|
|
|
|
|
|
xkb_keysym_get_name(
|
|
|
|
|
|
sym, &combo[len], sizeof(combo) - len);
|
|
|
|
|
|
|
|
|
|
|
|
LOG_WARN(
|
|
|
|
|
|
"%s: combo with both explicit modifier and shifted symbol "
|
|
|
|
|
|
"(level=%d, mod-mask=0x%08x), "
|
|
|
|
|
|
"replacing with %s",
|
|
|
|
|
|
combo, level_idx, mod_masks[j], base_name);
|
|
|
|
|
|
|
|
|
|
|
|
/* Replace with unshifted symbol */
|
|
|
|
|
|
return base_syms[0];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return sym;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
static void
|
input: repair key combos containing both explicit modifier and shifted symbol
If a user-provided key combo contains both an explicit modifier, and
the shifted symbol it produces, replace the symbol with its un-shifted
version.
Example: Control+Shift+U contains both ‘Shift’, and ‘U’, where ‘Shift’
is the modifier that shifts ‘u’ to ‘U’.
Such modifiers are “consumed”, i.e. filtered out, when matching key
combos. As such, Control+Shift+U will never trigger since we’ll never
be able to match the consumed modifier ‘Shift’.
The above combo can be written in two ways:
- Control+U
- Control+Shift+u
To be able to detect that Control+Shift+U is an invalid combination,
we need to check all *shifted* symbols for all key *codes*.
Once we’ve detected a shifted symbol (and where it differs from the
un-shifted symbol), we loop all modifier sets that produce the shift
level where our shifted symbol is. For each such set, check if it
intersects with the user-provided key combo’s modifier set.
If there is an intersection, it means the user provided a key combo
containing a modifier and symbol, such that the modifier is consumed
when the symbol is produced.
In this case, we replace the symbol with its un-shifted version.
2021-03-02 17:50:06 +01:00
|
|
|
|
convert_key_binding(const struct seat *seat,
|
2021-02-07 14:46:29 +01:00
|
|
|
|
const struct config_key_binding *conf_binding,
|
|
|
|
|
|
key_binding_list_t *bindings)
|
2020-08-09 22:40:53 +02:00
|
|
|
|
{
|
input: repair key combos containing both explicit modifier and shifted symbol
If a user-provided key combo contains both an explicit modifier, and
the shifted symbol it produces, replace the symbol with its un-shifted
version.
Example: Control+Shift+U contains both ‘Shift’, and ‘U’, where ‘Shift’
is the modifier that shifts ‘u’ to ‘U’.
Such modifiers are “consumed”, i.e. filtered out, when matching key
combos. As such, Control+Shift+U will never trigger since we’ll never
be able to match the consumed modifier ‘Shift’.
The above combo can be written in two ways:
- Control+U
- Control+Shift+u
To be able to detect that Control+Shift+U is an invalid combination,
we need to check all *shifted* symbols for all key *codes*.
Once we’ve detected a shifted symbol (and where it differs from the
un-shifted symbol), we loop all modifier sets that produce the shift
level where our shifted symbol is. For each such set, check if it
intersects with the user-provided key combo’s modifier set.
If there is an intersection, it means the user provided a key combo
containing a modifier and symbol, such that the modifier is consumed
when the symbol is produced.
In this case, we replace the symbol with its un-shifted version.
2021-03-02 17:50:06 +01:00
|
|
|
|
xkb_mod_mask_t mods = conf_modifiers_to_mask(seat, &conf_binding->modifiers);
|
|
|
|
|
|
xkb_keysym_t sym = maybe_repair_key_combo(seat, conf_binding->sym, mods);
|
|
|
|
|
|
|
2021-02-07 14:46:29 +01:00
|
|
|
|
struct key_binding binding = {
|
input: repair key combos containing both explicit modifier and shifted symbol
If a user-provided key combo contains both an explicit modifier, and
the shifted symbol it produces, replace the symbol with its un-shifted
version.
Example: Control+Shift+U contains both ‘Shift’, and ‘U’, where ‘Shift’
is the modifier that shifts ‘u’ to ‘U’.
Such modifiers are “consumed”, i.e. filtered out, when matching key
combos. As such, Control+Shift+U will never trigger since we’ll never
be able to match the consumed modifier ‘Shift’.
The above combo can be written in two ways:
- Control+U
- Control+Shift+u
To be able to detect that Control+Shift+U is an invalid combination,
we need to check all *shifted* symbols for all key *codes*.
Once we’ve detected a shifted symbol (and where it differs from the
un-shifted symbol), we loop all modifier sets that produce the shift
level where our shifted symbol is. For each such set, check if it
intersects with the user-provided key combo’s modifier set.
If there is an intersection, it means the user provided a key combo
containing a modifier and symbol, such that the modifier is consumed
when the symbol is produced.
In this case, we replace the symbol with its un-shifted version.
2021-03-02 17:50:06 +01:00
|
|
|
|
.mods = mods,
|
|
|
|
|
|
.sym = sym,
|
|
|
|
|
|
.key_codes = key_codes_for_xkb_sym(seat->kbd.xkb_keymap, sym),
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
.action = conf_binding->action,
|
2021-06-18 16:18:41 +02:00
|
|
|
|
.pipe_argv = conf_binding->pipe.argv.args,
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
};
|
2021-02-07 14:46:29 +01:00
|
|
|
|
tll_push_back(*bindings, binding);
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
}
|
2020-03-08 15:17:29 +01:00
|
|
|
|
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
static void
|
|
|
|
|
|
convert_key_bindings(const struct config *conf, struct seat *seat)
|
|
|
|
|
|
{
|
2021-06-17 18:15:29 +02:00
|
|
|
|
for (size_t i = 0; i < conf->bindings.key.count; i++) {
|
|
|
|
|
|
const struct config_key_binding *binding = &conf->bindings.key.arr[i];
|
|
|
|
|
|
convert_key_binding(seat, binding, &seat->kbd.bindings.key);
|
|
|
|
|
|
}
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
}
|
2020-03-18 10:48:42 +01:00
|
|
|
|
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
static void
|
|
|
|
|
|
convert_search_bindings(const struct config *conf, struct seat *seat)
|
|
|
|
|
|
{
|
2021-06-17 18:15:29 +02:00
|
|
|
|
for (size_t i = 0; i < conf->bindings.search.count; i++) {
|
|
|
|
|
|
const struct config_key_binding *binding = &conf->bindings.search.arr[i];
|
|
|
|
|
|
convert_key_binding(seat, binding, &seat->kbd.bindings.search);
|
|
|
|
|
|
}
|
2021-01-30 12:43:59 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
|
convert_url_bindings(const struct config *conf, struct seat *seat)
|
|
|
|
|
|
{
|
2021-06-17 18:15:29 +02:00
|
|
|
|
for (size_t i = 0; i < conf->bindings.url.count; i++) {
|
|
|
|
|
|
const struct config_key_binding *binding = &conf->bindings.url.arr[i];
|
|
|
|
|
|
convert_key_binding(seat, binding, &seat->kbd.bindings.url);
|
|
|
|
|
|
}
|
2021-01-30 12:43:59 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
static void
|
|
|
|
|
|
convert_mouse_binding(struct seat *seat,
|
|
|
|
|
|
const struct config_mouse_binding *conf_binding)
|
|
|
|
|
|
{
|
|
|
|
|
|
struct mouse_binding binding = {
|
|
|
|
|
|
.action = conf_binding->action,
|
|
|
|
|
|
.mods = conf_modifiers_to_mask(seat, &conf_binding->modifiers),
|
|
|
|
|
|
.button = conf_binding->button,
|
|
|
|
|
|
.count = conf_binding->count,
|
2021-06-18 16:18:41 +02:00
|
|
|
|
.pipe_argv = conf_binding->pipe.argv.args,
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
};
|
|
|
|
|
|
tll_push_back(seat->mouse.bindings, binding);
|
|
|
|
|
|
}
|
2020-03-08 15:17:29 +01:00
|
|
|
|
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
static void
|
|
|
|
|
|
convert_mouse_bindings(const struct config *conf, struct seat *seat)
|
|
|
|
|
|
{
|
2021-06-17 18:15:29 +02:00
|
|
|
|
for (size_t i = 0; i < conf->bindings.mouse.count; i++) {
|
|
|
|
|
|
const struct config_mouse_binding *binding = &conf->bindings.mouse.arr[i];
|
|
|
|
|
|
if (binding->action == BIND_ACTION_NONE)
|
|
|
|
|
|
continue;
|
|
|
|
|
|
convert_mouse_binding(seat, binding);
|
|
|
|
|
|
}
|
2020-03-08 15:17:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2019-06-19 10:04:47 +02:00
|
|
|
|
static void
|
|
|
|
|
|
keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard,
|
|
|
|
|
|
uint32_t format, int32_t fd, uint32_t size)
|
|
|
|
|
|
{
|
2020-03-14 11:51:47 +01:00
|
|
|
|
LOG_DBG("keyboard_keymap: keyboard=%p (format=%u, size=%u)",
|
2020-08-23 07:50:27 +02:00
|
|
|
|
(void *)wl_keyboard, format, size);
|
2020-03-14 11:51:47 +01:00
|
|
|
|
|
2020-07-08 16:45:26 +02:00
|
|
|
|
struct seat *seat = data;
|
|
|
|
|
|
struct wayland *wayl = seat->wayl;
|
2019-06-19 10:04:47 +02:00
|
|
|
|
|
2020-11-09 19:59:12 +01:00
|
|
|
|
/*
|
|
|
|
|
|
* Free old keymap state
|
|
|
|
|
|
*/
|
2019-06-19 10:04:47 +02:00
|
|
|
|
|
2020-07-08 16:45:26 +02:00
|
|
|
|
if (seat->kbd.xkb_compose_state != NULL) {
|
|
|
|
|
|
xkb_compose_state_unref(seat->kbd.xkb_compose_state);
|
|
|
|
|
|
seat->kbd.xkb_compose_state = NULL;
|
2019-08-12 21:32:53 +02:00
|
|
|
|
}
|
2020-07-08 16:45:26 +02:00
|
|
|
|
if (seat->kbd.xkb_compose_table != NULL) {
|
|
|
|
|
|
xkb_compose_table_unref(seat->kbd.xkb_compose_table);
|
|
|
|
|
|
seat->kbd.xkb_compose_table = NULL;
|
2019-08-12 21:32:53 +02:00
|
|
|
|
}
|
2020-07-08 16:45:26 +02:00
|
|
|
|
if (seat->kbd.xkb_keymap != NULL) {
|
|
|
|
|
|
xkb_keymap_unref(seat->kbd.xkb_keymap);
|
|
|
|
|
|
seat->kbd.xkb_keymap = NULL;
|
2019-08-12 21:32:53 +02:00
|
|
|
|
}
|
2020-07-08 16:45:26 +02:00
|
|
|
|
if (seat->kbd.xkb_state != NULL) {
|
|
|
|
|
|
xkb_state_unref(seat->kbd.xkb_state);
|
|
|
|
|
|
seat->kbd.xkb_state = NULL;
|
2019-08-12 21:32:53 +02:00
|
|
|
|
}
|
2020-07-08 16:45:26 +02:00
|
|
|
|
if (seat->kbd.xkb != NULL) {
|
|
|
|
|
|
xkb_context_unref(seat->kbd.xkb);
|
|
|
|
|
|
seat->kbd.xkb = NULL;
|
2019-08-12 21:32:53 +02:00
|
|
|
|
}
|
2020-03-18 14:29:34 +01:00
|
|
|
|
|
2020-07-08 16:45:26 +02:00
|
|
|
|
tll_foreach(seat->kbd.bindings.key, it)
|
2021-02-07 14:46:29 +01:00
|
|
|
|
tll_free(it->item.key_codes);
|
2020-07-08 16:45:26 +02:00
|
|
|
|
tll_free(seat->kbd.bindings.key);
|
2019-06-19 10:04:47 +02:00
|
|
|
|
|
2020-07-08 16:45:26 +02:00
|
|
|
|
tll_foreach(seat->kbd.bindings.search, it)
|
2021-02-07 14:46:29 +01:00
|
|
|
|
tll_free(it->item.key_codes);
|
2020-07-08 16:45:26 +02:00
|
|
|
|
tll_free(seat->kbd.bindings.search);
|
2020-03-18 14:29:34 +01:00
|
|
|
|
|
2020-08-09 22:40:53 +02:00
|
|
|
|
tll_free(seat->mouse.bindings);
|
|
|
|
|
|
|
2020-11-09 19:59:12 +01:00
|
|
|
|
/* Verify keymap is in a format we understand */
|
|
|
|
|
|
switch ((enum wl_keyboard_keymap_format)format) {
|
|
|
|
|
|
case WL_KEYBOARD_KEYMAP_FORMAT_NO_KEYMAP:
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
case WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1:
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
LOG_WARN("unrecognized keymap format: %u", format);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
char *map_str = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
|
|
|
|
|
|
if (map_str == MAP_FAILED) {
|
|
|
|
|
|
LOG_ERRNO("failed to mmap keyboard keymap");
|
|
|
|
|
|
close(fd);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
while (map_str[size - 1] == '\0')
|
|
|
|
|
|
size--;
|
|
|
|
|
|
|
2020-07-08 16:45:26 +02:00
|
|
|
|
seat->kbd.xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
2019-06-19 10:04:47 +02:00
|
|
|
|
|
2020-10-20 21:01:33 +02:00
|
|
|
|
if (seat->kbd.xkb != NULL) {
|
|
|
|
|
|
seat->kbd.xkb_keymap = xkb_keymap_new_from_buffer(
|
|
|
|
|
|
seat->kbd.xkb, map_str, size, XKB_KEYMAP_FORMAT_TEXT_V1,
|
|
|
|
|
|
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
|
|
|
|
|
|
|
|
|
|
|
/* Compose (dead keys) */
|
|
|
|
|
|
seat->kbd.xkb_compose_table = xkb_compose_table_new_from_locale(
|
|
|
|
|
|
seat->kbd.xkb, setlocale(LC_CTYPE, NULL), XKB_COMPOSE_COMPILE_NO_FLAGS);
|
2019-06-19 10:04:47 +02:00
|
|
|
|
|
2020-10-20 21:01:33 +02:00
|
|
|
|
if (seat->kbd.xkb_compose_table == NULL) {
|
|
|
|
|
|
LOG_WARN("failed to instantiate compose table; dead keys will not work");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
seat->kbd.xkb_compose_state = xkb_compose_state_new(
|
|
|
|
|
|
seat->kbd.xkb_compose_table, XKB_COMPOSE_STATE_NO_FLAGS);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2019-07-05 15:13:06 +02:00
|
|
|
|
|
2020-10-20 21:01:33 +02:00
|
|
|
|
if (seat->kbd.xkb_keymap != NULL) {
|
|
|
|
|
|
seat->kbd.xkb_state = xkb_state_new(seat->kbd.xkb_keymap);
|
2020-09-15 18:44:54 +02:00
|
|
|
|
|
2020-11-11 18:25:54 +01:00
|
|
|
|
seat->kbd.mod_shift = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, XKB_MOD_NAME_SHIFT);
|
|
|
|
|
|
seat->kbd.mod_alt = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, XKB_MOD_NAME_ALT) ;
|
|
|
|
|
|
seat->kbd.mod_ctrl = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, XKB_MOD_NAME_CTRL);
|
2021-11-21 12:01:16 +01:00
|
|
|
|
seat->kbd.mod_super = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, XKB_MOD_NAME_LOGO);
|
2021-11-20 20:31:59 +01:00
|
|
|
|
seat->kbd.mod_caps = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, XKB_MOD_NAME_CAPS);
|
|
|
|
|
|
seat->kbd.mod_num = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, XKB_MOD_NAME_NUM);
|
2020-10-19 18:30:19 +02:00
|
|
|
|
|
2021-11-21 11:53:22 +01:00
|
|
|
|
seat->kbd.bind_significant = 0;
|
|
|
|
|
|
if (seat->kbd.mod_shift != XKB_MOD_INVALID)
|
|
|
|
|
|
seat->kbd.bind_significant |= 1 << seat->kbd.mod_shift;
|
|
|
|
|
|
if (seat->kbd.mod_alt != XKB_MOD_INVALID)
|
|
|
|
|
|
seat->kbd.bind_significant |= 1 << seat->kbd.mod_alt;
|
|
|
|
|
|
if (seat->kbd.mod_ctrl != XKB_MOD_INVALID)
|
|
|
|
|
|
seat->kbd.bind_significant |= 1 << seat->kbd.mod_ctrl;
|
2021-11-21 12:01:16 +01:00
|
|
|
|
if (seat->kbd.mod_super != XKB_MOD_INVALID)
|
|
|
|
|
|
seat->kbd.bind_significant |= 1 << seat->kbd.mod_super;
|
2021-11-21 11:53:22 +01:00
|
|
|
|
|
|
|
|
|
|
seat->kbd.kitty_significant = seat->kbd.bind_significant;
|
|
|
|
|
|
if (seat->kbd.mod_caps != XKB_MOD_INVALID)
|
|
|
|
|
|
seat->kbd.kitty_significant |= 1 << seat->kbd.mod_caps;
|
|
|
|
|
|
if (seat->kbd.mod_num != XKB_MOD_INVALID)
|
|
|
|
|
|
seat->kbd.kitty_significant |= 1 << seat->kbd.mod_num;
|
2021-11-21 11:27:31 +01:00
|
|
|
|
|
2020-10-20 21:01:33 +02:00
|
|
|
|
seat->kbd.key_arrow_up = xkb_keymap_key_by_name(seat->kbd.xkb_keymap, "UP");
|
|
|
|
|
|
seat->kbd.key_arrow_down = xkb_keymap_key_by_name(seat->kbd.xkb_keymap, "DOWN");
|
2020-10-19 18:30:19 +02:00
|
|
|
|
}
|
2019-07-09 10:00:54 +02:00
|
|
|
|
|
2019-06-19 10:04:47 +02:00
|
|
|
|
munmap(map_str, size);
|
|
|
|
|
|
close(fd);
|
2020-03-08 12:08:46 +01:00
|
|
|
|
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
convert_key_bindings(wayl->conf, seat);
|
|
|
|
|
|
convert_search_bindings(wayl->conf, seat);
|
2021-01-30 12:43:59 +01:00
|
|
|
|
convert_url_bindings(wayl->conf, seat);
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
convert_mouse_bindings(wayl->conf, seat);
|
2019-06-19 10:04:47 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-01-14 19:28:37 +01:00
|
|
|
|
static void
|
2019-06-19 10:04:47 +02:00
|
|
|
|
keyboard_enter(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
|
|
|
|
|
|
struct wl_surface *surface, struct wl_array *keys)
|
|
|
|
|
|
{
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(surface != NULL);
|
2019-11-28 19:46:27 +01:00
|
|
|
|
|
2020-07-08 16:45:26 +02:00
|
|
|
|
struct seat *seat = data;
|
2020-02-29 12:13:58 +01:00
|
|
|
|
struct wl_window *win = wl_surface_get_user_data(surface);
|
|
|
|
|
|
struct terminal *term = win->term;
|
|
|
|
|
|
|
2020-07-11 11:13:45 +02:00
|
|
|
|
LOG_DBG("%s: keyboard_enter: keyboard=%p, serial=%u, surface=%p",
|
2020-08-23 07:50:27 +02:00
|
|
|
|
seat->name, (void *)wl_keyboard, serial, (void *)surface);
|
2020-03-14 11:51:47 +01:00
|
|
|
|
|
2020-07-14 08:52:11 +02:00
|
|
|
|
if (seat->kbd.xkb == NULL)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
2020-07-09 09:16:54 +02:00
|
|
|
|
term_kbd_focus_in(term);
|
2020-07-08 16:45:26 +02:00
|
|
|
|
seat->kbd_focus = term;
|
2020-07-09 11:20:46 +02:00
|
|
|
|
seat->kbd.serial = serial;
|
2019-06-19 10:04:47 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2019-08-05 19:33:01 +02:00
|
|
|
|
static bool
|
2020-07-08 16:45:26 +02:00
|
|
|
|
start_repeater(struct seat *seat, uint32_t key)
|
2019-06-19 10:04:47 +02:00
|
|
|
|
{
|
2020-07-08 16:45:26 +02:00
|
|
|
|
if (seat->kbd.repeat.dont_re_repeat)
|
2019-08-05 19:33:01 +02:00
|
|
|
|
return true;
|
|
|
|
|
|
|
2021-01-16 12:27:58 +01:00
|
|
|
|
if (seat->kbd.repeat.rate == 0)
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
2019-08-05 19:33:01 +02:00
|
|
|
|
struct itimerspec t = {
|
2020-07-08 16:45:26 +02:00
|
|
|
|
.it_value = {.tv_sec = 0, .tv_nsec = seat->kbd.repeat.delay * 1000000},
|
|
|
|
|
|
.it_interval = {.tv_sec = 0, .tv_nsec = 1000000000 / seat->kbd.repeat.rate},
|
2019-08-05 19:33:01 +02:00
|
|
|
|
};
|
2019-06-21 18:40:53 +02:00
|
|
|
|
|
2019-08-05 19:33:01 +02:00
|
|
|
|
if (t.it_value.tv_nsec >= 1000000000) {
|
|
|
|
|
|
t.it_value.tv_sec += t.it_value.tv_nsec / 1000000000;
|
|
|
|
|
|
t.it_value.tv_nsec %= 1000000000;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (t.it_interval.tv_nsec >= 1000000000) {
|
|
|
|
|
|
t.it_interval.tv_sec += t.it_interval.tv_nsec / 1000000000;
|
|
|
|
|
|
t.it_interval.tv_nsec %= 1000000000;
|
|
|
|
|
|
}
|
2020-07-08 16:45:26 +02:00
|
|
|
|
if (timerfd_settime(seat->kbd.repeat.fd, 0, &t, NULL) < 0) {
|
|
|
|
|
|
LOG_ERRNO("%s: failed to arm keyboard repeat timer", seat->name);
|
2019-08-05 19:33:01 +02:00
|
|
|
|
return false;
|
2019-06-21 18:40:53 +02:00
|
|
|
|
}
|
2019-08-02 18:19:07 +02:00
|
|
|
|
|
2020-07-08 16:45:26 +02:00
|
|
|
|
seat->kbd.repeat.key = key;
|
2019-08-05 19:33:01 +02:00
|
|
|
|
return true;
|
2019-08-02 18:19:07 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2019-08-05 19:33:01 +02:00
|
|
|
|
static bool
|
2020-07-08 16:45:26 +02:00
|
|
|
|
stop_repeater(struct seat *seat, uint32_t key)
|
2019-08-02 18:19:07 +02:00
|
|
|
|
{
|
2020-07-08 16:45:26 +02:00
|
|
|
|
if (key != -1 && key != seat->kbd.repeat.key)
|
2019-08-05 19:33:01 +02:00
|
|
|
|
return true;
|
|
|
|
|
|
|
2020-08-23 07:42:20 +02:00
|
|
|
|
if (timerfd_settime(seat->kbd.repeat.fd, 0, &(struct itimerspec){{0}}, NULL) < 0) {
|
2020-07-08 16:45:26 +02:00
|
|
|
|
LOG_ERRNO("%s: failed to disarm keyboard repeat timer", seat->name);
|
2019-08-05 19:33:01 +02:00
|
|
|
|
return false;
|
2019-08-02 18:19:07 +02:00
|
|
|
|
}
|
2019-08-05 19:33:01 +02:00
|
|
|
|
|
|
|
|
|
|
return true;
|
2019-08-02 18:19:07 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2019-08-05 19:33:01 +02:00
|
|
|
|
keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
|
|
|
|
|
|
struct wl_surface *surface)
|
2019-08-02 18:19:07 +02:00
|
|
|
|
{
|
2020-07-08 16:45:26 +02:00
|
|
|
|
struct seat *seat = data;
|
2019-08-05 19:33:01 +02:00
|
|
|
|
|
2020-03-14 11:51:47 +01:00
|
|
|
|
LOG_DBG("keyboard_leave: keyboard=%p, serial=%u, surface=%p",
|
2020-08-23 07:50:27 +02:00
|
|
|
|
(void *)wl_keyboard, serial, (void *)surface);
|
2020-03-14 11:51:47 +01:00
|
|
|
|
|
2020-07-14 08:52:11 +02:00
|
|
|
|
if (seat->kbd.xkb == NULL)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(
|
2020-07-08 16:45:26 +02:00
|
|
|
|
seat->kbd_focus == NULL ||
|
2019-11-28 19:24:16 +01:00
|
|
|
|
surface == NULL || /* Seen on Sway 1.2 */
|
2020-07-08 16:45:26 +02:00
|
|
|
|
((const struct wl_window *)wl_surface_get_user_data(surface))->term == seat->kbd_focus
|
2020-02-29 12:13:58 +01:00
|
|
|
|
);
|
2019-11-21 18:17:02 +01:00
|
|
|
|
|
2020-07-08 16:45:26 +02:00
|
|
|
|
struct terminal *old_focused = seat->kbd_focus;
|
|
|
|
|
|
seat->kbd_focus = NULL;
|
2019-11-29 22:11:41 +01:00
|
|
|
|
|
2020-07-08 16:45:26 +02:00
|
|
|
|
stop_repeater(seat, -1);
|
2020-07-09 08:46:25 +02:00
|
|
|
|
seat->kbd.shift = false;
|
|
|
|
|
|
seat->kbd.alt = false;
|
|
|
|
|
|
seat->kbd.ctrl = false;
|
2021-11-21 12:01:16 +01:00
|
|
|
|
seat->kbd.super = false;
|
2020-10-19 18:30:19 +02:00
|
|
|
|
if (seat->kbd.xkb_compose_state != NULL)
|
|
|
|
|
|
xkb_compose_state_reset(seat->kbd.xkb_compose_state);
|
2020-01-13 19:58:57 +01:00
|
|
|
|
|
2020-07-31 17:09:06 +02:00
|
|
|
|
if (old_focused != NULL) {
|
|
|
|
|
|
seat->pointer.hidden = false;
|
|
|
|
|
|
term_xcursor_update_for_seat(old_focused, seat);
|
2020-01-02 19:30:34 +01:00
|
|
|
|
term_kbd_focus_out(old_focused);
|
2020-07-31 17:09:06 +02:00
|
|
|
|
} else {
|
2019-11-24 00:06:50 +01:00
|
|
|
|
/*
|
|
|
|
|
|
* Sway bug - under certain conditions we get a
|
|
|
|
|
|
* keyboard_leave() (and keyboard_key()) without first having
|
|
|
|
|
|
* received a keyboard_enter()
|
|
|
|
|
|
*/
|
2019-11-28 19:47:00 +01:00
|
|
|
|
LOG_WARN(
|
|
|
|
|
|
"compositor sent keyboard_leave event without a keyboard_enter "
|
2020-08-23 07:50:27 +02:00
|
|
|
|
"event: surface=%p", (void *)surface);
|
2019-11-24 00:06:50 +01:00
|
|
|
|
}
|
2019-06-19 10:04:47 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-01-11 18:18:31 +01:00
|
|
|
|
static const struct key_data *
|
2020-01-11 18:40:27 +01:00
|
|
|
|
keymap_data_for_sym(xkb_keysym_t sym, size_t *count)
|
2020-01-11 18:18:31 +01:00
|
|
|
|
{
|
|
|
|
|
|
switch (sym) {
|
|
|
|
|
|
case XKB_KEY_Escape: *count = ALEN(key_escape); return key_escape;
|
|
|
|
|
|
case XKB_KEY_Return: *count = ALEN(key_return); return key_return;
|
keymap: handle shift+tab combos correctly, after consuming modifiers
Shift+Tab is an odd bird in XKB; it produces a shifted variant of tab,
ISO_Left_Tab, with Shift being a consumed modifier.
From a terminal perspective, Tab and ISO_Left_Tab are the same key,
with Shift+Tab generating ‘CSI Z’, and all other combos generating a
‘CSI 27;mods;9~’ escape.
Before, when we didn’t filter out consumed modifiers, we could simply
re-use the keymap for Tab when translating ISO_Left_Tab.
This is no longer possible, since shift has been filtered out. As a
result, Shift+Tab no longer generated ‘CSI Z’, but ‘\t’. I.e as if
shift wasn’t being pressed.
This patch adds a separate keymap table for ISO_Left_Tab. It’s MOD_ANY
entry corresponds to Shift+Tab, and emits ‘CSI Z’. All its other
entries *exclude* shift (since it has been consumed), *but*, the
encoded modifier (in the escape sequences) *include* shift.
2021-02-28 11:50:05 +01:00
|
|
|
|
case XKB_KEY_ISO_Left_Tab: *count = ALEN(key_iso_left_tab); return key_iso_left_tab;
|
|
|
|
|
|
case XKB_KEY_Tab: *count = ALEN(key_tab); return key_tab;
|
2020-01-11 18:18:31 +01:00
|
|
|
|
case XKB_KEY_BackSpace: *count = ALEN(key_backspace); return key_backspace;
|
|
|
|
|
|
case XKB_KEY_Up: *count = ALEN(key_up); return key_up;
|
|
|
|
|
|
case XKB_KEY_Down: *count = ALEN(key_down); return key_down;
|
|
|
|
|
|
case XKB_KEY_Right: *count = ALEN(key_right); return key_right;
|
|
|
|
|
|
case XKB_KEY_Left: *count = ALEN(key_left); return key_left;
|
|
|
|
|
|
case XKB_KEY_Home: *count = ALEN(key_home); return key_home;
|
|
|
|
|
|
case XKB_KEY_End: *count = ALEN(key_end); return key_end;
|
|
|
|
|
|
case XKB_KEY_Insert: *count = ALEN(key_insert); return key_insert;
|
|
|
|
|
|
case XKB_KEY_Delete: *count = ALEN(key_delete); return key_delete;
|
|
|
|
|
|
case XKB_KEY_Page_Up: *count = ALEN(key_pageup); return key_pageup;
|
|
|
|
|
|
case XKB_KEY_Page_Down: *count = ALEN(key_pagedown); return key_pagedown;
|
|
|
|
|
|
case XKB_KEY_F1: *count = ALEN(key_f1); return key_f1;
|
|
|
|
|
|
case XKB_KEY_F2: *count = ALEN(key_f2); return key_f2;
|
|
|
|
|
|
case XKB_KEY_F3: *count = ALEN(key_f3); return key_f3;
|
|
|
|
|
|
case XKB_KEY_F4: *count = ALEN(key_f4); return key_f4;
|
|
|
|
|
|
case XKB_KEY_F5: *count = ALEN(key_f5); return key_f5;
|
|
|
|
|
|
case XKB_KEY_F6: *count = ALEN(key_f6); return key_f6;
|
|
|
|
|
|
case XKB_KEY_F7: *count = ALEN(key_f7); return key_f7;
|
|
|
|
|
|
case XKB_KEY_F8: *count = ALEN(key_f8); return key_f8;
|
|
|
|
|
|
case XKB_KEY_F9: *count = ALEN(key_f9); return key_f9;
|
|
|
|
|
|
case XKB_KEY_F10: *count = ALEN(key_f10); return key_f10;
|
|
|
|
|
|
case XKB_KEY_F11: *count = ALEN(key_f11); return key_f11;
|
|
|
|
|
|
case XKB_KEY_F12: *count = ALEN(key_f12); return key_f12;
|
|
|
|
|
|
case XKB_KEY_F13: *count = ALEN(key_f13); return key_f13;
|
|
|
|
|
|
case XKB_KEY_F14: *count = ALEN(key_f14); return key_f14;
|
|
|
|
|
|
case XKB_KEY_F15: *count = ALEN(key_f15); return key_f15;
|
|
|
|
|
|
case XKB_KEY_F16: *count = ALEN(key_f16); return key_f16;
|
|
|
|
|
|
case XKB_KEY_F17: *count = ALEN(key_f17); return key_f17;
|
|
|
|
|
|
case XKB_KEY_F18: *count = ALEN(key_f18); return key_f18;
|
|
|
|
|
|
case XKB_KEY_F19: *count = ALEN(key_f19); return key_f19;
|
|
|
|
|
|
case XKB_KEY_F20: *count = ALEN(key_f20); return key_f20;
|
|
|
|
|
|
case XKB_KEY_F21: *count = ALEN(key_f21); return key_f21;
|
|
|
|
|
|
case XKB_KEY_F22: *count = ALEN(key_f22); return key_f22;
|
|
|
|
|
|
case XKB_KEY_F23: *count = ALEN(key_f23); return key_f23;
|
|
|
|
|
|
case XKB_KEY_F24: *count = ALEN(key_f24); return key_f24;
|
|
|
|
|
|
case XKB_KEY_F25: *count = ALEN(key_f25); return key_f25;
|
|
|
|
|
|
case XKB_KEY_F26: *count = ALEN(key_f26); return key_f26;
|
|
|
|
|
|
case XKB_KEY_F27: *count = ALEN(key_f27); return key_f27;
|
|
|
|
|
|
case XKB_KEY_F28: *count = ALEN(key_f28); return key_f28;
|
|
|
|
|
|
case XKB_KEY_F29: *count = ALEN(key_f29); return key_f29;
|
|
|
|
|
|
case XKB_KEY_F30: *count = ALEN(key_f30); return key_f30;
|
|
|
|
|
|
case XKB_KEY_F31: *count = ALEN(key_f31); return key_f31;
|
|
|
|
|
|
case XKB_KEY_F32: *count = ALEN(key_f32); return key_f32;
|
|
|
|
|
|
case XKB_KEY_F33: *count = ALEN(key_f33); return key_f33;
|
|
|
|
|
|
case XKB_KEY_F34: *count = ALEN(key_f34); return key_f34;
|
|
|
|
|
|
case XKB_KEY_F35: *count = ALEN(key_f35); return key_f35;
|
|
|
|
|
|
case XKB_KEY_KP_Up: *count = ALEN(key_kp_up); return key_kp_up;
|
|
|
|
|
|
case XKB_KEY_KP_Down: *count = ALEN(key_kp_down); return key_kp_down;
|
|
|
|
|
|
case XKB_KEY_KP_Right: *count = ALEN(key_kp_right); return key_kp_right;
|
|
|
|
|
|
case XKB_KEY_KP_Left: *count = ALEN(key_kp_left); return key_kp_left;
|
|
|
|
|
|
case XKB_KEY_KP_Begin: *count = ALEN(key_kp_begin); return key_kp_begin;
|
|
|
|
|
|
case XKB_KEY_KP_Home: *count = ALEN(key_kp_home); return key_kp_home;
|
|
|
|
|
|
case XKB_KEY_KP_End: *count = ALEN(key_kp_end); return key_kp_end;
|
|
|
|
|
|
case XKB_KEY_KP_Insert: *count = ALEN(key_kp_insert); return key_kp_insert;
|
|
|
|
|
|
case XKB_KEY_KP_Delete: *count = ALEN(key_kp_delete); return key_kp_delete;
|
|
|
|
|
|
case XKB_KEY_KP_Page_Up: *count = ALEN(key_kp_pageup); return key_kp_pageup;
|
|
|
|
|
|
case XKB_KEY_KP_Page_Down: *count = ALEN(key_kp_pagedown); return key_kp_pagedown;
|
|
|
|
|
|
case XKB_KEY_KP_Enter: *count = ALEN(key_kp_enter); return key_kp_enter;
|
|
|
|
|
|
case XKB_KEY_KP_Divide: *count = ALEN(key_kp_divide); return key_kp_divide;
|
|
|
|
|
|
case XKB_KEY_KP_Multiply: *count = ALEN(key_kp_multiply); return key_kp_multiply;
|
|
|
|
|
|
case XKB_KEY_KP_Subtract: *count = ALEN(key_kp_subtract); return key_kp_subtract;
|
|
|
|
|
|
case XKB_KEY_KP_Add: *count = ALEN(key_kp_add); return key_kp_add;
|
|
|
|
|
|
case XKB_KEY_KP_Separator: *count = ALEN(key_kp_separator); return key_kp_separator;
|
2020-06-27 20:03:41 +02:00
|
|
|
|
case XKB_KEY_KP_Decimal: *count = ALEN(key_kp_decimal); return key_kp_decimal;
|
2020-01-11 18:18:31 +01:00
|
|
|
|
case XKB_KEY_KP_0: *count = ALEN(key_kp_0); return key_kp_0;
|
|
|
|
|
|
case XKB_KEY_KP_1: *count = ALEN(key_kp_1); return key_kp_1;
|
|
|
|
|
|
case XKB_KEY_KP_2: *count = ALEN(key_kp_2); return key_kp_2;
|
|
|
|
|
|
case XKB_KEY_KP_3: *count = ALEN(key_kp_3); return key_kp_3;
|
|
|
|
|
|
case XKB_KEY_KP_4: *count = ALEN(key_kp_4); return key_kp_4;
|
|
|
|
|
|
case XKB_KEY_KP_5: *count = ALEN(key_kp_5); return key_kp_5;
|
|
|
|
|
|
case XKB_KEY_KP_6: *count = ALEN(key_kp_6); return key_kp_6;
|
|
|
|
|
|
case XKB_KEY_KP_7: *count = ALEN(key_kp_7); return key_kp_7;
|
|
|
|
|
|
case XKB_KEY_KP_8: *count = ALEN(key_kp_8); return key_kp_8;
|
|
|
|
|
|
case XKB_KEY_KP_9: *count = ALEN(key_kp_9); return key_kp_9;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
|
}
|
2020-01-11 17:55:45 +01:00
|
|
|
|
|
2020-01-11 18:40:27 +01:00
|
|
|
|
static const struct key_data *
|
2021-05-25 17:57:39 +02:00
|
|
|
|
keymap_lookup(struct terminal *term, xkb_keysym_t sym, enum modifier mods)
|
2020-01-11 17:55:45 +01:00
|
|
|
|
{
|
2020-01-11 18:18:31 +01:00
|
|
|
|
size_t count;
|
2020-01-11 18:40:27 +01:00
|
|
|
|
const struct key_data *info = keymap_data_for_sym(sym, &count);
|
2020-01-11 17:55:45 +01:00
|
|
|
|
|
2020-01-11 18:18:31 +01:00
|
|
|
|
if (info == NULL)
|
2020-01-11 18:40:27 +01:00
|
|
|
|
return NULL;
|
2020-01-11 17:55:45 +01:00
|
|
|
|
|
2020-11-11 18:28:37 +01:00
|
|
|
|
const enum cursor_keys cursor_keys_mode = term->cursor_keys_mode;
|
|
|
|
|
|
const enum keypad_keys keypad_keys_mode
|
2020-12-01 18:27:56 +01:00
|
|
|
|
= term->num_lock_modifier ? KEYPAD_NUMERICAL : term->keypad_keys_mode;
|
2020-11-11 18:28:37 +01:00
|
|
|
|
|
2020-12-23 18:13:40 +01:00
|
|
|
|
LOG_DBG("keypad mode: %d", keypad_keys_mode);
|
2020-12-01 19:31:49 +01:00
|
|
|
|
|
2020-01-11 18:18:31 +01:00
|
|
|
|
for (size_t j = 0; j < count; j++) {
|
2021-11-11 17:43:39 +01:00
|
|
|
|
enum modifier modifiers = info[j].modifiers;
|
|
|
|
|
|
|
|
|
|
|
|
if (modifiers & MOD_MODIFY_OTHER_KEYS_STATE1) {
|
|
|
|
|
|
if (term->modify_other_keys_2)
|
|
|
|
|
|
continue;
|
|
|
|
|
|
modifiers &= ~MOD_MODIFY_OTHER_KEYS_STATE1;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (modifiers & MOD_MODIFY_OTHER_KEYS_STATE2) {
|
|
|
|
|
|
if (!term->modify_other_keys_2)
|
|
|
|
|
|
continue;
|
|
|
|
|
|
modifiers &= ~MOD_MODIFY_OTHER_KEYS_STATE2;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (modifiers != MOD_ANY && modifiers != mods)
|
2020-01-11 18:18:31 +01:00
|
|
|
|
continue;
|
2020-01-11 17:55:45 +01:00
|
|
|
|
|
2020-01-11 18:18:31 +01:00
|
|
|
|
if (info[j].cursor_keys_mode != CURSOR_KEYS_DONTCARE &&
|
2020-11-11 18:28:37 +01:00
|
|
|
|
info[j].cursor_keys_mode != cursor_keys_mode)
|
2020-01-11 18:18:31 +01:00
|
|
|
|
continue;
|
2020-01-11 17:55:45 +01:00
|
|
|
|
|
2020-01-11 18:18:31 +01:00
|
|
|
|
if (info[j].keypad_keys_mode != KEYPAD_DONTCARE &&
|
2020-11-11 18:28:37 +01:00
|
|
|
|
info[j].keypad_keys_mode != keypad_keys_mode)
|
2020-01-11 18:18:31 +01:00
|
|
|
|
continue;
|
2020-01-11 17:55:45 +01:00
|
|
|
|
|
2020-01-11 18:40:27 +01:00
|
|
|
|
return &info[j];
|
2020-01-11 17:55:45 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-01-11 18:40:27 +01:00
|
|
|
|
return NULL;
|
2020-01-11 17:55:45 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-05-25 18:38:16 +01:00
|
|
|
|
UNITTEST
|
2021-05-25 17:57:53 +02:00
|
|
|
|
{
|
|
|
|
|
|
struct terminal term = {
|
|
|
|
|
|
.num_lock_modifier = false,
|
|
|
|
|
|
.keypad_keys_mode = KEYPAD_NUMERICAL,
|
|
|
|
|
|
.cursor_keys_mode = CURSOR_KEYS_NORMAL,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const struct key_data *info = keymap_lookup(&term, XKB_KEY_ISO_Left_Tab, MOD_SHIFT | MOD_CTRL);
|
2021-11-11 17:43:39 +01:00
|
|
|
|
xassert(info != NULL);
|
2021-05-25 17:57:53 +02:00
|
|
|
|
xassert(strcmp(info->seq, "\033[27;6;9~") == 0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-11 17:43:39 +01:00
|
|
|
|
UNITTEST
|
|
|
|
|
|
{
|
|
|
|
|
|
struct terminal term = {
|
|
|
|
|
|
.modify_other_keys_2 = false,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const struct key_data *info = keymap_lookup(&term, XKB_KEY_Return, MOD_ALT);
|
|
|
|
|
|
xassert(info != NULL);
|
|
|
|
|
|
xassert(strcmp(info->seq, "\033\r") == 0);
|
|
|
|
|
|
|
|
|
|
|
|
term.modify_other_keys_2 = true;
|
|
|
|
|
|
info = keymap_lookup(&term, XKB_KEY_Return, MOD_ALT);
|
|
|
|
|
|
xassert(info != NULL);
|
|
|
|
|
|
xassert(strcmp(info->seq, "\033[27;3;13~") == 0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-20 20:31:59 +01:00
|
|
|
|
static void
|
|
|
|
|
|
get_current_modifiers(const struct seat *seat,
|
|
|
|
|
|
xkb_mod_mask_t *effective,
|
|
|
|
|
|
xkb_mod_mask_t *consumed, uint32_t key)
|
|
|
|
|
|
{
|
2021-10-06 20:12:17 +02:00
|
|
|
|
if (effective != NULL) {
|
|
|
|
|
|
*effective = xkb_state_serialize_mods(
|
|
|
|
|
|
seat->kbd.xkb_state, XKB_STATE_MODS_EFFECTIVE);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-21 11:27:31 +01:00
|
|
|
|
if (consumed != NULL)
|
2021-10-06 20:12:17 +02:00
|
|
|
|
*consumed = xkb_state_key_get_consumed_mods(seat->kbd.xkb_state, key);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-20 20:31:59 +01:00
|
|
|
|
struct kbd_ctx {
|
|
|
|
|
|
xkb_layout_index_t layout;
|
|
|
|
|
|
xkb_keycode_t key;
|
|
|
|
|
|
xkb_keysym_t sym;
|
|
|
|
|
|
|
|
|
|
|
|
struct {
|
|
|
|
|
|
const xkb_keysym_t *syms;
|
|
|
|
|
|
size_t count;
|
|
|
|
|
|
} level0_syms;
|
|
|
|
|
|
|
|
|
|
|
|
xkb_mod_mask_t mods;
|
|
|
|
|
|
xkb_mod_mask_t consumed;
|
|
|
|
|
|
|
|
|
|
|
|
struct {
|
|
|
|
|
|
const uint8_t *buf;
|
|
|
|
|
|
size_t count;
|
|
|
|
|
|
} utf8;
|
|
|
|
|
|
uint32_t utf32;
|
|
|
|
|
|
|
|
|
|
|
|
enum xkb_compose_status compose_status;
|
|
|
|
|
|
enum wl_keyboard_key_state key_state;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2021-11-17 17:51:41 +01:00
|
|
|
|
static void
|
2021-11-20 20:31:59 +01:00
|
|
|
|
legacy_kbd_protocol(struct seat *seat, struct terminal *term,
|
|
|
|
|
|
const struct kbd_ctx *ctx)
|
2021-11-17 17:51:41 +01:00
|
|
|
|
{
|
2021-11-20 19:24:00 +01:00
|
|
|
|
enum modifier keymap_mods = MOD_NONE;
|
|
|
|
|
|
keymap_mods |= seat->kbd.shift ? MOD_SHIFT : MOD_NONE;
|
|
|
|
|
|
keymap_mods |= seat->kbd.alt ? MOD_ALT : MOD_NONE;
|
|
|
|
|
|
keymap_mods |= seat->kbd.ctrl ? MOD_CTRL : MOD_NONE;
|
2021-11-21 12:01:16 +01:00
|
|
|
|
keymap_mods |= seat->kbd.super ? MOD_META : MOD_NONE;
|
2021-11-20 19:24:00 +01:00
|
|
|
|
|
2021-11-20 20:31:59 +01:00
|
|
|
|
const xkb_keysym_t sym = ctx->sym;
|
|
|
|
|
|
const size_t count = ctx->utf8.count;
|
|
|
|
|
|
const uint8_t *const utf8 = ctx->utf8.buf;
|
|
|
|
|
|
|
2021-11-17 17:51:41 +01:00
|
|
|
|
const struct key_data *keymap;
|
2021-11-20 19:24:00 +01:00
|
|
|
|
if (sym == XKB_KEY_Escape && keymap_mods == MOD_NONE && term->modify_escape_key) {
|
2021-11-17 17:51:41 +01:00
|
|
|
|
static const struct key_data esc = {.seq = "\033[27;1;27~"};
|
|
|
|
|
|
keymap = &esc;
|
|
|
|
|
|
} else
|
2021-11-20 19:24:00 +01:00
|
|
|
|
keymap = keymap_lookup(term, sym, keymap_mods);
|
2021-11-17 17:51:41 +01:00
|
|
|
|
|
|
|
|
|
|
if (keymap != NULL) {
|
|
|
|
|
|
term_to_slave(term, keymap->seq, strlen(keymap->seq));
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (count == 0)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
#define is_control_key(x) ((x) >= 0x40 && (x) <= 0x7f)
|
|
|
|
|
|
#define IS_CTRL(x) ((x) < 0x20 || ((x) >= 0x7f && (x) <= 0x9f))
|
|
|
|
|
|
|
|
|
|
|
|
LOG_DBG("term->modify_other_keys=%d, count=%d, is_ctrl=%d (utf8=0x%02x), sym=%d",
|
|
|
|
|
|
term->modify_other_keys_2, count, IS_CTRL(utf8[0]), utf8[0], sym);
|
|
|
|
|
|
|
2021-11-20 19:24:00 +01:00
|
|
|
|
bool ctrl_is_in_effect = (keymap_mods & MOD_CTRL) != 0;
|
2021-11-17 17:51:41 +01:00
|
|
|
|
bool ctrl_seq = is_control_key(sym) || (count == 1 && IS_CTRL(utf8[0]));
|
|
|
|
|
|
|
2021-11-20 19:24:00 +01:00
|
|
|
|
if (keymap_mods != MOD_NONE && (term->modify_other_keys_2 ||
|
|
|
|
|
|
(ctrl_is_in_effect && !ctrl_seq)))
|
2021-11-17 17:51:41 +01:00
|
|
|
|
{
|
|
|
|
|
|
static const int mod_param_map[32] = {
|
|
|
|
|
|
[MOD_SHIFT] = 2,
|
|
|
|
|
|
[MOD_ALT] = 3,
|
|
|
|
|
|
[MOD_SHIFT | MOD_ALT] = 4,
|
|
|
|
|
|
[MOD_CTRL] = 5,
|
|
|
|
|
|
[MOD_SHIFT | MOD_CTRL] = 6,
|
|
|
|
|
|
[MOD_ALT | MOD_CTRL] = 7,
|
|
|
|
|
|
[MOD_SHIFT | MOD_ALT | MOD_CTRL] = 8,
|
|
|
|
|
|
[MOD_META] = 9,
|
|
|
|
|
|
[MOD_META | MOD_SHIFT] = 10,
|
|
|
|
|
|
[MOD_META | MOD_ALT] = 11,
|
|
|
|
|
|
[MOD_META | MOD_SHIFT | MOD_ALT] = 12,
|
|
|
|
|
|
[MOD_META | MOD_CTRL] = 13,
|
|
|
|
|
|
[MOD_META | MOD_SHIFT | MOD_CTRL] = 14,
|
|
|
|
|
|
[MOD_META | MOD_ALT | MOD_CTRL] = 15,
|
|
|
|
|
|
[MOD_META | MOD_SHIFT | MOD_ALT | MOD_CTRL] = 16,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2021-11-20 19:24:00 +01:00
|
|
|
|
xassert(keymap_mods < sizeof(mod_param_map) / sizeof(mod_param_map[0]));
|
|
|
|
|
|
int modify_param = mod_param_map[keymap_mods];
|
2021-11-17 17:51:41 +01:00
|
|
|
|
xassert(modify_param != 0);
|
|
|
|
|
|
|
2021-11-21 15:05:38 +01:00
|
|
|
|
char reply[32];
|
2021-11-17 17:51:41 +01:00
|
|
|
|
size_t n = xsnprintf(reply, sizeof(reply), "\x1b[27;%d;%d~", modify_param, sym);
|
|
|
|
|
|
term_to_slave(term, reply, n);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-20 19:24:00 +01:00
|
|
|
|
else if (keymap_mods & MOD_ALT) {
|
2021-11-17 17:51:41 +01:00
|
|
|
|
/*
|
|
|
|
|
|
* When the alt modifier is pressed, we do one out of three things:
|
|
|
|
|
|
*
|
|
|
|
|
|
* 1. we prefix the output bytes with ESC
|
|
|
|
|
|
* 2. we set the 8:th bit in the output byte
|
|
|
|
|
|
* 3. we ignore the alt modifier
|
|
|
|
|
|
*
|
|
|
|
|
|
* #1 is configured with \E[?1036, and is on by default
|
|
|
|
|
|
*
|
2021-11-21 15:06:41 +01:00
|
|
|
|
* If #1 has been disabled, we use #2, *if* it's a single byte
|
|
|
|
|
|
* we're emitting. Since this is a UTF-8 terminal, we then
|
|
|
|
|
|
* UTF8-encode the 8-bit character. #2 is configured with
|
|
|
|
|
|
* \E[?1034, and is on by default.
|
2021-11-17 17:51:41 +01:00
|
|
|
|
*
|
|
|
|
|
|
* Lastly, if both #1 and #2 have been disabled, the alt
|
|
|
|
|
|
* modifier is ignored.
|
|
|
|
|
|
*/
|
|
|
|
|
|
if (term->meta.esc_prefix) {
|
|
|
|
|
|
term_to_slave(term, "\x1b", 1);
|
|
|
|
|
|
term_to_slave(term, utf8, count);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
else if (term->meta.eight_bit && count == 1) {
|
|
|
|
|
|
const wchar_t wc = 0x80 | utf8[0];
|
|
|
|
|
|
|
|
|
|
|
|
char wc_as_utf8[8];
|
|
|
|
|
|
mbstate_t ps = {0};
|
|
|
|
|
|
size_t chars = wcrtomb(wc_as_utf8, wc, &ps);
|
|
|
|
|
|
|
|
|
|
|
|
if (chars != (size_t)-1)
|
|
|
|
|
|
term_to_slave(term, wc_as_utf8, chars);
|
|
|
|
|
|
else
|
|
|
|
|
|
term_to_slave(term, wc_as_utf8, count);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
else {
|
|
|
|
|
|
/* Alt ignored */
|
|
|
|
|
|
term_to_slave(term, utf8, count);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else
|
|
|
|
|
|
term_to_slave(term, utf8, count);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-20 19:24:00 +01:00
|
|
|
|
static void
|
2021-11-20 20:31:59 +01:00
|
|
|
|
kitty_kbd_protocol(struct seat *seat, struct terminal *term,
|
|
|
|
|
|
const struct kbd_ctx *ctx)
|
2021-11-20 19:24:00 +01:00
|
|
|
|
{
|
2021-11-21 11:27:31 +01:00
|
|
|
|
const xkb_mod_mask_t mods = ctx->mods & seat->kbd.kitty_significant;
|
|
|
|
|
|
const xkb_mod_mask_t consumed = ctx->consumed & seat->kbd.kitty_significant;
|
2021-11-20 20:31:59 +01:00
|
|
|
|
const xkb_mod_mask_t effective = mods & ~consumed;
|
2021-11-21 11:53:22 +01:00
|
|
|
|
const xkb_mod_mask_t caps_num =
|
|
|
|
|
|
(seat->kbd.mod_caps != XKB_MOD_INVALID ? 1 << seat->kbd.mod_caps : 0) |
|
|
|
|
|
|
(seat->kbd.mod_num != XKB_MOD_INVALID ? 1 << seat->kbd.mod_num : 0);
|
2021-11-20 20:31:59 +01:00
|
|
|
|
const xkb_keysym_t sym = ctx->sym;
|
|
|
|
|
|
const uint32_t utf32 = ctx->utf32;
|
|
|
|
|
|
const uint8_t *const utf8 = ctx->utf8.buf;
|
|
|
|
|
|
const size_t count = ctx->utf8.count;
|
|
|
|
|
|
|
|
|
|
|
|
if (ctx->compose_status == XKB_COMPOSE_COMPOSED) {
|
2021-11-20 19:24:00 +01:00
|
|
|
|
term_to_slave(term, utf8, count);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (effective == 0) {
|
|
|
|
|
|
switch (sym) {
|
|
|
|
|
|
case XKB_KEY_Return: term_to_slave(term, "\r", 1); return;
|
|
|
|
|
|
case XKB_KEY_BackSpace: term_to_slave(term, "\x7f", 1); return;
|
|
|
|
|
|
case XKB_KEY_Tab: term_to_slave(term, "\t", 1); return;
|
|
|
|
|
|
}
|
2021-11-21 11:38:36 +01:00
|
|
|
|
}
|
2021-11-20 19:24:00 +01:00
|
|
|
|
|
2021-11-21 11:38:36 +01:00
|
|
|
|
/*
|
|
|
|
|
|
* Printables without any modifiers are printed as is.
|
|
|
|
|
|
*
|
|
|
|
|
|
* TODO: plain text keys (a-z, 0-9 etc) are still printed as text,
|
|
|
|
|
|
* even when NumLock is active, despite NumLock being a
|
|
|
|
|
|
* significant modifier, *and* despite NumLock affecting other
|
|
|
|
|
|
* keys, like Return and Backspace; figure out if there’s some
|
|
|
|
|
|
* better magic than filtering out Caps- and Num-Lock here..
|
|
|
|
|
|
*/
|
2021-11-21 11:53:22 +01:00
|
|
|
|
if (iswprint(utf32) && (effective & ~caps_num) == 0) {
|
2021-11-21 11:38:36 +01:00
|
|
|
|
term_to_slave(term, utf8, count);
|
|
|
|
|
|
return;
|
2021-11-20 19:24:00 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
unsigned int encoded_mods = 0;
|
2021-11-21 11:53:22 +01:00
|
|
|
|
if (seat->kbd.mod_shift != XKB_MOD_INVALID)
|
|
|
|
|
|
encoded_mods |= mods & (1 << seat->kbd.mod_shift) ? (1 << 0) : 0;
|
|
|
|
|
|
if (seat->kbd.mod_alt != XKB_MOD_INVALID)
|
|
|
|
|
|
encoded_mods |= mods & (1 << seat->kbd.mod_alt) ? (1 << 1) : 0;
|
|
|
|
|
|
if (seat->kbd.mod_ctrl != XKB_MOD_INVALID)
|
|
|
|
|
|
encoded_mods |= mods & (1 << seat->kbd.mod_ctrl) ? (1 << 2) : 0;
|
2021-11-21 12:01:16 +01:00
|
|
|
|
if (seat->kbd.mod_super != XKB_MOD_INVALID)
|
|
|
|
|
|
encoded_mods |= mods & (1 << seat->kbd.mod_super) ? (1 << 3) : 0;
|
2021-11-21 11:53:22 +01:00
|
|
|
|
if (seat->kbd.mod_caps != XKB_MOD_INVALID)
|
|
|
|
|
|
encoded_mods |= mods & (1 << seat->kbd.mod_caps) ? (1 << 6) : 0;
|
|
|
|
|
|
if (seat->kbd.mod_num != XKB_MOD_INVALID)
|
|
|
|
|
|
encoded_mods |= mods & (1 << seat->kbd.mod_num) ? (1 << 7) : 0;
|
2021-11-20 19:24:00 +01:00
|
|
|
|
encoded_mods++;
|
|
|
|
|
|
|
|
|
|
|
|
int key = -1;
|
|
|
|
|
|
char final;
|
|
|
|
|
|
|
|
|
|
|
|
switch (sym) {
|
|
|
|
|
|
case XKB_KEY_Escape: key = 27; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_Return: key = 13; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_Tab: key = 9; final = 'u'; break;
|
2021-11-21 12:20:26 +01:00
|
|
|
|
case XKB_KEY_ISO_Left_Tab: key = 9; final = 'u'; break;
|
2021-11-20 19:24:00 +01:00
|
|
|
|
case XKB_KEY_BackSpace: key = 127; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_Insert: key = 2; final = '~'; break;
|
|
|
|
|
|
case XKB_KEY_Delete: key = 3; final = '~'; break;
|
|
|
|
|
|
case XKB_KEY_Left: key = 1; final = 'D'; break;
|
|
|
|
|
|
case XKB_KEY_Right: key = 1; final = 'C'; break;
|
|
|
|
|
|
case XKB_KEY_Up: key = 1; final = 'A'; break;
|
|
|
|
|
|
case XKB_KEY_Down: key = 1; final = 'B'; break;
|
|
|
|
|
|
case XKB_KEY_Page_Up: key = 5; final = '~'; break;
|
|
|
|
|
|
case XKB_KEY_Page_Down: key = 6; final = '~'; break;
|
|
|
|
|
|
case XKB_KEY_Home: key = 1; final = 'H'; break;
|
|
|
|
|
|
case XKB_KEY_End: key = 1; final = 'F'; break;
|
|
|
|
|
|
case XKB_KEY_Scroll_Lock: key = 57359; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_Print: key = 57361; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_Pause: key = 57362; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_Menu: key = 57363; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_F1: key = 1; final = 'P'; break;
|
|
|
|
|
|
case XKB_KEY_F2: key = 1; final = 'Q'; break;
|
|
|
|
|
|
case XKB_KEY_F3: key = 1; final = 'R'; break;
|
|
|
|
|
|
case XKB_KEY_F4: key = 1; final = 'S'; break;
|
|
|
|
|
|
case XKB_KEY_F5: key = 15; final = '~'; break;
|
|
|
|
|
|
case XKB_KEY_F6: key = 17; final = '~'; break;
|
|
|
|
|
|
case XKB_KEY_F7: key = 18; final = '~'; break;
|
|
|
|
|
|
case XKB_KEY_F8: key = 19; final = '~'; break;
|
|
|
|
|
|
case XKB_KEY_F9: key = 20; final = '~'; break;
|
|
|
|
|
|
case XKB_KEY_F10: key = 21; final = '~'; break;
|
|
|
|
|
|
case XKB_KEY_F11: key = 23; final = '~'; break;
|
|
|
|
|
|
case XKB_KEY_F12: key = 24; final = '~'; break;
|
|
|
|
|
|
case XKB_KEY_F13: key = 57376; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_F14: key = 57377; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_F15: key = 57378; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_F16: key = 57379; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_F17: key = 57380; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_F18: key = 57381; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_F19: key = 57382; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_F20: key = 57383; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_F21: key = 57384; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_F22: key = 57385; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_F23: key = 57386; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_F24: key = 57387; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_F25: key = 57388; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_F26: key = 57389; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_F27: key = 57390; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_F28: key = 57391; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_F29: key = 57392; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_F30: key = 57393; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_F31: key = 57394; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_F32: key = 57395; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_F33: key = 57396; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_F34: key = 57397; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_F35: key = 57398; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_KP_0: key = 57399; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_KP_1: key = 57400; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_KP_2: key = 57401; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_KP_3: key = 57402; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_KP_4: key = 57403; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_KP_5: key = 57404; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_KP_6: key = 57405; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_KP_7: key = 57406; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_KP_8: key = 57407; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_KP_9: key = 57408; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_KP_Decimal: key = 57409; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_KP_Divide: key = 57410; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_KP_Multiply: key = 57411; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_KP_Subtract: key = 57412; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_KP_Add: key = 57413; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_KP_Enter: key = 57414; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_KP_Equal: key = 57415; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_KP_Separator: key = 57416; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_KP_Left: key = 57417; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_KP_Right: key = 57418; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_KP_Up: key = 57419; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_KP_Down: key = 57420; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_KP_Page_Up: key = 57421; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_KP_Page_Down: key = 57422; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_KP_Home: key = 57423; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_KP_End: key = 57424; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_KP_Insert: key = 57425; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_KP_Delete: key = 57426; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_KP_Begin: key = 1; final = 'E'; break;
|
|
|
|
|
|
|
2021-11-21 12:39:40 +01:00
|
|
|
|
case XKB_KEY_XF86AudioPlay: key = 57428; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_XF86AudioPause: key = 57429; final = 'u'; break;
|
|
|
|
|
|
//case XKB_KEY_XF86AudioPlayPause: key = 57430; final = 'u'; break;
|
|
|
|
|
|
//case XKB_KEY_XF86AudioReverse: key = 57431; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_XF86AudioStop: key = 57432; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_XF86AudioForward: key = 57433; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_XF86AudioRewind: key = 57434; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_XF86AudioNext: key = 57435; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_XF86AudioPrev: key = 57436; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_XF86AudioRecord: key = 57437; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_XF86AudioLowerVolume: key = 57438; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_XF86AudioRaiseVolume: key = 57439; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_XF86AudioMute: key = 57440; final = 'u'; break;
|
|
|
|
|
|
|
2021-11-21 13:53:52 +01:00
|
|
|
|
#if 0 /* TODO: enable when “Report all keys as escape codes” is enabled */
|
|
|
|
|
|
case XKB_KEY_Caps_Lock: key = 57358; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_Num_Lock: key = 57360; final = 'u'; break;
|
|
|
|
|
|
|
|
|
|
|
|
case XKB_KEY_Shift_L: key = 57441; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_Control_L: key = 57442; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_Alt_L: key = 57443; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_Super_L: key = 57444; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_Hyper_L: key = 57445; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_Meta_L: key = 57446; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_Shift_R: key = 57447; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_Control_R: key = 57448; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_Alt_R: key = 57449; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_Super_R: key = 57450; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_Hyper_R: key = 57451; final = 'u'; break;
|
|
|
|
|
|
case XKB_KEY_Meta_R: key = 57452; final = 'u'; break;
|
|
|
|
|
|
#endif
|
2021-11-21 12:40:04 +01:00
|
|
|
|
|
2021-11-20 19:24:00 +01:00
|
|
|
|
default:
|
|
|
|
|
|
if (count > 0) {
|
|
|
|
|
|
if (effective == 0) {
|
|
|
|
|
|
term_to_slave(term, utf8, count);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-20 20:31:59 +01:00
|
|
|
|
/*
|
|
|
|
|
|
* Use keysym (typically its Unicode codepoint value).
|
|
|
|
|
|
*
|
|
|
|
|
|
* If the keysym is shifted, use its unshifted codepoint
|
|
|
|
|
|
* instead. In other words, ctrl+a and ctrl+shift+a should
|
|
|
|
|
|
* both use the same value for ‘key’ (97 - i.a. ‘a’).
|
|
|
|
|
|
*
|
|
|
|
|
|
* However, if a non-significant modifier was used to
|
|
|
|
|
|
* generate the symbol. This is needed since we cannot
|
|
|
|
|
|
* encode non-significant modifiers, and thus the “extra”
|
|
|
|
|
|
* modifier(s) would get lost.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Example:
|
|
|
|
|
|
*
|
|
|
|
|
|
* the Swedish layout has ‘2’, QUOTATION MARK (“double
|
|
|
|
|
|
* quote”), ‘@’, and ‘²’ on the same key. ‘2’ is the base
|
|
|
|
|
|
* symbol.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Shift+2 results in QUOTATION MARK
|
|
|
|
|
|
* AltGr+2 results in ‘@’
|
|
|
|
|
|
* AltGr+Shift+2 results in ‘²’
|
|
|
|
|
|
*
|
|
|
|
|
|
* The kitty kbd protocol can’t encode AltGr. So, if we
|
|
|
|
|
|
* always used the base symbol (‘2’), Alt+Shift+2 would
|
|
|
|
|
|
* result in the same escape sequence as
|
|
|
|
|
|
* AltGr+Alt+Shift+2.
|
|
|
|
|
|
*
|
|
|
|
|
|
* (yes, this matches what kitty does, as of 0.23.1)
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
/* Get the key’s shift level */
|
|
|
|
|
|
xkb_level_index_t lvl = xkb_state_key_get_level(
|
|
|
|
|
|
seat->kbd.xkb_state, ctx->key, ctx->layout);
|
|
|
|
|
|
|
|
|
|
|
|
/* And get all modifier combinations that, combined with
|
|
|
|
|
|
* the pressed key, results in the current shift level */
|
|
|
|
|
|
xkb_mod_mask_t masks[32];
|
|
|
|
|
|
size_t mask_count = xkb_keymap_key_get_mods_for_level(
|
|
|
|
|
|
seat->kbd.xkb_keymap, ctx->key, ctx->layout, lvl,
|
|
|
|
|
|
masks, ALEN(masks));
|
|
|
|
|
|
|
|
|
|
|
|
/* Check modifier combinations - if a combination has
|
|
|
|
|
|
* modifiers not in our set of ‘significant’ modifiers,
|
|
|
|
|
|
* use key sym as-is */
|
|
|
|
|
|
bool use_level0_sym = true;
|
|
|
|
|
|
for (size_t i = 0; i < mask_count; i++) {
|
2021-11-21 11:27:31 +01:00
|
|
|
|
if ((masks[i] & ~seat->kbd.kitty_significant) > 0) {
|
2021-11-20 20:31:59 +01:00
|
|
|
|
use_level0_sym = false;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
key = use_level0_sym && ctx->level0_syms.count > 0
|
|
|
|
|
|
? ctx->level0_syms.syms[0]
|
|
|
|
|
|
: sym;
|
2021-11-20 19:24:00 +01:00
|
|
|
|
final = 'u';
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
xassert(encoded_mods >= 1);
|
|
|
|
|
|
|
|
|
|
|
|
char buf[16];
|
|
|
|
|
|
int bytes;
|
|
|
|
|
|
|
|
|
|
|
|
if (key < 0)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
if (final == 'u' || final == '~') {
|
|
|
|
|
|
if (encoded_mods > 1)
|
|
|
|
|
|
bytes = snprintf(buf, sizeof(buf), "\x1b[%u;%u%c",
|
|
|
|
|
|
key, encoded_mods, final);
|
|
|
|
|
|
else
|
|
|
|
|
|
bytes = snprintf(buf, sizeof(buf), "\x1b[%u%c", key, final);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if (encoded_mods > 1)
|
|
|
|
|
|
bytes = snprintf(buf, sizeof(buf), "\x1b[1;%u%c", encoded_mods, final);
|
|
|
|
|
|
else
|
|
|
|
|
|
bytes = snprintf(buf, sizeof(buf), "\x1b[%c", final);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
term_to_slave(term, buf, bytes);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-06-19 10:04:47 +02:00
|
|
|
|
static void
|
2020-11-03 19:44:51 +01:00
|
|
|
|
key_press_release(struct seat *seat, struct terminal *term, uint32_t serial,
|
|
|
|
|
|
uint32_t key, uint32_t state)
|
2019-06-19 10:04:47 +02:00
|
|
|
|
{
|
2021-10-20 18:47:36 +02:00
|
|
|
|
seat->kbd.serial = serial;
|
2020-10-20 21:01:33 +02:00
|
|
|
|
if (seat->kbd.xkb == NULL ||
|
|
|
|
|
|
seat->kbd.xkb_keymap == NULL ||
|
|
|
|
|
|
seat->kbd.xkb_state == NULL)
|
|
|
|
|
|
{
|
2020-07-14 08:52:11 +02:00
|
|
|
|
return;
|
2020-10-20 21:01:33 +02:00
|
|
|
|
}
|
2020-07-14 08:52:11 +02:00
|
|
|
|
|
2019-06-19 10:04:47 +02:00
|
|
|
|
if (state == XKB_KEY_UP) {
|
2020-07-08 16:45:26 +02:00
|
|
|
|
stop_repeater(seat, key);
|
2019-06-19 10:04:47 +02:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-08 16:45:26 +02:00
|
|
|
|
bool should_repeat = xkb_keymap_key_repeats(seat->kbd.xkb_keymap, key);
|
|
|
|
|
|
xkb_keysym_t sym = xkb_state_key_get_one_sym(seat->kbd.xkb_state, key);
|
2019-06-19 10:04:47 +02:00
|
|
|
|
|
2020-08-04 07:33:15 +02:00
|
|
|
|
if (state == XKB_KEY_DOWN && term->conf->mouse.hide_when_typing &&
|
2020-08-15 19:39:00 +01:00
|
|
|
|
/* TODO: better way to detect modifiers */
|
2020-08-03 18:19:43 +02:00
|
|
|
|
sym != XKB_KEY_Shift_L && sym != XKB_KEY_Shift_R &&
|
|
|
|
|
|
sym != XKB_KEY_Control_L && sym != XKB_KEY_Control_R &&
|
|
|
|
|
|
sym != XKB_KEY_Alt_L && sym != XKB_KEY_Alt_R &&
|
2020-08-04 07:38:21 +02:00
|
|
|
|
sym != XKB_KEY_ISO_Level3_Shift &&
|
2020-08-03 18:19:43 +02:00
|
|
|
|
sym != XKB_KEY_Super_L && sym != XKB_KEY_Super_R &&
|
|
|
|
|
|
sym != XKB_KEY_Meta_L && sym != XKB_KEY_Meta_R &&
|
|
|
|
|
|
sym != XKB_KEY_Menu)
|
2020-08-01 15:39:25 +02:00
|
|
|
|
{
|
|
|
|
|
|
seat->pointer.hidden = true;
|
|
|
|
|
|
term_xcursor_update_for_seat(term, seat);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-10-19 18:30:19 +02:00
|
|
|
|
enum xkb_compose_status compose_status = XKB_COMPOSE_NOTHING;
|
|
|
|
|
|
|
|
|
|
|
|
if (seat->kbd.xkb_compose_state != NULL) {
|
|
|
|
|
|
xkb_compose_state_feed(seat->kbd.xkb_compose_state, sym);
|
|
|
|
|
|
compose_status = xkb_compose_state_get_status(
|
|
|
|
|
|
seat->kbd.xkb_compose_state);
|
|
|
|
|
|
}
|
2019-07-09 10:00:54 +02:00
|
|
|
|
|
2021-11-20 19:23:39 +01:00
|
|
|
|
if (compose_status == XKB_COMPOSE_COMPOSING)
|
|
|
|
|
|
goto maybe_repeat;
|
2019-07-09 10:00:54 +02:00
|
|
|
|
|
2021-10-06 20:12:17 +02:00
|
|
|
|
xkb_mod_mask_t mods, consumed;
|
|
|
|
|
|
get_current_modifiers(seat, &mods, &consumed, key);
|
2019-06-19 10:04:47 +02:00
|
|
|
|
|
2021-11-21 11:27:31 +01:00
|
|
|
|
xkb_mod_mask_t bind_mods = mods & seat->kbd.bind_significant;
|
|
|
|
|
|
xkb_mod_mask_t bind_consumed = consumed & seat->kbd.bind_significant;
|
|
|
|
|
|
|
input: rewrite of how we match foot’s own key bindings
Bindings are matched in one out of three ways:
* By translated (by XKB) symbols
* By untranslated symbols
* By raw key codes
A translated symbol is affected by pressed modifiers, some of which
can be “consumed”. Consumed modifiers to not partake in the comparison
with the binding’s modifiers. In this mode, ctrl+shift+2 maps to
ctrl+@ on a US layout.
Untranslated symbols, or un-shifted symbols refer to the “base” symbol
of the pressed key, i.e. it’s unaffected by modifiers. In this mode,
consumed modifiers *do* partake in the comparison with the binding’s
modifiers, and ctrl+shift+2 maps to ctrl+shift+2 on a US layout.
More examples: ctrl+shift+u maps to ctrl+U in the translated lookup,
while ctrl+shift+u maps to ctrl+shift+u in the untranslated lookup.
Finally, we also match raw key codes. This allows our bindings to work
using the same physical keys when the user switches between latin and
non-latin layouts.
This means key bindings in foot.ini *must* not include both +shift+
and a *shifted* key. I.e. ctrl+shift+U is not a valid combo as it
cannot be triggered. Unfortunately, this was how you were supposed to
write bindings up until now... so, we try to detect such bindings, log
a deprecation warning and then “fix” the binding for the user.
When specifying bindings in foot.ini, both ctrl+U and ctrl+shift+u are
valid, and will work. The latter is preferred though, since we cannot
detect the raw key code for the former variant. Personally, I also
prefer the latter one because it is more explicit; it’s more obvious
which keys are involved.
However, in some cases it makes more sense to use the other
variant. Typically for non-letter combos.
2021-02-27 20:42:31 +01:00
|
|
|
|
xkb_layout_index_t layout_idx =
|
|
|
|
|
|
xkb_state_key_get_layout(seat->kbd.xkb_state, key);
|
|
|
|
|
|
|
|
|
|
|
|
const xkb_keysym_t *raw_syms = NULL;
|
|
|
|
|
|
size_t raw_count = xkb_keymap_key_get_syms_by_level(
|
|
|
|
|
|
seat->kbd.xkb_keymap, key, layout_idx, 0, &raw_syms);
|
|
|
|
|
|
|
2019-08-27 17:23:28 +02:00
|
|
|
|
if (term->is_searching) {
|
2019-11-22 22:19:00 +01:00
|
|
|
|
if (should_repeat)
|
2020-11-03 19:44:51 +01:00
|
|
|
|
start_repeater(seat, key);
|
input: rewrite of how we match foot’s own key bindings
Bindings are matched in one out of three ways:
* By translated (by XKB) symbols
* By untranslated symbols
* By raw key codes
A translated symbol is affected by pressed modifiers, some of which
can be “consumed”. Consumed modifiers to not partake in the comparison
with the binding’s modifiers. In this mode, ctrl+shift+2 maps to
ctrl+@ on a US layout.
Untranslated symbols, or un-shifted symbols refer to the “base” symbol
of the pressed key, i.e. it’s unaffected by modifiers. In this mode,
consumed modifiers *do* partake in the comparison with the binding’s
modifiers, and ctrl+shift+2 maps to ctrl+shift+2 on a US layout.
More examples: ctrl+shift+u maps to ctrl+U in the translated lookup,
while ctrl+shift+u maps to ctrl+shift+u in the untranslated lookup.
Finally, we also match raw key codes. This allows our bindings to work
using the same physical keys when the user switches between latin and
non-latin layouts.
This means key bindings in foot.ini *must* not include both +shift+
and a *shifted* key. I.e. ctrl+shift+U is not a valid combo as it
cannot be triggered. Unfortunately, this was how you were supposed to
write bindings up until now... so, we try to detect such bindings, log
a deprecation warning and then “fix” the binding for the user.
When specifying bindings in foot.ini, both ctrl+U and ctrl+shift+u are
valid, and will work. The latter is preferred though, since we cannot
detect the raw key code for the former variant. Personally, I also
prefer the latter one because it is more explicit; it’s more obvious
which keys are involved.
However, in some cases it makes more sense to use the other
variant. Typically for non-letter combos.
2021-02-27 20:42:31 +01:00
|
|
|
|
search_input(
|
2021-11-21 11:27:31 +01:00
|
|
|
|
seat, term, key, sym, bind_mods, bind_consumed, raw_syms, raw_count, serial);
|
2019-08-27 17:23:28 +02:00
|
|
|
|
return;
|
2021-01-31 11:12:07 +01:00
|
|
|
|
} else if (urls_mode_is_active(term)) {
|
|
|
|
|
|
if (should_repeat)
|
|
|
|
|
|
start_repeater(seat, key);
|
input: rewrite of how we match foot’s own key bindings
Bindings are matched in one out of three ways:
* By translated (by XKB) symbols
* By untranslated symbols
* By raw key codes
A translated symbol is affected by pressed modifiers, some of which
can be “consumed”. Consumed modifiers to not partake in the comparison
with the binding’s modifiers. In this mode, ctrl+shift+2 maps to
ctrl+@ on a US layout.
Untranslated symbols, or un-shifted symbols refer to the “base” symbol
of the pressed key, i.e. it’s unaffected by modifiers. In this mode,
consumed modifiers *do* partake in the comparison with the binding’s
modifiers, and ctrl+shift+2 maps to ctrl+shift+2 on a US layout.
More examples: ctrl+shift+u maps to ctrl+U in the translated lookup,
while ctrl+shift+u maps to ctrl+shift+u in the untranslated lookup.
Finally, we also match raw key codes. This allows our bindings to work
using the same physical keys when the user switches between latin and
non-latin layouts.
This means key bindings in foot.ini *must* not include both +shift+
and a *shifted* key. I.e. ctrl+shift+U is not a valid combo as it
cannot be triggered. Unfortunately, this was how you were supposed to
write bindings up until now... so, we try to detect such bindings, log
a deprecation warning and then “fix” the binding for the user.
When specifying bindings in foot.ini, both ctrl+U and ctrl+shift+u are
valid, and will work. The latter is preferred though, since we cannot
detect the raw key code for the former variant. Personally, I also
prefer the latter one because it is more explicit; it’s more obvious
which keys are involved.
However, in some cases it makes more sense to use the other
variant. Typically for non-letter combos.
2021-02-27 20:42:31 +01:00
|
|
|
|
urls_input(
|
2021-11-21 11:27:31 +01:00
|
|
|
|
seat, term, key, sym, bind_mods, bind_consumed, raw_syms, raw_count, serial);
|
2021-01-31 11:12:07 +01:00
|
|
|
|
return;
|
2019-08-27 17:23:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2019-06-19 10:04:47 +02:00
|
|
|
|
#if 0
|
|
|
|
|
|
for (size_t i = 0; i < 32; i++) {
|
|
|
|
|
|
if (mods & (1 << i)) {
|
2020-07-08 16:45:26 +02:00
|
|
|
|
LOG_INFO("%s", xkb_keymap_mod_get_name(seat->kbd.xkb_keymap, i));
|
2019-06-19 10:04:47 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
2020-11-30 20:01:46 +01:00
|
|
|
|
#if defined(_DEBUG) && defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG
|
|
|
|
|
|
char sym_name[100];
|
|
|
|
|
|
xkb_keysym_get_name(sym, sym_name, sizeof(sym_name));
|
|
|
|
|
|
|
|
|
|
|
|
LOG_DBG("%s (%u/0x%x): seat=%s, term=%p, serial=%u, "
|
2021-03-04 09:42:36 +01:00
|
|
|
|
"mods=0x%08x, consumed=0x%08x, repeats=%d",
|
2020-11-30 20:01:46 +01:00
|
|
|
|
sym_name, sym, sym, seat->name, (void *)term, serial,
|
2021-03-04 09:42:36 +01:00
|
|
|
|
mods, consumed, should_repeat);
|
2021-08-04 17:27:34 +02:00
|
|
|
|
#endif
|
2019-07-09 14:27:26 +02:00
|
|
|
|
|
2020-01-11 18:59:46 +01:00
|
|
|
|
/*
|
2020-03-08 15:17:29 +01:00
|
|
|
|
* User configurable bindings
|
2020-01-11 18:59:46 +01:00
|
|
|
|
*/
|
2020-07-08 16:45:26 +02:00
|
|
|
|
tll_foreach(seat->kbd.bindings.key, it) {
|
input: rewrite of how we match foot’s own key bindings
Bindings are matched in one out of three ways:
* By translated (by XKB) symbols
* By untranslated symbols
* By raw key codes
A translated symbol is affected by pressed modifiers, some of which
can be “consumed”. Consumed modifiers to not partake in the comparison
with the binding’s modifiers. In this mode, ctrl+shift+2 maps to
ctrl+@ on a US layout.
Untranslated symbols, or un-shifted symbols refer to the “base” symbol
of the pressed key, i.e. it’s unaffected by modifiers. In this mode,
consumed modifiers *do* partake in the comparison with the binding’s
modifiers, and ctrl+shift+2 maps to ctrl+shift+2 on a US layout.
More examples: ctrl+shift+u maps to ctrl+U in the translated lookup,
while ctrl+shift+u maps to ctrl+shift+u in the untranslated lookup.
Finally, we also match raw key codes. This allows our bindings to work
using the same physical keys when the user switches between latin and
non-latin layouts.
This means key bindings in foot.ini *must* not include both +shift+
and a *shifted* key. I.e. ctrl+shift+U is not a valid combo as it
cannot be triggered. Unfortunately, this was how you were supposed to
write bindings up until now... so, we try to detect such bindings, log
a deprecation warning and then “fix” the binding for the user.
When specifying bindings in foot.ini, both ctrl+U and ctrl+shift+u are
valid, and will work. The latter is preferred though, since we cannot
detect the raw key code for the former variant. Personally, I also
prefer the latter one because it is more explicit; it’s more obvious
which keys are involved.
However, in some cases it makes more sense to use the other
variant. Typically for non-letter combos.
2021-02-27 20:42:31 +01:00
|
|
|
|
const struct key_binding *bind = &it->item;
|
|
|
|
|
|
|
|
|
|
|
|
/* Match translated symbol */
|
|
|
|
|
|
if (bind->sym == sym &&
|
2021-11-21 11:27:31 +01:00
|
|
|
|
bind->mods == (bind_mods & ~bind_consumed) &&
|
input: rewrite of how we match foot’s own key bindings
Bindings are matched in one out of three ways:
* By translated (by XKB) symbols
* By untranslated symbols
* By raw key codes
A translated symbol is affected by pressed modifiers, some of which
can be “consumed”. Consumed modifiers to not partake in the comparison
with the binding’s modifiers. In this mode, ctrl+shift+2 maps to
ctrl+@ on a US layout.
Untranslated symbols, or un-shifted symbols refer to the “base” symbol
of the pressed key, i.e. it’s unaffected by modifiers. In this mode,
consumed modifiers *do* partake in the comparison with the binding’s
modifiers, and ctrl+shift+2 maps to ctrl+shift+2 on a US layout.
More examples: ctrl+shift+u maps to ctrl+U in the translated lookup,
while ctrl+shift+u maps to ctrl+shift+u in the untranslated lookup.
Finally, we also match raw key codes. This allows our bindings to work
using the same physical keys when the user switches between latin and
non-latin layouts.
This means key bindings in foot.ini *must* not include both +shift+
and a *shifted* key. I.e. ctrl+shift+U is not a valid combo as it
cannot be triggered. Unfortunately, this was how you were supposed to
write bindings up until now... so, we try to detect such bindings, log
a deprecation warning and then “fix” the binding for the user.
When specifying bindings in foot.ini, both ctrl+U and ctrl+shift+u are
valid, and will work. The latter is preferred though, since we cannot
detect the raw key code for the former variant. Personally, I also
prefer the latter one because it is more explicit; it’s more obvious
which keys are involved.
However, in some cases it makes more sense to use the other
variant. Typically for non-letter combos.
2021-02-27 20:42:31 +01:00
|
|
|
|
execute_binding(
|
|
|
|
|
|
seat, term, bind->action, bind->pipe_argv, serial))
|
|
|
|
|
|
{
|
|
|
|
|
|
goto maybe_repeat;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-21 11:27:31 +01:00
|
|
|
|
if (bind->mods != bind_mods)
|
2020-03-18 14:52:39 +01:00
|
|
|
|
continue;
|
|
|
|
|
|
|
input: rewrite of how we match foot’s own key bindings
Bindings are matched in one out of three ways:
* By translated (by XKB) symbols
* By untranslated symbols
* By raw key codes
A translated symbol is affected by pressed modifiers, some of which
can be “consumed”. Consumed modifiers to not partake in the comparison
with the binding’s modifiers. In this mode, ctrl+shift+2 maps to
ctrl+@ on a US layout.
Untranslated symbols, or un-shifted symbols refer to the “base” symbol
of the pressed key, i.e. it’s unaffected by modifiers. In this mode,
consumed modifiers *do* partake in the comparison with the binding’s
modifiers, and ctrl+shift+2 maps to ctrl+shift+2 on a US layout.
More examples: ctrl+shift+u maps to ctrl+U in the translated lookup,
while ctrl+shift+u maps to ctrl+shift+u in the untranslated lookup.
Finally, we also match raw key codes. This allows our bindings to work
using the same physical keys when the user switches between latin and
non-latin layouts.
This means key bindings in foot.ini *must* not include both +shift+
and a *shifted* key. I.e. ctrl+shift+U is not a valid combo as it
cannot be triggered. Unfortunately, this was how you were supposed to
write bindings up until now... so, we try to detect such bindings, log
a deprecation warning and then “fix” the binding for the user.
When specifying bindings in foot.ini, both ctrl+U and ctrl+shift+u are
valid, and will work. The latter is preferred though, since we cannot
detect the raw key code for the former variant. Personally, I also
prefer the latter one because it is more explicit; it’s more obvious
which keys are involved.
However, in some cases it makes more sense to use the other
variant. Typically for non-letter combos.
2021-02-27 20:42:31 +01:00
|
|
|
|
/* Match untranslated symbols */
|
|
|
|
|
|
for (size_t i = 0; i < raw_count; i++) {
|
|
|
|
|
|
if (bind->sym == raw_syms[i] && execute_binding(
|
|
|
|
|
|
seat, term, bind->action, bind->pipe_argv, serial))
|
|
|
|
|
|
{
|
2020-08-22 13:35:36 +02:00
|
|
|
|
goto maybe_repeat;
|
input: rewrite of how we match foot’s own key bindings
Bindings are matched in one out of three ways:
* By translated (by XKB) symbols
* By untranslated symbols
* By raw key codes
A translated symbol is affected by pressed modifiers, some of which
can be “consumed”. Consumed modifiers to not partake in the comparison
with the binding’s modifiers. In this mode, ctrl+shift+2 maps to
ctrl+@ on a US layout.
Untranslated symbols, or un-shifted symbols refer to the “base” symbol
of the pressed key, i.e. it’s unaffected by modifiers. In this mode,
consumed modifiers *do* partake in the comparison with the binding’s
modifiers, and ctrl+shift+2 maps to ctrl+shift+2 on a US layout.
More examples: ctrl+shift+u maps to ctrl+U in the translated lookup,
while ctrl+shift+u maps to ctrl+shift+u in the untranslated lookup.
Finally, we also match raw key codes. This allows our bindings to work
using the same physical keys when the user switches between latin and
non-latin layouts.
This means key bindings in foot.ini *must* not include both +shift+
and a *shifted* key. I.e. ctrl+shift+U is not a valid combo as it
cannot be triggered. Unfortunately, this was how you were supposed to
write bindings up until now... so, we try to detect such bindings, log
a deprecation warning and then “fix” the binding for the user.
When specifying bindings in foot.ini, both ctrl+U and ctrl+shift+u are
valid, and will work. The latter is preferred though, since we cannot
detect the raw key code for the former variant. Personally, I also
prefer the latter one because it is more explicit; it’s more obvious
which keys are involved.
However, in some cases it makes more sense to use the other
variant. Typically for non-letter combos.
2021-02-27 20:42:31 +01:00
|
|
|
|
}
|
2019-08-27 17:23:28 +02:00
|
|
|
|
}
|
2020-03-18 14:29:34 +01:00
|
|
|
|
|
|
|
|
|
|
/* Match raw key code */
|
input: rewrite of how we match foot’s own key bindings
Bindings are matched in one out of three ways:
* By translated (by XKB) symbols
* By untranslated symbols
* By raw key codes
A translated symbol is affected by pressed modifiers, some of which
can be “consumed”. Consumed modifiers to not partake in the comparison
with the binding’s modifiers. In this mode, ctrl+shift+2 maps to
ctrl+@ on a US layout.
Untranslated symbols, or un-shifted symbols refer to the “base” symbol
of the pressed key, i.e. it’s unaffected by modifiers. In this mode,
consumed modifiers *do* partake in the comparison with the binding’s
modifiers, and ctrl+shift+2 maps to ctrl+shift+2 on a US layout.
More examples: ctrl+shift+u maps to ctrl+U in the translated lookup,
while ctrl+shift+u maps to ctrl+shift+u in the untranslated lookup.
Finally, we also match raw key codes. This allows our bindings to work
using the same physical keys when the user switches between latin and
non-latin layouts.
This means key bindings in foot.ini *must* not include both +shift+
and a *shifted* key. I.e. ctrl+shift+U is not a valid combo as it
cannot be triggered. Unfortunately, this was how you were supposed to
write bindings up until now... so, we try to detect such bindings, log
a deprecation warning and then “fix” the binding for the user.
When specifying bindings in foot.ini, both ctrl+U and ctrl+shift+u are
valid, and will work. The latter is preferred though, since we cannot
detect the raw key code for the former variant. Personally, I also
prefer the latter one because it is more explicit; it’s more obvious
which keys are involved.
However, in some cases it makes more sense to use the other
variant. Typically for non-letter combos.
2021-02-27 20:42:31 +01:00
|
|
|
|
tll_foreach(bind->key_codes, code) {
|
|
|
|
|
|
if (code->item == key && execute_binding(
|
|
|
|
|
|
seat, term, bind->action, bind->pipe_argv, serial))
|
|
|
|
|
|
{
|
|
|
|
|
|
goto maybe_repeat;
|
2020-03-18 14:29:34 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2019-07-11 12:16:50 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-01-11 18:59:46 +01:00
|
|
|
|
/*
|
|
|
|
|
|
* Keys generating escape sequences
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* Compose, and maybe emit "normal" character
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(seat->kbd.xkb_compose_state != NULL ||
|
2020-10-20 21:01:33 +02:00
|
|
|
|
compose_status != XKB_COMPOSE_COMPOSED);
|
2019-11-22 22:02:50 +01:00
|
|
|
|
|
2021-11-17 17:51:41 +01:00
|
|
|
|
if (compose_status == XKB_COMPOSE_CANCELLED)
|
|
|
|
|
|
goto maybe_repeat;
|
|
|
|
|
|
|
2020-10-20 21:01:33 +02:00
|
|
|
|
int count = compose_status == XKB_COMPOSE_COMPOSED
|
|
|
|
|
|
? xkb_compose_state_get_utf8(seat->kbd.xkb_compose_state, NULL, 0)
|
|
|
|
|
|
: xkb_state_key_get_utf8(seat->kbd.xkb_state, key, NULL, 0);
|
2020-10-19 18:30:19 +02:00
|
|
|
|
|
2020-10-20 21:01:33 +02:00
|
|
|
|
/* Buffer for translated key. Use a static buffer in most cases,
|
|
|
|
|
|
* and use a malloc:ed buffer when necessary */
|
|
|
|
|
|
uint8_t buf[32];
|
|
|
|
|
|
uint8_t *utf8 = count < sizeof(buf) ? buf : xmalloc(count + 1);
|
2021-11-20 19:24:00 +01:00
|
|
|
|
uint32_t utf32 = (uint32_t)-1;
|
2019-06-19 10:04:47 +02:00
|
|
|
|
|
2021-11-20 19:24:00 +01:00
|
|
|
|
if (compose_status == XKB_COMPOSE_COMPOSED) {
|
|
|
|
|
|
xkb_compose_state_get_utf8(
|
|
|
|
|
|
seat->kbd.xkb_compose_state, (char *)utf8, count + 1);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
xkb_state_key_get_utf8(
|
2020-10-20 21:01:33 +02:00
|
|
|
|
seat->kbd.xkb_state, key, (char *)utf8, count + 1);
|
2021-11-20 19:24:00 +01:00
|
|
|
|
utf32 = xkb_state_key_get_utf32(seat->kbd.xkb_state, key);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-20 20:31:59 +01:00
|
|
|
|
struct kbd_ctx ctx = {
|
|
|
|
|
|
.layout = layout_idx,
|
|
|
|
|
|
.key = key,
|
|
|
|
|
|
.sym = sym,
|
|
|
|
|
|
.level0_syms = {
|
|
|
|
|
|
.syms = raw_syms,
|
|
|
|
|
|
.count = raw_count,
|
|
|
|
|
|
},
|
|
|
|
|
|
.mods = mods,
|
|
|
|
|
|
.consumed = consumed,
|
|
|
|
|
|
.utf8 = {
|
|
|
|
|
|
.buf = utf8,
|
|
|
|
|
|
.count = count,
|
|
|
|
|
|
},
|
|
|
|
|
|
.utf32 = utf32,
|
|
|
|
|
|
.compose_status = compose_status,
|
|
|
|
|
|
.key_state = state,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2021-11-20 19:24:00 +01:00
|
|
|
|
if (term->grid->kitty_kbd.flags[term->grid->kitty_kbd.idx] != 0)
|
2021-11-20 20:31:59 +01:00
|
|
|
|
kitty_kbd_protocol(seat, term, &ctx);
|
2021-11-20 19:24:00 +01:00
|
|
|
|
else
|
2021-11-20 20:31:59 +01:00
|
|
|
|
legacy_kbd_protocol(seat, term, &ctx);
|
2020-10-20 21:01:33 +02:00
|
|
|
|
|
|
|
|
|
|
if (seat->kbd.xkb_compose_state != NULL)
|
|
|
|
|
|
xkb_compose_state_reset(seat->kbd.xkb_compose_state);
|
2019-06-19 10:04:47 +02:00
|
|
|
|
|
2020-10-20 21:01:33 +02:00
|
|
|
|
if (utf8 != buf)
|
|
|
|
|
|
free(utf8);
|
|
|
|
|
|
|
2020-01-11 18:59:46 +01:00
|
|
|
|
term_reset_view(term);
|
|
|
|
|
|
selection_cancel(term);
|
2019-11-22 22:19:00 +01:00
|
|
|
|
|
|
|
|
|
|
maybe_repeat:
|
2020-01-21 18:51:04 +01:00
|
|
|
|
clock_gettime(
|
|
|
|
|
|
term->wl->presentation_clock_id, &term->render.input_time);
|
|
|
|
|
|
|
2019-11-22 22:19:00 +01:00
|
|
|
|
if (should_repeat)
|
2020-11-03 19:44:51 +01:00
|
|
|
|
start_repeater(seat, key);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
|
keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
|
|
|
|
|
|
uint32_t time, uint32_t key, uint32_t state)
|
|
|
|
|
|
{
|
|
|
|
|
|
struct seat *seat = data;
|
|
|
|
|
|
key_press_release(seat, seat->kbd_focus, serial, key + 8, state);
|
2019-06-19 10:04:47 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
|
keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
|
|
|
|
|
|
uint32_t mods_depressed, uint32_t mods_latched,
|
|
|
|
|
|
uint32_t mods_locked, uint32_t group)
|
|
|
|
|
|
{
|
2020-07-08 16:45:26 +02:00
|
|
|
|
struct seat *seat = data;
|
2019-06-19 10:04:47 +02:00
|
|
|
|
|
|
|
|
|
|
LOG_DBG("modifiers: depressed=0x%x, latched=0x%x, locked=0x%x, group=%u",
|
|
|
|
|
|
mods_depressed, mods_latched, mods_locked, group);
|
|
|
|
|
|
|
2020-10-20 21:01:33 +02:00
|
|
|
|
if (seat->kbd.xkb_state != NULL) {
|
|
|
|
|
|
xkb_state_update_mask(
|
|
|
|
|
|
seat->kbd.xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group);
|
|
|
|
|
|
|
|
|
|
|
|
/* Update state of modifiers we're interested in for e.g mouse events */
|
2021-11-21 11:53:22 +01:00
|
|
|
|
seat->kbd.shift = seat->kbd.mod_shift != XKB_MOD_INVALID
|
|
|
|
|
|
? xkb_state_mod_index_is_active(
|
|
|
|
|
|
seat->kbd.xkb_state, seat->kbd.mod_shift, XKB_STATE_MODS_EFFECTIVE)
|
|
|
|
|
|
: false;
|
|
|
|
|
|
seat->kbd.alt = seat->kbd.mod_alt != XKB_MOD_INVALID
|
|
|
|
|
|
? xkb_state_mod_index_is_active(
|
|
|
|
|
|
seat->kbd.xkb_state, seat->kbd.mod_alt, XKB_STATE_MODS_EFFECTIVE)
|
|
|
|
|
|
: false;
|
|
|
|
|
|
seat->kbd.ctrl = seat->kbd.mod_ctrl != XKB_MOD_INVALID
|
|
|
|
|
|
? xkb_state_mod_index_is_active(
|
|
|
|
|
|
seat->kbd.xkb_state, seat->kbd.mod_ctrl, XKB_STATE_MODS_EFFECTIVE)
|
|
|
|
|
|
: false;
|
2021-11-21 12:01:16 +01:00
|
|
|
|
seat->kbd.super = seat->kbd.mod_super != XKB_MOD_INVALID
|
2021-11-21 11:53:22 +01:00
|
|
|
|
? xkb_state_mod_index_is_active(
|
2021-11-21 12:01:16 +01:00
|
|
|
|
seat->kbd.xkb_state, seat->kbd.mod_super, XKB_STATE_MODS_EFFECTIVE)
|
2021-11-21 11:53:22 +01:00
|
|
|
|
: false;
|
2020-10-20 21:01:33 +02:00
|
|
|
|
}
|
2020-07-08 16:45:26 +02:00
|
|
|
|
|
|
|
|
|
|
if (seat->kbd_focus && seat->kbd_focus->active_surface == TERM_SURF_GRID)
|
2020-07-31 17:09:06 +02:00
|
|
|
|
term_xcursor_update_for_seat(seat->kbd_focus, seat);
|
2019-06-19 10:04:47 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
|
keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard,
|
|
|
|
|
|
int32_t rate, int32_t delay)
|
|
|
|
|
|
{
|
2020-07-08 16:45:26 +02:00
|
|
|
|
struct seat *seat = data;
|
2019-06-19 10:04:47 +02:00
|
|
|
|
LOG_DBG("keyboard repeat: rate=%d, delay=%d", rate, delay);
|
2020-07-08 16:45:26 +02:00
|
|
|
|
seat->kbd.repeat.rate = rate;
|
|
|
|
|
|
seat->kbd.repeat.delay = delay;
|
2019-06-19 10:04:47 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const struct wl_keyboard_listener keyboard_listener = {
|
|
|
|
|
|
.keymap = &keyboard_keymap,
|
|
|
|
|
|
.enter = &keyboard_enter,
|
|
|
|
|
|
.leave = &keyboard_leave,
|
|
|
|
|
|
.key = &keyboard_key,
|
|
|
|
|
|
.modifiers = &keyboard_modifiers,
|
|
|
|
|
|
.repeat_info = &keyboard_repeat_info,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2020-07-08 16:45:26 +02:00
|
|
|
|
input_repeat(struct seat *seat, uint32_t key)
|
2019-06-19 10:04:47 +02:00
|
|
|
|
{
|
2020-11-03 19:44:51 +01:00
|
|
|
|
/* Should be cleared as soon as we loose focus */
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(seat->kbd_focus != NULL);
|
2020-11-03 19:44:51 +01:00
|
|
|
|
struct terminal *term = seat->kbd_focus;
|
|
|
|
|
|
|
|
|
|
|
|
key_press_release(seat, term, seat->kbd.serial, key, XKB_KEY_DOWN);
|
2019-06-19 10:04:47 +02:00
|
|
|
|
}
|
2019-07-05 10:44:57 +02:00
|
|
|
|
|
2020-02-29 09:32:22 +01:00
|
|
|
|
static bool
|
|
|
|
|
|
is_top_left(const struct terminal *term, int x, int y)
|
|
|
|
|
|
{
|
2020-03-02 18:42:49 +01:00
|
|
|
|
int csd_border_size = term->conf->csd.border_width;
|
2020-02-29 09:32:22 +01:00
|
|
|
|
return (
|
2020-10-20 21:00:19 +02:00
|
|
|
|
(!term->window->is_tiled_top && !term->window->is_tiled_left) &&
|
|
|
|
|
|
((term->active_surface == TERM_SURF_BORDER_LEFT && y < 10 * term->scale) ||
|
|
|
|
|
|
(term->active_surface == TERM_SURF_BORDER_TOP && x < (10 + csd_border_size) * term->scale)));
|
2020-02-29 09:32:22 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
|
is_top_right(const struct terminal *term, int x, int y)
|
|
|
|
|
|
{
|
2020-03-02 18:42:49 +01:00
|
|
|
|
int csd_border_size = term->conf->csd.border_width;
|
2020-02-29 09:32:22 +01:00
|
|
|
|
return (
|
2020-10-20 21:00:19 +02:00
|
|
|
|
(!term->window->is_tiled_top && !term->window->is_tiled_right) &&
|
|
|
|
|
|
((term->active_surface == TERM_SURF_BORDER_RIGHT && y < 10 * term->scale) ||
|
|
|
|
|
|
(term->active_surface == TERM_SURF_BORDER_TOP && x > term->width + 1 * csd_border_size * term->scale - 10 * term->scale)));
|
2020-02-29 09:32:22 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
|
is_bottom_left(const struct terminal *term, int x, int y)
|
|
|
|
|
|
{
|
2020-03-02 18:42:49 +01:00
|
|
|
|
int csd_title_size = term->conf->csd.title_height;
|
|
|
|
|
|
int csd_border_size = term->conf->csd.border_width;
|
2020-02-29 09:32:22 +01:00
|
|
|
|
return (
|
2020-10-20 21:00:19 +02:00
|
|
|
|
(!term->window->is_tiled_bottom && !term->window->is_tiled_left) &&
|
|
|
|
|
|
((term->active_surface == TERM_SURF_BORDER_LEFT && y > csd_title_size * term->scale + term->height) ||
|
|
|
|
|
|
(term->active_surface == TERM_SURF_BORDER_BOTTOM && x < (10 + csd_border_size) * term->scale)));
|
2020-02-29 09:32:22 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
|
is_bottom_right(const struct terminal *term, int x, int y)
|
|
|
|
|
|
{
|
2020-03-02 18:42:49 +01:00
|
|
|
|
int csd_title_size = term->conf->csd.title_height;
|
|
|
|
|
|
int csd_border_size = term->conf->csd.border_width;
|
2020-02-29 09:32:22 +01:00
|
|
|
|
return (
|
2020-10-20 21:00:19 +02:00
|
|
|
|
(!term->window->is_tiled_bottom && !term->window->is_tiled_right) &&
|
|
|
|
|
|
((term->active_surface == TERM_SURF_BORDER_RIGHT && y > csd_title_size * term->scale + term->height) ||
|
|
|
|
|
|
(term->active_surface == TERM_SURF_BORDER_BOTTOM && x > term->width + 1 * csd_border_size * term->scale - 10 * term->scale)));
|
2020-02-29 09:32:22 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
|
|
xcursor_for_csd_border(struct terminal *term, int x, int y)
|
|
|
|
|
|
{
|
2020-07-09 09:52:11 +02:00
|
|
|
|
if (is_top_left(term, x, y)) return XCURSOR_TOP_LEFT_CORNER;
|
|
|
|
|
|
else if (is_top_right(term, x, y)) return XCURSOR_TOP_RIGHT_CORNER;
|
|
|
|
|
|
else if (is_bottom_left(term, x, y)) return XCURSOR_BOTTOM_LEFT_CORNER;
|
|
|
|
|
|
else if (is_bottom_right(term, x, y)) return XCURSOR_BOTTOM_RIGHT_CORNER;
|
|
|
|
|
|
else if (term->active_surface == TERM_SURF_BORDER_LEFT) return XCURSOR_LEFT_SIDE;
|
|
|
|
|
|
else if (term->active_surface == TERM_SURF_BORDER_RIGHT) return XCURSOR_RIGHT_SIDE;
|
|
|
|
|
|
else if (term->active_surface == TERM_SURF_BORDER_TOP) return XCURSOR_TOP_SIDE;
|
|
|
|
|
|
else if (term->active_surface == TERM_SURF_BORDER_BOTTOM) return XCURSOR_BOTTOM_SIDE;
|
2020-02-29 09:32:22 +01:00
|
|
|
|
else {
|
2021-02-09 15:16:19 +00:00
|
|
|
|
BUG("Unreachable");
|
2020-02-29 09:32:22 +01:00
|
|
|
|
return NULL;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-07-05 10:44:57 +02:00
|
|
|
|
static void
|
|
|
|
|
|
wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
|
|
|
|
|
|
uint32_t serial, struct wl_surface *surface,
|
|
|
|
|
|
wl_fixed_t surface_x, wl_fixed_t surface_y)
|
|
|
|
|
|
{
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(surface != NULL);
|
2019-11-28 19:46:27 +01:00
|
|
|
|
|
2021-03-04 08:57:31 +01:00
|
|
|
|
if (surface == NULL) {
|
|
|
|
|
|
/* Seen on mutter-3.38 */
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-08 16:45:26 +02:00
|
|
|
|
struct seat *seat = data;
|
2020-02-29 12:09:28 +01:00
|
|
|
|
struct wl_window *win = wl_surface_get_user_data(surface);
|
|
|
|
|
|
struct terminal *term = win->term;
|
2019-10-27 18:43:07 +01:00
|
|
|
|
|
2020-07-09 11:20:46 +02:00
|
|
|
|
seat->pointer.serial = serial;
|
2020-07-31 17:09:06 +02:00
|
|
|
|
seat->pointer.hidden = false;
|
2020-07-09 11:20:46 +02:00
|
|
|
|
|
2020-03-14 11:51:47 +01:00
|
|
|
|
LOG_DBG("pointer-enter: pointer=%p, serial=%u, surface = %p, new-moused = %p",
|
2020-08-23 07:50:27 +02:00
|
|
|
|
(void *)wl_pointer, serial, (void *)surface, (void *)term);
|
2019-11-29 22:12:54 +01:00
|
|
|
|
|
2021-08-04 17:27:34 +02:00
|
|
|
|
xassert(tll_length(seat->mouse.buttons) == 0);
|
|
|
|
|
|
|
2020-07-08 19:52:17 +02:00
|
|
|
|
/* Scale may have changed */
|
|
|
|
|
|
wayl_reload_xcursor_theme(seat, term->scale);
|
|
|
|
|
|
|
2020-07-08 16:45:26 +02:00
|
|
|
|
seat->mouse_focus = term;
|
2019-07-05 15:13:06 +02:00
|
|
|
|
|
2019-09-26 18:41:26 +02:00
|
|
|
|
int x = wl_fixed_to_int(surface_x) * term->scale;
|
|
|
|
|
|
int y = wl_fixed_to_int(surface_y) * term->scale;
|
2019-07-05 15:13:06 +02:00
|
|
|
|
|
2020-02-24 22:40:24 +01:00
|
|
|
|
switch ((term->active_surface = term_surface_kind(term, surface))) {
|
2020-07-26 12:31:13 +02:00
|
|
|
|
case TERM_SURF_GRID: {
|
2020-08-08 10:07:25 +02:00
|
|
|
|
/*
|
|
|
|
|
|
* Translate x,y pixel coordinate to a cell coordinate, or -1
|
|
|
|
|
|
* if the cursor is outside the grid. I.e. if it is inside the
|
|
|
|
|
|
* margins.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2020-08-07 22:04:37 +02:00
|
|
|
|
if (x < term->margins.left || x >= term->width - term->margins.right)
|
|
|
|
|
|
seat->mouse.col = -1;
|
|
|
|
|
|
else
|
|
|
|
|
|
seat->mouse.col = (x - term->margins.left) / term->cell_width;
|
2020-07-26 12:31:13 +02:00
|
|
|
|
|
2020-08-07 22:04:37 +02:00
|
|
|
|
if (y < term->margins.top || y >= term->height - term->margins.bottom)
|
|
|
|
|
|
seat->mouse.row = -1;
|
|
|
|
|
|
else
|
|
|
|
|
|
seat->mouse.row = (y - term->margins.top) / term->cell_height;
|
2020-07-26 12:31:13 +02:00
|
|
|
|
|
2020-07-31 17:09:06 +02:00
|
|
|
|
term_xcursor_update_for_seat(term, seat);
|
2020-02-24 22:40:24 +01:00
|
|
|
|
break;
|
2020-07-26 12:31:13 +02:00
|
|
|
|
}
|
2020-02-24 22:40:24 +01:00
|
|
|
|
|
|
|
|
|
|
case TERM_SURF_SEARCH:
|
2020-07-26 12:37:35 +02:00
|
|
|
|
case TERM_SURF_SCROLLBACK_INDICATOR:
|
2020-08-14 07:35:01 +02:00
|
|
|
|
case TERM_SURF_RENDER_TIMER:
|
2021-02-06 11:31:22 +01:00
|
|
|
|
case TERM_SURF_JUMP_LABEL:
|
2020-02-29 15:29:00 +01:00
|
|
|
|
case TERM_SURF_TITLE:
|
2020-07-09 09:52:11 +02:00
|
|
|
|
render_xcursor_set(seat, term, XCURSOR_LEFT_PTR);
|
2020-02-24 22:40:24 +01:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case TERM_SURF_BORDER_LEFT:
|
|
|
|
|
|
case TERM_SURF_BORDER_RIGHT:
|
|
|
|
|
|
case TERM_SURF_BORDER_TOP:
|
2020-02-29 09:32:22 +01:00
|
|
|
|
case TERM_SURF_BORDER_BOTTOM:
|
2020-07-09 09:52:11 +02:00
|
|
|
|
render_xcursor_set(seat, term, xcursor_for_csd_border(term, x, y));
|
2020-02-25 20:31:13 +01:00
|
|
|
|
break;
|
|
|
|
|
|
|
2020-03-02 20:29:28 +01:00
|
|
|
|
case TERM_SURF_BUTTON_MINIMIZE:
|
|
|
|
|
|
case TERM_SURF_BUTTON_MAXIMIZE:
|
2020-03-03 18:22:32 +01:00
|
|
|
|
case TERM_SURF_BUTTON_CLOSE:
|
2020-07-09 09:52:11 +02:00
|
|
|
|
render_xcursor_set(seat, term, XCURSOR_LEFT_PTR);
|
2020-03-06 19:16:54 +01:00
|
|
|
|
render_refresh_csd(term);
|
2020-03-02 20:29:28 +01:00
|
|
|
|
break;
|
|
|
|
|
|
|
2020-02-29 15:29:00 +01:00
|
|
|
|
case TERM_SURF_NONE:
|
2021-02-10 09:01:51 +00:00
|
|
|
|
BUG("Invalid surface type");
|
2020-02-24 22:40:24 +01:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
2019-07-05 10:44:57 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
|
wl_pointer_leave(void *data, struct wl_pointer *wl_pointer,
|
|
|
|
|
|
uint32_t serial, struct wl_surface *surface)
|
|
|
|
|
|
{
|
2020-07-08 16:45:26 +02:00
|
|
|
|
struct seat *seat = data;
|
|
|
|
|
|
struct terminal *old_moused = seat->mouse_focus;
|
2019-11-29 22:13:19 +01:00
|
|
|
|
|
2020-03-14 11:51:47 +01:00
|
|
|
|
LOG_DBG(
|
2020-07-11 11:13:45 +02:00
|
|
|
|
"%s: pointer-leave: pointer=%p, serial=%u, surface = %p, old-moused = %p",
|
2020-08-23 07:50:27 +02:00
|
|
|
|
seat->name, (void *)wl_pointer, serial, (void *)surface,
|
|
|
|
|
|
(void *)old_moused);
|
2019-11-29 22:13:19 +01:00
|
|
|
|
|
2020-07-31 17:09:06 +02:00
|
|
|
|
seat->pointer.hidden = false;
|
|
|
|
|
|
|
2020-07-08 16:45:26 +02:00
|
|
|
|
if (seat->pointer.xcursor_callback != NULL) {
|
2020-02-15 19:02:43 +01:00
|
|
|
|
/* A cursor frame callback may never be called if the pointer leaves our surface */
|
2020-07-08 16:45:26 +02:00
|
|
|
|
wl_callback_destroy(seat->pointer.xcursor_callback);
|
|
|
|
|
|
seat->pointer.xcursor_callback = NULL;
|
2020-07-09 09:52:11 +02:00
|
|
|
|
seat->pointer.xcursor_pending = false;
|
2020-07-08 16:45:26 +02:00
|
|
|
|
seat->pointer.xcursor = NULL;
|
2020-02-15 19:02:43 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-02-29 12:08:59 +01:00
|
|
|
|
/* Reset mouse state */
|
2020-08-10 18:59:03 +02:00
|
|
|
|
seat->mouse.x = seat->mouse.y = 0;
|
|
|
|
|
|
seat->mouse.col = seat->mouse.row = 0;
|
2020-12-12 19:05:24 +01:00
|
|
|
|
tll_free(seat->mouse.buttons);
|
|
|
|
|
|
seat->mouse.count = 0;
|
|
|
|
|
|
seat->mouse.last_released_button = 0;
|
2020-08-10 18:59:03 +02:00
|
|
|
|
memset(&seat->mouse.last_time, 0, sizeof(seat->mouse.last_time));
|
2021-08-05 18:34:09 +02:00
|
|
|
|
for (size_t i = 0; i < ALEN(seat->mouse.aggregated); i++)
|
|
|
|
|
|
seat->mouse.aggregated[i] = 0.0;
|
2020-08-10 18:59:03 +02:00
|
|
|
|
seat->mouse.have_discrete = false;
|
2020-02-29 12:08:59 +01:00
|
|
|
|
|
2020-07-08 16:45:26 +02:00
|
|
|
|
seat->mouse_focus = NULL;
|
2019-11-29 22:13:19 +01:00
|
|
|
|
if (old_moused == NULL) {
|
2019-11-28 19:47:00 +01:00
|
|
|
|
LOG_WARN(
|
|
|
|
|
|
"compositor sent pointer_leave event without a pointer_enter "
|
2020-08-23 07:50:27 +02:00
|
|
|
|
"event: surface=%p", (void *)surface);
|
2020-02-24 22:41:46 +01:00
|
|
|
|
} else {
|
2020-02-29 12:09:28 +01:00
|
|
|
|
if (surface != NULL) {
|
|
|
|
|
|
/* Sway 1.4 sends this event with a NULL surface when we destroy the window */
|
2020-08-07 20:42:34 +01:00
|
|
|
|
const struct wl_window UNUSED *win = wl_surface_get_user_data(surface);
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(old_moused == win->term);
|
2020-02-29 12:09:28 +01:00
|
|
|
|
}
|
2020-03-02 20:29:28 +01:00
|
|
|
|
|
|
|
|
|
|
enum term_surface active_surface = old_moused->active_surface;
|
|
|
|
|
|
|
2020-02-24 22:41:46 +01:00
|
|
|
|
old_moused->active_surface = TERM_SURF_NONE;
|
2020-07-31 17:09:06 +02:00
|
|
|
|
term_xcursor_update_for_seat(old_moused, seat);
|
2020-03-02 20:29:28 +01:00
|
|
|
|
|
|
|
|
|
|
switch (active_surface) {
|
|
|
|
|
|
case TERM_SURF_BUTTON_MINIMIZE:
|
|
|
|
|
|
case TERM_SURF_BUTTON_MAXIMIZE:
|
2020-03-03 18:22:32 +01:00
|
|
|
|
case TERM_SURF_BUTTON_CLOSE:
|
2021-07-31 19:08:51 +02:00
|
|
|
|
if (old_moused->shutdown.in_progress)
|
2020-03-03 18:22:32 +01:00
|
|
|
|
break;
|
|
|
|
|
|
|
2020-03-06 19:16:54 +01:00
|
|
|
|
render_refresh_csd(old_moused);
|
2020-03-02 20:29:28 +01:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case TERM_SURF_GRID:
|
2021-11-25 15:21:53 +01:00
|
|
|
|
selection_finalize(seat, old_moused, seat->pointer.serial);
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case TERM_SURF_NONE:
|
2020-03-02 20:29:28 +01:00
|
|
|
|
case TERM_SURF_SEARCH:
|
2020-07-26 12:37:35 +02:00
|
|
|
|
case TERM_SURF_SCROLLBACK_INDICATOR:
|
2020-08-14 07:35:01 +02:00
|
|
|
|
case TERM_SURF_RENDER_TIMER:
|
2021-02-06 11:31:22 +01:00
|
|
|
|
case TERM_SURF_JUMP_LABEL:
|
2020-03-02 20:29:28 +01:00
|
|
|
|
case TERM_SURF_TITLE:
|
|
|
|
|
|
case TERM_SURF_BORDER_LEFT:
|
|
|
|
|
|
case TERM_SURF_BORDER_RIGHT:
|
|
|
|
|
|
case TERM_SURF_BORDER_TOP:
|
|
|
|
|
|
case TERM_SURF_BORDER_BOTTOM:
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-02-24 22:41:46 +01:00
|
|
|
|
}
|
2019-07-05 10:44:57 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
|
wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
|
|
|
|
|
|
uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y)
|
|
|
|
|
|
{
|
2020-07-08 16:45:26 +02:00
|
|
|
|
struct seat *seat = data;
|
|
|
|
|
|
struct wayland *wayl = seat->wayl;
|
|
|
|
|
|
struct terminal *term = seat->mouse_focus;
|
2020-02-29 15:38:04 +01:00
|
|
|
|
struct wl_window *win = term->window;
|
2019-11-22 21:49:50 +01:00
|
|
|
|
|
2020-08-23 07:50:27 +02:00
|
|
|
|
LOG_DBG("pointer_motion: pointer=%p, x=%d, y=%d", (void *)wl_pointer,
|
2020-03-14 11:51:47 +01:00
|
|
|
|
wl_fixed_to_int(surface_x), wl_fixed_to_int(surface_y));
|
|
|
|
|
|
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(term != NULL);
|
2019-07-05 15:13:06 +02:00
|
|
|
|
|
2019-09-26 18:39:49 +02:00
|
|
|
|
int x = wl_fixed_to_int(surface_x) * term->scale;
|
|
|
|
|
|
int y = wl_fixed_to_int(surface_y) * term->scale;
|
2019-07-05 15:13:06 +02:00
|
|
|
|
|
2020-07-31 17:09:06 +02:00
|
|
|
|
seat->pointer.hidden = false;
|
2020-07-08 16:45:26 +02:00
|
|
|
|
seat->mouse.x = x;
|
|
|
|
|
|
seat->mouse.y = y;
|
2020-02-29 09:32:22 +01:00
|
|
|
|
|
2020-12-12 19:05:24 +01:00
|
|
|
|
enum term_surface surf_kind = term->active_surface;
|
|
|
|
|
|
int button = 0;
|
|
|
|
|
|
bool send_to_client = false;
|
|
|
|
|
|
|
|
|
|
|
|
if (tll_length(seat->mouse.buttons) > 0) {
|
|
|
|
|
|
const struct button_tracker *tracker = &tll_front(seat->mouse.buttons);
|
|
|
|
|
|
surf_kind = tracker->surf_kind;
|
|
|
|
|
|
button = tracker->button;
|
|
|
|
|
|
send_to_client = tracker->send_to_client;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
switch (surf_kind) {
|
2020-02-25 20:31:13 +01:00
|
|
|
|
case TERM_SURF_NONE:
|
|
|
|
|
|
case TERM_SURF_SEARCH:
|
2020-07-26 12:37:35 +02:00
|
|
|
|
case TERM_SURF_SCROLLBACK_INDICATOR:
|
2020-08-14 07:35:01 +02:00
|
|
|
|
case TERM_SURF_RENDER_TIMER:
|
2021-02-06 11:31:22 +01:00
|
|
|
|
case TERM_SURF_JUMP_LABEL:
|
2020-03-02 20:29:28 +01:00
|
|
|
|
case TERM_SURF_BUTTON_MINIMIZE:
|
|
|
|
|
|
case TERM_SURF_BUTTON_MAXIMIZE:
|
|
|
|
|
|
case TERM_SURF_BUTTON_CLOSE:
|
2020-02-29 15:38:04 +01:00
|
|
|
|
break;
|
|
|
|
|
|
|
2020-02-25 20:31:13 +01:00
|
|
|
|
case TERM_SURF_TITLE:
|
2020-02-29 15:38:04 +01:00
|
|
|
|
/* We've started a 'move' timer, but user started dragging
|
|
|
|
|
|
* right away - abort the timer and initiate the actual move
|
|
|
|
|
|
* right away */
|
2020-12-12 19:05:24 +01:00
|
|
|
|
if (button == BTN_LEFT && win->csd.move_timeout_fd != -1) {
|
2020-02-29 15:38:04 +01:00
|
|
|
|
fdm_del(wayl->fdm, win->csd.move_timeout_fd);
|
|
|
|
|
|
win->csd.move_timeout_fd = -1;
|
2020-07-08 16:45:26 +02:00
|
|
|
|
xdg_toplevel_move(win->xdg_toplevel, seat->wl_seat, win->csd.serial);
|
2020-02-29 15:38:04 +01:00
|
|
|
|
}
|
2020-02-25 20:31:13 +01:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case TERM_SURF_BORDER_LEFT:
|
|
|
|
|
|
case TERM_SURF_BORDER_RIGHT:
|
|
|
|
|
|
case TERM_SURF_BORDER_TOP:
|
|
|
|
|
|
case TERM_SURF_BORDER_BOTTOM:
|
2020-07-09 09:52:11 +02:00
|
|
|
|
render_xcursor_set(seat, term, xcursor_for_csd_border(term, x, y));
|
2020-02-25 20:31:13 +01:00
|
|
|
|
break;
|
|
|
|
|
|
|
2020-02-29 15:29:00 +01:00
|
|
|
|
case TERM_SURF_GRID: {
|
2020-07-26 12:31:13 +02:00
|
|
|
|
int old_col = seat->mouse.col;
|
|
|
|
|
|
int old_row = seat->mouse.row;
|
|
|
|
|
|
|
2020-08-07 22:04:37 +02:00
|
|
|
|
/*
|
|
|
|
|
|
* While the seat's mouse coordinates must always be on the
|
|
|
|
|
|
* grid, or -1, we allow updating the selection even when the
|
|
|
|
|
|
* mouse is outside the grid (could also be outside the
|
|
|
|
|
|
* terminal window).
|
|
|
|
|
|
*/
|
|
|
|
|
|
int selection_col;
|
|
|
|
|
|
int selection_row;
|
|
|
|
|
|
|
|
|
|
|
|
if (x < term->margins.left) {
|
|
|
|
|
|
seat->mouse.col = -1;
|
|
|
|
|
|
selection_col = 0;
|
|
|
|
|
|
} else if (x >= term->width - term->margins.right) {
|
|
|
|
|
|
seat->mouse.col = -1;
|
|
|
|
|
|
selection_col = term->cols - 1;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
seat->mouse.col = (x - term->margins.left) / term->cell_width;
|
|
|
|
|
|
selection_col = seat->mouse.col;
|
|
|
|
|
|
}
|
2020-07-26 12:31:13 +02:00
|
|
|
|
|
2020-08-07 22:04:37 +02:00
|
|
|
|
if (y < term->margins.top) {
|
|
|
|
|
|
seat->mouse.row = -1;
|
|
|
|
|
|
selection_row = 0;
|
|
|
|
|
|
} else if (y >= term->height - term->margins.bottom) {
|
|
|
|
|
|
seat->mouse.row = -1;
|
|
|
|
|
|
selection_row = term->rows - 1;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
seat->mouse.row = (y - term->margins.top) / term->cell_height;
|
|
|
|
|
|
selection_row = seat->mouse.row;
|
|
|
|
|
|
}
|
2019-07-05 15:13:06 +02:00
|
|
|
|
|
input: report mouse drag events also when the pointer is outside the grid
As long as the mouse button was *pressed* while the pointer was inside
the grid, we want to keep reporting motion events until the button is
released.
Even when the pointer moves outside the grid (but in this case, the
reported coordinates are bounded by the grid size).
This patch also tries to improve multi-button handling (i.e. multiple
buttons pressed at the same time), and the events we report to the
client for these, in the following ways:
* Motion events now report the *initial* button. That is, if you start
a drag operation with the LEFT button, then press RIGHT (before
releasing LEFT), keep reporting LEFT in the motion events.
* Mouse release events are reported for *any* button, as long as the
pointer is *inside* the grid, *or*, the button released was the
button used to start a drag operation.
The last point is important; if we have reported a button press
followed by motion events (i.e. a drag operation), we need to report
the button release, *even* if the pointer is outside the grid.
Note that the client may receive unbalanced button press/release
events in the following ways if the user pressed one, and then a
second button *inside* the grid, then releases the *first*
button (possibly outside the grid), and finally releases the *second*
button *outside* the grid.
In this case, both buttons will report press events. The first button
will report a release event since it is the initial button in the drag
operation.
However, we don’t track the fact that the second button is being
pressed, and thus if it is released outside the grid, it wont generate
a release event.
2020-12-11 20:48:43 +01:00
|
|
|
|
/*
|
|
|
|
|
|
* If client is receiving events (because the button was
|
|
|
|
|
|
* pressed while the cursor was inside the grid area), then
|
|
|
|
|
|
* make sure it receives valid coordinates.
|
|
|
|
|
|
*/
|
2020-12-12 19:05:24 +01:00
|
|
|
|
if (send_to_client) {
|
input: report mouse drag events also when the pointer is outside the grid
As long as the mouse button was *pressed* while the pointer was inside
the grid, we want to keep reporting motion events until the button is
released.
Even when the pointer moves outside the grid (but in this case, the
reported coordinates are bounded by the grid size).
This patch also tries to improve multi-button handling (i.e. multiple
buttons pressed at the same time), and the events we report to the
client for these, in the following ways:
* Motion events now report the *initial* button. That is, if you start
a drag operation with the LEFT button, then press RIGHT (before
releasing LEFT), keep reporting LEFT in the motion events.
* Mouse release events are reported for *any* button, as long as the
pointer is *inside* the grid, *or*, the button released was the
button used to start a drag operation.
The last point is important; if we have reported a button press
followed by motion events (i.e. a drag operation), we need to report
the button release, *even* if the pointer is outside the grid.
Note that the client may receive unbalanced button press/release
events in the following ways if the user pressed one, and then a
second button *inside* the grid, then releases the *first*
button (possibly outside the grid), and finally releases the *second*
button *outside* the grid.
In this case, both buttons will report press events. The first button
will report a release event since it is the initial button in the drag
operation.
However, we don’t track the fact that the second button is being
pressed, and thus if it is released outside the grid, it wont generate
a release event.
2020-12-11 20:48:43 +01:00
|
|
|
|
seat->mouse.col = selection_col;
|
|
|
|
|
|
seat->mouse.row = selection_row;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(seat->mouse.col == -1 || (seat->mouse.col >= 0 && seat->mouse.col < term->cols));
|
|
|
|
|
|
xassert(seat->mouse.row == -1 || (seat->mouse.row >= 0 && seat->mouse.row < term->rows));
|
2019-07-11 11:09:34 +02:00
|
|
|
|
|
2020-08-07 22:04:37 +02:00
|
|
|
|
term_xcursor_update_for_seat(term, seat);
|
2019-07-11 09:51:51 +02:00
|
|
|
|
|
2020-08-07 22:04:37 +02:00
|
|
|
|
/* Cursor has moved to a different cell since last time */
|
|
|
|
|
|
bool cursor_is_on_new_cell
|
|
|
|
|
|
= old_col != seat->mouse.col || old_row != seat->mouse.row;
|
2019-07-11 09:51:51 +02:00
|
|
|
|
|
2020-08-08 10:08:47 +02:00
|
|
|
|
/* Cursor is inside the grid, i.e. *not* in the margins */
|
2020-10-11 12:03:20 +02:00
|
|
|
|
const bool cursor_is_on_grid = seat->mouse.col >= 0 && seat->mouse.row >= 0;
|
|
|
|
|
|
|
2020-10-11 18:18:18 +02:00
|
|
|
|
enum selection_scroll_direction auto_scroll_direction
|
|
|
|
|
|
= y < term->margins.top ? SELECTION_SCROLL_UP
|
|
|
|
|
|
: y > term->height - term->margins.bottom ? SELECTION_SCROLL_DOWN
|
|
|
|
|
|
: SELECTION_SCROLL_NOT;
|
selection: auto-scroll: selection keeps scrolling while mouse is outside grid
Moving the mouse outside the grid while we have an on-going selection
now starts a timer. The interval of this timer depends on the mouse’s
distance from the grid - the further away the mouse is, the shorter
interval.
On each timer timeout, we scroll one line, and update the
selection. Thus, the shorter the interval, the faster we scroll.
The timer is canceled as soon as the mouse enters the grid again, or
the selection is either canceled or finalized.
The timer FD is created and destroyed on-demand.
Most of the logic is now in selection.c. The exception is the
calculation of the timer interval, which depends on the mouse’s
position. Thus, this is done in input.c.
The scroll+selection update logic needs to know a) which direction
we’re scrolling in, and b) which *column* the selection should be
updated with.
If the mouse is outside the grid’s left or right margins, the stored
mouse column will be -1. I.e. we don’t know whether the mouse is on
the left or right side of the grid. This is why the caller, that
starts the timer, must provide this value.
The same applies to top and bottom margins, but since we already have
the scroll *direction*, which row value to use can be derived from this.
2020-10-11 15:44:20 +02:00
|
|
|
|
|
2020-10-11 18:18:18 +02:00
|
|
|
|
if (auto_scroll_direction == SELECTION_SCROLL_NOT)
|
selection: auto-scroll: selection keeps scrolling while mouse is outside grid
Moving the mouse outside the grid while we have an on-going selection
now starts a timer. The interval of this timer depends on the mouse’s
distance from the grid - the further away the mouse is, the shorter
interval.
On each timer timeout, we scroll one line, and update the
selection. Thus, the shorter the interval, the faster we scroll.
The timer is canceled as soon as the mouse enters the grid again, or
the selection is either canceled or finalized.
The timer FD is created and destroyed on-demand.
Most of the logic is now in selection.c. The exception is the
calculation of the timer interval, which depends on the mouse’s
position. Thus, this is done in input.c.
The scroll+selection update logic needs to know a) which direction
we’re scrolling in, and b) which *column* the selection should be
updated with.
If the mouse is outside the grid’s left or right margins, the stored
mouse column will be -1. I.e. we don’t know whether the mouse is on
the left or right side of the grid. This is why the caller, that
starts the timer, must provide this value.
The same applies to top and bottom margins, but since we already have
the scroll *direction*, which row value to use can be derived from this.
2020-10-11 15:44:20 +02:00
|
|
|
|
selection_stop_scroll_timer(term);
|
2020-08-08 10:00:18 +02:00
|
|
|
|
|
2020-08-07 22:04:37 +02:00
|
|
|
|
/* Update selection */
|
2020-08-11 16:36:42 +02:00
|
|
|
|
if (!term->is_searching) {
|
2020-10-11 18:18:18 +02:00
|
|
|
|
if (auto_scroll_direction != SELECTION_SCROLL_NOT) {
|
|
|
|
|
|
/*
|
|
|
|
|
|
* Start ‘selection auto-scrolling’
|
|
|
|
|
|
*
|
|
|
|
|
|
* The speed of the scrolling is proportional to the
|
|
|
|
|
|
* distance between the mouse and the grid; the
|
2020-10-11 18:22:29 +02:00
|
|
|
|
* further away the mouse is, the faster we scroll.
|
2020-10-11 18:18:18 +02:00
|
|
|
|
*
|
2020-10-11 18:23:01 +02:00
|
|
|
|
* Note that the speed is measured in ‘intervals (in
|
2020-10-11 18:18:18 +02:00
|
|
|
|
* ns) between each timed scroll of a single line’.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Thus, the further away the mouse is, the smaller
|
|
|
|
|
|
* interval value we use.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
int distance = auto_scroll_direction == SELECTION_SCROLL_UP
|
selection: auto-scroll: selection keeps scrolling while mouse is outside grid
Moving the mouse outside the grid while we have an on-going selection
now starts a timer. The interval of this timer depends on the mouse’s
distance from the grid - the further away the mouse is, the shorter
interval.
On each timer timeout, we scroll one line, and update the
selection. Thus, the shorter the interval, the faster we scroll.
The timer is canceled as soon as the mouse enters the grid again, or
the selection is either canceled or finalized.
The timer FD is created and destroyed on-demand.
Most of the logic is now in selection.c. The exception is the
calculation of the timer interval, which depends on the mouse’s
position. Thus, this is done in input.c.
The scroll+selection update logic needs to know a) which direction
we’re scrolling in, and b) which *column* the selection should be
updated with.
If the mouse is outside the grid’s left or right margins, the stored
mouse column will be -1. I.e. we don’t know whether the mouse is on
the left or right side of the grid. This is why the caller, that
starts the timer, must provide this value.
The same applies to top and bottom margins, but since we already have
the scroll *direction*, which row value to use can be derived from this.
2020-10-11 15:44:20 +02:00
|
|
|
|
? term->margins.top - y
|
|
|
|
|
|
: y - (term->height - term->margins.bottom);
|
|
|
|
|
|
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(distance > 0);
|
2020-10-11 18:09:30 +02:00
|
|
|
|
int divisor
|
|
|
|
|
|
= distance * term->conf->scrollback.multiplier / term->scale;
|
selection: auto-scroll: selection keeps scrolling while mouse is outside grid
Moving the mouse outside the grid while we have an on-going selection
now starts a timer. The interval of this timer depends on the mouse’s
distance from the grid - the further away the mouse is, the shorter
interval.
On each timer timeout, we scroll one line, and update the
selection. Thus, the shorter the interval, the faster we scroll.
The timer is canceled as soon as the mouse enters the grid again, or
the selection is either canceled or finalized.
The timer FD is created and destroyed on-demand.
Most of the logic is now in selection.c. The exception is the
calculation of the timer interval, which depends on the mouse’s
position. Thus, this is done in input.c.
The scroll+selection update logic needs to know a) which direction
we’re scrolling in, and b) which *column* the selection should be
updated with.
If the mouse is outside the grid’s left or right margins, the stored
mouse column will be -1. I.e. we don’t know whether the mouse is on
the left or right side of the grid. This is why the caller, that
starts the timer, must provide this value.
The same applies to top and bottom margins, but since we already have
the scroll *direction*, which row value to use can be derived from this.
2020-10-11 15:44:20 +02:00
|
|
|
|
|
|
|
|
|
|
selection_start_scroll_timer(
|
2020-10-11 18:09:30 +02:00
|
|
|
|
term, 400000000 / (divisor > 0 ? divisor : 1),
|
2020-10-11 18:18:18 +02:00
|
|
|
|
auto_scroll_direction, selection_col);
|
2020-10-11 12:03:20 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-12-12 19:05:24 +01:00
|
|
|
|
if (term->selection.ongoing && (cursor_is_on_new_cell ||
|
|
|
|
|
|
term->selection.end.row < 0))
|
|
|
|
|
|
{
|
2020-08-11 16:36:42 +02:00
|
|
|
|
selection_update(term, selection_col, selection_row);
|
2020-12-12 19:05:24 +01:00
|
|
|
|
}
|
2020-08-11 16:36:42 +02:00
|
|
|
|
}
|
2019-07-11 09:51:51 +02:00
|
|
|
|
|
2020-08-07 22:04:37 +02:00
|
|
|
|
/* Send mouse event to client application */
|
2020-12-12 19:05:24 +01:00
|
|
|
|
if (!term_mouse_grabbed(term, seat) &&
|
input: report mouse drag events also when the pointer is outside the grid
As long as the mouse button was *pressed* while the pointer was inside
the grid, we want to keep reporting motion events until the button is
released.
Even when the pointer moves outside the grid (but in this case, the
reported coordinates are bounded by the grid size).
This patch also tries to improve multi-button handling (i.e. multiple
buttons pressed at the same time), and the events we report to the
client for these, in the following ways:
* Motion events now report the *initial* button. That is, if you start
a drag operation with the LEFT button, then press RIGHT (before
releasing LEFT), keep reporting LEFT in the motion events.
* Mouse release events are reported for *any* button, as long as the
pointer is *inside* the grid, *or*, the button released was the
button used to start a drag operation.
The last point is important; if we have reported a button press
followed by motion events (i.e. a drag operation), we need to report
the button release, *even* if the pointer is outside the grid.
Note that the client may receive unbalanced button press/release
events in the following ways if the user pressed one, and then a
second button *inside* the grid, then releases the *first*
button (possibly outside the grid), and finally releases the *second*
button *outside* the grid.
In this case, both buttons will report press events. The first button
will report a release event since it is the initial button in the drag
operation.
However, we don’t track the fact that the second button is being
pressed, and thus if it is released outside the grid, it wont generate
a release event.
2020-12-11 20:48:43 +01:00
|
|
|
|
cursor_is_on_new_cell &&
|
2020-12-12 19:05:24 +01:00
|
|
|
|
((button == 0 && cursor_is_on_grid) ||
|
|
|
|
|
|
(button != 0 && send_to_client)))
|
2020-08-07 19:55:02 +02:00
|
|
|
|
{
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(seat->mouse.col < term->cols);
|
|
|
|
|
|
xassert(seat->mouse.row < term->rows);
|
2019-07-05 15:13:06 +02:00
|
|
|
|
|
2020-07-09 09:52:11 +02:00
|
|
|
|
term_mouse_motion(
|
2020-12-12 19:05:24 +01:00
|
|
|
|
term, button,
|
input: report mouse drag events also when the pointer is outside the grid
As long as the mouse button was *pressed* while the pointer was inside
the grid, we want to keep reporting motion events until the button is
released.
Even when the pointer moves outside the grid (but in this case, the
reported coordinates are bounded by the grid size).
This patch also tries to improve multi-button handling (i.e. multiple
buttons pressed at the same time), and the events we report to the
client for these, in the following ways:
* Motion events now report the *initial* button. That is, if you start
a drag operation with the LEFT button, then press RIGHT (before
releasing LEFT), keep reporting LEFT in the motion events.
* Mouse release events are reported for *any* button, as long as the
pointer is *inside* the grid, *or*, the button released was the
button used to start a drag operation.
The last point is important; if we have reported a button press
followed by motion events (i.e. a drag operation), we need to report
the button release, *even* if the pointer is outside the grid.
Note that the client may receive unbalanced button press/release
events in the following ways if the user pressed one, and then a
second button *inside* the grid, then releases the *first*
button (possibly outside the grid), and finally releases the *second*
button *outside* the grid.
In this case, both buttons will report press events. The first button
will report a release event since it is the initial button in the drag
operation.
However, we don’t track the fact that the second button is being
pressed, and thus if it is released outside the grid, it wont generate
a release event.
2020-12-11 20:48:43 +01:00
|
|
|
|
seat->mouse.row, seat->mouse.col,
|
2020-07-09 09:52:11 +02:00
|
|
|
|
seat->kbd.shift, seat->kbd.alt, seat->kbd.ctrl);
|
|
|
|
|
|
}
|
2020-02-29 15:29:00 +01:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2019-07-05 10:44:57 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-02-29 12:52:55 +01:00
|
|
|
|
static bool
|
|
|
|
|
|
fdm_csd_move(struct fdm *fdm, int fd, int events, void *data)
|
|
|
|
|
|
{
|
2020-07-08 16:45:26 +02:00
|
|
|
|
struct seat *seat = data;
|
2020-02-29 12:52:55 +01:00
|
|
|
|
fdm_del(fdm, fd);
|
|
|
|
|
|
|
2020-07-08 16:45:26 +02:00
|
|
|
|
if (seat->mouse_focus == NULL) {
|
|
|
|
|
|
LOG_WARN(
|
|
|
|
|
|
"%s: CSD move timeout triggered, but seat's has no mouse focused terminal",
|
|
|
|
|
|
seat->name);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct wl_window *win = seat->mouse_focus->window;
|
|
|
|
|
|
|
|
|
|
|
|
win->csd.move_timeout_fd = -1;
|
|
|
|
|
|
xdg_toplevel_move(win->xdg_toplevel, seat->wl_seat, win->csd.serial);
|
2020-02-29 12:52:55 +01:00
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-07-05 10:44:57 +02:00
|
|
|
|
static void
|
|
|
|
|
|
wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
|
|
|
|
|
|
uint32_t serial, uint32_t time, uint32_t button, uint32_t state)
|
|
|
|
|
|
{
|
2020-03-14 11:51:47 +01:00
|
|
|
|
LOG_DBG("BUTTON: pointer=%p, serial=%u, button=%x, state=%u",
|
2020-08-23 07:50:27 +02:00
|
|
|
|
(void *)wl_pointer, serial, button, state);
|
2019-07-05 14:24:51 +02:00
|
|
|
|
|
2020-07-08 16:45:26 +02:00
|
|
|
|
struct seat *seat = data;
|
|
|
|
|
|
struct wayland *wayl = seat->wayl;
|
|
|
|
|
|
struct terminal *term = seat->mouse_focus;
|
2019-07-05 14:24:51 +02:00
|
|
|
|
|
2021-10-20 18:47:36 +02:00
|
|
|
|
seat->pointer.serial = serial;
|
2020-07-31 17:09:06 +02:00
|
|
|
|
seat->pointer.hidden = false;
|
|
|
|
|
|
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(term != NULL);
|
2020-02-24 22:42:04 +01:00
|
|
|
|
|
2020-12-12 21:10:28 +01:00
|
|
|
|
enum term_surface surf_kind = TERM_SURF_NONE;
|
2020-12-12 19:05:24 +01:00
|
|
|
|
bool send_to_client = false;
|
|
|
|
|
|
|
2020-02-29 11:55:43 +01:00
|
|
|
|
if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
|
|
|
|
|
|
/* Time since last click */
|
|
|
|
|
|
struct timeval now, since_last;
|
|
|
|
|
|
gettimeofday(&now, NULL);
|
2020-07-08 16:45:26 +02:00
|
|
|
|
timersub(&now, &seat->mouse.last_time, &since_last);
|
2020-02-29 11:55:43 +01:00
|
|
|
|
|
2020-12-12 19:05:24 +01:00
|
|
|
|
if (seat->mouse.last_released_button == button &&
|
|
|
|
|
|
since_last.tv_sec == 0 && since_last.tv_usec <= 300 * 1000)
|
2020-02-29 11:55:43 +01:00
|
|
|
|
{
|
2020-07-08 16:45:26 +02:00
|
|
|
|
seat->mouse.count++;
|
2020-02-29 11:55:43 +01:00
|
|
|
|
} else
|
2020-07-08 16:45:26 +02:00
|
|
|
|
seat->mouse.count = 1;
|
2020-02-29 11:55:43 +01:00
|
|
|
|
|
2021-09-06 18:12:45 +02:00
|
|
|
|
/*
|
|
|
|
|
|
* Workaround GNOME bug
|
|
|
|
|
|
*
|
|
|
|
|
|
* Dragging the window, then stopping the drag (releasing the
|
|
|
|
|
|
* mouse button), *without* moving the mouse, and then
|
|
|
|
|
|
* clicking twice, waiting for the CSD timer, and finally
|
|
|
|
|
|
* clicking once more, results in the following sequence
|
|
|
|
|
|
* (keyboard and other irrelevant events filtered out, unless
|
|
|
|
|
|
* they’re needed to prove a point):
|
|
|
|
|
|
*
|
|
|
|
|
|
* dbg: input.c:1551: cancelling drag timer, moving window
|
|
|
|
|
|
* dbg: input.c:759: keyboard_leave: keyboard=0x607000003580, serial=873, surface=0x6070000036d0
|
|
|
|
|
|
* dbg: input.c:1432: seat0: pointer-leave: pointer=0x607000003660, serial=874, surface = 0x6070000396e0, old-moused = 0x622000006100
|
|
|
|
|
|
*
|
|
|
|
|
|
* --> drag stopped here
|
|
|
|
|
|
*
|
|
|
|
|
|
* --> LMB clicked first time after the drag (generates the
|
|
|
|
|
|
* enter event on *release*, but no button events)
|
|
|
|
|
|
* dbg: input.c:1360: pointer-enter: pointer=0x607000003660, serial=876, surface = 0x6070000396e0, new-moused = 0x622000006100
|
|
|
|
|
|
*
|
|
|
|
|
|
* --> LMB clicked, and held until the timer times out, second
|
|
|
|
|
|
* time after the drag
|
|
|
|
|
|
* dbg: input.c:1712: BUTTON: pointer=0x607000003660, serial=877, button=110, state=1
|
|
|
|
|
|
* dbg: input.c:1806: starting move timer
|
|
|
|
|
|
* dbg: input.c:1692: move timer timed out
|
|
|
|
|
|
* dbg: input.c:759: keyboard_leave: keyboard=0x607000003580, serial=878, surface=0x6070000036d0
|
|
|
|
|
|
*
|
|
|
|
|
|
* --> NOTE: ^^ no pointer leave event this time, only the
|
|
|
|
|
|
* keyboard leave
|
|
|
|
|
|
*
|
|
|
|
|
|
* --> LMB clicked one last time
|
|
|
|
|
|
* dbg: input.c:697: seat0: keyboard_enter: keyboard=0x607000003580, serial=879, surface=0x6070000036d0
|
|
|
|
|
|
* dbg: input.c:1712: BUTTON: pointer=0x607000003660, serial=880, button=110, state=1
|
|
|
|
|
|
* err: input.c:1741: BUG in wl_pointer_button(): assertion failed: 'it->item.button != button'
|
|
|
|
|
|
*
|
|
|
|
|
|
* What are we seeing?
|
|
|
|
|
|
*
|
|
|
|
|
|
* - GNOME does *not* send a pointer *enter* event after the drag
|
|
|
|
|
|
* has stopped
|
|
|
|
|
|
* - The second drag does *not* generate a pointer *leave* event
|
|
|
|
|
|
* - The missing leave event means we’re still tracking LMB as
|
|
|
|
|
|
* being held down in our seat struct.
|
|
|
|
|
|
* - This leads to an assert (debug builds) when LMB is clicked
|
|
|
|
|
|
* again (seat’s button list already contains LMB).
|
|
|
|
|
|
*
|
|
|
|
|
|
* Note: I’ve also observed variants of the above
|
|
|
|
|
|
*/
|
|
|
|
|
|
tll_foreach(seat->mouse.buttons, it) {
|
|
|
|
|
|
if (it->item.button == button) {
|
|
|
|
|
|
LOG_WARN("multiple button press events for button %d "
|
|
|
|
|
|
"(compositor bug?)", button);
|
|
|
|
|
|
tll_remove(seat->mouse.buttons, it);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-12-12 19:05:24 +01:00
|
|
|
|
#if defined(_DEBUG)
|
|
|
|
|
|
tll_foreach(seat->mouse.buttons, it)
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(it->item.button != button);
|
2020-12-12 19:05:24 +01:00
|
|
|
|
#endif
|
|
|
|
|
|
|
2021-01-04 18:32:00 +01:00
|
|
|
|
/*
|
|
|
|
|
|
* Remember which surface "owns" this button, so that we can
|
|
|
|
|
|
* send motion and button release events to that surface, even
|
|
|
|
|
|
* if the pointer is no longer over it.
|
|
|
|
|
|
*/
|
2020-12-12 19:05:24 +01:00
|
|
|
|
tll_push_back(
|
|
|
|
|
|
seat->mouse.buttons,
|
|
|
|
|
|
((struct button_tracker){
|
|
|
|
|
|
.button = button,
|
|
|
|
|
|
.surf_kind = term->active_surface,
|
|
|
|
|
|
.send_to_client = false}));
|
|
|
|
|
|
|
2020-07-08 16:45:26 +02:00
|
|
|
|
seat->mouse.last_time = now;
|
2020-02-29 11:55:43 +01:00
|
|
|
|
|
2020-12-12 19:05:24 +01:00
|
|
|
|
surf_kind = term->active_surface;
|
|
|
|
|
|
send_to_client = false; /* For now, may be set to true if a binding consumes the button */
|
|
|
|
|
|
} else {
|
|
|
|
|
|
bool UNUSED have_button = false;
|
|
|
|
|
|
tll_foreach(seat->mouse.buttons, it) {
|
|
|
|
|
|
if (it->item.button == button) {
|
|
|
|
|
|
have_button = true;
|
|
|
|
|
|
surf_kind = it->item.surf_kind;
|
|
|
|
|
|
send_to_client = it->item.send_to_client;
|
|
|
|
|
|
tll_remove(seat->mouse.buttons, it);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2021-01-04 18:32:00 +01:00
|
|
|
|
|
|
|
|
|
|
if (!have_button) {
|
|
|
|
|
|
/*
|
|
|
|
|
|
* Seen on Sway with slurp
|
|
|
|
|
|
*
|
|
|
|
|
|
* 1. Run slurp
|
|
|
|
|
|
* 2. Press, and hold left mouse button
|
|
|
|
|
|
* 3. Press escape, to cancel slurp
|
|
|
|
|
|
* 4. Release mouse button
|
|
|
|
|
|
* 5. BAM!
|
|
|
|
|
|
*/
|
2021-09-06 18:12:45 +02:00
|
|
|
|
LOG_WARN("stray button release event (compositor bug?)");
|
2021-01-04 18:32:00 +01:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-12-12 19:05:24 +01:00
|
|
|
|
seat->mouse.last_released_button = button;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
switch (surf_kind) {
|
2020-02-25 20:31:13 +01:00
|
|
|
|
case TERM_SURF_TITLE:
|
2020-02-29 11:56:16 +01:00
|
|
|
|
if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
|
|
|
|
|
|
|
2020-02-29 12:52:55 +01:00
|
|
|
|
struct wl_window *win = term->window;
|
|
|
|
|
|
|
2020-02-29 11:56:16 +01:00
|
|
|
|
/* Toggle maximized state on double-click */
|
2020-07-08 16:45:26 +02:00
|
|
|
|
if (button == BTN_LEFT && seat->mouse.count == 2) {
|
2020-02-29 12:52:55 +01:00
|
|
|
|
if (win->is_maximized)
|
|
|
|
|
|
xdg_toplevel_unset_maximized(win->xdg_toplevel);
|
2020-02-29 11:56:16 +01:00
|
|
|
|
else
|
2020-02-29 12:52:55 +01:00
|
|
|
|
xdg_toplevel_set_maximized(win->xdg_toplevel);
|
2020-02-26 13:23:11 +01:00
|
|
|
|
}
|
2020-02-29 11:56:16 +01:00
|
|
|
|
|
2021-01-04 18:32:22 +01:00
|
|
|
|
else if (button == BTN_LEFT && win->csd.move_timeout_fd < 0) {
|
2020-02-29 12:52:55 +01:00
|
|
|
|
const struct itimerspec timeout = {
|
2020-03-01 13:20:07 +01:00
|
|
|
|
.it_value = {.tv_nsec = 200000000},
|
2020-02-29 12:52:55 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
int fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK);
|
|
|
|
|
|
if (fd >= 0 &&
|
|
|
|
|
|
timerfd_settime(fd, 0, &timeout, NULL) == 0 &&
|
2020-07-08 16:45:26 +02:00
|
|
|
|
fdm_add(wayl->fdm, fd, EPOLLIN, &fdm_csd_move, seat))
|
2020-02-29 12:52:55 +01:00
|
|
|
|
{
|
|
|
|
|
|
win->csd.move_timeout_fd = fd;
|
|
|
|
|
|
win->csd.serial = serial;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
LOG_ERRNO("failed to configure XDG toplevel move timer FD");
|
2021-01-04 18:32:22 +01:00
|
|
|
|
if (fd >= 0)
|
|
|
|
|
|
close(fd);
|
2020-02-29 12:52:55 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2020-02-26 13:23:11 +01:00
|
|
|
|
}
|
2020-02-29 15:38:04 +01:00
|
|
|
|
|
|
|
|
|
|
else if (state == WL_POINTER_BUTTON_STATE_RELEASED) {
|
|
|
|
|
|
struct wl_window *win = term->window;
|
2021-01-04 18:32:22 +01:00
|
|
|
|
if (win->csd.move_timeout_fd >= 0) {
|
2020-02-29 15:38:04 +01:00
|
|
|
|
fdm_del(wayl->fdm, win->csd.move_timeout_fd);
|
|
|
|
|
|
win->csd.move_timeout_fd = -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2020-02-25 20:31:13 +01:00
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
case TERM_SURF_BORDER_LEFT:
|
|
|
|
|
|
case TERM_SURF_BORDER_RIGHT:
|
|
|
|
|
|
case TERM_SURF_BORDER_TOP:
|
|
|
|
|
|
case TERM_SURF_BORDER_BOTTOM: {
|
2020-03-01 11:43:58 +01:00
|
|
|
|
static const enum xdg_toplevel_resize_edge map[] = {
|
2020-02-25 20:31:13 +01:00
|
|
|
|
[TERM_SURF_BORDER_LEFT] = XDG_TOPLEVEL_RESIZE_EDGE_LEFT,
|
|
|
|
|
|
[TERM_SURF_BORDER_RIGHT] = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT,
|
|
|
|
|
|
[TERM_SURF_BORDER_TOP] = XDG_TOPLEVEL_RESIZE_EDGE_TOP,
|
|
|
|
|
|
[TERM_SURF_BORDER_BOTTOM] = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM,
|
|
|
|
|
|
};
|
2020-02-29 09:32:22 +01:00
|
|
|
|
|
|
|
|
|
|
if (button == BTN_LEFT && state == WL_POINTER_BUTTON_STATE_PRESSED) {
|
2020-03-01 11:43:58 +01:00
|
|
|
|
enum xdg_toplevel_resize_edge resize_type;
|
2020-02-29 09:32:22 +01:00
|
|
|
|
|
2020-07-08 16:45:26 +02:00
|
|
|
|
int x = seat->mouse.x;
|
|
|
|
|
|
int y = seat->mouse.y;
|
2020-02-29 09:32:22 +01:00
|
|
|
|
|
|
|
|
|
|
if (is_top_left(term, x, y))
|
|
|
|
|
|
resize_type = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT;
|
|
|
|
|
|
else if (is_top_right(term, x, y))
|
|
|
|
|
|
resize_type = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT;
|
|
|
|
|
|
else if (is_bottom_left(term, x, y))
|
|
|
|
|
|
resize_type = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT;
|
|
|
|
|
|
else if (is_bottom_right(term, x, y))
|
|
|
|
|
|
resize_type = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT;
|
|
|
|
|
|
else
|
|
|
|
|
|
resize_type = map[term->active_surface];
|
|
|
|
|
|
|
|
|
|
|
|
xdg_toplevel_resize(
|
2020-07-08 16:45:26 +02:00
|
|
|
|
term->window->xdg_toplevel, seat->wl_seat, serial, resize_type);
|
2020-02-29 09:32:22 +01:00
|
|
|
|
}
|
2020-02-25 20:31:13 +01:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-03-02 20:29:28 +01:00
|
|
|
|
case TERM_SURF_BUTTON_MINIMIZE:
|
|
|
|
|
|
if (button == BTN_LEFT && state == WL_POINTER_BUTTON_STATE_PRESSED)
|
|
|
|
|
|
xdg_toplevel_set_minimized(term->window->xdg_toplevel);
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case TERM_SURF_BUTTON_MAXIMIZE:
|
|
|
|
|
|
if (button == BTN_LEFT && state == WL_POINTER_BUTTON_STATE_PRESSED) {
|
|
|
|
|
|
if (term->window->is_maximized)
|
|
|
|
|
|
xdg_toplevel_unset_maximized(term->window->xdg_toplevel);
|
|
|
|
|
|
else
|
|
|
|
|
|
xdg_toplevel_set_maximized(term->window->xdg_toplevel);
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case TERM_SURF_BUTTON_CLOSE:
|
|
|
|
|
|
if (button == BTN_LEFT && state == WL_POINTER_BUTTON_STATE_PRESSED)
|
|
|
|
|
|
term_shutdown(term);
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
2020-02-25 20:31:13 +01:00
|
|
|
|
case TERM_SURF_SEARCH:
|
2020-07-26 12:37:35 +02:00
|
|
|
|
case TERM_SURF_SCROLLBACK_INDICATOR:
|
2020-08-14 07:35:01 +02:00
|
|
|
|
case TERM_SURF_RENDER_TIMER:
|
2021-02-06 11:31:22 +01:00
|
|
|
|
case TERM_SURF_JUMP_LABEL:
|
2020-02-25 20:31:13 +01:00
|
|
|
|
break;
|
2020-02-24 22:42:04 +01:00
|
|
|
|
|
2020-02-29 15:29:00 +01:00
|
|
|
|
case TERM_SURF_GRID: {
|
|
|
|
|
|
search_cancel(term);
|
2021-03-04 08:57:03 +01:00
|
|
|
|
urls_reset(term);
|
2020-02-29 15:29:00 +01:00
|
|
|
|
|
2020-08-07 19:59:03 +02:00
|
|
|
|
bool cursor_is_on_grid = seat->mouse.col >= 0 && seat->mouse.row >= 0;
|
|
|
|
|
|
|
2020-02-29 15:29:00 +01:00
|
|
|
|
switch (state) {
|
|
|
|
|
|
case WL_POINTER_BUTTON_STATE_PRESSED: {
|
2020-12-12 19:05:24 +01:00
|
|
|
|
bool consumed = false;
|
2020-09-07 19:35:25 +02:00
|
|
|
|
|
2021-02-02 09:52:05 +01:00
|
|
|
|
if (cursor_is_on_grid && term_mouse_grabbed(term, seat)) {
|
|
|
|
|
|
if (seat->wl_keyboard != NULL && seat->kbd.xkb_state != NULL) {
|
|
|
|
|
|
/* Seat has keyboard - use mouse bindings *with* modifiers */
|
2020-09-07 19:35:25 +02:00
|
|
|
|
|
2021-10-06 20:12:17 +02:00
|
|
|
|
xkb_mod_mask_t mods;
|
|
|
|
|
|
get_current_modifiers(seat, &mods, NULL, 0);
|
2021-11-21 11:27:31 +01:00
|
|
|
|
mods &= seat->kbd.bind_significant;
|
2020-09-27 14:31:39 +02:00
|
|
|
|
|
2021-02-02 09:52:05 +01:00
|
|
|
|
/* Ignore Shift when matching modifiers, since it is
|
|
|
|
|
|
* used to enable selection in mouse grabbing client
|
|
|
|
|
|
* applications */
|
2021-11-21 11:53:22 +01:00
|
|
|
|
if (seat->kbd.mod_shift != XKB_MOD_INVALID)
|
|
|
|
|
|
mods &= ~(1 << seat->kbd.mod_shift);
|
2020-09-07 19:35:25 +02:00
|
|
|
|
|
2021-02-02 09:52:05 +01:00
|
|
|
|
const struct mouse_binding *match = NULL;
|
2020-09-07 19:35:25 +02:00
|
|
|
|
|
2021-02-02 09:52:05 +01:00
|
|
|
|
tll_foreach(seat->mouse.bindings, it) {
|
|
|
|
|
|
const struct mouse_binding *binding = &it->item;
|
2020-09-07 19:35:25 +02:00
|
|
|
|
|
2021-02-02 09:52:05 +01:00
|
|
|
|
if (binding->button != button) {
|
|
|
|
|
|
/* Wrong button */
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
2020-09-07 19:35:25 +02:00
|
|
|
|
|
2021-02-02 09:52:05 +01:00
|
|
|
|
if (binding->mods != mods) {
|
|
|
|
|
|
/* Modifier mismatch */
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (binding->count > seat->mouse.count) {
|
|
|
|
|
|
/* Not correct click count */
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
2020-09-27 14:31:39 +02:00
|
|
|
|
|
2021-02-02 09:52:05 +01:00
|
|
|
|
if (match == NULL || binding->count > match->count)
|
|
|
|
|
|
match = binding;
|
2020-08-09 22:40:53 +02:00
|
|
|
|
}
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
|
2021-02-02 09:52:05 +01:00
|
|
|
|
if (match != NULL) {
|
|
|
|
|
|
consumed = execute_binding(
|
|
|
|
|
|
seat, term, match->action, match->pipe_argv, serial);
|
|
|
|
|
|
}
|
2020-12-12 19:05:24 +01:00
|
|
|
|
}
|
2020-09-27 14:31:39 +02:00
|
|
|
|
|
2021-02-02 09:52:05 +01:00
|
|
|
|
else {
|
|
|
|
|
|
/* Seat does NOT have a keyboard - use mouse bindings *without* modifiers */
|
|
|
|
|
|
const struct config_mouse_binding *match = NULL;
|
2021-06-17 18:15:29 +02:00
|
|
|
|
const struct config *conf = seat->wayl->conf;
|
2020-09-07 19:35:25 +02:00
|
|
|
|
|
2021-06-17 18:15:29 +02:00
|
|
|
|
for (size_t i = 0; i < conf->bindings.mouse.count; i++) {
|
|
|
|
|
|
const struct config_mouse_binding *binding =
|
|
|
|
|
|
&conf->bindings.mouse.arr[i];
|
2020-09-07 19:35:25 +02:00
|
|
|
|
|
2021-02-02 09:52:05 +01:00
|
|
|
|
if (binding->button != button) {
|
|
|
|
|
|
/* Wrong button */
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
2020-09-07 19:35:25 +02:00
|
|
|
|
|
2021-02-02 09:52:05 +01:00
|
|
|
|
if (binding->count > seat->mouse.count) {
|
|
|
|
|
|
/* Incorrect click count */
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
2020-09-07 19:35:25 +02:00
|
|
|
|
|
2021-02-02 09:52:05 +01:00
|
|
|
|
const struct config_key_modifiers no_mods = {0};
|
|
|
|
|
|
if (memcmp(&binding->modifiers, &no_mods, sizeof(no_mods)) != 0) {
|
|
|
|
|
|
/* Binding has modifiers */
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
2020-09-27 14:31:39 +02:00
|
|
|
|
|
2021-02-02 09:52:05 +01:00
|
|
|
|
if (match == NULL || binding->count > match->count)
|
|
|
|
|
|
match = binding;
|
config: key/mouse bindings: refactor: less parsing in keyboard_enter()
This simplifies the handling of mouse and keyboard bindings.
Before, the bindings where parsed *both* when loading the
configuration, and then on every keyboard enter event. This was done
since keys require a keymap to be decoded. Something we don't have at
configuration time. The idea was that at config time, we used a
default keymap just to verify the key combo strings were valid.
The following has changed:
* The bindings in the config struct is now *one* key combo per
entry. Previously, it was one *action* per entry, and each entry
had one or more key combos.
Doing it this way makes it easier when converting the binding in the
keyboard enter event (which previously had to expand the combos
anyway).
* The bindings in the config struct no longer contains any unparsed
strings.
A key binding contains a decoded 'modifier' struct (which specifies
whether e.g. ctrl, or shift, or ctrl+shift must be pressed for the
binding to be used).
It also contains a decoded XKB keysym.
* A mouse binding in the config struct is similar to a key binding,
except it contains the button, and click count instead of the XKB
key sym.
* The modifiers in the user-specified key combo is decoded at config
time, by using the pre-defined XKB constants
XKB_MOD_NAME_<modifier>.
The result is stored in a 'modifiers' struct, which is just a
collection of booleans; one for each supported modifier.
The supported modifiers are: shift, ctrl, alt and meta/super.
* The key sym is decoded at config time using
xkb_keysym_from_name(). This call does *not* depend on a keymap.
* The mouse button is decoded at config time using a hardcoded mapping
table (just like before).
* The click count is currently hard-coded to 1.
* In the keyboard enter event, all we need to do is pre-compute the
xkb_mod_mask_t variable for each key/mouse binding, and find all the
*key codes* that map to the (already decoded) symbol.
For mouse bindings, the modifiers are the *only* reason we convert
the mouse bindings at all.
In fact, on button events, we check if the seat has a keyboard. If
not, we use the mouse bindings from the configuration directly, and
simply filter out those with a non-empty set of modifiers.
2020-08-10 19:00:03 +02:00
|
|
|
|
}
|
2020-12-12 19:05:24 +01:00
|
|
|
|
|
2021-02-02 09:52:05 +01:00
|
|
|
|
if (match != NULL) {
|
|
|
|
|
|
consumed = execute_binding(
|
2021-06-18 16:18:41 +02:00
|
|
|
|
seat, term, match->action, match->pipe.argv.args,
|
|
|
|
|
|
serial);
|
2021-02-02 09:52:05 +01:00
|
|
|
|
}
|
2020-12-12 19:05:24 +01:00
|
|
|
|
}
|
2020-10-13 08:02:09 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-12-12 19:05:24 +01:00
|
|
|
|
send_to_client = !consumed && cursor_is_on_grid;
|
|
|
|
|
|
|
|
|
|
|
|
if (send_to_client)
|
|
|
|
|
|
tll_back(seat->mouse.buttons).send_to_client = true;
|
|
|
|
|
|
|
|
|
|
|
|
if (send_to_client &&
|
2020-10-13 08:02:09 +02:00
|
|
|
|
!term_mouse_grabbed(term, seat) &&
|
|
|
|
|
|
cursor_is_on_grid)
|
|
|
|
|
|
{
|
|
|
|
|
|
term_mouse_down(
|
|
|
|
|
|
term, button, seat->mouse.row, seat->mouse.col,
|
|
|
|
|
|
seat->kbd.shift, seat->kbd.alt, seat->kbd.ctrl);
|
2020-07-09 09:52:11 +02:00
|
|
|
|
}
|
2020-02-29 15:29:00 +01:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
2019-08-06 19:32:06 +02:00
|
|
|
|
|
2020-02-29 15:29:00 +01:00
|
|
|
|
case WL_POINTER_BUTTON_STATE_RELEASED:
|
2020-08-11 09:55:33 +02:00
|
|
|
|
selection_finalize(seat, term, serial);
|
2019-08-06 19:32:06 +02:00
|
|
|
|
|
2020-12-12 19:05:24 +01:00
|
|
|
|
if (send_to_client && !term_mouse_grabbed(term, seat)) {
|
2020-07-09 09:52:11 +02:00
|
|
|
|
term_mouse_up(
|
|
|
|
|
|
term, button, seat->mouse.row, seat->mouse.col,
|
|
|
|
|
|
seat->kbd.shift, seat->kbd.alt, seat->kbd.ctrl);
|
|
|
|
|
|
}
|
2020-02-29 15:29:00 +01:00
|
|
|
|
break;
|
2019-07-11 16:42:59 +02:00
|
|
|
|
}
|
2019-07-05 14:24:51 +02:00
|
|
|
|
break;
|
2019-07-17 21:30:57 +02:00
|
|
|
|
}
|
2019-07-05 14:24:51 +02:00
|
|
|
|
|
2020-02-29 15:29:00 +01:00
|
|
|
|
case TERM_SURF_NONE:
|
2021-02-10 09:01:51 +00:00
|
|
|
|
BUG("Invalid surface type");
|
2019-07-05 14:24:51 +02:00
|
|
|
|
break;
|
2020-02-29 15:29:00 +01:00
|
|
|
|
|
2019-07-05 14:24:51 +02:00
|
|
|
|
}
|
2019-07-05 10:44:57 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-09-15 18:56:22 +02:00
|
|
|
|
static void
|
|
|
|
|
|
alternate_scroll(struct seat *seat, int amount, int button)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (seat->wl_keyboard == NULL)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
2020-11-03 19:44:51 +01:00
|
|
|
|
/* Should be cleared in leave event */
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(seat->mouse_focus != NULL);
|
2020-11-03 19:44:51 +01:00
|
|
|
|
struct terminal *term = seat->mouse_focus;
|
|
|
|
|
|
|
2021-10-25 19:39:46 +02:00
|
|
|
|
assert(button == BTN_BACK || button == BTN_FORWARD);
|
|
|
|
|
|
|
2020-09-15 18:56:22 +02:00
|
|
|
|
xkb_keycode_t key = button == BTN_BACK
|
|
|
|
|
|
? seat->kbd.key_arrow_up : seat->kbd.key_arrow_down;
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < amount; i++)
|
2020-11-03 19:44:51 +01:00
|
|
|
|
key_press_release(seat, term, seat->kbd.serial, key, XKB_KEY_DOWN);
|
|
|
|
|
|
key_press_release(seat, term, seat->kbd.serial, key, XKB_KEY_UP);
|
2020-09-15 18:56:22 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2019-07-26 18:47:56 +02:00
|
|
|
|
static void
|
2021-08-05 18:34:09 +02:00
|
|
|
|
mouse_scroll(struct seat *seat, int amount, enum wl_pointer_axis axis)
|
2019-07-26 18:47:56 +02:00
|
|
|
|
{
|
2020-07-08 16:45:26 +02:00
|
|
|
|
struct terminal *term = seat->mouse_focus;
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(term != NULL);
|
2019-10-27 18:43:07 +01:00
|
|
|
|
|
2021-08-05 18:34:09 +02:00
|
|
|
|
int button = axis == WL_POINTER_AXIS_VERTICAL_SCROLL
|
|
|
|
|
|
? amount < 0 ? BTN_BACK : BTN_FORWARD
|
|
|
|
|
|
: amount < 0 ? BTN_WHEEL_LEFT : BTN_WHEEL_RIGHT;
|
2019-07-26 18:47:56 +02:00
|
|
|
|
amount = abs(amount);
|
|
|
|
|
|
|
2021-10-25 19:39:46 +02:00
|
|
|
|
if (term_mouse_grabbed(term, seat)) {
|
2020-09-15 18:56:22 +02:00
|
|
|
|
if (term->grid == &term->alt) {
|
2021-10-25 19:39:46 +02:00
|
|
|
|
if (term->alt_scrolling) {
|
|
|
|
|
|
switch (button) {
|
|
|
|
|
|
case BTN_BACK:
|
|
|
|
|
|
case BTN_FORWARD:
|
|
|
|
|
|
alternate_scroll(seat, amount, button);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2020-09-15 18:56:22 +02:00
|
|
|
|
} else {
|
2021-10-25 19:39:46 +02:00
|
|
|
|
switch (button) {
|
|
|
|
|
|
case BTN_BACK:
|
2020-09-15 18:56:22 +02:00
|
|
|
|
cmd_scrollback_up(term, amount);
|
2021-10-25 19:39:46 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case BTN_FORWARD:
|
2020-09-15 18:56:22 +02:00
|
|
|
|
cmd_scrollback_down(term, amount);
|
2021-10-25 19:39:46 +02:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
2019-11-30 16:48:58 +01:00
|
|
|
|
}
|
2021-10-25 19:39:46 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
else if (seat->mouse.col >= 0 && seat->mouse.row >= 0) {
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(seat->mouse.col < term->cols);
|
|
|
|
|
|
xassert(seat->mouse.row < term->rows);
|
2020-08-07 22:07:16 +02:00
|
|
|
|
|
2020-09-15 18:56:22 +02:00
|
|
|
|
for (int i = 0; i < amount; i++) {
|
|
|
|
|
|
term_mouse_down(
|
2020-07-08 18:16:43 +02:00
|
|
|
|
term, button, seat->mouse.row, seat->mouse.col,
|
|
|
|
|
|
seat->kbd.shift, seat->kbd.alt, seat->kbd.ctrl);
|
2020-09-15 18:56:22 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
term_mouse_up(
|
|
|
|
|
|
term, button, seat->mouse.row, seat->mouse.col,
|
|
|
|
|
|
seat->kbd.shift, seat->kbd.alt, seat->kbd.ctrl);
|
2019-08-19 21:16:47 +02:00
|
|
|
|
}
|
2019-07-26 18:47:56 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-08 18:41:42 +01:00
|
|
|
|
static float
|
|
|
|
|
|
mouse_scroll_multiplier(const struct terminal *term)
|
|
|
|
|
|
{
|
|
|
|
|
|
return term->grid == &term->normal
|
|
|
|
|
|
? term->conf->scrollback.multiplier
|
|
|
|
|
|
: 1.0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-07-05 10:44:57 +02:00
|
|
|
|
static void
|
|
|
|
|
|
wl_pointer_axis(void *data, struct wl_pointer *wl_pointer,
|
|
|
|
|
|
uint32_t time, uint32_t axis, wl_fixed_t value)
|
|
|
|
|
|
{
|
2020-07-08 16:45:26 +02:00
|
|
|
|
struct seat *seat = data;
|
2019-10-27 18:43:07 +01:00
|
|
|
|
|
2020-07-08 16:45:26 +02:00
|
|
|
|
if (seat->mouse.have_discrete)
|
2019-07-26 18:47:56 +02:00
|
|
|
|
return;
|
2019-07-10 09:15:37 +02:00
|
|
|
|
|
2021-01-16 20:16:00 +00:00
|
|
|
|
xassert(seat->mouse_focus != NULL);
|
2021-08-05 18:34:09 +02:00
|
|
|
|
xassert(axis < ALEN(seat->mouse.aggregated));
|
2020-09-27 11:11:45 +02:00
|
|
|
|
|
2020-04-10 18:43:29 +02:00
|
|
|
|
/*
|
|
|
|
|
|
* Aggregate scrolled amount until we get at least 1.0
|
|
|
|
|
|
*
|
|
|
|
|
|
* Without this, very slow scrolling will never actually scroll
|
|
|
|
|
|
* anything.
|
|
|
|
|
|
*/
|
2021-11-08 18:41:42 +01:00
|
|
|
|
seat->mouse.aggregated[axis] +=
|
|
|
|
|
|
mouse_scroll_multiplier(seat->mouse_focus) * wl_fixed_to_double(value);
|
2021-08-05 18:34:09 +02:00
|
|
|
|
if (fabs(seat->mouse.aggregated[axis]) < seat->mouse_focus->cell_height)
|
2020-09-27 11:11:45 +02:00
|
|
|
|
return;
|
|
|
|
|
|
|
2021-08-05 18:34:09 +02:00
|
|
|
|
int lines = seat->mouse.aggregated[axis] / seat->mouse_focus->cell_height;
|
|
|
|
|
|
mouse_scroll(seat, lines, axis);
|
|
|
|
|
|
seat->mouse.aggregated[axis] -= (double)lines * seat->mouse_focus->cell_height;
|
2019-07-26 18:47:56 +02:00
|
|
|
|
}
|
2019-07-10 09:15:37 +02:00
|
|
|
|
|
2019-07-26 18:47:56 +02:00
|
|
|
|
static void
|
|
|
|
|
|
wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer,
|
|
|
|
|
|
uint32_t axis, int32_t discrete)
|
|
|
|
|
|
{
|
2020-07-08 16:45:26 +02:00
|
|
|
|
struct seat *seat = data;
|
|
|
|
|
|
seat->mouse.have_discrete = true;
|
2021-08-05 18:34:09 +02:00
|
|
|
|
|
|
|
|
|
|
int amount = discrete;
|
|
|
|
|
|
|
|
|
|
|
|
if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) {
|
|
|
|
|
|
/* Treat mouse wheel left/right as regular buttons */
|
|
|
|
|
|
} else
|
2021-11-08 18:41:42 +01:00
|
|
|
|
amount *= mouse_scroll_multiplier(seat->mouse_focus);
|
2021-08-05 18:34:09 +02:00
|
|
|
|
|
|
|
|
|
|
mouse_scroll(seat, amount, axis);
|
2019-07-05 10:44:57 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
|
wl_pointer_frame(void *data, struct wl_pointer *wl_pointer)
|
|
|
|
|
|
{
|
2020-07-08 16:45:26 +02:00
|
|
|
|
struct seat *seat = data;
|
|
|
|
|
|
seat->mouse.have_discrete = false;
|
2019-07-05 10:44:57 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
|
wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer,
|
|
|
|
|
|
uint32_t axis_source)
|
|
|
|
|
|
{
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
|
wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer,
|
|
|
|
|
|
uint32_t time, uint32_t axis)
|
|
|
|
|
|
{
|
2020-07-08 16:45:26 +02:00
|
|
|
|
struct seat *seat = data;
|
2021-08-05 18:34:09 +02:00
|
|
|
|
|
|
|
|
|
|
xassert(axis < ALEN(seat->mouse.aggregated));
|
|
|
|
|
|
seat->mouse.aggregated[axis] = 0.;
|
2019-07-05 10:44:57 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const struct wl_pointer_listener pointer_listener = {
|
|
|
|
|
|
.enter = wl_pointer_enter,
|
|
|
|
|
|
.leave = wl_pointer_leave,
|
|
|
|
|
|
.motion = wl_pointer_motion,
|
|
|
|
|
|
.button = wl_pointer_button,
|
|
|
|
|
|
.axis = wl_pointer_axis,
|
|
|
|
|
|
.frame = wl_pointer_frame,
|
|
|
|
|
|
.axis_source = wl_pointer_axis_source,
|
|
|
|
|
|
.axis_stop = wl_pointer_axis_stop,
|
|
|
|
|
|
.axis_discrete = wl_pointer_axis_discrete,
|
|
|
|
|
|
};
|