forked from 12to11/12to11

* compositor.h: Update prototypes. * idle_inhibit.c (DestroyIdleInhibitManager): New function. (idle_inhibit_manager_impl): Add missing op handler. * seat.c (struct _Seat): Remove pointer unlock surface. Add last_seen_subcompositor. (ReleaseSeat): Release the last seen subcompositor callback. (ClearPointerUnlockSurface, SwapUnlockSurface): Delete functions. (HandleSubcompositorDestroy): New function. (EnteredSurface): Stop calling SwapUnlockSurface. (DispatchEntryExit): Set the last seen subcompositor. (DispatchMotion, CancelGrab1, CancelGrab): Use the last seen subcompositor to decide where to unlock instead. (LockSurfaceFocus, DispatchButton, DispatchGesturePinch) (DispatchGestureSwipe): Stop calling SwapUnlockSurface. (FakePointerEdge, ForceEntry): Stop calling SwapUnlockSurface. * subcompositor.c (struct _SubcompositorDestroyCallback): New struct. (struct _Subcompositor): New field `destroy_callbacks'. (MakeSubcompositor): Initialize new field. (SubcompositorFree): Run and free destroy callbacks. (SubcompositorOnDestroy, SubcompositorRemoveDestroyCallback): New functions. * tests/run_tests.sh: * tests/select_test.c (verify_sample_text_multiple): Fix ommissions and typos.
458 lines
11 KiB
C
458 lines
11 KiB
C
/* Wayland compositor running on top of an X server.
|
||
|
||
Copyright (C) 2022 to various contributors.
|
||
|
||
This file is part of 12to11.
|
||
|
||
12to11 is free software: you can redistribute it and/or modify it
|
||
under the terms of the GNU General Public License as published by the
|
||
Free Software Foundation, either version 3 of the License, or (at your
|
||
option) any later version.
|
||
|
||
12to11 is distributed in the hope that it will be useful, but WITHOUT
|
||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||
for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with 12to11. If not, see <https://www.gnu.org/licenses/>. */
|
||
|
||
#include <string.h>
|
||
#include <stdio.h>
|
||
|
||
#include "compositor.h"
|
||
#include "idle-inhibit-unstable-v1.h"
|
||
|
||
typedef struct _IdleInhibitDataRecord IdleInhibitDataRecord;
|
||
typedef struct _IdleInhibitor IdleInhibitor;
|
||
typedef enum _IdleInhibition IdleInhibition;
|
||
|
||
/* Idle inhibition is tricky because there is no threshold that tells
|
||
the protocol translator whether or not to apply idle inhibition for
|
||
surfaces that are already focused. So, contrary to the protocol
|
||
specification, we inhibit idle-ness as long as a surface with an
|
||
idle inhibitor is focused, even if the user was already idle at the
|
||
time the idle inhibitor was created. */
|
||
|
||
struct _IdleInhibitor
|
||
{
|
||
/* The next and last idle inhibitors on this surface. */
|
||
IdleInhibitor *next, *last;
|
||
|
||
/* The next and last idle inhibitors globally. */
|
||
IdleInhibitor *global_next, *global_last;
|
||
|
||
/* The corresponding Surface. */
|
||
Surface *surface;
|
||
|
||
/* The corresponding wl_resource. */
|
||
struct wl_resource *resource;
|
||
};
|
||
|
||
struct _IdleInhibitDataRecord
|
||
{
|
||
/* List of idle inhibitors on this surface. */
|
||
IdleInhibitor inhibitors;
|
||
};
|
||
|
||
enum _IdleInhibition
|
||
{
|
||
IdleAllowed,
|
||
IdleInhibited,
|
||
};
|
||
|
||
/* The idle inhibit manager global. */
|
||
static struct wl_global *idle_inhibit_manager_global;
|
||
|
||
/* List of all idle inhibitors. */
|
||
static IdleInhibitor all_inhibitors;
|
||
|
||
/* The current idle inhibition. */
|
||
static IdleInhibition current_inhibition;
|
||
|
||
/* Commands run while idle. The first command is run once upon idle
|
||
being inhibited; the second is run every N seconds while idle is
|
||
inhibited, and the third command is run every time idle is
|
||
deinhibited. */
|
||
static char **inhibit_command, **timer_command, **deinhibit_command;
|
||
|
||
/* How many seconds the protocol translator waits before running the
|
||
timer command. */
|
||
static int timer_seconds;
|
||
|
||
/* Timer used to run the timer command. */
|
||
static Timer *command_timer;
|
||
|
||
/* Process queue used to run those commands. */
|
||
static ProcessQueue *process_queue;
|
||
|
||
static void
|
||
HandleCommandTimer (Timer *timer, void *data, struct timespec time)
|
||
{
|
||
/* The timer shouldn't have been started if the command is NULL. */
|
||
RunProcess (process_queue, timer_command);
|
||
}
|
||
|
||
static void
|
||
ChangeInhibitionTo (IdleInhibition inhibition)
|
||
{
|
||
if (current_inhibition == inhibition)
|
||
/* Nothing changed. */
|
||
return;
|
||
|
||
current_inhibition = inhibition;
|
||
|
||
if (current_inhibition == IdleInhibited)
|
||
{
|
||
/* Run the idle inhibit command, if it exists. */
|
||
|
||
if (inhibit_command)
|
||
RunProcess (process_queue, inhibit_command);
|
||
|
||
/* Schedule a timer to run the timer command. */
|
||
|
||
if (timer_command)
|
||
command_timer = AddTimer (HandleCommandTimer, NULL,
|
||
MakeTimespec (timer_seconds, 0));
|
||
}
|
||
else
|
||
{
|
||
/* Cancel the command timer. */
|
||
if (command_timer)
|
||
RemoveTimer (command_timer);
|
||
command_timer = NULL;
|
||
|
||
/* Run the deinhibit command. */
|
||
|
||
if (deinhibit_command)
|
||
RunProcess (process_queue, deinhibit_command);
|
||
}
|
||
}
|
||
|
||
static void
|
||
DetectSurfaceIdleInhibit (void)
|
||
{
|
||
IdleInhibitor *inhibitor;
|
||
Surface *root;
|
||
|
||
inhibitor = all_inhibitors.global_next;
|
||
while (inhibitor != &all_inhibitors)
|
||
{
|
||
if (inhibitor->surface->num_focused_seats)
|
||
{
|
||
ChangeInhibitionTo (IdleInhibited);
|
||
return;
|
||
}
|
||
else if (inhibitor->surface->role
|
||
&& inhibitor->surface->role_type == SubsurfaceType)
|
||
{
|
||
/* This is a subsurface. If it is visible and the toplevel
|
||
parent has the input focus, then also inhibit system
|
||
idle. */
|
||
root = XLSubsurfaceGetRoot (inhibitor->surface);
|
||
|
||
if (root && ViewIsVisible (inhibitor->surface->view)
|
||
&& root->num_focused_seats)
|
||
ChangeInhibitionTo (IdleInhibited);
|
||
}
|
||
|
||
inhibitor = inhibitor->global_next;
|
||
}
|
||
|
||
/* There are no live idle inhibitors for focused seats. */
|
||
ChangeInhibitionTo (IdleAllowed);
|
||
|
||
return;
|
||
}
|
||
|
||
static void
|
||
NoticeSurfaceFocused (Surface *surface)
|
||
{
|
||
IdleInhibitDataRecord *record;
|
||
|
||
record = XLSurfaceFindClientData (surface, IdleInhibitData);
|
||
|
||
if (!record)
|
||
return;
|
||
|
||
if (record->inhibitors.next == &record->inhibitors)
|
||
return;
|
||
|
||
/* There is an idle inhibitor for this idle surface. */
|
||
ChangeInhibitionTo (IdleInhibited);
|
||
}
|
||
|
||
|
||
|
||
static void
|
||
Destroy (struct wl_client *client, struct wl_resource *resource)
|
||
{
|
||
wl_resource_destroy (resource);
|
||
}
|
||
|
||
static const struct zwp_idle_inhibitor_v1_interface idle_inhibitor_impl =
|
||
{
|
||
.destroy = Destroy,
|
||
};
|
||
|
||
static void
|
||
HandleResourceDestroy (struct wl_resource *resource)
|
||
{
|
||
IdleInhibitor *inhibitor;
|
||
|
||
inhibitor = wl_resource_get_user_data (resource);
|
||
|
||
if (inhibitor->surface)
|
||
{
|
||
/* Unlink the inhibitor. */
|
||
inhibitor->next->last = inhibitor->last;
|
||
inhibitor->last->next = inhibitor->next;
|
||
inhibitor->global_next->global_last = inhibitor->global_last;
|
||
inhibitor->global_last->global_next = inhibitor->global_next;
|
||
}
|
||
|
||
/* Free the inhibitor; then, check if any other idle inhibitors are
|
||
still active. */
|
||
XLFree (inhibitor);
|
||
DetectSurfaceIdleInhibit ();
|
||
}
|
||
|
||
|
||
|
||
static void
|
||
FreeIdleInhibitData (void *data)
|
||
{
|
||
IdleInhibitDataRecord *record;
|
||
IdleInhibitor *inhibitor, *last;
|
||
|
||
record = data;
|
||
|
||
/* Loop through each idle inhibitor. Unlink it. */
|
||
inhibitor = record->inhibitors.next;
|
||
while (inhibitor != &record->inhibitors)
|
||
{
|
||
last = inhibitor;
|
||
inhibitor = inhibitor->next;
|
||
|
||
last->next = NULL;
|
||
last->last = NULL;
|
||
last->global_next->global_last = last->global_last;
|
||
last->global_last->global_next = last->global_next;
|
||
last->surface = NULL;
|
||
}
|
||
|
||
/* Check if any idle inhibitors are still active. */
|
||
DetectSurfaceIdleInhibit ();
|
||
}
|
||
|
||
static void
|
||
InitIdleInhibitData (IdleInhibitDataRecord *record)
|
||
{
|
||
if (record->inhibitors.next)
|
||
/* The data is already initialized. */
|
||
return;
|
||
|
||
record->inhibitors.next = &record->inhibitors;
|
||
record->inhibitors.last = &record->inhibitors;
|
||
}
|
||
|
||
static void
|
||
CreateInhibitor (struct wl_client *client, struct wl_resource *resource,
|
||
uint32_t id, struct wl_resource *surface_resource)
|
||
{
|
||
Surface *surface;
|
||
IdleInhibitor *inhibitor;
|
||
IdleInhibitDataRecord *record;
|
||
|
||
inhibitor = XLSafeMalloc (sizeof *inhibitor);
|
||
|
||
if (!inhibitor)
|
||
{
|
||
wl_resource_post_no_memory (resource);
|
||
return;
|
||
}
|
||
|
||
memset (inhibitor, 0, sizeof *inhibitor);
|
||
inhibitor->resource
|
||
= wl_resource_create (client, &zwp_idle_inhibitor_v1_interface,
|
||
wl_resource_get_version (resource), id);
|
||
|
||
surface = wl_resource_get_user_data (surface_resource);
|
||
record = XLSurfaceGetClientData (surface, IdleInhibitData,
|
||
sizeof *record, FreeIdleInhibitData);
|
||
InitIdleInhibitData (record);
|
||
|
||
/* Set the inhibitor's surface. */
|
||
inhibitor->surface = surface;
|
||
|
||
/* And link it onto the list of all idle inhibitors on both the
|
||
surface and globally. */
|
||
inhibitor->next = record->inhibitors.next;
|
||
inhibitor->last = &record->inhibitors;
|
||
record->inhibitors.next->last = inhibitor;
|
||
record->inhibitors.next = inhibitor;
|
||
inhibitor->global_next = all_inhibitors.global_next;
|
||
inhibitor->global_last = &all_inhibitors;
|
||
all_inhibitors.global_next->global_last = inhibitor;
|
||
all_inhibitors.global_next = inhibitor;
|
||
|
||
if (surface->num_focused_seats)
|
||
/* See the comment at the beginning of the file. */
|
||
ChangeInhibitionTo (IdleInhibited);
|
||
else if (surface->role && surface->role_type == SubsurfaceType)
|
||
/* Check if this subsurface should cause inhibition. */
|
||
DetectSurfaceIdleInhibit ();
|
||
|
||
/* Set the implementation. */
|
||
wl_resource_set_implementation (inhibitor->resource, &idle_inhibitor_impl,
|
||
inhibitor, HandleResourceDestroy);
|
||
}
|
||
|
||
static void
|
||
DestroyIdleInhibitManager (struct wl_client *client,
|
||
struct wl_resource *resource)
|
||
{
|
||
wl_resource_destroy (resource);
|
||
}
|
||
|
||
static struct zwp_idle_inhibit_manager_v1_interface idle_inhibit_manager_impl =
|
||
{
|
||
.destroy = DestroyIdleInhibitManager,
|
||
.create_inhibitor = CreateInhibitor,
|
||
};
|
||
|
||
static void
|
||
HandleBind (struct wl_client *client, void *data,
|
||
uint32_t version, uint32_t id)
|
||
{
|
||
struct wl_resource *resource;
|
||
|
||
resource = wl_resource_create (client,
|
||
&zwp_idle_inhibit_manager_v1_interface,
|
||
version, id);
|
||
|
||
if (!resource)
|
||
{
|
||
wl_client_post_no_memory (client);
|
||
return;
|
||
}
|
||
|
||
wl_resource_set_implementation (resource, &idle_inhibit_manager_impl,
|
||
NULL, NULL);
|
||
}
|
||
|
||
static char **
|
||
ReadCommandResource (const char *name, const char *class)
|
||
{
|
||
XrmDatabase rdb;
|
||
XrmName namelist[3];
|
||
XrmClass classlist[3];
|
||
XrmValue value;
|
||
XrmRepresentation type;
|
||
char **arguments;
|
||
size_t num_args;
|
||
|
||
rdb = XrmGetDatabase (compositor.display);
|
||
|
||
if (!rdb)
|
||
return NULL;
|
||
|
||
namelist[1] = XrmStringToQuark (name);
|
||
namelist[0] = app_quark;
|
||
namelist[2] = NULLQUARK;
|
||
|
||
classlist[1] = XrmStringToQuark (class);
|
||
classlist[0] = resource_quark;
|
||
classlist[2] = NULLQUARK;
|
||
|
||
if (XrmQGetResource (rdb, namelist, classlist,
|
||
&type, &value)
|
||
&& type == QString)
|
||
{
|
||
ParseProcessString ((const char *) value.addr,
|
||
&arguments, &num_args);
|
||
|
||
return arguments;
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
static int
|
||
ReadIntegerResource (const char *name, const char *class,
|
||
int default_value)
|
||
{
|
||
XrmDatabase rdb;
|
||
XrmName namelist[3];
|
||
XrmClass classlist[3];
|
||
XrmValue value;
|
||
XrmRepresentation type;
|
||
int result;
|
||
|
||
rdb = XrmGetDatabase (compositor.display);
|
||
|
||
if (!rdb)
|
||
return default_value;
|
||
|
||
namelist[1] = XrmStringToQuark (name);
|
||
namelist[0] = app_quark;
|
||
namelist[2] = NULLQUARK;
|
||
|
||
classlist[1] = XrmStringToQuark (class);
|
||
classlist[0] = resource_quark;
|
||
classlist[2] = NULLQUARK;
|
||
|
||
if (XrmQGetResource (rdb, namelist, classlist,
|
||
&type, &value)
|
||
&& type == QString)
|
||
{
|
||
result = atoi ((char *) value.addr);
|
||
|
||
if (!result)
|
||
return default_value;
|
||
|
||
return result;
|
||
}
|
||
|
||
return default_value;
|
||
}
|
||
|
||
void
|
||
XLInitIdleInhibit (void)
|
||
{
|
||
idle_inhibit_manager_global
|
||
= wl_global_create (compositor.wl_display,
|
||
&zwp_idle_inhibit_manager_v1_interface,
|
||
1, NULL, HandleBind);
|
||
|
||
all_inhibitors.global_next = &all_inhibitors;
|
||
all_inhibitors.global_last = &all_inhibitors;
|
||
|
||
/* Read various commands from resources. */
|
||
inhibit_command = ReadCommandResource ("idleInhibitCommand",
|
||
"IdleInhibitCommand");
|
||
timer_command = ReadCommandResource ("idleIntervalCommand",
|
||
"IdleInhibitCommand");
|
||
deinhibit_command = ReadCommandResource ("idleDeinhibitCommand",
|
||
"IdleDeinhibitCommand");
|
||
|
||
/* Initialize the default value for timer_seconds. */
|
||
timer_seconds = ReadIntegerResource ("idleCommandInterval",
|
||
"IdleCommandInterval",
|
||
60);
|
||
|
||
/* Initialize the process queue. */
|
||
process_queue = MakeProcessQueue ();
|
||
}
|
||
|
||
void
|
||
XLIdleInhibitNoticeSurfaceFocused (Surface *surface)
|
||
{
|
||
NoticeSurfaceFocused (surface);
|
||
}
|
||
|
||
void
|
||
XLDetectSurfaceIdleInhibit (void)
|
||
{
|
||
DetectSurfaceIdleInhibit ();
|
||
}
|