GHOST/Wayland: support for multiple seats (one active seat at a time)

This isn't full multi-seat support, instead set the active seat using
pointer/tablet & keyboard enter handlers.

This means that seats beside the first aren't prevented from having
their events handled.
This commit is contained in:
Campbell Barton 2022-11-01 10:41:06 +11:00
parent c3e4fa7404
commit 409070e0d4
2 changed files with 109 additions and 40 deletions

View File

@ -776,6 +776,18 @@ struct GWL_Display {
struct wl_shm *wl_shm = nullptr;
std::vector<GWL_Output *> outputs;
std::vector<GWL_Seat *> seats;
/**
* Support a single active seat at once, this isn't an exact or correct mapping from WAYLAND.
* Only allow input from different seats, not full concurrent multi-seat support.
*
* The main purpose of having an active seat is an alternative from always using the first
* seat which prevents events from any other seat.
*
* NOTE(@campbellbarton): This could be extended and developed further extended to support
* an active seat per window (for e.g.), basic support is sufficient for now as currently isn't
* a widely used feature.
*/
int seats_active_index = 0;
/* Managers. */
struct wl_data_device_manager *wl_data_device_manager = nullptr;
@ -830,6 +842,37 @@ static void gwl_display_destroy(GWL_Display *display)
delete display;
}
static int gwl_display_seat_index(GWL_Display *display, const GWL_Seat *seat)
{
std::vector<GWL_Seat *>::iterator iter = std::find(
display->seats.begin(), display->seats.end(), seat);
const int index = (iter != display->seats.cend()) ? std::distance(display->seats.begin(), iter) :
-1;
GHOST_ASSERT(index != -1, "invalid internal state");
return index;
}
static GWL_Seat *gwl_display_seat_active_get(const GWL_Display *display)
{
if (UNLIKELY(display->seats.empty())) {
return nullptr;
}
return display->seats[display->seats_active_index];
}
static bool gwl_display_seat_active_set(GWL_Display *display, const GWL_Seat *seat)
{
if (UNLIKELY(display->seats.empty())) {
return false;
}
const int index = gwl_display_seat_index(display, seat);
if (index == display->seats_active_index) {
return false;
}
display->seats_active_index = index;
return true;
}
/** \} */
/* -------------------------------------------------------------------- */
@ -1925,6 +1968,9 @@ static void data_device_handle_enter(void *data,
}
seat->wl_surface_focus_dnd = wl_surface;
seat->system->seat_active_set(seat);
dnd_events(seat, GHOST_kEventDraggingEntered);
}
@ -2256,6 +2302,8 @@ static void pointer_handle_enter(void *data,
seat->pointer.wl_surface = wl_surface;
seat->system->seat_active_set(seat);
win->setCursorShape(win->getCursorShape());
const wl_fixed_t scale = win->scale();
@ -2900,6 +2948,8 @@ static void tablet_tool_handle_proximity_in(void *data,
seat->data_source_serial = serial;
seat->system->seat_active_set(seat);
/* Update #GHOST_TabletData. */
GHOST_TabletData &td = tablet_tool->data;
/* Reset, to avoid using stale tilt/pressure. */
@ -3286,6 +3336,8 @@ static void keyboard_handle_enter(void *data,
seat->keyboard.serial = serial;
seat->keyboard.wl_surface = wl_surface;
seat->system->seat_active_set(seat);
/* If there are any keys held when activating the window,
* modifiers will be compared against the seat state,
* only enabling modifiers that were previously disabled. */
@ -4472,6 +4524,9 @@ static void gwl_registry_wl_seat_remove(GWL_Display *display, void *user_data, c
GHOST_ASSERT(index != -1, "invalid internal state");
if (!on_exit) {
if (display->seats_active_index >= index) {
display->seats_active_index -= 1;
}
display->seats.erase(display->seats.begin() + index);
}
delete seat;
@ -4980,12 +5035,11 @@ bool GHOST_SystemWayland::setConsoleWindowState(GHOST_TConsoleWindowState /*acti
GHOST_TSuccess GHOST_SystemWayland::getModifierKeys(GHOST_ModifierKeys &keys) const
{
if (UNLIKELY(display_->seats.empty())) {
GWL_Seat *seat = gwl_display_seat_active_get(display_);
if (UNLIKELY(!seat)) {
return GHOST_kFailure;
}
GWL_Seat *seat = display_->seats[0];
const xkb_mod_mask_t state = xkb_state_serialize_mods(seat->xkb_state, XKB_STATE_MODS_DEPRESSED);
bool show_warning = true;
@ -5042,10 +5096,10 @@ GHOST_TSuccess GHOST_SystemWayland::getModifierKeys(GHOST_ModifierKeys &keys) co
GHOST_TSuccess GHOST_SystemWayland::getButtons(GHOST_Buttons &buttons) const
{
if (UNLIKELY(display_->seats.empty())) {
GWL_Seat *seat = gwl_display_seat_active_get(display_);
if (UNLIKELY(!seat)) {
return GHOST_kFailure;
}
GWL_Seat *seat = display_->seats[0];
GWL_SeatStatePointer *seat_state_pointer = gwl_seat_state_pointer_active(seat);
if (!seat_state_pointer) {
return GHOST_kFailure;
@ -5073,7 +5127,10 @@ static const char *system_clipboard_text_mime_type(
static char *system_clipboard_get_primary_selection(GWL_Display *display)
{
GWL_Seat *seat = display->seats[0];
GWL_Seat *seat = gwl_display_seat_active_get(display);
if (UNLIKELY(!seat)) {
return nullptr;
}
GWL_PrimarySelection *primary = &seat->primary_selection;
std::mutex &mutex = primary->data_offer_mutex;
@ -5120,7 +5177,10 @@ static char *system_clipboard_get_primary_selection(GWL_Display *display)
static char *system_clipboard_get(GWL_Display *display)
{
GWL_Seat *seat = display->seats[0];
GWL_Seat *seat = gwl_display_seat_active_get(display);
if (UNLIKELY(!seat)) {
return nullptr;
}
std::mutex &mutex = seat->data_offer_copy_paste_mutex;
mutex.lock();
@ -5166,10 +5226,6 @@ static char *system_clipboard_get(GWL_Display *display)
char *GHOST_SystemWayland::getClipboard(bool selection) const
{
if (UNLIKELY(display_->seats.empty())) {
return nullptr;
}
char *data = nullptr;
if (selection) {
data = system_clipboard_get_primary_selection(display_);
@ -5185,7 +5241,10 @@ static void system_clipboard_put_primary_selection(GWL_Display *display, const c
if (!display->wp_primary_selection_device_manager) {
return;
}
GWL_Seat *seat = display->seats[0];
GWL_Seat *seat = gwl_display_seat_active_get(display);
if (UNLIKELY(!seat)) {
return;
}
GWL_PrimarySelection *primary = &seat->primary_selection;
std::lock_guard lock{primary->data_source_mutex};
@ -5219,8 +5278,10 @@ static void system_clipboard_put(GWL_Display *display, const char *buffer)
if (!display->wl_data_device_manager) {
return;
}
GWL_Seat *seat = display->seats[0];
GWL_Seat *seat = gwl_display_seat_active_get(display);
if (UNLIKELY(!seat)) {
return;
}
std::lock_guard lock{seat->data_source_mutex};
GWL_DataSource *data_source = seat->data_source;
@ -5245,10 +5306,6 @@ static void system_clipboard_put(GWL_Display *display, const char *buffer)
void GHOST_SystemWayland::putClipboard(const char *buffer, bool selection) const
{
if (UNLIKELY(display_->seats.empty())) {
return;
}
if (selection) {
system_clipboard_put_primary_selection(display_, buffer);
}
@ -5301,10 +5358,10 @@ GHOST_TSuccess GHOST_SystemWayland::getCursorPositionClientRelative(const GHOST_
int32_t &x,
int32_t &y) const
{
if (UNLIKELY(display_->seats.empty())) {
GWL_Seat *seat = gwl_display_seat_active_get(display_);
if (UNLIKELY(!seat)) {
return GHOST_kFailure;
}
GWL_Seat *seat = display_->seats[0];
GWL_SeatStatePointer *seat_state_pointer = gwl_seat_state_pointer_active(seat);
if (!seat_state_pointer || !seat_state_pointer->wl_surface) {
return GHOST_kFailure;
@ -5317,20 +5374,20 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorPositionClientRelative(GHOST_IWindo
const int32_t x,
const int32_t y)
{
if (UNLIKELY(display_->seats.empty())) {
GWL_Seat *seat = gwl_display_seat_active_get(display_);
if (UNLIKELY(!seat)) {
return GHOST_kFailure;
}
GWL_Seat *seat = display_->seats[0];
GHOST_WindowWayland *win = static_cast<GHOST_WindowWayland *>(window);
return setCursorPositionClientRelative_impl(seat, win, x, y);
}
GHOST_TSuccess GHOST_SystemWayland::getCursorPosition(int32_t &x, int32_t &y) const
{
if (UNLIKELY(display_->seats.empty())) {
GWL_Seat *seat = gwl_display_seat_active_get(display_);
if (UNLIKELY(!seat)) {
return GHOST_kFailure;
}
GWL_Seat *seat = display_->seats[0];
GWL_SeatStatePointer *seat_state_pointer = gwl_seat_state_pointer_active(seat);
if (!seat_state_pointer) {
return GHOST_kFailure;
@ -5345,10 +5402,10 @@ GHOST_TSuccess GHOST_SystemWayland::getCursorPosition(int32_t &x, int32_t &y) co
GHOST_TSuccess GHOST_SystemWayland::setCursorPosition(const int32_t x, const int32_t y)
{
if (UNLIKELY(display_->seats.empty())) {
GWL_Seat *seat = gwl_display_seat_active_get(display_);
if (UNLIKELY(!seat)) {
return GHOST_kFailure;
}
GWL_Seat *seat = display_->seats[0];
/* Intentionally different from `getCursorPosition` which supports both tablet & pointer.
* In the case of setting the cursor location, tablets don't support this. */
@ -5699,7 +5756,8 @@ static bool cursor_is_software(const GHOST_TGrabCursorMode mode, const bool use_
GHOST_TSuccess GHOST_SystemWayland::setCursorShape(const GHOST_TStandardCursor shape)
{
if (UNLIKELY(display_->seats.empty())) {
GWL_Seat *seat = gwl_display_seat_active_get(display_);
if (UNLIKELY(!seat)) {
return GHOST_kFailure;
}
auto cursor_find = ghost_wl_cursors.find(shape);
@ -5707,7 +5765,6 @@ GHOST_TSuccess GHOST_SystemWayland::setCursorShape(const GHOST_TStandardCursor s
ghost_wl_cursors.at(GHOST_kStandardCursorDefault) :
(*cursor_find).second;
GWL_Seat *seat = display_->seats[0];
GWL_Cursor *cursor = &seat->cursor;
if (!cursor->wl_theme) {
@ -5760,12 +5817,12 @@ GHOST_TSuccess GHOST_SystemWayland::setCustomCursorShape(uint8_t *bitmap,
const int hotY,
const bool /*canInvertColor*/)
{
if (UNLIKELY(display_->seats.empty())) {
GWL_Seat *seat = gwl_display_seat_active_get(display_);
if (UNLIKELY(!seat)) {
return GHOST_kFailure;
}
GWL_Cursor *cursor = &display_->seats[0]->cursor;
GWL_Cursor *cursor = &seat->cursor;
if (cursor->custom_data) {
munmap(cursor->custom_data, cursor->custom_data_size);
cursor->custom_data = nullptr;
@ -5823,14 +5880,19 @@ GHOST_TSuccess GHOST_SystemWayland::setCustomCursorShape(uint8_t *bitmap,
cursor->wl_image.hotspot_x = uint32_t(hotX);
cursor->wl_image.hotspot_y = uint32_t(hotY);
cursor_buffer_set(display_->seats[0], buffer);
cursor_buffer_set(seat, buffer);
return GHOST_kSuccess;
}
GHOST_TSuccess GHOST_SystemWayland::getCursorBitmap(GHOST_CursorBitmapRef *bitmap)
{
GWL_Cursor *cursor = &display_->seats[0]->cursor;
GWL_Seat *seat = gwl_display_seat_active_get(display_);
if (UNLIKELY(!seat)) {
return GHOST_kFailure;
}
GWL_Cursor *cursor = &seat->cursor;
if (cursor->custom_data == nullptr) {
return GHOST_kFailure;
}
@ -5851,11 +5913,11 @@ GHOST_TSuccess GHOST_SystemWayland::getCursorBitmap(GHOST_CursorBitmapRef *bitma
GHOST_TSuccess GHOST_SystemWayland::setCursorVisibility(const bool visible)
{
if (UNLIKELY(display_->seats.empty())) {
GWL_Seat *seat = gwl_display_seat_active_get(display_);
if (UNLIKELY(!seat)) {
return GHOST_kFailure;
}
GWL_Seat *seat = display_->seats[0];
cursor_visible_set(seat, visible, seat->cursor.is_hardware, CURSOR_VISIBLE_ALWAYS_SET);
return GHOST_kSuccess;
}
@ -5875,12 +5937,12 @@ bool GHOST_SystemWayland::supportsWindowPosition()
bool GHOST_SystemWayland::getCursorGrabUseSoftwareDisplay(const GHOST_TGrabCursorMode mode)
{
if (UNLIKELY(display_->seats.empty())) {
GWL_Seat *seat = gwl_display_seat_active_get(display_);
if (UNLIKELY(!seat)) {
return false;
}
#ifdef USE_GNOME_CONFINE_HACK
GWL_Seat *seat = display_->seats[0];
const bool use_software_confine = seat->use_pointer_software_confine;
#else
const bool use_software_confine = false;
@ -6068,6 +6130,11 @@ GHOST_WindowWayland *ghost_wl_surface_user_data(struct wl_surface *wl_surface)
* Functionality only used for the WAYLAND implementation.
* \{ */
void GHOST_SystemWayland::seat_active_set(const struct GWL_Seat *seat)
{
gwl_display_seat_active_set(display_, seat);
}
void GHOST_SystemWayland::window_surface_unref(const wl_surface *wl_surface)
{
#define SURFACE_CLEAR_PTR(surface_test) \
@ -6099,7 +6166,8 @@ bool GHOST_SystemWayland::window_cursor_grab_set(const GHOST_TGrabCursorMode mod
return GHOST_kFailure;
}
if (UNLIKELY(display_->seats.empty())) {
GWL_Seat *seat = gwl_display_seat_active_get(display_);
if (UNLIKELY(!seat)) {
return GHOST_kFailure;
}
/* No change, success. */
@ -6107,8 +6175,6 @@ bool GHOST_SystemWayland::window_cursor_grab_set(const GHOST_TGrabCursorMode mod
return GHOST_kSuccess;
}
GWL_Seat *seat = display_->seats[0];
#ifdef USE_GNOME_CONFINE_HACK
const bool was_software_confine = seat->use_pointer_software_confine;
const bool use_software_confine = setCursorGrab_use_software_confine(mode, wl_surface);

View File

@ -178,6 +178,9 @@ class GHOST_SystemWayland : public GHOST_System {
/* WAYLAND utility functions. */
/** Set this seat to be active. */
void seat_active_set(const struct GWL_Seat *seat);
/** Clear all references to this surface to prevent accessing NULL pointers. */
void window_surface_unref(const wl_surface *wl_surface);