File kmscon_PR75.patch of Package kmscon_fork
From 978c416a01e80effe64b9a5d31233ade4de2d11b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mirco=20M=C3=BCller?= <macslow@gmail.com>
Date: Mon, 24 Jul 2023 16:13:33 +0200
Subject: [PATCH 1/4] Ported output-rotation support from my (MacSlow) old
kmscon-upstream branch to aetf's meson-based develop branch.
---
COPYING | 1 +
docs/man/kmscon.1.xml.in | 28 +++++++++++
src/kmscon_conf.c | 26 +++++++---
src/kmscon_conf.h | 6 +++
src/kmscon_terminal.c | 103 +++++++++++++++++++++++++++++++++-----
src/text.c | 80 +++++++++++++++++++++++++++--
src/text.h | 16 +++++-
src/text_gltex.c | 73 +++++++++++++++++++++++++--
src/text_gltex_atlas.vert | 12 ++++-
9 files changed, 316 insertions(+), 29 deletions(-)
diff --git a/COPYING b/COPYING
index 3ad47b73..a9321fb9 100644
--- a/COPYING
+++ b/COPYING
@@ -15,6 +15,7 @@ This software was written by:
Hoàng Đức Hiếu <github@zahe.me>
Swift Geek <swiftgeek@gmail.com>
Aetf <aetf@unlimited-code.works>
+ Mirco Müller <macslow@gmail.com>
= Copyright Notice =
diff --git a/docs/man/kmscon.1.xml.in b/docs/man/kmscon.1.xml.in
index c6ddb0f0..f3319fd0 100644
--- a/docs/man/kmscon.1.xml.in
+++ b/docs/man/kmscon.1.xml.in
@@ -430,6 +430,22 @@
(default: <Ctrl><Logo>Return)</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><option>--grab-rotate-cw {grab}</option></term>
+ <listitem>
+ <para>Rotate output clock-wise: Normal -> Right -> Inverted -> Left and so on.
+ (default: <Logo>Plus)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--grab-rotate-ccw {grab}</option></term>
+ <listitem>
+ <para>Rotate output counter-clock-wise: Normal -> Left -> Inverted -> Right and so on.
+ (default: <Logo>Minus)</para>
+ </listitem>
+ </varlistentry>
</variablelist>
<para>Video Options:</para>
@@ -482,6 +498,18 @@
only be used to debug render engines. (default: off)</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><option>--rotate {orientation}</option></term>
+ <listitem>
+ <para>Select the desired orientation of the output. Available orientations
+ are 'normal' meaning output is not rotated, 'right' meaning output is
+ rotated 90° clock-wise, 'inverted' meaning output is rotated 180°
+ clock-wise and 'left' meaning output is rotated 90° counter clock-wise.
+ This has only effect for the 'gltex' render-engine. Other render-engines
+ currently ignore this option. (default: 'normal')</para>
+ </listitem>
+ </varlistentry>
</variablelist>
<para>Font Options:</para>
diff --git a/src/kmscon_conf.c b/src/kmscon_conf.c
index 2f3b1519..8877548b 100644
--- a/src/kmscon_conf.c
+++ b/src/kmscon_conf.c
@@ -134,14 +134,19 @@ static void print_help()
"\t Close current session\n"
"\t --grab-terminal-new <grab> [<Ctrl><Logo>Return]\n"
"\t Create new terminal session\n"
+ "\t --grab-rotate-cw <grab> [<Logo>Plus]\n"
+ "\t Rotate output clock-wise\n"
+ "\t --grab-rotate-ccw <grab> [<Logo>Minus]\n"
+ "\t Rotate output counter-clock-wise\n"
"\n"
"Video Options:\n"
- "\t --drm [on] Use DRM if available\n"
- "\t --hwaccel [off] Use 3D hardware-acceleration if\n"
- "\t available\n"
- "\t --gpus={all,aux,primary}[all] GPU selection mode\n"
- "\t --render-engine <eng> [-] Console renderer\n"
- "\t --render-timing [off] Print renderer timing information\n"
+ "\t --drm [on] Use DRM if available\n"
+ "\t --hwaccel [off] Use 3D hardware-acceleration if\n"
+ "\t available\n"
+ "\t --gpus={all,aux,primary}[all] GPU selection mode\n"
+ "\t --render-engine <eng> [-] Console renderer\n"
+ "\t --render-timing [off] Print renderer timing information\n"
+ "\t --rotate <orientation> [normal] normal, right, inverted, left\n"
"\n"
"Font Options:\n"
"\t --font-engine <engine> [pango]\n"
@@ -645,6 +650,12 @@ static struct conf_grab def_grab_session_close =
static struct conf_grab def_grab_terminal_new =
CONF_SINGLE_GRAB(SHL_CONTROL_MASK | SHL_LOGO_MASK, XKB_KEY_Return);
+static struct conf_grab def_grab_rotate_cw =
+ CONF_SINGLE_GRAB(SHL_LOGO_MASK, XKB_KEY_plus);
+
+static struct conf_grab def_grab_rotate_ccw =
+ CONF_SINGLE_GRAB(SHL_LOGO_MASK, XKB_KEY_minus);
+
static palette_t def_palette = {
[TSM_COLOR_BLACK] = { 0, 0, 0 }, /* black */
[TSM_COLOR_RED] = { 205, 0, 0 }, /* red */
@@ -728,12 +739,15 @@ int kmscon_conf_new(struct conf_ctx **out)
CONF_OPTION_GRAB(0, "grab-session-dummy", &conf->grab_session_dummy, &def_grab_session_dummy),
CONF_OPTION_GRAB(0, "grab-session-close", &conf->grab_session_close, &def_grab_session_close),
CONF_OPTION_GRAB(0, "grab-terminal-new", &conf->grab_terminal_new, &def_grab_terminal_new),
+ CONF_OPTION_GRAB(0, "grab-rotate-cw", &conf->grab_rotate_cw, &def_grab_rotate_cw),
+ CONF_OPTION_GRAB(0, "grab-rotate-ccw", &conf->grab_rotate_ccw, &def_grab_rotate_ccw),
/* Video Options */
CONF_OPTION_BOOL_FULL(0, "drm", aftercheck_drm, NULL, NULL, &conf->drm, true),
CONF_OPTION_BOOL(0, "hwaccel", &conf->hwaccel, false),
CONF_OPTION(0, 0, "gpus", &conf_gpus, NULL, NULL, NULL, &conf->gpus, KMSCON_GPU_ALL),
CONF_OPTION_STRING(0, "render-engine", &conf->render_engine, NULL),
+ CONF_OPTION_STRING(0, "rotate", &conf->rotate, "normal"),
/* Font Options */
CONF_OPTION_STRING(0, "font-engine", &conf->font_engine, "pango"),
diff --git a/src/kmscon_conf.h b/src/kmscon_conf.h
index 9ae9ecff..b7d0ae87 100644
--- a/src/kmscon_conf.h
+++ b/src/kmscon_conf.h
@@ -135,6 +135,10 @@ struct kmscon_conf_t {
struct conf_grab *grab_session_close;
/* terminal-new grab */
struct conf_grab *grab_terminal_new;
+ /* rotate output clock-wise grab */
+ struct conf_grab *grab_rotate_cw;
+ /* rotate output counter-clock-wise grab */
+ struct conf_grab *grab_rotate_ccw;
/* Video Options */
/* use DRM if available */
@@ -145,6 +149,8 @@ struct kmscon_conf_t {
unsigned int gpus;
/* render engine */
char *render_engine;
+ /* orientation/rotation of output */
+ char *rotate;
/* Font Options */
/* font engine */
diff --git a/src/kmscon_terminal.c b/src/kmscon_terminal.c
index 508bd971..44efdf63 100644
--- a/src/kmscon_terminal.c
+++ b/src/kmscon_terminal.c
@@ -84,12 +84,16 @@ struct kmscon_terminal {
struct kmscon_font *bold_font;
};
+static void terminal_resize(struct kmscon_terminal *term,
+ unsigned int cols, unsigned int rows,
+ bool force, bool notify);
+
static void do_clear_margins(struct screen *scr)
{
- unsigned int w, h, sw, sh;
+ unsigned int h, sw, sh;
struct uterm_mode *mode;
struct tsm_screen_attr attr;
- int dw, dh;
+ int dh;
mode = uterm_display_get_current(scr->disp);
if (!mode)
@@ -97,21 +101,30 @@ static void do_clear_margins(struct screen *scr)
sw = uterm_mode_get_width(mode);
sh = uterm_mode_get_height(mode);
- w = scr->txt->font->attr.width * scr->txt->cols;
h = scr->txt->font->attr.height * scr->txt->rows;
- dw = sw - w;
dh = sh - h;
tsm_vte_get_def_attr(scr->term->vte, &attr);
- if (dw > 0)
- uterm_display_fill(scr->disp, attr.br, attr.bg, attr.bb,
- w, 0,
- dw, h);
- if (dh > 0)
- uterm_display_fill(scr->disp, attr.br, attr.bg, attr.bb,
- 0, h,
- sw, dh);
+ switch (scr->txt->orientation) {
+ case ORIENTATION_NORMAL:
+ uterm_display_fill(scr->disp, 0, 0, 0, 0, h, sw, dh);
+ break;
+
+ case ORIENTATION_RIGHT:
+ uterm_display_fill(scr->disp, 0, 0, 0, 0, 0, sw - h, sh);
+ break;
+
+ case ORIENTATION_INVERTED:
+ uterm_display_fill(scr->disp, 0, 0, 0, 0, 0, sw, dh);
+ break;
+
+ case ORIENTATION_LEFT:
+ uterm_display_fill(scr->disp, 0, 0, 0, h, 0, sw - h, sh);
+ break;
+
+ default : break;
+ }
}
static void do_redraw_screen(struct screen *scr)
@@ -276,6 +289,58 @@ static int font_set(struct kmscon_terminal *term)
return 0;
}
+static void rotate_cw_screen(struct screen *scr)
+{
+ unsigned int orientation = kmscon_text_get_orientation(scr->txt);
+ orientation = (orientation + 1) % ORIENTATION_MAX;
+ if (orientation == ORIENTATION_UNDEFINED) orientation = ORIENTATION_NORMAL;
+ kmscon_text_rotate(scr->txt, orientation);
+}
+
+static void rotate_cw_all(struct kmscon_terminal *term)
+{
+ struct shl_dlist *iter;
+ struct screen *scr;
+
+ shl_dlist_for_each(iter, &term->screens) {
+ scr = shl_dlist_entry(iter, struct screen, list);
+ rotate_cw_screen(scr);
+ term->min_cols = 0;
+ term->min_rows = 0;
+ terminal_resize(term,
+ kmscon_text_get_cols(scr->txt),
+ kmscon_text_get_rows(scr->txt),
+ true,
+ true);
+ }
+}
+
+static void rotate_ccw_screen(struct screen *scr)
+{
+ unsigned int orientation = kmscon_text_get_orientation(scr->txt);
+ orientation = (orientation - 1) % ORIENTATION_MAX;
+ if (orientation == ORIENTATION_UNDEFINED) orientation = ORIENTATION_LEFT;
+ kmscon_text_rotate(scr->txt, orientation);
+}
+
+static void rotate_ccw_all(struct kmscon_terminal *term)
+{
+ struct shl_dlist *iter;
+ struct screen *scr;
+
+ shl_dlist_for_each(iter, &term->screens) {
+ scr = shl_dlist_entry(iter, struct screen, list);
+ rotate_ccw_screen(scr);
+ term->min_cols = 0;
+ term->min_rows = 0;
+ terminal_resize(term,
+ kmscon_text_get_cols(scr->txt),
+ kmscon_text_get_rows(scr->txt),
+ true,
+ true);
+ }
+}
+
static int add_display(struct kmscon_terminal *term, struct uterm_display *disp)
{
struct shl_dlist *iter;
@@ -313,7 +378,7 @@ static int add_display(struct kmscon_terminal *term, struct uterm_display *disp)
else
be = "bbulk";
- ret = kmscon_text_new(&scr->txt, be);
+ ret = kmscon_text_new(&scr->txt, be, term->conf->rotate);
if (ret) {
log_error("cannot create text-renderer");
goto err_cb;
@@ -453,6 +518,18 @@ static void input_event(struct uterm_input *input,
++term->font_attr.points;
return;
}
+ if (conf_grab_matches(term->conf->grab_rotate_cw,
+ ev->mods, ev->num_syms, ev->keysyms)) {
+ rotate_cw_all(term);
+ ev->handled = true;
+ return;
+ }
+ if (conf_grab_matches(term->conf->grab_rotate_ccw,
+ ev->mods, ev->num_syms, ev->keysyms)) {
+ rotate_ccw_all(term);
+ ev->handled = true;
+ return;
+ }
/* TODO: xkbcommon supports multiple keysyms, but it is currently
* unclear how this feature will be used. There is no keymap, which
diff --git a/src/text.c b/src/text.c
index 24b579b8..9b116e27 100644
--- a/src/text.c
+++ b/src/text.c
@@ -103,7 +103,7 @@ void kmscon_text_unregister(const char *name)
shl_register_remove(&text_reg, name);
}
-static int new_text(struct kmscon_text *text, const char *backend)
+static int new_text(struct kmscon_text *text, const char *backend, unsigned int orientation)
{
struct shl_register_record *record;
const char *name = backend ? backend : "<default>";
@@ -124,6 +124,7 @@ static int new_text(struct kmscon_text *text, const char *backend)
text->record = record;
text->ops = record->data;
+ text->orientation = orientation;
if (text->ops->init)
ret = text->ops->init(text);
@@ -143,10 +144,11 @@ static int new_text(struct kmscon_text *text, const char *backend)
* kmscon_text_new:
* @out: A pointer to the new text-renderer is stored here
* @backend: Backend to use or NULL for default backend
+ * @rotate: Orientation ("normal", "inverted", "right" or "left") to use for output
*
* Returns: 0 on success, error code on failure
*/
-int kmscon_text_new(struct kmscon_text **out, const char *backend)
+int kmscon_text_new(struct kmscon_text **out, const char *backend, const char *rotate)
{
struct kmscon_text *text;
int ret;
@@ -160,10 +162,31 @@ int kmscon_text_new(struct kmscon_text **out, const char *backend)
return -ENOMEM;
}
- ret = new_text(text, backend);
+ text->orientation = ORIENTATION_UNDEFINED;
+
+ if (rotate) {
+ if (strncmp(rotate, "normal", 6) == 0) {
+ text->orientation = ORIENTATION_NORMAL;
+ log_debug("using: orientation: normal");
+ }
+ else if (strncmp(rotate, "right", 5) == 0) {
+ text->orientation = ORIENTATION_RIGHT;
+ log_debug("using: orientation: right");
+ }
+ else if (strncmp(rotate, "inverted", 8) == 0) {
+ text->orientation = ORIENTATION_INVERTED;
+ log_debug("using: orientation: inverted");
+ }
+ else if (strncmp(rotate, "left", 4) == 0) {
+ text->orientation = ORIENTATION_LEFT;
+ log_debug("using: orientation: left");
+ }
+ }
+
+ ret = new_text(text, backend, text->orientation);
if (ret) {
if (backend)
- ret = new_text(text, NULL);
+ ret = new_text(text, NULL, text->orientation);
if (ret)
goto err_free;
}
@@ -333,6 +356,55 @@ unsigned int kmscon_text_get_rows(struct kmscon_text *txt)
return txt->rows;
}
+/**
+ * kmscon_text_get_orientation:
+ * @txt: valid text renderer
+ *
+ * With a valid @txt passed it, this returns the currently active orientation
+ * of the output/screen. Possible values are:
+ *
+ * - ORIENTATION_NORMAL
+ * - ORIENTATION_RIGHT
+ * - ORIENTATION_INVERTED
+ * - ORIENTATION_LEFT
+ *
+ * Returns: Current orientation enum or ORIENTATION_UNDEFINED if @txt is invalid
+ */
+unsigned int kmscon_text_get_orientation(struct kmscon_text *txt)
+{
+ if (!txt)
+ return ORIENTATION_UNDEFINED;
+
+ return txt->orientation;
+}
+
+/**
+ * kmscon_text_rotate:
+ * @txt: valid text renderer
+ * @orientation: enum value representing the desired output-orientation
+ *
+ * Update the rotation/orientation of the text. It can be one of:
+ *
+ * - ORIENTATION_NORMAL
+ * - ORIENTATION_RIGHT
+ * - ORIENTATION_INVERTED
+ * - ORIENTATION_LEFT
+ *
+ * Returns: 0 on success, negative error code on failure.
+ */
+int kmscon_text_rotate(struct kmscon_text *txt, unsigned int orientation)
+{
+ int ret = 0;
+
+ if (orientation == ORIENTATION_UNDEFINED)
+ return -EINVAL;
+
+ if (txt->ops->rotate)
+ ret = txt->ops->rotate(txt, orientation);
+
+ return ret;
+}
+
/**
* kmscon_text_prepare:
* @txt: valid text renderer
diff --git a/src/text.h b/src/text.h
index 570df6b7..269671e4 100644
--- a/src/text.h
+++ b/src/text.h
@@ -42,6 +42,15 @@
/* text renderer */
+enum Orientation {
+ ORIENTATION_UNDEFINED = 0,
+ ORIENTATION_NORMAL,
+ ORIENTATION_RIGHT,
+ ORIENTATION_INVERTED,
+ ORIENTATION_LEFT,
+ ORIENTATION_MAX
+};
+
struct kmscon_text;
struct kmscon_text_ops;
@@ -57,6 +66,7 @@ struct kmscon_text {
unsigned int cols;
unsigned int rows;
bool rendering;
+ unsigned int orientation;
};
struct kmscon_text_ops {
@@ -66,6 +76,7 @@ struct kmscon_text_ops {
void (*destroy) (struct kmscon_text *txt);
int (*set) (struct kmscon_text *txt);
void (*unset) (struct kmscon_text *txt);
+ int (*rotate) (struct kmscon_text *txt, unsigned int orientation);
int (*prepare) (struct kmscon_text *txt);
int (*draw) (struct kmscon_text *txt,
uint64_t id, const uint32_t *ch, size_t len,
@@ -79,7 +90,7 @@ struct kmscon_text_ops {
int kmscon_text_register(const struct kmscon_text_ops *ops);
void kmscon_text_unregister(const char *name);
-int kmscon_text_new(struct kmscon_text **out, const char *backend);
+int kmscon_text_new(struct kmscon_text **out, const char *backend, const char *rotate);
void kmscon_text_ref(struct kmscon_text *txt);
void kmscon_text_unref(struct kmscon_text *txt);
@@ -91,6 +102,9 @@ void kmscon_text_unset(struct kmscon_text *txt);
unsigned int kmscon_text_get_cols(struct kmscon_text *txt);
unsigned int kmscon_text_get_rows(struct kmscon_text *txt);
+unsigned int kmscon_text_get_orientation(struct kmscon_text *txt);
+int kmscon_text_rotate(struct kmscon_text *txt, unsigned int orientation);
+
int kmscon_text_prepare(struct kmscon_text *txt);
int kmscon_text_draw(struct kmscon_text *txt,
uint64_t id, const uint32_t *ch, size_t len,
diff --git a/src/text_gltex.c b/src/text_gltex.c
index 4b132d21..d7d25136 100644
--- a/src/text_gltex.c
+++ b/src/text_gltex.c
@@ -42,6 +42,7 @@
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <limits.h>
+#include <math.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
@@ -105,6 +106,7 @@ struct gltex {
GLfloat advance_y;
struct gl_shader *shader;
+ GLuint uni_orientation;
GLuint uni_proj;
GLuint uni_atlas;
GLuint uni_advance_htex;
@@ -112,6 +114,8 @@ struct gltex {
unsigned int sw;
unsigned int sh;
+
+ GLfloat angle;
};
#define FONT_WIDTH(txt) ((txt)->font->attr.width)
@@ -186,6 +190,7 @@ static int gltex_set(struct kmscon_text *txt)
if (ret)
goto err_bold_htable;
+ gt->uni_orientation = gl_shader_get_uniform(gt->shader, "orientation");
gt->uni_proj = gl_shader_get_uniform(gt->shader, "projection");
gt->uni_atlas = gl_shader_get_uniform(gt->shader, "atlas");
gt->uni_advance_htex = gl_shader_get_uniform(gt->shader,
@@ -202,8 +207,13 @@ static int gltex_set(struct kmscon_text *txt)
gt->sw = uterm_mode_get_width(mode);
gt->sh = uterm_mode_get_height(mode);
- txt->cols = gt->sw / FONT_WIDTH(txt);
- txt->rows = gt->sh / FONT_HEIGHT(txt);
+ if (txt->orientation == ORIENTATION_NORMAL || txt->orientation == ORIENTATION_INVERTED) {
+ txt->cols = gt->sw / FONT_WIDTH(txt);
+ txt->rows = gt->sh / FONT_HEIGHT(txt);
+ } else if (txt->orientation == ORIENTATION_RIGHT || txt->orientation == ORIENTATION_LEFT) {
+ txt->cols = gt->sh / FONT_WIDTH(txt);
+ txt->rows = gt->sw / FONT_HEIGHT(txt);
+ }
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &s);
if (s <= 0)
@@ -523,6 +533,43 @@ static int find_glyph(struct kmscon_text *txt, struct glyph **out,
return ret;
}
+static int gltex_rotate(struct kmscon_text *txt, unsigned int orientation)
+{
+ struct gltex *gt = txt->data;
+ int ret = 0;
+
+ txt->orientation = orientation;
+
+ if (txt->orientation == ORIENTATION_NORMAL || txt->orientation == ORIENTATION_INVERTED) {
+ txt->cols = gt->sw / FONT_WIDTH(txt);
+ txt->rows = gt->sh / FONT_HEIGHT(txt);
+ } else if (txt->orientation == ORIENTATION_RIGHT || txt->orientation == ORIENTATION_LEFT) {
+ txt->cols = gt->sh / FONT_WIDTH(txt);
+ txt->rows = gt->sw / FONT_HEIGHT(txt);
+ }
+
+ if (txt->orientation == ORIENTATION_NORMAL ) {
+ gt->angle = .0;
+ } else if (txt->orientation == ORIENTATION_INVERTED) {
+ gt->angle = 180.;
+ } else if (txt->orientation == ORIENTATION_RIGHT) {
+ gt->angle = 90.;
+ } else if (txt->orientation == ORIENTATION_LEFT) {
+ gt->angle = -90.;
+ }
+
+ if (txt->orientation == ORIENTATION_NORMAL || txt->orientation == ORIENTATION_INVERTED) {
+ gt->advance_x = 2.0 / gt->sw * FONT_WIDTH(txt);
+ gt->advance_y = 2.0 / gt->sh * FONT_HEIGHT(txt);
+ } else if (txt->orientation == ORIENTATION_RIGHT || txt->orientation == ORIENTATION_LEFT) {
+ float aspect = (float) gt->sw / (float) gt->sh;
+ gt->advance_x = 2.0 / gt->sw * FONT_WIDTH(txt) * aspect;
+ gt->advance_y = 2.0 / gt->sh * FONT_HEIGHT(txt) * (1./aspect);
+ }
+
+ return ret;
+}
+
static int gltex_prepare(struct kmscon_text *txt)
{
struct gltex *gt = txt->data;
@@ -540,8 +587,24 @@ static int gltex_prepare(struct kmscon_text *txt)
atlas->cache_num = 0;
}
- gt->advance_x = 2.0 / gt->sw * FONT_WIDTH(txt);
- gt->advance_y = 2.0 / gt->sh * FONT_HEIGHT(txt);
+ if (txt->orientation == ORIENTATION_NORMAL ) {
+ gt->angle = .0;
+ } else if (txt->orientation == ORIENTATION_INVERTED) {
+ gt->angle = 180.;
+ } else if (txt->orientation == ORIENTATION_RIGHT) {
+ gt->angle = 90.;
+ } else if (txt->orientation == ORIENTATION_LEFT) {
+ gt->angle = -90.;
+ }
+
+ if (txt->orientation == ORIENTATION_NORMAL || txt->orientation == ORIENTATION_INVERTED) {
+ gt->advance_x = 2.0 / gt->sw * FONT_WIDTH(txt);
+ gt->advance_y = 2.0 / gt->sh * FONT_HEIGHT(txt);
+ } else if (txt->orientation == ORIENTATION_RIGHT || txt->orientation == ORIENTATION_LEFT) {
+ float aspect = (float) gt->sw / (float) gt->sh;
+ gt->advance_x = 2.0 / gt->sw * FONT_WIDTH(txt) * aspect;
+ gt->advance_y = 2.0 / gt->sh * FONT_HEIGHT(txt) * (1./aspect);
+ }
return 0;
}
@@ -648,6 +711,7 @@ static int gltex_render(struct kmscon_text *txt)
gl_m4_identity(mat);
glUniformMatrix4fv(gt->uni_proj, 1, GL_FALSE, mat);
+ glUniform1f(gt->uni_orientation, gt->angle);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
@@ -693,6 +757,7 @@ struct kmscon_text_ops kmscon_text_gltex_ops = {
.destroy = gltex_destroy,
.set = gltex_set,
.unset = gltex_unset,
+ .rotate = gltex_rotate,
.prepare = gltex_prepare,
.draw = gltex_draw,
.render = gltex_render,
diff --git a/src/text_gltex_atlas.vert b/src/text_gltex_atlas.vert
index e99e1b4a..9bacae9f 100644
--- a/src/text_gltex_atlas.vert
+++ b/src/text_gltex_atlas.vert
@@ -31,6 +31,7 @@
*/
uniform mat4 projection;
+uniform float orientation;
attribute vec2 position;
attribute vec2 texture_position;
@@ -41,9 +42,18 @@ varying vec2 texpos;
varying vec3 fgcol;
varying vec3 bgcol;
+vec2 opRotate(in vec2 p, in float degrees)
+{
+ float rad = radians(degrees);
+ float c = cos(rad);
+ float s = sin(rad);
+ return p * mat2(vec2(c, s), vec2(-s, c));
+}
+
void main()
{
- gl_Position = projection * vec4(position, 0.0, 1.0);
+ vec2 rotatedPosition = opRotate(position, orientation);
+ gl_Position = projection * vec4(rotatedPosition, 0.0, 1.0);
texpos = texture_position;
fgcol = fgcolor;
bgcol = bgcolor;
From 9205158a737e94a07eb251f70c539a433bfd48f1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mirco=20M=C3=BCller?= <macslow@gmail.com>
Date: Mon, 24 Jul 2023 16:16:26 +0200
Subject: [PATCH 2/4] Ported mouse-selection/cut/paste support from my
(MacSlow) old kmscon-upstream branch to aetf's meson-based develop branch.
---
src/kmscon_mouse.c | 478 +++++++++++++++++++++++++++++++++++++++++
src/kmscon_mouse.h | 138 ++++++++++++
src/kmscon_seat.c | 23 +-
src/kmscon_seat.h | 5 +
src/kmscon_terminal.c | 122 +++++++++++
src/meson.build | 3 +
src/mouse_pointer.frag | 39 ++++
src/mouse_pointer.vert | 50 +++++
src/text.c | 25 +++
src/text.h | 4 +
src/text_gltex.c | 116 ++++++++++
11 files changed, 1002 insertions(+), 1 deletion(-)
create mode 100644 src/kmscon_mouse.c
create mode 100644 src/kmscon_mouse.h
create mode 100644 src/mouse_pointer.frag
create mode 100644 src/mouse_pointer.vert
diff --git a/src/kmscon_mouse.c b/src/kmscon_mouse.c
new file mode 100644
index 00000000..2e618a85
--- /dev/null
+++ b/src/kmscon_mouse.c
@@ -0,0 +1,478 @@
+#include <fcntl.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "kmscon_mouse.h"
+#include "kmscon_seat.h"
+#include "shl_log.h"
+#include "text.h"
+
+// utility functions for internal (kmscon_mouse) use only
+int mouse_ready_to_read(int handle);
+void mouse_hide_timer_cb(struct ev_timer *timer, uint64_t num, void *data);
+void handle_clicks (struct kmscon_mouse_info* mouse, int button);
+void update_mouse_button_state (struct kmscon_mouse_info* mouse,
+ int button,
+ char button_mask,
+ char buffer);
+void mouse_query_timer_cb(struct ev_timer *timer, uint64_t num, void *data);
+
+// public API
+struct kmscon_mouse_info* kmscon_mouse_init(struct ev_eloop* eloop,
+ struct kmscon_seat* seat)
+{
+ struct kmscon_mouse_info* mouse;
+ size_t size = sizeof(struct kmscon_mouse_info);
+ mouse = (struct kmscon_mouse_info*) calloc(1, size);
+ if (!mouse)
+ goto err;
+
+ mouse->seat = seat;
+ mouse->state[KMSCON_MOUSE_BUTTON_LEFT] = KMSCON_MOUSE_BUTTON_RELEASED;
+ mouse->state[KMSCON_MOUSE_BUTTON_MIDDLE] = KMSCON_MOUSE_BUTTON_RELEASED;
+ mouse->state[KMSCON_MOUSE_BUTTON_RIGHT] = KMSCON_MOUSE_BUTTON_RELEASED;
+ gettimeofday(&mouse->last_up, NULL);
+ gettimeofday(&mouse->last_down, NULL);
+ gettimeofday(&mouse->click, NULL);
+
+ size = sizeof(struct kmscon_selection_info);
+ mouse->selection = (struct kmscon_selection_info*) calloc(1, size);
+ if (!mouse->selection)
+ goto err;
+
+ int use_mouse = true;
+ char devname[MAX_DEVNAME_SIZE];
+ int length = sprintf (devname, DEVNAME_TEMPLATE);
+ if (length > MAX_DEVNAME_SIZE) {
+ log_warn ("Error while creating device string!\n");
+ use_mouse = false;
+ }
+
+ mouse->device = open (devname, O_RDONLY | O_NONBLOCK);
+ if (mouse->device < 0) {
+ log_warn ("Error while opening device '%s'!\n", devname);
+ use_mouse = false;
+ }
+
+ if (use_mouse) {
+ mouse->hide = true;
+
+ // mouse-query timer
+ mouse->query_timer_spec.it_interval.tv_sec = 0;
+ mouse->query_timer_spec.it_interval.tv_nsec = 10*1000*1000;
+ mouse->query_timer_spec.it_value.tv_sec = 0;
+ mouse->query_timer_spec.it_value.tv_nsec = 10*1000*1000;
+
+ int ret = ev_timer_new(&mouse->query_timer,
+ &mouse->query_timer_spec,
+ mouse_query_timer_cb,
+ mouse,
+ NULL,
+ NULL);
+ if (ret) {
+ log_error("cannot create mouse-query timer: %d", ret);
+ goto err;
+ }
+ ev_timer_enable(mouse->query_timer);
+
+ ret = ev_eloop_add_timer(eloop, mouse->query_timer);
+ if (ret) {
+ log_error("cannot add mouse-query timer to event-loop: %d", ret);
+ goto err;
+ }
+
+ // mouse-hide timer
+ mouse->hide_timer_spec.it_interval.tv_sec = 5;
+ mouse->hide_timer_spec.it_interval.tv_nsec = 0;
+ mouse->hide_timer_spec.it_value.tv_sec = 5;
+ mouse->hide_timer_spec.it_value.tv_nsec = 0;
+
+ ret = ev_timer_new(&mouse->hide_timer,
+ &mouse->hide_timer_spec,
+ mouse_hide_timer_cb,
+ mouse,
+ NULL,
+ NULL);
+ if (ret) {
+ log_error("cannot create mouse-hide timer: %d", ret);
+ goto err;
+ }
+ ev_timer_enable(mouse->hide_timer);
+
+ ret = ev_eloop_add_timer(eloop, mouse->hide_timer);
+ if (ret) {
+ log_error("cannot add mouse-hide timer to event-loop: %d", ret);
+ goto err;
+ }
+ }
+
+ return mouse;
+
+err:
+ kmscon_mouse_cleanup(mouse);
+ return NULL;
+}
+
+void kmscon_mouse_cleanup(struct kmscon_mouse_info* mouse)
+{
+ if (mouse) {
+ if (mouse->selection) {
+ free(mouse->selection);
+ }
+ free(mouse);
+ }
+}
+
+void kmscon_mouse_set_mapping(struct kmscon_mouse_info* mouse,
+ struct uterm_display* disp,
+ struct kmscon_text* txt)
+{
+ if (mouse && disp && txt) {
+ mouse->disp = disp;
+ mouse->txt = txt;
+ }
+}
+
+int kmscon_mouse_get_x(struct kmscon_mouse_info* mouse)
+{
+ int x = 0;
+ struct uterm_mode *mode = uterm_display_get_current(mouse->disp);
+ int sw = uterm_mode_get_width(mode);
+ int sh = uterm_mode_get_height(mode);
+ float fw = mouse->txt->font->attr.width;
+
+ if (mouse && mouse->disp && mouse->txt) {
+
+ if (mouse->txt->orientation == ORIENTATION_NORMAL ||
+ mouse->txt->orientation == ORIENTATION_INVERTED) {
+
+ float pixel_w = 2.f / (float) sw;
+ float cursor_x = (1.f + mouse->x)/pixel_w;
+ cursor_x = cursor_x - fmodf(cursor_x, 1.f);
+ x = (int) cursor_x/fw;
+ }
+
+ if (mouse->txt->orientation == ORIENTATION_RIGHT ||
+ mouse->txt->orientation == ORIENTATION_LEFT) {
+
+ float pixel_w = 2.f / (float) sh;
+ float cursor_x = (1.f + mouse->x)/pixel_w;
+ cursor_x = cursor_x - fmodf(cursor_x, 1.f);
+ x = (int) cursor_x/fw;
+ }
+
+ x = (x >= mouse->txt->cols) ? mouse->txt->cols - 1 : x;
+ }
+
+ return x;
+}
+
+int kmscon_mouse_get_y(struct kmscon_mouse_info* mouse)
+{
+ int y = 0;
+ struct uterm_mode *mode = uterm_display_get_current(mouse->disp);
+ int sw = uterm_mode_get_width(mode);
+ int sh = uterm_mode_get_height(mode);
+ float fh = mouse->txt->font->attr.height;
+
+ if (mouse && mouse->disp && mouse->txt) {
+
+ if (mouse->txt->orientation == ORIENTATION_NORMAL ||
+ mouse->txt->orientation == ORIENTATION_INVERTED) {
+
+ float pixel_h = 2.f / (float) sh;
+ float cursor_y = fabsf(-1.f + mouse->y)/pixel_h;
+ cursor_y = cursor_y - fmodf(cursor_y, 1.f);
+ y = (int) cursor_y/fh;
+ }
+
+ if (mouse->txt->orientation == ORIENTATION_RIGHT ||
+ mouse->txt->orientation == ORIENTATION_LEFT) {
+
+ float pixel_h = 2.f / (float) sw;
+ float cursor_y = fabsf(-1.f + mouse->y)/pixel_h;
+ cursor_y = cursor_y - fmodf(cursor_y, 1.f);
+ y = (int) cursor_y/fh;
+ }
+
+ y = (y >= mouse->txt->rows) ? mouse->txt->rows - 1 : y;
+ }
+
+ return y;
+}
+
+int kmscon_mouse_is_clicked(struct kmscon_mouse_info* mouse,
+ int button)
+{
+ if (mouse && button <= 3 && button >= 0)
+ return mouse->clicked[button];
+
+ return 0;
+}
+
+void kmscon_mouse_clear_clicked(struct kmscon_mouse_info* mouse,
+ int button)
+{
+ if (mouse) {
+ mouse->clicked[button] = false;
+ }
+}
+
+int kmscon_mouse_is_dbl_clicked(struct kmscon_mouse_info* mouse,
+ int button)
+{
+ if (mouse && button <= 3 && button >= 0)
+ return mouse->dbl_clicked[button];
+
+ return 0;
+}
+
+void kmscon_mouse_clear_dbl_clicked(struct kmscon_mouse_info* mouse,
+ int button)
+{
+ if (mouse) {
+ mouse->dbl_clicked[button] = false;
+ }
+}
+
+int kmscon_mouse_is_up(struct kmscon_mouse_info* mouse,
+ int button)
+{
+ if (mouse && button <= 3 && button >= 0)
+ return (mouse->state[button] == KMSCON_MOUSE_BUTTON_UP);
+
+ return 0;
+}
+
+int kmscon_mouse_is_down(struct kmscon_mouse_info* mouse,
+ int button)
+{
+ if (mouse && button <= 3 && button >= 0)
+ return (mouse->state[button] == KMSCON_MOUSE_BUTTON_DOWN);
+
+ return 0;
+}
+
+int kmscon_mouse_is_released(struct kmscon_mouse_info* mouse,
+ int button)
+{
+ if (mouse && button <= 3 && button >= 0)
+ return (mouse->state[button] == KMSCON_MOUSE_BUTTON_RELEASED);
+
+ return 0;
+}
+
+int kmscon_mouse_is_pressed(struct kmscon_mouse_info* mouse,
+ int button)
+{
+ if (mouse && button <= 3 && button >= 0)
+ return (mouse->state[button] == KMSCON_MOUSE_BUTTON_PRESSED);
+
+ return 0;
+}
+
+int kmscon_mouse_is_hidden(struct kmscon_mouse_info* mouse)
+{
+ if (mouse)
+ return mouse->hide;
+
+ return 0;
+}
+
+void kmscon_mouse_selection_copy(struct kmscon_mouse_info* mouse,
+ struct tsm_screen *console)
+{
+ if (mouse && console) {
+ int length = tsm_screen_selection_copy(console,
+ &mouse->selection->buffer);
+ mouse->selection->buffer_length = length;
+ }
+}
+
+int kmscon_mouse_is_selection_empty(struct kmscon_mouse_info* mouse)
+{
+ if (!mouse || !mouse->selection)
+ return true;
+
+ if (mouse->selection->buffer_length == 0)
+ return true;
+ else
+ return false;
+}
+
+// internal API/utility functions
+int mouse_ready_to_read(int handle)
+{
+ const struct timespec timeout = {0, 3*1000*1000};
+ fd_set readfds;
+
+ FD_ZERO(&readfds);
+ FD_SET(handle, &readfds);
+ int result = pselect(handle + 1, &readfds, NULL, NULL, &timeout, NULL);
+ if (result == -1) {
+ log_error("There was an error trying to check for available mouse-data.");
+ return 0;
+ } else if (result == 0) {
+ return 0;
+ } else {
+ result = FD_ISSET(handle, &readfds);
+ if (result) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+void mouse_hide_timer_cb(struct ev_timer *timer,
+ uint64_t num,
+ void *data)
+{
+ if (!data) {
+ log_warn("No valid pointer passed to mouse_hide_timer_cb().");
+ return;
+ }
+
+ struct kmscon_mouse_info* mouse = NULL;
+ mouse = (struct kmscon_mouse_info*) data;
+ mouse->hide = true;
+}
+
+void handle_clicks (struct kmscon_mouse_info* mouse, int button)
+{
+ if (!mouse)
+ return;
+
+ time_t seconds = mouse->last_down.tv_sec;
+ suseconds_t useconds = mouse->last_down.tv_usec;
+ gettimeofday(&mouse->last_up, NULL);
+
+ // check for click
+ if (seconds == mouse->last_up.tv_sec &&
+ mouse->last_up.tv_usec - useconds <= 150000) {
+
+ mouse->clicked[button] = true;
+
+ // check for double-click
+ time_t dbl_seconds = mouse->click.tv_sec;
+ suseconds_t dbl_useconds = mouse->click.tv_usec;
+ gettimeofday(&mouse->click, NULL);
+
+ if (dbl_seconds == mouse->click.tv_sec &&
+ mouse->click.tv_usec - dbl_useconds <= 300000) {
+
+ mouse->dbl_clicked[button] = true;
+ }
+ }
+}
+
+void update_mouse_button_state(struct kmscon_mouse_info* mouse,
+ int button,
+ char button_mask,
+ char buffer)
+{
+ if (!mouse)
+ return;
+
+ int new_state = mouse->state[button];
+
+ // released -> down
+ if (mouse->state[button] == KMSCON_MOUSE_BUTTON_RELEASED &&
+ (buffer & button_mask)) {
+ new_state = KMSCON_MOUSE_BUTTON_DOWN;
+ gettimeofday(&mouse->last_down, NULL);
+ }
+
+ // down -> pressed
+ if (mouse->state[button] == KMSCON_MOUSE_BUTTON_DOWN &&
+ (buffer & button_mask)) {
+ new_state = KMSCON_MOUSE_BUTTON_PRESSED;
+ }
+
+ // pressed -> up
+ if (mouse->state[button] == KMSCON_MOUSE_BUTTON_PRESSED &&
+ !(buffer & button_mask)) {
+ new_state = KMSCON_MOUSE_BUTTON_UP;
+ }
+
+ // up -> released
+ if (mouse->state[button] == KMSCON_MOUSE_BUTTON_UP &&
+ !(buffer & button_mask)) {
+ new_state = KMSCON_MOUSE_BUTTON_RELEASED;
+ handle_clicks (mouse, button);
+ }
+
+ // edge-case (no mouse motion): down -> released
+ if (mouse->state[button] == KMSCON_MOUSE_BUTTON_DOWN &&
+ !(buffer & button_mask)) {
+ new_state = KMSCON_MOUSE_BUTTON_RELEASED;
+ handle_clicks (mouse, button);
+ }
+
+ // edge-case (no mouse motion): up -> pressed
+ if (mouse->state[button] == KMSCON_MOUSE_BUTTON_UP &&
+ (buffer & button_mask)) {
+ new_state = KMSCON_MOUSE_BUTTON_PRESSED;
+ }
+
+ mouse->state[button] = new_state;
+}
+
+void mouse_query_timer_cb(struct ev_timer *timer, uint64_t num, void *data)
+{
+ if (!data) {
+ log_warn("No valid pointer passed to mouse_query_timer_cb().");
+ return;
+ }
+
+ struct kmscon_mouse_info* mouse = (struct kmscon_mouse_info*) data;
+ ssize_t size = 0;
+ short relative_x = 0;
+ short relative_y = 0;
+ char buffer[BUFFER_SIZE];
+
+ if (mouse_ready_to_read (mouse->device)) {
+ size = read(mouse->device, buffer, sizeof (buffer));
+ if (size != BUFFER_SIZE) {
+ log_error("Error reading device event... buffer-size mismatch!");
+ return;
+ }
+ relative_x = (short) buffer[1];
+ relative_y = (short) buffer[2];
+ mouse->x += .0025 * (float) relative_x/MOTION_SCALE;
+ mouse->y += .0025 * (float) relative_y/MOTION_SCALE;
+
+ // limit 'normalized' coordinates to the extents of the GL-viewport
+ if (mouse->x < -1.f) mouse->x = -1.f;
+ if (mouse->x > 1.f) mouse->x = 1.f;
+ if (mouse->y < -1.f) mouse->y = -1.f;
+ if (mouse->y > 1.f) mouse->y = 1.f;
+
+ update_mouse_button_state (mouse,
+ KMSCON_MOUSE_BUTTON_LEFT,
+ BUTTON_MASK_LEFT,
+ buffer[0]);
+ update_mouse_button_state (mouse,
+ KMSCON_MOUSE_BUTTON_MIDDLE,
+ BUTTON_MASK_MIDDLE,
+ buffer[0]);
+ update_mouse_button_state (mouse,
+ KMSCON_MOUSE_BUTTON_RIGHT,
+ BUTTON_MASK_RIGHT,
+ buffer[0]);
+
+ mouse->hide = false;
+
+ // FIXME: Triggering the refresh like this works, but
+ // it's not an elegant solution. Using an event would
+ // be nicer, but I did not yet fully grasp the pile of
+ // event-loops kmscon uses. There has to be a way to
+ // trigger a refresh via the event-loops of uterm_video.
+ kmscon_seat_refresh_display(mouse->seat, mouse->disp);
+ }
+}
diff --git a/src/kmscon_mouse.h b/src/kmscon_mouse.h
new file mode 100644
index 00000000..ea0e5b7c
--- /dev/null
+++ b/src/kmscon_mouse.h
@@ -0,0 +1,138 @@
+/*
+ * Mouse
+ *
+ * Copyright (c) 2023 Mirco Müller <macslow@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * Mouse
+ *
+ * Support for mouse/pointer-devices (usb-mice, trackpoints,
+ * trackpads etc.) and text copy&paste à la gpm is implemented here.
+ *
+ * You can get x- and y-coordinates (in character-cells) and check if
+ * LMB, MMB or RMB are up, down, pressed or released. You can also check
+ * for single- or double-click of LMB, MMB or RMB.
+ *
+ * There should only ever be one kmscon_mouse_info structure be
+ * created via kmscon_mouse_init() in all of kmscon. It will handle
+ * everything that's plugged into the system via the Linux kernels
+ * input sub-system.
+ *
+ * It is written in a way to be easy to read and understand, not waste
+ * cpu-cycles and just support enough features in order to implement
+ * gpm-like cursor- and copy&paste-functionality. Take note that scroll-
+ * -wheels are currently NOT supported.
+ *
+ * Do not use this in a general UI-toolkit manner like mouse-support in
+ * Qt, gtk+ or the like.
+ */
+
+#ifndef KMSCON_MOUSE_H
+#define KMSCON_MOUSE_H
+
+#include <libtsm.h>
+#include <stdint.h>
+#include <time.h>
+
+#include "eloop.h"
+
+#define MAX_DEVNAME_SIZE 16
+#define DEVNAME_TEMPLATE "/dev/input/mice"
+#define BUTTON_MASK_LEFT 0x01
+#define BUTTON_MASK_RIGHT 0x02
+#define BUTTON_MASK_MIDDLE 0x04
+#define MOTION_SCALE 2
+#define BUFFER_SIZE 3
+
+#define KMSCON_MOUSE_BUTTON_LEFT 0
+#define KMSCON_MOUSE_BUTTON_MIDDLE 1
+#define KMSCON_MOUSE_BUTTON_RIGHT 2
+
+typedef enum {
+ KMSCON_MOUSE_BUTTON_UNDEFINED = -1,
+ KMSCON_MOUSE_BUTTON_PRESSED = 0,
+ KMSCON_MOUSE_BUTTON_RELEASED = 1,
+ KMSCON_MOUSE_BUTTON_DOWN = 2,
+ KMSCON_MOUSE_BUTTON_UP = 3
+} kmscon_mouse_button_state;
+
+struct kmscon_selection_info {
+ char* buffer;
+ int buffer_length;
+};
+
+struct kmscon_mouse_info {
+ struct kmscon_selection_info* selection;
+
+ struct ev_timer* query_timer;
+ struct itimerspec query_timer_spec;
+
+ struct ev_timer* hide_timer;
+ struct itimerspec hide_timer_spec;
+
+ int device;
+ float x;
+ float y;
+ int hide;
+
+ kmscon_mouse_button_state state[3];
+ int clicked[3];
+ int dbl_clicked[3];
+
+ struct timeval last_up;
+ struct timeval last_down;
+ struct timeval click;
+
+ struct uterm_display* disp;
+ struct kmscon_text* txt;
+ struct kmscon_seat* seat;
+};
+
+struct kmscon_mouse_info* kmscon_mouse_init(struct ev_eloop* eloop,
+ struct kmscon_seat* seat);
+void kmscon_mouse_cleanup(struct kmscon_mouse_info* mouse);
+
+void kmscon_mouse_set_mapping(struct kmscon_mouse_info* mouse,
+ struct uterm_display* disp,
+ struct kmscon_text* txt);
+
+int kmscon_mouse_get_x(struct kmscon_mouse_info* mouse);
+int kmscon_mouse_get_y(struct kmscon_mouse_info* mouse);
+
+int kmscon_mouse_is_clicked(struct kmscon_mouse_info* mouse, int button);
+void kmscon_mouse_clear_clicked(struct kmscon_mouse_info* mouse, int button);
+int kmscon_mouse_is_dbl_clicked(struct kmscon_mouse_info* mouse, int button);
+void kmscon_mouse_clear_dbl_clicked(struct kmscon_mouse_info* mouse, int button);
+
+int kmscon_mouse_is_up(struct kmscon_mouse_info* mouse, int button);
+int kmscon_mouse_is_down(struct kmscon_mouse_info* mouse, int button);
+int kmscon_mouse_is_released(struct kmscon_mouse_info* mouse, int button);
+int kmscon_mouse_is_pressed(struct kmscon_mouse_info* mouse, int button);
+int kmscon_mouse_is_hidden(struct kmscon_mouse_info* mouse);
+
+void kmscon_mouse_selection_copy(struct kmscon_mouse_info* mouse,
+ struct tsm_screen *console);
+
+int kmscon_mouse_is_selection_empty(struct kmscon_mouse_info* mouse);
+
+#endif /* KMSCON_MOUSE_H */
diff --git a/src/kmscon_seat.c b/src/kmscon_seat.c
index 56a65366..bb02f551 100644
--- a/src/kmscon_seat.c
+++ b/src/kmscon_seat.c
@@ -30,12 +30,14 @@
*/
#include <errno.h>
+#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include "conf.h"
#include "eloop.h"
#include "kmscon_conf.h"
#include "kmscon_dummy.h"
+#include "kmscon_mouse.h"
#include "kmscon_seat.h"
#include "kmscon_terminal.h"
#include "shl_dlist.h"
@@ -96,6 +98,8 @@ struct kmscon_seat {
unsigned int async_schedule;
+ struct kmscon_mouse_info* mouse;
+
kmscon_seat_cb_t cb;
void *data;
};
@@ -680,7 +684,7 @@ int kmscon_seat_new(struct kmscon_seat **out,
void *data)
{
struct kmscon_seat *seat;
- int ret;
+ int ret = 0;
const char *locale;
char *keymap, *compose_file;
size_t compose_file_len;
@@ -699,6 +703,12 @@ int kmscon_seat_new(struct kmscon_seat **out,
shl_dlist_init(&seat->displays);
shl_dlist_init(&seat->sessions);
+ seat->mouse = kmscon_mouse_init(seat->eloop, seat);
+ if (!seat->mouse) {
+ log_error("failed to initalize mouse");
+ goto err_free;
+ }
+
seat->name = strdup(seatname);
if (!seat->name) {
log_error("cannot copy string");
@@ -783,6 +793,7 @@ int kmscon_seat_new(struct kmscon_seat **out,
err_name:
free(seat->name);
err_free:
+ kmscon_mouse_cleanup(seat->mouse);
free(seat);
return ret;
}
@@ -824,6 +835,8 @@ void kmscon_seat_free(struct kmscon_seat *seat)
uterm_input_unregister_cb(seat->input, seat_input_event, seat);
uterm_input_unref(seat->input);
kmscon_conf_free(seat->conf_ctx);
+ free(seat->mouse->selection);
+ free(seat->mouse);
free(seat->name);
uterm_vt_master_unref(seat->vtm);
ev_eloop_unref(seat->eloop);
@@ -960,6 +973,14 @@ struct conf_ctx *kmscon_seat_get_conf(struct kmscon_seat *seat)
return seat->conf_ctx;
}
+struct kmscon_mouse_info *kmscon_seat_get_mouse(struct kmscon_seat *seat)
+{
+ if (!seat)
+ return NULL;
+
+ return seat->mouse;
+}
+
void kmscon_seat_schedule(struct kmscon_seat *seat, unsigned int id)
{
struct shl_dlist *iter;
diff --git a/src/kmscon_seat.h b/src/kmscon_seat.h
index 81169686..11d9efa2 100644
--- a/src/kmscon_seat.h
+++ b/src/kmscon_seat.h
@@ -34,6 +34,8 @@
#include <stdbool.h>
#include <stdlib.h>
+#include <sys/time.h>
+#include "kmscon_mouse.h"
#include "conf.h"
#include "eloop.h"
#include "uterm_input.h"
@@ -98,6 +100,9 @@ struct uterm_input *kmscon_seat_get_input(struct kmscon_seat *seat);
struct ev_eloop *kmscon_seat_get_eloop(struct kmscon_seat *seat);
struct conf_ctx *kmscon_seat_get_conf(struct kmscon_seat *seat);
+struct kmscon_mouse_info *kmscon_seat_get_mouse(struct kmscon_seat *seat);
+struct shl_dlist kmscon_seat_get_displays(struct kmscon_seat *seat);
+
void kmscon_seat_schedule(struct kmscon_seat *seat, unsigned int id);
int kmscon_seat_register_session(struct kmscon_seat *seat,
diff --git a/src/kmscon_terminal.c b/src/kmscon_terminal.c
index 44efdf63..30e1d9ac 100644
--- a/src/kmscon_terminal.c
+++ b/src/kmscon_terminal.c
@@ -33,11 +33,21 @@
#include <errno.h>
#include <inttypes.h>
#include <libtsm.h>
+#include <math.h>
#include <stdlib.h>
#include <string.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <time.h>
+#include <sys/select.h>
+#include <linux/input.h>
+
#include "conf.h"
#include "eloop.h"
#include "kmscon_conf.h"
+#include "kmscon_mouse.h"
#include "kmscon_seat.h"
#include "kmscon_terminal.h"
#include "pty.h"
@@ -82,6 +92,9 @@ struct kmscon_terminal {
struct kmscon_font_attr font_attr;
struct kmscon_font *font;
struct kmscon_font *bold_font;
+
+ struct kmscon_mouse_info* mouse;
+ struct kmscon_selection_info* selection;
};
static void terminal_resize(struct kmscon_terminal *term,
@@ -127,6 +140,107 @@ static void do_clear_margins(struct screen *scr)
}
}
+static void handle_mouse_word_selection(struct kmscon_mouse_info* mouse,
+ struct kmscon_text* text,
+ struct tsm_screen* console)
+{
+ if (!mouse || !text || !console)
+ return;
+
+ // on left double-click trigger word-wise selection of text
+ if (kmscon_mouse_is_dbl_clicked(mouse, KMSCON_MOUSE_BUTTON_LEFT)) {
+ int from_x = kmscon_mouse_get_x(mouse);
+ int from_y = kmscon_mouse_get_y(mouse);
+ tsm_screen_selection_reset(console);
+ tsm_screen_selection_start(console, 0, from_y);
+ tsm_screen_selection_target(console,
+ text->cols - 1, from_y);
+ kmscon_mouse_selection_copy(mouse, console);
+ tsm_screen_selection_reset(console);
+
+ char* buf = mouse->selection->buffer;
+
+ int start_x = 0;
+ int target_x = 0;
+
+ // find trailing space or end of line
+ for (target_x = from_x; target_x <= text->cols - 1; ++target_x) {
+ if (buf[target_x] == ' ' || buf[target_x] == '\0') {
+ --target_x;
+ break;
+ }
+ }
+
+ // find leading space of start of line
+ for (start_x = from_x; start_x >= 0; --start_x) {
+ if (buf[start_x] == ' ') {
+ ++start_x;
+ break;
+ } else if (start_x == 0) {
+ break;
+ }
+ }
+
+ // mark word under cusor and update selection-buffer
+ tsm_screen_selection_start(console, start_x, from_y);
+ tsm_screen_selection_target(console, target_x, from_y);
+ kmscon_mouse_selection_copy(mouse, console);
+ kmscon_mouse_clear_dbl_clicked(mouse, KMSCON_MOUSE_BUTTON_LEFT);
+ }
+}
+
+static void handle_mouse_random_selection(struct kmscon_mouse_info* mouse,
+ struct kmscon_terminal* terminal)
+{
+ if (!mouse || !terminal)
+ return;
+
+ // paste current selection at current cursor position from buffer
+ if (kmscon_mouse_is_clicked (mouse, KMSCON_MOUSE_BUTTON_MIDDLE) &&
+ !kmscon_mouse_is_selection_empty (mouse)) {
+ kmscon_pty_write(terminal->pty,
+ mouse->selection->buffer,
+ mouse->selection->buffer_length);
+ tsm_screen_selection_reset(terminal->console);
+ kmscon_mouse_clear_clicked(mouse, KMSCON_MOUSE_BUTTON_MIDDLE);
+ }
+
+ // mark start of new selection
+ if (kmscon_mouse_is_down(mouse, KMSCON_MOUSE_BUTTON_LEFT)) {
+ int from_x = kmscon_mouse_get_x(mouse);
+ int from_y = kmscon_mouse_get_y(mouse);
+ tsm_screen_selection_reset(terminal->console);
+ tsm_screen_selection_start(terminal->console, from_x, from_y);
+ tsm_screen_selection_target(terminal->console, from_x, from_y);
+ } else if (kmscon_mouse_is_pressed(mouse, KMSCON_MOUSE_BUTTON_LEFT)) {
+ tsm_screen_selection_target(terminal->console,
+ kmscon_mouse_get_x(mouse),
+ kmscon_mouse_get_y(mouse));
+ }
+
+ // copy new selection to buffer
+ if (kmscon_mouse_is_up(mouse, KMSCON_MOUSE_BUTTON_LEFT)) {
+ kmscon_mouse_selection_copy(mouse, terminal->console);
+ }
+}
+
+static void handle_mouse_drawing(struct kmscon_mouse_info* mouse,
+ struct kmscon_text* txt)
+{
+ if (!mouse || !txt)
+ return;
+
+ // draw mouse-cursor only if no buttons are pressed and hiding is off
+ if (kmscon_mouse_is_released(mouse, KMSCON_MOUSE_BUTTON_LEFT) &&
+ kmscon_mouse_is_released(mouse, KMSCON_MOUSE_BUTTON_MIDDLE) &&
+ kmscon_mouse_is_released(mouse, KMSCON_MOUSE_BUTTON_RIGHT) &&
+ !kmscon_mouse_is_hidden(mouse)) {
+ kmscon_text_render_pointer(txt,
+ kmscon_mouse_get_x(mouse),
+ kmscon_mouse_get_y(mouse));
+ }
+}
+
static void do_redraw_screen(struct screen *scr)
{
int ret;
@@ -141,6 +255,13 @@ static void do_redraw_screen(struct screen *scr)
tsm_screen_draw(scr->term->console, kmscon_text_draw_cb, scr->txt);
kmscon_text_render(scr->txt);
+ // deal with mapping normalized coords to character-cell coords
+ kmscon_mouse_set_mapping(scr->term->mouse, scr->disp, scr->txt);
+
+ handle_mouse_word_selection(scr->term->mouse,scr->txt, scr->term->console);
+ handle_mouse_random_selection(scr->term->mouse, scr->term);
+ handle_mouse_drawing(scr->term->mouse, scr->txt);
+
ret = uterm_display_swap(scr->disp, false);
if (ret) {
log_warning("cannot swap display %p", scr->disp);
@@ -681,6 +802,7 @@ int kmscon_terminal_register(struct kmscon_session **out,
term->ref = 1;
term->eloop = kmscon_seat_get_eloop(seat);
term->input = kmscon_seat_get_input(seat);
+ term->mouse = kmscon_seat_get_mouse(seat);
shl_dlist_init(&term->screens);
term->conf_ctx = kmscon_seat_get_conf(seat);
diff --git a/src/meson.build b/src/meson.build
index c18759f4..1a3c9cb4 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -205,6 +205,8 @@ if enable_renderer_gltex
'kmscon_mod_gltex.c',
embed_gen.process('text_gltex_atlas.vert', extra_args: shader_regex),
embed_gen.process('text_gltex_atlas.frag', extra_args: shader_regex),
+ embed_gen.process('mouse_pointer.vert', extra_args: shader_regex),
+ embed_gen.process('mouse_pointer.frag', extra_args: shader_regex),
],
name_prefix: '',
dependencies: [libtsm_deps, glesv2_deps, shl_deps],
@@ -249,6 +251,7 @@ kmscon_srcs = [
'kmscon_seat.c',
'kmscon_conf.c',
'kmscon_main.c',
+ 'kmscon_mouse.c',
]
if enable_session_dummy
kmscon_srcs += 'kmscon_dummy.c'
diff --git a/src/mouse_pointer.frag b/src/mouse_pointer.frag
new file mode 100644
index 00000000..847f01d3
--- /dev/null
+++ b/src/mouse_pointer.frag
@@ -0,0 +1,39 @@
+/*
+ * kmscon - Fragment Shader
+ *
+ * Copyright (c) 2011-2012 David Herrmann <dh.herrmann@googlemail.com>
+ * Copyright (c) 2011 University of Tuebingen
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * Fragment Shader
+ * A basic fragment shader for drawing a simple mouse-pointer.
+ */
+
+precision mediump float;
+
+uniform vec4 color;
+
+void main()
+{
+ gl_FragColor = vec4(color);
+}
\ No newline at end of file
diff --git a/src/mouse_pointer.vert b/src/mouse_pointer.vert
new file mode 100644
index 00000000..50dac93a
--- /dev/null
+++ b/src/mouse_pointer.vert
@@ -0,0 +1,50 @@
+/*
+ * kmscon - Vertex Shader
+ *
+ * Copyright (c) 2011-2012 David Herrmann <dh.herrmann@googlemail.com>
+ * Copyright (c) 2011 University of Tuebingen
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * Vertex Shader
+ * A basic vertex shader for drawing a simple mouse-pointer.
+ */
+
+uniform mat4 projection;
+uniform float orientation;
+uniform vec2 offset;
+
+attribute vec2 position;
+
+vec2 opRotate(in vec2 p, in float degrees)
+{
+ float rad = radians(degrees);
+ float c = cos(rad);
+ float s = sin(rad);
+ return p * mat2(vec2(c, s), vec2(-s, c));
+}
+
+void main()
+{
+ vec2 rotatedPosition = opRotate(position + offset, orientation);
+ gl_Position = projection * vec4(rotatedPosition, 0.0, 1.0);
+}
\ No newline at end of file
diff --git a/src/text.c b/src/text.c
index 9b116e27..ddbb77ec 100644
--- a/src/text.c
+++ b/src/text.c
@@ -489,6 +489,31 @@ int kmscon_text_render(struct kmscon_text *txt)
return ret;
}
+/**
+ * kmscon_text_render_pointer:
+ * @txt: valid text renderer
+ * @cursor_x: column cell coordinate
+ * @cursor_y: row cell coordinate
+ *
+ * This draws a mouse-pointer cross-hair at the current mouse-position..
+ *
+ * Returns: 0 on success, negative error on failure.
+ */
+int kmscon_text_render_pointer(struct kmscon_text *txt,
+ int cursor_x,
+ int cursor_y)
+{
+ int ret = 0;
+
+ if (!txt)
+ return -EINVAL;
+
+ if (txt->ops->render_pointer)
+ ret = txt->ops->render_pointer(txt, cursor_x, cursor_y);
+
+ return ret;
+}
+
/**
* kmscon_text_abort:
* @txt: valid text renderer
diff --git a/src/text.h b/src/text.h
index 269671e4..7a973613 100644
--- a/src/text.h
+++ b/src/text.h
@@ -84,6 +84,7 @@ struct kmscon_text_ops {
unsigned int posx, unsigned int posy,
const struct tsm_screen_attr *attr);
int (*render) (struct kmscon_text *txt);
+ int (*render_pointer) (struct kmscon_text *txt, int cursor_x, int cursor_y);
void (*abort) (struct kmscon_text *txt);
};
@@ -112,6 +113,9 @@ int kmscon_text_draw(struct kmscon_text *txt,
unsigned int posx, unsigned int posy,
const struct tsm_screen_attr *attr);
int kmscon_text_render(struct kmscon_text *txt);
+int kmscon_text_render_pointer(struct kmscon_text *txt,
+ int cursor_x,
+ int cursor_y);
void kmscon_text_abort(struct kmscon_text *txt);
int kmscon_text_draw_cb(struct tsm_screen *con,
diff --git a/src/text_gltex.c b/src/text_gltex.c
index d7d25136..5b219545 100644
--- a/src/text_gltex.c
+++ b/src/text_gltex.c
@@ -55,6 +55,8 @@
#include "uterm_video.h"
#include "text_gltex_atlas.frag.bin.h"
#include "text_gltex_atlas.vert.bin.h"
+#include "mouse_pointer.frag.bin.h"
+#include "mouse_pointer.vert.bin.h"
#define LOG_SUBSYSTEM "text_gltex"
@@ -94,6 +96,19 @@ struct glyph {
#define GLYPH_STRIDE(gly) ((gly)->glyph->buf.stride)
#define GLYPH_DATA(gly) ((gly)->glyph->buf.data)
+GLfloat mouse_block_vertices[] = {-.01f, .02f, // 0) top-left
+ .01f, .02f, // 1) top-right
+ -.01f, -.02f, // 2) bottom-left
+ .01f, -.02f}; // 3) bottom-right
+
+GLuint mouse_block_outline_indices[] = {0, 1,
+ 1, 3,
+ 0, 2,
+ 2, 3};
+
+GLuint mouse_block_fill_indices[] = {0, 1, 3,
+ 0, 3, 2};
+
struct gltex {
struct shl_hashtable *glyphs;
struct shl_hashtable *bold_glyphs;
@@ -116,6 +131,12 @@ struct gltex {
unsigned int sh;
GLfloat angle;
+
+ struct gl_shader *mouse_pointer_shader;
+ GLuint uni_orientation_mouse;
+ GLuint uni_proj_mouse;
+ GLuint uni_color_mouse;
+ GLuint uni_offset_mouse;
};
#define FONT_WIDTH(txt) ((txt)->font->attr.width)
@@ -203,6 +224,34 @@ static int gltex_set(struct kmscon_text *txt)
goto err_shader;
}
+ vert = _binary_mouse_pointer_vert_start;
+ vlen = _binary_mouse_pointer_vert_size;
+ frag = _binary_mouse_pointer_frag_start;
+ flen = _binary_mouse_pointer_frag_size;
+ gl_clear_error();
+ static char *mouse_pointer_attr[] = { "position" };
+ ret = gl_shader_new(>->mouse_pointer_shader,
+ vert, vlen,
+ frag, flen,
+ mouse_pointer_attr, 1,
+ log_llog, NULL);
+ if (ret)
+ goto err_shader;
+
+ gt->uni_orientation_mouse = gl_shader_get_uniform(gt->mouse_pointer_shader,
+ "orientation");
+ gt->uni_proj_mouse = gl_shader_get_uniform(gt->mouse_pointer_shader,
+ "projection");
+ gt->uni_color_mouse = gl_shader_get_uniform(gt->mouse_pointer_shader,
+ "color");
+ gt->uni_offset_mouse = gl_shader_get_uniform(gt->mouse_pointer_shader,
+ "offset");
+
+ if (gl_has_error(gt->mouse_pointer_shader)) {
+ log_warning("cannot create mouse-pointer shader");
+ goto err_mouse_pointer_shader;
+ }
+
mode = uterm_display_get_current(txt->disp);
gt->sw = uterm_mode_get_width(mode);
gt->sh = uterm_mode_get_height(mode);
@@ -233,6 +282,8 @@ static int gltex_set(struct kmscon_text *txt)
return 0;
+err_mouse_pointer_shader:
+ gl_shader_unref(gt->mouse_pointer_shader);
err_shader:
gl_shader_unref(gt->shader);
err_bold_htable:
@@ -750,6 +801,70 @@ static int gltex_render(struct kmscon_text *txt)
return 0;
}
+static int gltex_render_pointer(struct kmscon_text *txt, int cursor_x, int cursor_y)
+{
+ struct gltex *gt = txt->data;
+ float mat[16];
+
+ gl_clear_error();
+
+ gl_shader_use(gt->mouse_pointer_shader);
+
+ GLfloat pixel_w = .0f;
+ GLfloat pixel_h = .0f;
+ GLfloat hw = .0f;
+ GLfloat hh = .0f;
+
+ if (txt->orientation == ORIENTATION_NORMAL || txt->orientation == ORIENTATION_INVERTED) {
+ pixel_w = 2.0f/gt->sw;
+ pixel_h = 2.0f/gt->sh;
+ hw = FONT_WIDTH(txt)*pixel_w*.5f;
+ hh = FONT_HEIGHT(txt)*pixel_h*.5f;
+ } else if (txt->orientation == ORIENTATION_RIGHT || txt->orientation == ORIENTATION_LEFT) {
+ pixel_w = 2.0f/gt->sh;
+ pixel_h = 2.0f/gt->sw;
+ hw = FONT_WIDTH(txt)*pixel_w*.5f;
+ hh = FONT_HEIGHT(txt)*pixel_h*.5f;
+ }
+
+ mouse_block_vertices[0] = -hw; // top-left
+ mouse_block_vertices[1] = hh;
+ mouse_block_vertices[2] = hw - pixel_w; // top-right
+ mouse_block_vertices[3] = hh;
+ mouse_block_vertices[4] = -hw; // bottom-left
+ mouse_block_vertices[5] = -hh + pixel_h;
+ mouse_block_vertices[6] = hw - pixel_w; // bottom-right
+ mouse_block_vertices[7] = -hh + pixel_h;
+
+ glViewport(0, 0, gt->sw, gt->sh);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ gl_m4_identity(mat);
+ glUniformMatrix4fv(gt->uni_proj_mouse, 1, GL_FALSE, mat);
+ glUniform1f(gt->uni_orientation_mouse, gt->angle);
+
+ GLfloat top_left[2] = {-1.f, 1.f};
+ GLfloat x = top_left[0] + hw + cursor_x*FONT_WIDTH(txt)*pixel_w;
+ GLfloat y = top_left[1] - hh - cursor_y*FONT_HEIGHT(txt)*pixel_h;
+ glUniform2f(gt->uni_offset_mouse, x, y);
+
+ glEnableVertexAttribArray(0);
+
+ // block-cursor outline
+ glVertexAttribPointer(0, 2, GL_FLOAT, false, 0, mouse_block_vertices);
+ glUniform4f(gt->uni_color_mouse, 1.f, .5f, .25f, .9f);
+ glDrawElements(GL_LINES, 8, GL_UNSIGNED_INT, mouse_block_outline_indices);
+
+ // block-cursor fill
+ glUniform4f(gt->uni_color_mouse, 1.f, .5f, .25f, .35f);
+ glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, mouse_block_fill_indices);
+
+ glDisableVertexAttribArray(0);
+
+ return 0;
+}
+
struct kmscon_text_ops kmscon_text_gltex_ops = {
.name = "gltex",
.owner = NULL,
@@ -761,5 +876,6 @@ struct kmscon_text_ops kmscon_text_gltex_ops = {
.prepare = gltex_prepare,
.draw = gltex_draw,
.render = gltex_render,
+ .render_pointer = gltex_render_pointer,
.abort = NULL,
};
From 7103379b42dd6e1f9feb2ecc1f084e2b625c4365 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mirco=20M=C3=BCller?= <macslow@gmail.com>
Date: Mon, 24 Jul 2023 16:18:31 +0200
Subject: [PATCH 3/4] Ported gyro-sensor support from my (MacSlow) old
kmscon-upstream branch to aetf's meson-based develop branch.
---
meson.build | 1 +
src/kmscon_terminal.c | 256 +++++++++++++++++++++++++++++++++++++++++-
src/meson.build | 2 +-
3 files changed, 257 insertions(+), 2 deletions(-)
diff --git a/meson.build b/meson.build
index 3f4fce25..1750bb50 100644
--- a/meson.build
+++ b/meson.build
@@ -51,6 +51,7 @@ libtsm_deps = dependency('libtsm', version: '>=4.0.2')
libudev_deps = dependency('libudev', version: '>=172')
dl_deps = dependency('dl')
threads_deps = dependency('threads')
+dbus_deps = dependency('dbus-1', version: '>=1.14.4')
python = find_program('python3')
diff --git a/src/kmscon_terminal.c b/src/kmscon_terminal.c
index 30e1d9ac..9ffb6664 100644
--- a/src/kmscon_terminal.c
+++ b/src/kmscon_terminal.c
@@ -59,6 +59,16 @@
#define LOG_SUBSYSTEM "terminal"
+#include <dbus/dbus.h>
+static const char* FDO_PROPS_INTERFACE = "org.freedesktop.DBus.Properties";
+static const char* FDO_GET_METHOD = "Get";
+static const char* GYRO_CLAIM_METHOD = "ClaimAccelerometer";
+static const char* GYRO_RELEASE_METHOD = "ReleaseAccelerometer";
+static const char* SENSOR_INTERFACE = "net.hadess.SensorProxy";
+static const char* DESTINATION = "net.hadess.SensorProxy";
+static const char* SENSOR_PATH = "/net/hadess/SensorProxy";
+static const char* PROPERTY_HAS_GYRO = "HasAccelerometer";
+
struct screen {
struct shl_dlist list;
struct kmscon_terminal *term;
@@ -95,6 +105,12 @@ struct kmscon_terminal {
struct kmscon_mouse_info* mouse;
struct kmscon_selection_info* selection;
+
+ DBusError dbus_error;
+ DBusConnection* dbus_connection;
+ struct ev_timer* dbus_gyro_query_timer;
+ struct itimerspec dbus_gyro_query_timer_spec;
+ bool has_gyro;
};
static void terminal_resize(struct kmscon_terminal *term,
@@ -241,6 +257,184 @@ static void handle_mouse_drawing(struct kmscon_mouse_info* mouse,
}
}
+DBusHandlerResult properties_changed_cb(DBusConnection* connection,
+ DBusMessage* message,
+ void* user_data)
+{
+ // ignore these
+ (void) connection;
+
+ struct kmscon_terminal* term = (struct kmscon_terminal*) user_data;
+ struct shl_dlist *iter;
+
+ const char* orientation = "undefined"; //ORIENTATION_UNDEFINED;
+
+ DBusError error;
+ dbus_error_init (&error);
+
+ const char* interface = dbus_message_get_interface (message);
+ const char* path = dbus_message_get_path (message);
+
+ if (!interface || !path ||
+ (strncmp (interface, FDO_PROPS_INTERFACE, 31) != 0 &&
+ strncmp (path, SENSOR_PATH, 23) != 0)) {
+ dbus_error_free (&error);
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ DBusMessageIter args;
+ if (message && !dbus_message_iter_init (message, &args)) {
+ dbus_error_free (&error);
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ char* value = NULL;
+ int type = 0;
+ DBusMessageIter dict;
+ DBusMessageIter entry;
+ DBusMessageIter variant;
+ while ((type = dbus_message_iter_get_arg_type (&args)) != DBUS_TYPE_INVALID) {
+ switch (type) {
+ case DBUS_TYPE_ARRAY:
+ dbus_message_iter_recurse (&args, &dict);
+ type = dbus_message_iter_get_arg_type (&dict);
+ if (type == DBUS_TYPE_DICT_ENTRY) {
+ dbus_message_iter_recurse (&dict, &entry);
+ while ((type = dbus_message_iter_get_arg_type (&entry)) != DBUS_TYPE_INVALID) {
+ type = dbus_message_iter_get_arg_type (&entry);
+ switch (type) {
+ case DBUS_TYPE_VARIANT:
+ dbus_message_iter_recurse (&entry, &variant);
+ type = dbus_message_iter_get_arg_type (&variant);
+ if (type == DBUS_TYPE_STRING) {
+ dbus_message_iter_get_basic (&variant, &value);
+ orientation = value;
+ }
+ break;
+
+ default: break;
+ }
+ dbus_message_iter_next (&entry);
+ }
+ }
+ break;
+
+ default : break;
+ }
+ dbus_message_iter_next (&args);
+ }
+
+ shl_dlist_for_each(iter, &term->screens) {
+ struct screen *scr = shl_dlist_entry(iter, struct screen, list);
+
+ if (strncmp(orientation, "normal", 6) == 0)
+ kmscon_text_rotate(scr->txt, ORIENTATION_NORMAL);
+
+ if (strncmp(orientation, "left-up", 7) == 0)
+ kmscon_text_rotate(scr->txt, ORIENTATION_LEFT);
+
+ if (strncmp(orientation, "right-up", 8) == 0)
+ kmscon_text_rotate(scr->txt, ORIENTATION_RIGHT);
+
+ if (strncmp(orientation, "bottom-up", 9) == 0)
+ kmscon_text_rotate(scr->txt, ORIENTATION_INVERTED);
+
+ term->min_cols = 0;
+ term->min_rows = 0;
+ terminal_resize(term,
+ kmscon_text_get_cols(scr->txt),
+ kmscon_text_get_rows(scr->txt),
+ true,
+ true);
+ }
+
+ log_info("kmscon_terminal... orientation: %s", orientation);
+
+ dbus_error_free (&error);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+DBusMessage* get_property (DBusConnection* connection,
+ DBusError* error,
+ const char* property)
+{
+ DBusMessage* message = dbus_message_new_method_call (DESTINATION,
+ SENSOR_PATH,
+ FDO_PROPS_INTERFACE,
+ FDO_GET_METHOD);
+ if (!message) {
+ return NULL;
+ }
+
+ const char* interface = SENSOR_INTERFACE;
+ DBusMessageIter args;
+ dbus_message_iter_init_append (message, &args);
+ dbus_message_iter_append_basic (&args, DBUS_TYPE_STRING, &interface);
+ dbus_message_iter_append_basic (&args, DBUS_TYPE_STRING, &property);
+
+ DBusMessage* reply = dbus_connection_send_with_reply_and_block (connection,
+ message,
+ -1,
+ error);
+ dbus_message_unref (message);
+
+ return reply;
+}
+
+dbus_bool_t has_gyro (DBusConnection* connection, DBusError* error)
+{
+ DBusMessage* reply = get_property (connection, error, PROPERTY_HAS_GYRO);
+
+ if (!reply) {
+ log_error("dbus-message is NULL!");
+ return 0;
+ }
+
+ if (dbus_message_get_type (reply) == DBUS_MESSAGE_TYPE_ERROR) {
+ log_error("%s", dbus_message_get_error_name (reply));
+ return 0;
+ }
+
+ DBusMessageIter reply_args;
+ dbus_message_iter_init (reply, &reply_args);
+ DBusMessageIter variant_iter;
+ dbus_message_iter_recurse (&reply_args, &variant_iter);
+ dbus_bool_t accelerometer;
+ dbus_message_iter_get_basic (&variant_iter, &accelerometer);
+ dbus_message_unref (reply);
+
+ return accelerometer;
+}
+
+void call_method (DBusConnection* connection, const char* method)
+{
+ DBusMessage* message = dbus_message_new_method_call (DESTINATION,
+ SENSOR_PATH,
+ SENSOR_INTERFACE,
+ method);
+
+ dbus_uint32_t serial;
+ dbus_bool_t success = dbus_connection_send (connection, message, &serial);
+
+ if (!success) {
+ log_info("There was an error with the message call '%s'", method);
+ }
+
+ dbus_message_unref (message);
+}
+
+void dbus_gyro_query_timer_cb(struct ev_timer *timer, uint64_t num, void *data)
+{
+ if (!data) {
+ log_warn(" No valid pointer passed to dbus_gyro_query_timer_cb().");
+ return;
+ }
+
+ DBusConnection* dbus_connection = (DBusConnection*) data;
+ dbus_connection_read_write_dispatch (dbus_connection, 1);
+}
+
static void do_redraw_screen(struct screen *scr)
{
int ret;
@@ -721,6 +915,9 @@ static void terminal_destroy(struct kmscon_terminal *term)
tsm_screen_unref(term->console);
uterm_input_unref(term->input);
ev_eloop_unref(term->eloop);
+ call_method (term->dbus_connection, GYRO_RELEASE_METHOD);
+ dbus_error_free (&term->dbus_error);
+ dbus_connection_unref (term->dbus_connection);
free(term);
}
@@ -879,10 +1076,64 @@ int kmscon_terminal_register(struct kmscon_session **out,
ret = kmscon_seat_register_session(seat, &term->session, session_event,
term);
if (ret) {
- log_error("cannot register session for terminal: %d", ret);
+ log_error("Cannot register session for terminal: %d", ret);
goto err_input;
}
+ dbus_error_init (&term->dbus_error);
+ term->dbus_connection = dbus_bus_get (DBUS_BUS_SYSTEM, &term->dbus_error);
+
+ if (has_gyro (term->dbus_connection, &term->dbus_error)) {
+ term->has_gyro = true;
+ log_info("This system has a gyro-sensor");
+ call_method (term->dbus_connection, GYRO_CLAIM_METHOD);
+ } else {
+ term->has_gyro = false;
+ log_info("This system has NO gyro-sensor");
+ }
+
+ if (term->has_gyro) {
+ dbus_bus_add_match (term->dbus_connection,
+ "type='signal',\
+ interface='org.freedesktop.DBus.Properties',\
+ member='PropertiesChanged',\
+ sender='net.hadess.SensorProxy'",
+ &term->dbus_error);
+
+ dbus_bool_t result = dbus_connection_add_filter(term->dbus_connection,
+ properties_changed_cb,
+ term,
+ NULL);
+
+ if (!result) {
+ log_info("Failed to add filter to connection");
+ }
+
+ // dbus-gyro-query timer
+ term->dbus_gyro_query_timer_spec.it_interval.tv_sec = 0;
+ term->dbus_gyro_query_timer_spec.it_interval.tv_nsec = 200*1000*1000;
+ term->dbus_gyro_query_timer_spec.it_value.tv_sec = 0;
+ term->dbus_gyro_query_timer_spec.it_value.tv_nsec = 200*1000*1000;
+
+ ret = ev_timer_new (&term->dbus_gyro_query_timer,
+ &term->dbus_gyro_query_timer_spec,
+ dbus_gyro_query_timer_cb,
+ term->dbus_connection,
+ NULL,
+ NULL);
+ if (ret) {
+ log_error("Cannot create dbus-gyro-query timer: %d", ret);
+ goto err_free;
+ }
+ ev_timer_enable(term->dbus_gyro_query_timer);
+
+ ret = ev_eloop_add_timer(term->eloop, term->dbus_gyro_query_timer);
+ if (ret) {
+ log_error("Cannot add dbus-gyro-query timer to event-loop: %d", ret);
+ goto err_free;
+ }
+ }
+
ev_eloop_ref(term->eloop);
uterm_input_ref(term->input);
*out = term->session;
@@ -903,6 +1154,9 @@ int kmscon_terminal_register(struct kmscon_session **out,
err_con:
tsm_screen_unref(term->console);
err_free:
+ call_method (term->dbus_connection, GYRO_RELEASE_METHOD);
+ dbus_error_free (&term->dbus_error);
+ dbus_connection_unref (term->dbus_connection);
free(term);
return ret;
}
diff --git a/src/meson.build b/src/meson.build
index 1a3c9cb4..244cea41 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -260,7 +260,7 @@ if enable_session_terminal
kmscon_srcs += 'kmscon_terminal.c'
endif
kmscon = executable('kmscon', kmscon_srcs,
- dependencies: [xkbcommon_deps, libtsm_deps, threads_deps, dl_deps, conf_deps, shl_deps, eloop_deps, uterm_deps],
+ dependencies: [xkbcommon_deps, libtsm_deps, threads_deps, dbus_deps, dl_deps, conf_deps, shl_deps, eloop_deps, uterm_deps],
export_dynamic: true,
install: true,
install_dir: libexecdir,
From 7394629f2bb3916e365442938accf3b2018bf818 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mirco=20M=C3=BCller?= <macslow@gmail.com>
Date: Mon, 24 Jul 2023 16:37:17 +0200
Subject: [PATCH 4/4] Add a man-page for the kmscon.conf file.
---
docs/man/kmscon.conf.1.xml.in | 443 ++++++++++++++++++++++++++++++++++
docs/meson.build | 1 +
2 files changed, 444 insertions(+)
create mode 100644 docs/man/kmscon.conf.1.xml.in
diff --git a/docs/man/kmscon.conf.1.xml.in b/docs/man/kmscon.conf.1.xml.in
new file mode 100644
index 00000000..c4f7cb20
--- /dev/null
+++ b/docs/man/kmscon.conf.1.xml.in
@@ -0,0 +1,443 @@
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<!--
+ Written 2023 by Mirco "MacSlow" Müller <macslow@gmail.com>
+ Dedicated to the Public Domain
+-->
+
+<refentry id="kmscon.conf">
+ <refentryinfo>
+ <title>kmscon.conf</title>
+ <productname>kmscon.conf</productname>
+ <date>January 2023</date>
+ <authorgroup>
+ <author>
+ <contrib>Developer</contrib>
+ <firstname>Mirco</firstname>
+ <surname>Müller</surname>
+ <email>macslow@gmail.com</email>
+ </author>
+ </authorgroup>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>kmscon.conf</refentrytitle>
+ <manvolnum>1</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>kmscon.conf</refname>
+ <refpurpose>Configuration file for KMS/DRM based System Console</refpurpose>
+ </refnamediv>
+
+ <refsect1>
+ <title>Description</title>
+ <para>kmscon.conf is the configuration file to control the behavior of kmscon
+ and adjust it to your system-setup. It allows to remap the bindings of
+ keyboard-shortcuts, define the desired keyboard-layout, select font
+ attributes for text-rendering, hardware-accelerationn, orientation of
+ output and much more.</para>
+
+ <para>Below is a complete list of all recognized <emphasis>Options</emphasis>, their meaning and
+ possible values. In section <emphasis>Example</emphasis> is a typical real-world sample of
+ a configuration to guide you in creating your own.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Example</title>
+ <para>Here is a real-world example of a typical kmscon.conf file:</para>
+ <programlisting>
+### General Options ###
+verbose
+
+### Seat Options ###
+vt=1
+switchvt
+
+### Session Options ###
+session-max=6
+session-control
+
+### Terminal Options ###
+term=linux
+
+### Input Options ###
+xkb-model=pc102
+xkb-layout=de
+xkb-repeat-delay=200
+xkb-repeat-rate=65
+
+### Video Options ###
+drm
+hwaccel
+gpus=all
+render-engine=gltex
+rotate=normal
+
+### Font Options ###
+font-engine=pango
+font-size=18
+font-name=Ubuntu Mono
+ </programlisting>
+ <para>Any line starting with a #-character is ignored and considered to be a comment.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Options</title>
+ <para>Below is a complete list of all recognized options, their meaning and
+ possible values.</para>
+
+ <variablelist>
+ <para><emphasis>### General Options ###</emphasis></para>
+ <varlistentry>
+ <term><option>verbose</option></term>
+ <listitem>
+ <para>Make kmscon be very chatty about what it is doing. It prints to
+ stdout unless redirected. Off if not present in kmsconf.conf or
+ commented out. (default: off)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>debug</option></term>
+ <listitem>
+ <para>Let kmscon be even more chatty. The text-output goes to stdout or
+ any file it was redirected to. Off if not present in kmsconf.conf
+ or commented out. (default: off)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>silent</option></term>
+ <listitem>
+ <para>Suppress notices and warnings. (default: off)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>configdir</option></term>
+ <listitem>
+ <para>Path to config directory. (default: /etc/kmscon)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>listen</option></term>
+ <listitem>
+ <para>Listen for new seats and spawn sessions accordingly. (default: off)</para>
+ </listitem>
+ </varlistentry>
+
+ <para><emphasis>### Seat Options ###</emphasis></para>
+ <varlistentry>
+ <term><option>vt</option></term>
+ <listitem>
+ <para>Select which VT to run on. (default: auto)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>switchvt</option></term>
+ <listitem>
+ <para>Automatically switch to VT. (default: on)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>seats</option></term>
+ <listitem>
+ <para>Select seats to run on. (default: current)</para>
+ </listitem>
+ </varlistentry>
+
+ <para><emphasis>### Session Options ###</emphasis></para>
+
+ <varlistentry>
+ <term><option>session-max</option></term>
+ <listitem>
+ <para>Maximum number of sessions. (default: 50)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>session-control</option></term>
+ <listitem>
+ <para>Allow keyboard session-control. (default: off)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>terminal-session</option></term>
+ <listitem>
+ <para>Enable terminal session. (default: on)</para>
+ </listitem>
+ </varlistentry>
+
+ <para><emphasis>### Terminal Options ###</emphasis></para>
+
+ <varlistentry>
+ <term><option>login</option></term>
+ <listitem>
+ <para>Start the given login process instead of the default process; all arguments following '--' will be be parsed as argv to this process. No more options after '--' will be parsed so use it at the end of the argument string. (default: /bin/login -p)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>term</option></term>
+ <listitem>
+ <para>Value of the TERM environment variable for the child process. (default: xterm-256color)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>reset-env</option></term>
+ <listitem>
+ <para>Reset environment before running child process. (default: on)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>palette</option></term>
+ <listitem>
+ <para>Select the used color palette. (default: default)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>sb-size</option></term>
+ <listitem>
+ <para>Size of the scrollback-buffer in lines. (default: 1000)</para>
+ </listitem>
+ </varlistentry>
+
+ <para><emphasis>### Input Options ###</emphasis></para>
+ <varlistentry>
+ <term><option>xkb-model</option></term>
+ <listitem>
+ <para>Set XkbModel for input devices. (default: unset)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>xkb-layout</option></term>
+ <listitem>
+ <para>Set XkbLayout for input devices. (default: unset)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>xkb-variant</option></term>
+ <listitem>
+ <para>Set XkbVariant for input devices. (default: unset)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>xkb-options</option></term>
+ <listitem>
+ <para>Set XkbOptions for input devices. (default: unset)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>xkb-keymap</option></term>
+ <listitem>
+ <para>Use a predefined keymap for input devices. (default: unset)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>xkb-repeat-delay</option></term>
+ <listitem>
+ <para>Initial delay for key-repeat in ms. (default: 250)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>xkb-repeat-rate</option></term>
+ <listitem>
+ <para>Delay between two key-repeats in ms. (default: 50)</para>
+ </listitem>
+ </varlistentry>
+
+ <para><emphasis>### Grabs/Keyboard-Shortcuts ###</emphasis></para>
+ <varlistentry>
+ <term><option>grab-scroll-up</option></term>
+ <listitem>
+ <para>Shortcut to scroll up. (default: <Shift>Up)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>grab-scroll-down</option></term>
+ <listitem>
+ <para>Shortcut to scroll down. (default: <Shift>Down)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>grab-page-up</option></term>
+ <listitem>
+ <para>Shortcut to scroll page up. (default: <Shift>Prior)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>grab-page-down</option></term>
+ <listitem>
+ <para>Shortcut to scroll page down. (default: <Shift>Next)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>grab-zoom-in</option></term>
+ <listitem>
+ <para>Shortcut to increase font size. (default: <Ctrl>Plus)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>grab-zoom-out</option></term>
+ <listitem>
+ <para>Shortcut to decrease font size. (default: <Ctrl>Minus)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>grab-session-next</option></term>
+ <listitem>
+ <para>Switch to next session. (default: <Ctrl><Logo>Right)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>grab-session-prev</option></term>
+ <listitem>
+ <para>Switch to previous session. (default: <Ctrl><Logo>Left)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>grab-session-dummy</option></term>
+ <listitem>
+ <para>Switch to dummy session. (default: <Ctrl><Logo>Escape)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>grab-session-close</option></term>
+ <listitem>
+ <para>Close current session. (default: <Ctrl><Logo>BackSpace)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>grab-terminal-new</option></term>
+ <listitem>
+ <para>Create new terminal session. (default: <Ctrl><Logo>Return)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>grab-rotate-cw</option></term>
+ <listitem>
+ <para>Rotate output clock-wise. (default: <Logo>Plus)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>grab-rotate-ccw</option></term>
+ <listitem>
+ <para>Rotate output counter-clock-wise. (default: <Logo>Minus)</para>
+ </listitem>
+ </varlistentry>
+
+ <para><emphasis>### Video Options ###</emphasis></para>
+ <varlistentry>
+ <term><option>drm</option></term>
+ <listitem>
+ <para>Use DRM if available. (default: on)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>hwaccel</option></term>
+ <listitem>
+ <para>Use 3D hardware-acceleration if available. (default: off)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>gpus</option></term>
+ <listitem>
+ <para>GPU selection mode. (default: all)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>render-engine</option></term>
+ <listitem>
+ <para>Console renderer. (default: not set)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>render-timing</option></term>
+ <listitem>
+ <para>Print renderer timing information. (default: off)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>rotate</option></term>
+ <listitem>
+ <para>Orientation of output to use. (default: normal)</para>
+ </listitem>
+ </varlistentry>
+
+ <para><emphasis>### Font Options ###</emphasis></para>
+ <varlistentry>
+ <term><option>font-engine</option></term>
+ <listitem>
+ <para>Font engine to use. (default: pango)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>font-size</option></term>
+ <listitem>
+ <para>Font size in points. (default: 12)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>font-name</option></term>
+ <listitem>
+ <para>Font name to use. (default: monospace)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>font-dpi</option></term>
+ <listitem>
+ <para>Force DPI value for all fonts. (default: 96)</para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Files</title>
+ <para><emphasis>/etc/kmsconf.conf</emphasis></para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+ <para>
+ <citerefentry><refentrytitle>kmscon</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+</refentry>
diff --git a/docs/meson.build b/docs/meson.build
index a26e520f..cf33578c 100644
--- a/docs/meson.build
+++ b/docs/meson.build
@@ -4,6 +4,7 @@
manpages = [
'kmscon.1.xml.in',
+ 'kmscon.conf.1.xml.in',
]
data = configuration_data()