Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions data/sm.puri.phoc.gschema.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,17 @@
Wayland applications and only their main window.
</description>
</key>
<key name="touch-points" type="b">
<default>false</default>
<summary>Draw a marker at each touch point</summary>
<description>
Whether to draw a marker wherever a finger is touching the screen.
The same overlay as the touch-points debug flag, switchable while the
compositor runs so that a screen recording can show what was pressed
and put it away again afterwards.
</description>
</key>

<key name="scale-to-fit" type="b">
<default>false</default>
<summary>Enable automatic scaling of all oversized windows</summary>
Expand Down
21 changes: 21 additions & 0 deletions src/desktop.c
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,24 @@ handle_pointer_constraint (struct wl_listener *listener, void *data)
}


static void
touch_points_changed_cb (PhocDesktop *self, const char *key, GSettings *settings)
{
gboolean enable = g_settings_get_boolean (settings, key);
PhocOutput *output;

g_debug ("touch-points: %d", enable);
phoc_server_set_debug_flag (phoc_server_get_default (),
PHOC_SERVER_DEBUG_FLAG_TOUCH_POINTS,
enable);

/* The markers are drawn from the render pass, so nothing appears or clears
* until something asks for a frame. */
wl_list_for_each (output, &self->outputs, link)
phoc_output_damage_whole (output);
}


static void
auto_maximize_changed_cb (PhocDesktop *self,
const gchar *key,
Expand Down Expand Up @@ -767,6 +785,9 @@ phoc_desktop_constructed (GObject *object)
G_CALLBACK (auto_maximize_changed_cb), self);
auto_maximize_changed_cb (self, "auto-maximize", priv->settings);
g_settings_bind (priv->settings, "scale-to-fit", self, "scale-to-fit", G_SETTINGS_BIND_DEFAULT);
g_signal_connect_swapped (priv->settings, "changed::touch-points",
G_CALLBACK (touch_points_changed_cb), self);
touch_points_changed_cb (self, "touch-points", priv->settings);

/* org.gnome.desktop.interface settings */
priv->interface_settings = g_settings_new ("org.gnome.desktop.interface");
Expand Down
154 changes: 143 additions & 11 deletions src/render.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <assert.h>
#include <drm_fourcc.h>
#include <fcntl.h>
#include <math.h>
#include <stdbool.h>
#include <stdlib.h>
#include <time.h>
Expand All @@ -41,11 +42,30 @@
#include <wlr/types/wlr_linux_dmabuf_v1.h>
#include <wlr/util/region.h>
#include <wlr/render/allocator.h>
#include <wlr/render/swapchain.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <EGL/egl.h>

/* Set to 0 to build the upstream touch point markers instead of the FuriOS
* ones. Everything this fork changes about them is behind it. */
#define IS_FURIOS 1

#if IS_FURIOS
#define TOUCH_POINT_SIZE 60
#else
#define TOUCH_POINT_SIZE 20
#endif
#define TOUCH_POINT_BORDER 0.1
#if IS_FURIOS
/* How long the marker takes to shrink from full size to its resting size, and
* what fraction of full size it rests at. It does not shrink to nothing: a
* finger held down or dragged has to stay visible, which is the whole point of
* the overlay during a screen recording. */
#define TOUCH_POINT_SHRINK_MS 333
#define TOUCH_POINT_REST 0.34
#define TOUCH_POINT_MAX_IDS 10
#endif

#define COLOR_BLACK ((struct wlr_render_color){0.0f, 0.0f, 0.0f, 1.0f})
#define COLOR_TRANSPARENT {0.0f, 0.0f, 0.0f, 0.0f}
Expand Down Expand Up @@ -97,6 +117,9 @@ struct touch_point_data {
int id;
double x;
double y;
#if IS_FURIOS
double radius; /* in logical pixels, animated down from TOUCH_POINT_SIZE */
#endif
};


Expand Down Expand Up @@ -189,6 +212,46 @@ render_texture (PhocOutput *output,
pixman_region32_fini (&damage);
}

#if IS_FURIOS
/*
* The size of a touch marker, shrinking from full to its resting size over
* TOUCH_POINT_SHRINK_MS from the moment the finger went down.
*
* The start times are kept here rather than on the touch point, because the
* points are rebuilt from the seat every frame and carry no history. An id that
* has not been seen for a frame is treated as a new touch, which is what makes
* a second tap animate again rather than staying at its resting size.
*/
static gint64 touch_point_started[TOUCH_POINT_MAX_IDS];
static gint64 touch_point_last_seen[TOUCH_POINT_MAX_IDS];

static double
touch_point_radius (int id)
{
gint64 now = g_get_monotonic_time ();
double age_ms, t;

if (id < 0 || id >= TOUCH_POINT_MAX_IDS)
id = 0;

/* A gap of more than a couple of frames means the finger was lifted and this
* is a new touch, not the same one continuing. */
if (now - touch_point_last_seen[id] > 100 * G_TIME_SPAN_MILLISECOND)
touch_point_started[id] = now;

touch_point_last_seen[id] = now;

age_ms = (now - touch_point_started[id]) / (double) G_TIME_SPAN_MILLISECOND;
t = CLAMP (age_ms / TOUCH_POINT_SHRINK_MS, 0.0, 1.0);
/* Ease out, so it drops away quickly and settles gently */
t = 1.0 - (1.0 - t) * (1.0 - t);

return TOUCH_POINT_SIZE * (1.0 - t * (1.0 - TOUCH_POINT_REST));
}
#endif



static void
collect_touch_points (PhocOutput *output, struct wlr_surface *surface, struct wlr_box box, float scale)
{
Expand All @@ -211,6 +274,9 @@ collect_touch_points (PhocOutput *output, struct wlr_surface *surface, struct wl

touch_point = g_new (struct touch_point_data, 1);
touch_point->id = point->touch_id;
#if IS_FURIOS
touch_point->radius = touch_point_radius (point->touch_id);
#endif
touch_point->x = box.x + point->sx * output->wlr_output->scale * scale;
touch_point->y = box.y + point->sy * output->wlr_output->scale * scale;
output->debug_touch_points = g_list_append (output->debug_touch_points, touch_point);
Expand Down Expand Up @@ -317,6 +383,80 @@ render_drag_icons (PhocInput *input, PhocRenderContext *ctx)
}


static struct wlr_box
phoc_box_from_touch_point (struct touch_point_data *touch_point, int width, int height)
{
return (struct wlr_box) {
.x = touch_point->x - width / 2.0,
.y = touch_point->y - height / 2.0,
.width = width,
.height = height
};
}

#if IS_FURIOS
/*
* Draw a filled circle out of horizontal slices.
*
* The render pass can only add rectangles, and a couple of dozen slices is
* indistinguishable from a circle at this size while staying inside the
* existing pass -- no shader, no GL state to save and restore.
*/
static void
render_disc (PhocRenderContext *ctx, double cx, double cy, double radius, struct wlr_render_color color)
{
int steps = CLAMP ((int) (radius / 1.5), 6, 28);

for (int i = 0; i < steps; i++) {
/* Sample each slice at its centre, so the top and bottom do not flatten */
double t0 = (i + 0.5) / steps;
double dy = (t0 * 2.0 - 1.0) * radius;
double half_w = sqrt (MAX (radius * radius - dy * dy, 0.0));
double y0 = cy - radius + (i / (double) steps) * radius * 2.0;
double h = (radius * 2.0) / steps;
struct wlr_box box;

if (half_w < 0.5)
continue;

box = (struct wlr_box) {
.x = round (cx - half_w),
.y = round (y0),
.width = MAX (1, (int) round (half_w * 2.0)),
.height = MAX (1, (int) ceil (h)),
};
phoc_output_transform_box (ctx->output, &box);
wlr_render_pass_add_rect (ctx->render_pass, &(struct wlr_render_rect_options){
.box = box,
.color = color,
});
}
}


static void
render_touch_point_cb (gpointer data, gpointer user_data)
{
struct touch_point_data *touch_point = data;
PhocRenderContext *ctx = user_data;
struct wlr_output *wlr_output = ctx->output->wlr_output;
/* Blue, and the same blue for every finger: this marks where the screen was
* touched, it is not trying to tell one finger from another. */
struct wlr_render_color color = {0.21f, 0.52f, 0.89f, 0.55f};
struct wlr_render_color core = {0.85f, 0.92f, 1.0f, 0.75f};
double radius = touch_point->radius * wlr_output->scale / 2.0;

if (radius < 1.0)
return;

render_disc (ctx, touch_point->x, touch_point->y, radius, color);
/* A small bright centre so the exact point stays readable once the ring has
* shrunk, and while it is over a busy wallpaper. */
render_disc (ctx, touch_point->x, touch_point->y, radius * 0.3, core);
}

#else

static void
color_hsv_to_rgb (struct wlr_render_color *color)
{
Expand Down Expand Up @@ -344,17 +484,6 @@ color_hsv_to_rgb (struct wlr_render_color *color)
}


static struct wlr_box
phoc_box_from_touch_point (struct touch_point_data *touch_point, int width, int height)
{
return (struct wlr_box) {
.x = touch_point->x - width / 2.0,
.y = touch_point->y - height / 2.0,
.width = width,
.height = height
};
}

static void
render_touch_point_cb (gpointer data, gpointer user_data)
{
Expand Down Expand Up @@ -397,6 +526,9 @@ render_touch_point_cb (gpointer data, gpointer user_data)
});
}

#endif


static void
render_touch_points (PhocRenderContext *ctx)
{
Expand Down
23 changes: 23 additions & 0 deletions src/server.c
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,29 @@ phoc_server_get_config (PhocServer *self)
return self->config;
}

/**
* phoc_server_set_debug_flag:
* @self: The server
* @flag: The flag to change
* @enable: Whether to set or clear it
*
* Turn a debug flag on or off while the compositor is running. The flags are
* otherwise parsed once from PHOC_DEBUG at startup, which is no use to anything
* that wants to switch one on for a moment -- a screen recording showing where
* the screen was touched, say -- and off again afterwards.
*/
void
phoc_server_set_debug_flag (PhocServer *self, PhocServerDebugFlags flag, gboolean enable)
{
g_assert (PHOC_IS_SERVER (self));

if (enable)
self->debug_flags |= flag;
else
self->debug_flags &= ~flag;
}


/**
* phoc_server_check_debug_flags:
* @self: The server
Expand Down
3 changes: 3 additions & 0 deletions src/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ gboolean phoc_server_setup (PhocServer *self,
GMainLoop *mainloop,
PhocServerFlags flags,
PhocServerDebugFlags debug_flags);
void phoc_server_set_debug_flag (PhocServer *self,
PhocServerDebugFlags flag,
gboolean enable);
gboolean phoc_server_check_debug_flags (PhocServer *self,
PhocServerDebugFlags check);
const char *phoc_server_get_session_exec (PhocServer *self);
Expand Down