mirror of
https://github.com/gnif/LookingGlass.git
synced 2024-11-22 05:27:20 +00:00
[client] wayland: move libdecor and xdg into seperate backends
This allows us to build with libdecor enabled as the selection to use it is decided upon at runtime if the compositor `gnome-shell` is detected. If the libdecor development headers are installed, by default it will now be compiled in unless overridden by the user at compile time.
This commit is contained in:
parent
d592f13f88
commit
aa9dbe654d
@ -45,12 +45,6 @@ add_feature_info(ENABLE_UBSAN ENABLE_UBSAN "UndefinedBehaviorSanitizer support."
|
|||||||
option(ENABLE_X11 "Build with X11 support" ON)
|
option(ENABLE_X11 "Build with X11 support" ON)
|
||||||
add_feature_info(ENABLE_X11 ENABLE_X11 "X11 support.")
|
add_feature_info(ENABLE_X11 ENABLE_X11 "X11 support.")
|
||||||
|
|
||||||
option(ENABLE_WAYLAND "Build with Wayland support" ON)
|
|
||||||
add_feature_info(ENABLE_WAYLAND ENABLE_WAYLAND "Wayland support.")
|
|
||||||
|
|
||||||
option(ENABLE_LIBDECOR "Build with libdecor support" OFF)
|
|
||||||
add_feature_info(ENABLE_LIBDECOR ENABLE_LIBDECOR "libdecor support.")
|
|
||||||
|
|
||||||
option(ENABLE_PIPEWIRE "Build with PipeWire audio output support" ON)
|
option(ENABLE_PIPEWIRE "Build with PipeWire audio output support" ON)
|
||||||
add_feature_info(ENABLE_PIPEWIRE ENABLE_PIPEWIRE "PipeWire audio support.")
|
add_feature_info(ENABLE_PIPEWIRE ENABLE_PIPEWIRE "PipeWire audio support.")
|
||||||
|
|
||||||
|
@ -18,6 +18,9 @@ function(add_displayserver name)
|
|||||||
add_subdirectory(${name})
|
add_subdirectory(${name})
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
|
option(ENABLE_WAYLAND "Build with Wayland support" ON)
|
||||||
|
add_feature_info(ENABLE_WAYLAND ENABLE_WAYLAND "Wayland support.")
|
||||||
|
|
||||||
# Add/remove displayservers here!
|
# Add/remove displayservers here!
|
||||||
if (ENABLE_WAYLAND)
|
if (ENABLE_WAYLAND)
|
||||||
add_displayserver(Wayland)
|
add_displayserver(Wayland)
|
||||||
|
@ -8,19 +8,7 @@ pkg_check_modules(DISPLAYSERVER_Wayland REQUIRED IMPORTED_TARGET
|
|||||||
xkbcommon
|
xkbcommon
|
||||||
)
|
)
|
||||||
|
|
||||||
set(DISPLAYSERVER_Wayland_OPT_PKGCONFIG_LIBRARIES "")
|
find_program(WAYLAND_SCANNER_EXECUTABLE NAMES wayland-scanner)
|
||||||
set(displayserver_Wayland_SHELL_SRC "")
|
|
||||||
|
|
||||||
if (ENABLE_LIBDECOR)
|
|
||||||
pkg_check_modules(DISPLAYSERVER_Wayland_LIBDECOR REQUIRED IMPORTED_TARGET
|
|
||||||
libdecor-0
|
|
||||||
)
|
|
||||||
list(APPEND DISPLAYSERVER_Wayland_OPT_PKGCONFIG_LIBRARIES PkgConfig::DISPLAYSERVER_Wayland_LIBDECOR)
|
|
||||||
list(APPEND displayserver_Wayland_SHELL_SRC shell_libdecor.c)
|
|
||||||
add_compile_definitions(ENABLE_LIBDECOR)
|
|
||||||
else()
|
|
||||||
list(APPEND displayserver_Wayland_SHELL_SRC shell_xdg.c)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_library(displayserver_Wayland STATIC
|
add_library(displayserver_Wayland STATIC
|
||||||
activation.c
|
activation.c
|
||||||
@ -36,66 +24,19 @@ add_library(displayserver_Wayland STATIC
|
|||||||
registry.c
|
registry.c
|
||||||
wayland.c
|
wayland.c
|
||||||
window.c
|
window.c
|
||||||
${displayserver_Wayland_SHELL_SRC}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
add_subdirectory(protocol)
|
||||||
|
add_subdirectory(desktops)
|
||||||
|
|
||||||
target_link_libraries(displayserver_Wayland
|
target_link_libraries(displayserver_Wayland
|
||||||
PkgConfig::DISPLAYSERVER_Wayland
|
PkgConfig::DISPLAYSERVER_Wayland
|
||||||
${DISPLAYSERVER_Wayland_OPT_PKGCONFIG_LIBRARIES}
|
|
||||||
lg_common
|
lg_common
|
||||||
|
wayland_protocol
|
||||||
|
wayland_desktops
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(displayserver_Wayland
|
target_include_directories(displayserver_Wayland
|
||||||
PRIVATE
|
PRIVATE
|
||||||
src
|
.
|
||||||
)
|
)
|
||||||
|
|
||||||
find_program(WAYLAND_SCANNER_EXECUTABLE NAMES wayland-scanner)
|
|
||||||
|
|
||||||
macro(wayland_generate protocol_file output_file)
|
|
||||||
add_custom_command(OUTPUT "${output_file}.h"
|
|
||||||
COMMAND "${WAYLAND_SCANNER_EXECUTABLE}" client-header "${protocol_file}" "${output_file}.h"
|
|
||||||
DEPENDS "${protocol_file}"
|
|
||||||
VERBATIM)
|
|
||||||
|
|
||||||
add_custom_command(OUTPUT "${output_file}.c"
|
|
||||||
COMMAND "${WAYLAND_SCANNER_EXECUTABLE}" private-code "${protocol_file}" "${output_file}.c"
|
|
||||||
DEPENDS "${protocol_file}"
|
|
||||||
VERBATIM)
|
|
||||||
|
|
||||||
target_sources(displayserver_Wayland PRIVATE "${output_file}.h" "${output_file}.c")
|
|
||||||
endmacro()
|
|
||||||
|
|
||||||
set(WAYLAND_PROTOCOLS_BASE "${PROJECT_TOP}/repos/wayland-protocols")
|
|
||||||
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/wayland")
|
|
||||||
include_directories("${CMAKE_BINARY_DIR}/wayland")
|
|
||||||
wayland_generate(
|
|
||||||
"${WAYLAND_PROTOCOLS_BASE}/stable/xdg-shell/xdg-shell.xml"
|
|
||||||
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-shell-client-protocol")
|
|
||||||
wayland_generate(
|
|
||||||
"${WAYLAND_PROTOCOLS_BASE}/stable/presentation-time/presentation-time.xml"
|
|
||||||
"${CMAKE_BINARY_DIR}/wayland/wayland-presentation-time-client-protocol")
|
|
||||||
wayland_generate(
|
|
||||||
"${WAYLAND_PROTOCOLS_BASE}/stable/viewporter/viewporter.xml"
|
|
||||||
"${CMAKE_BINARY_DIR}/wayland/wayland-viewporter-client-protocol")
|
|
||||||
wayland_generate(
|
|
||||||
"${WAYLAND_PROTOCOLS_BASE}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml"
|
|
||||||
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-decoration-unstable-v1-client-protocol")
|
|
||||||
wayland_generate(
|
|
||||||
"${WAYLAND_PROTOCOLS_BASE}/unstable/relative-pointer/relative-pointer-unstable-v1.xml"
|
|
||||||
"${CMAKE_BINARY_DIR}/wayland/wayland-relative-pointer-unstable-v1-client-protocol")
|
|
||||||
wayland_generate(
|
|
||||||
"${WAYLAND_PROTOCOLS_BASE}/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml"
|
|
||||||
"${CMAKE_BINARY_DIR}/wayland/wayland-pointer-constraints-unstable-v1-client-protocol")
|
|
||||||
wayland_generate(
|
|
||||||
"${WAYLAND_PROTOCOLS_BASE}/unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml"
|
|
||||||
"${CMAKE_BINARY_DIR}/wayland/wayland-keyboard-shortcuts-inhibit-unstable-v1-client-protocol")
|
|
||||||
wayland_generate(
|
|
||||||
"${WAYLAND_PROTOCOLS_BASE}/unstable/idle-inhibit/idle-inhibit-unstable-v1.xml"
|
|
||||||
"${CMAKE_BINARY_DIR}/wayland/wayland-idle-inhibit-unstable-v1-client-protocol")
|
|
||||||
wayland_generate(
|
|
||||||
"${WAYLAND_PROTOCOLS_BASE}/unstable/xdg-output/xdg-output-unstable-v1.xml"
|
|
||||||
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-output-unstable-v1-client-protocol")
|
|
||||||
wayland_generate(
|
|
||||||
"${WAYLAND_PROTOCOLS_BASE}/staging/xdg-activation/xdg-activation-v1.xml"
|
|
||||||
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-activation-v1-client-protocol")
|
|
||||||
|
58
client/displayservers/Wayland/desktops/CMakeLists.txt
Normal file
58
client/displayservers/Wayland/desktops/CMakeLists.txt
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.0)
|
||||||
|
project(wayland_desktops LANGUAGES C)
|
||||||
|
|
||||||
|
set(DESKTOP_H "${CMAKE_BINARY_DIR}/include/dynamic/wayland_desktops.h")
|
||||||
|
set(DESKTOP_C "${CMAKE_BINARY_DIR}/src/wayland_desktops.c")
|
||||||
|
|
||||||
|
file(WRITE ${DESKTOP_H} "#include \"interface/desktop.h\"\n\n")
|
||||||
|
file(APPEND ${DESKTOP_H} "extern struct WL_DesktopOps * WL_Desktops[];\n\n")
|
||||||
|
|
||||||
|
file(WRITE ${DESKTOP_C} "#include \"interface/desktop.h\"\n\n")
|
||||||
|
file(APPEND ${DESKTOP_C} "#include <stddef.h>\n\n")
|
||||||
|
|
||||||
|
set(DESKTOPS "_")
|
||||||
|
set(DESKTOPS_LINK "_")
|
||||||
|
function(add_desktop name)
|
||||||
|
set(DESKTOPS "${DESKTOPS};${name}" PARENT_SCOPE)
|
||||||
|
set(DESKTOPS_LINK "${DESKTOPS_LINK};wayland_desktop_${name}" PARENT_SCOPE)
|
||||||
|
add_subdirectory(${name})
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# Add/remove desktops here!
|
||||||
|
|
||||||
|
# the first entry here is the default
|
||||||
|
add_desktop(xdg)
|
||||||
|
|
||||||
|
pkg_check_modules(LIBDECOR REQUIRED IMPORTED_TARGET libdecor-0)
|
||||||
|
if(LIBDECOR_FOUND)
|
||||||
|
option(ENABLE_LIBDECOR "Build with libdecor support" ON)
|
||||||
|
else()
|
||||||
|
option(ENABLE_LIBDECOR "Build with libdecor support" OFF)
|
||||||
|
endif()
|
||||||
|
add_feature_info(ENABLE_LIBDECOR ENABLE_LIBDECOR "libdecor support.")
|
||||||
|
if (ENABLE_LIBDECOR)
|
||||||
|
add_desktop(libdecor)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
list(REMOVE_AT DESKTOPS 0)
|
||||||
|
list(REMOVE_AT DESKTOPS_LINK 0)
|
||||||
|
|
||||||
|
list(LENGTH DESKTOPS DESKTOP_COUNT)
|
||||||
|
file(APPEND ${DESKTOP_H} "#define WL_DESKTOP_COUNT ${DESKTOP_COUNT}\n")
|
||||||
|
|
||||||
|
foreach(desktop ${DESKTOPS})
|
||||||
|
file(APPEND ${DESKTOP_C} "extern struct WL_DesktopOps WLD_${desktop};\n")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
file(APPEND ${DESKTOP_C} "\nconst struct WL_DesktopOps * WL_Desktops[] =\n{\n")
|
||||||
|
foreach(desktop ${DESKTOPS})
|
||||||
|
file(APPEND ${DESKTOP_C} " &WLD_${desktop},\n")
|
||||||
|
endforeach()
|
||||||
|
file(APPEND ${DESKTOP_C} " NULL\n};")
|
||||||
|
|
||||||
|
add_library(wayland_desktops STATIC ${DESKTOP_C})
|
||||||
|
target_link_libraries(wayland_desktops ${DESKTOPS_LINK})
|
||||||
|
target_include_directories(wayland_desktops
|
||||||
|
PRIVATE
|
||||||
|
../
|
||||||
|
)
|
@ -0,0 +1,16 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.0)
|
||||||
|
project(wayland_desktop_libdecor LANGUAGES C)
|
||||||
|
|
||||||
|
add_library(wayland_desktop_libdecor STATIC
|
||||||
|
libdecor.c
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(wayland_desktop_libdecor
|
||||||
|
lg_common
|
||||||
|
wayland_protocol
|
||||||
|
PkgConfig::LIBDECOR
|
||||||
|
)
|
||||||
|
|
||||||
|
include_directories(
|
||||||
|
"../../"
|
||||||
|
)
|
269
client/displayservers/Wayland/desktops/libdecor/libdecor.c
Normal file
269
client/displayservers/Wayland/desktops/libdecor/libdecor.c
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
/**
|
||||||
|
* Looking Glass
|
||||||
|
* Copyright © 2017-2023 The Looking Glass Authors
|
||||||
|
* https://looking-glass.io
|
||||||
|
*
|
||||||
|
* This program 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 2 of the License, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program 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 this program; if not, write to the Free Software Foundation, Inc., 59
|
||||||
|
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "interface/desktop.h"
|
||||||
|
#include "wayland-xdg-shell-client-protocol.h"
|
||||||
|
|
||||||
|
#include "wayland.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/epoll.h>
|
||||||
|
|
||||||
|
#include <libdecor.h>
|
||||||
|
#include <wayland-client.h>
|
||||||
|
|
||||||
|
#include "app.h"
|
||||||
|
#include "common/debug.h"
|
||||||
|
|
||||||
|
// Maximum number of fds we can process at once in waylandWait
|
||||||
|
#define MAX_EPOLL_EVENTS 10
|
||||||
|
|
||||||
|
typedef struct LibDecorState
|
||||||
|
{
|
||||||
|
bool configured;
|
||||||
|
struct libdecor * libdecor;
|
||||||
|
struct libdecor_frame * libdecorFrame;
|
||||||
|
|
||||||
|
int32_t width, height;
|
||||||
|
bool needsResize;
|
||||||
|
bool fullscreen;
|
||||||
|
uint32_t resizeSerial;
|
||||||
|
}
|
||||||
|
LibDecorState;
|
||||||
|
|
||||||
|
static LibDecorState state = {0};
|
||||||
|
|
||||||
|
struct libdecor_configuration
|
||||||
|
{
|
||||||
|
uint32_t serial;
|
||||||
|
|
||||||
|
bool has_window_state;
|
||||||
|
enum libdecor_window_state window_state;
|
||||||
|
|
||||||
|
bool has_size;
|
||||||
|
int window_width;
|
||||||
|
int window_height;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void libdecorHandleError(struct libdecor * context, enum libdecor_error error,
|
||||||
|
const char *message)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Got libdecor error (%d): %s", error, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void libdecorFrameConfigure(struct libdecor_frame * frame,
|
||||||
|
struct libdecor_configuration * configuration, void * opaque)
|
||||||
|
{
|
||||||
|
if (!state.configured)
|
||||||
|
{
|
||||||
|
xdg_surface_ack_configure(libdecor_frame_get_xdg_surface(frame), configuration->serial);
|
||||||
|
state.configured = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int width, height;
|
||||||
|
if (libdecor_configuration_get_content_size(configuration, frame, &width, &height))
|
||||||
|
{
|
||||||
|
state.width = width;
|
||||||
|
state.height = height;
|
||||||
|
|
||||||
|
struct libdecor_state * s = libdecor_state_new(width, height);
|
||||||
|
libdecor_frame_commit(state.libdecorFrame, s, NULL);
|
||||||
|
libdecor_state_free(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum libdecor_window_state windowState;
|
||||||
|
if (libdecor_configuration_get_window_state(configuration, &windowState))
|
||||||
|
state.fullscreen = windowState & LIBDECOR_WINDOW_STATE_FULLSCREEN;
|
||||||
|
|
||||||
|
state.resizeSerial = configuration->serial;
|
||||||
|
waylandNeedsResize();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void libdecorFrameClose(struct libdecor_frame * frame, void * opaque)
|
||||||
|
{
|
||||||
|
app_handleCloseEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void libdecorFrameCommit(struct libdecor_frame * frame, void * opaque)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
|
||||||
|
static struct libdecor_interface libdecorListener =
|
||||||
|
{
|
||||||
|
libdecorHandleError,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct libdecor_frame_interface libdecorFrameListener =
|
||||||
|
{
|
||||||
|
libdecorFrameConfigure,
|
||||||
|
libdecorFrameClose,
|
||||||
|
libdecorFrameCommit,
|
||||||
|
};
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
|
static void libdecorCallback(uint32_t events, void * opaque)
|
||||||
|
{
|
||||||
|
libdecor_dispatch(state.libdecor, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool libdecor_shellInit(
|
||||||
|
struct wl_display * display, struct wl_surface * surface,
|
||||||
|
const char * title, bool fullscreen,
|
||||||
|
bool maximize, bool borderless, bool resizable)
|
||||||
|
{
|
||||||
|
state.libdecor = libdecor_new(display, &libdecorListener);
|
||||||
|
state.libdecorFrame = libdecor_decorate(state.libdecor, surface,
|
||||||
|
&libdecorFrameListener, NULL);
|
||||||
|
|
||||||
|
libdecor_frame_set_app_id(state.libdecorFrame, "looking-glass-client");
|
||||||
|
libdecor_frame_set_title(state.libdecorFrame, title);
|
||||||
|
libdecor_frame_map(state.libdecorFrame);
|
||||||
|
|
||||||
|
if (resizable)
|
||||||
|
libdecor_frame_set_capabilities(state.libdecorFrame,
|
||||||
|
LIBDECOR_ACTION_RESIZE);
|
||||||
|
else
|
||||||
|
libdecor_frame_unset_capabilities(state.libdecorFrame,
|
||||||
|
LIBDECOR_ACTION_RESIZE);
|
||||||
|
|
||||||
|
while (!state.configured)
|
||||||
|
libdecor_dispatch(state.libdecor, 0);
|
||||||
|
|
||||||
|
if (!waylandPollRegister(libdecor_get_fd(state.libdecor),
|
||||||
|
libdecorCallback, NULL, EPOLLIN))
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed register display to epoll: %s", strerror(errno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void libdecor_shellAckConfigureIfNeeded(void)
|
||||||
|
{
|
||||||
|
if (state.resizeSerial)
|
||||||
|
{
|
||||||
|
xdg_surface_ack_configure(
|
||||||
|
libdecor_frame_get_xdg_surface(state.libdecorFrame), state.resizeSerial);
|
||||||
|
state.resizeSerial = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void libdecor_setFullscreen(bool fs)
|
||||||
|
{
|
||||||
|
if (fs)
|
||||||
|
libdecor_frame_set_fullscreen(state.libdecorFrame, NULL);
|
||||||
|
else
|
||||||
|
libdecor_frame_unset_fullscreen(state.libdecorFrame);
|
||||||
|
|
||||||
|
libdecor_frame_set_visibility(state.libdecorFrame, !fs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool libdecor_getFullscreen(void)
|
||||||
|
{
|
||||||
|
return state.fullscreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void libdecor_minimize(void)
|
||||||
|
{
|
||||||
|
libdecor_frame_set_minimized(state.libdecorFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void libdecor_shellResize(int w, int h)
|
||||||
|
{
|
||||||
|
if (!libdecor_frame_is_floating(state.libdecorFrame))
|
||||||
|
return;
|
||||||
|
|
||||||
|
state.width = w;
|
||||||
|
state.height = h;
|
||||||
|
|
||||||
|
struct libdecor_state * s = libdecor_state_new(w, h);
|
||||||
|
libdecor_frame_commit(state.libdecorFrame, s, NULL);
|
||||||
|
libdecor_state_free(s);
|
||||||
|
|
||||||
|
waylandNeedsResize();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void libdecor_setSize(int w, int h)
|
||||||
|
{
|
||||||
|
state.width = w;
|
||||||
|
state.height = h;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void libdecor_getSize(int * w, int * h)
|
||||||
|
{
|
||||||
|
*w = state.width;
|
||||||
|
*h = state.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool libdecor_registryGlobalHandler(void * data,
|
||||||
|
struct wl_registry * registry, uint32_t name, const char * interface,
|
||||||
|
uint32_t version)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool libdecor_pollInit(struct wl_display * display)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void libdecor_pollWait(struct wl_display * display, int epollFd,
|
||||||
|
unsigned int time)
|
||||||
|
{
|
||||||
|
libdecor_dispatch(state.libdecor, 0);
|
||||||
|
|
||||||
|
struct epoll_event events[MAX_EPOLL_EVENTS];
|
||||||
|
int count;
|
||||||
|
if ((count = epoll_wait(epollFd, events, MAX_EPOLL_EVENTS, time)) < 0)
|
||||||
|
{
|
||||||
|
if (errno != EINTR)
|
||||||
|
DEBUG_INFO("epoll failed: %s", strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
struct WaylandPoll * poll = events[i].data.ptr;
|
||||||
|
if (!poll->removed)
|
||||||
|
poll->callback(events[i].events, poll->opaque);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WL_DesktopOps WLD_libdecor =
|
||||||
|
{
|
||||||
|
.name = "libdecor",
|
||||||
|
.compositor = "gnome-shell",
|
||||||
|
.shellInit = libdecor_shellInit,
|
||||||
|
.shellAckConfigureIfNeeded = libdecor_shellAckConfigureIfNeeded,
|
||||||
|
.setFullscreen = libdecor_setFullscreen,
|
||||||
|
.getFullscreen = libdecor_getFullscreen,
|
||||||
|
.minimize = libdecor_minimize,
|
||||||
|
.shellResize = libdecor_shellResize,
|
||||||
|
.setSize = libdecor_setSize,
|
||||||
|
.getSize = libdecor_getSize,
|
||||||
|
.registryGlobalHandler = libdecor_registryGlobalHandler,
|
||||||
|
.pollInit = libdecor_pollInit,
|
||||||
|
.pollWait = libdecor_pollWait
|
||||||
|
};
|
15
client/displayservers/Wayland/desktops/xdg/CMakeLists.txt
Normal file
15
client/displayservers/Wayland/desktops/xdg/CMakeLists.txt
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.0)
|
||||||
|
project(wayland_desktop_xdg LANGUAGES C)
|
||||||
|
|
||||||
|
add_library(wayland_desktop_xdg STATIC
|
||||||
|
xdg.c
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(wayland_desktop_xdg
|
||||||
|
lg_common
|
||||||
|
wayland_protocol
|
||||||
|
)
|
||||||
|
|
||||||
|
include_directories(
|
||||||
|
"../../"
|
||||||
|
)
|
314
client/displayservers/Wayland/desktops/xdg/xdg.c
Normal file
314
client/displayservers/Wayland/desktops/xdg/xdg.c
Normal file
@ -0,0 +1,314 @@
|
|||||||
|
/**
|
||||||
|
* Looking Glass
|
||||||
|
* Copyright © 2017-2023 The Looking Glass Authors
|
||||||
|
* https://looking-glass.io
|
||||||
|
*
|
||||||
|
* This program 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 2 of the License, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program 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 this program; if not, write to the Free Software Foundation, Inc., 59
|
||||||
|
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "wayland.h"
|
||||||
|
#include "wayland-xdg-shell-client-protocol.h"
|
||||||
|
#include "wayland-xdg-decoration-unstable-v1-client-protocol.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/epoll.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <wayland-client.h>
|
||||||
|
|
||||||
|
#include "app.h"
|
||||||
|
#include "common/debug.h"
|
||||||
|
|
||||||
|
// Maximum number of fds we can process at once in waylandWait
|
||||||
|
#define MAX_EPOLL_EVENTS 10
|
||||||
|
|
||||||
|
typedef struct XDGState
|
||||||
|
{
|
||||||
|
bool configured;
|
||||||
|
|
||||||
|
struct xdg_wm_base * wmBase;
|
||||||
|
struct xdg_surface * surface;
|
||||||
|
struct xdg_toplevel * toplevel;
|
||||||
|
struct zxdg_decoration_manager_v1 * decorationManager;
|
||||||
|
struct zxdg_toplevel_decoration_v1 * toplevelDecoration;
|
||||||
|
|
||||||
|
int32_t width, height;
|
||||||
|
uint32_t resizeSerial;
|
||||||
|
bool fullscreen;
|
||||||
|
bool floating;
|
||||||
|
int displayFd;
|
||||||
|
}
|
||||||
|
XDGState;
|
||||||
|
|
||||||
|
static XDGState state = {0};
|
||||||
|
|
||||||
|
// XDG WM base listeners.
|
||||||
|
|
||||||
|
static void xdgWmBasePing(void * data, struct xdg_wm_base * xdgWmBase, uint32_t serial)
|
||||||
|
{
|
||||||
|
xdg_wm_base_pong(xdgWmBase, serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct xdg_wm_base_listener xdgWmBaseListener = {
|
||||||
|
.ping = xdgWmBasePing,
|
||||||
|
};
|
||||||
|
|
||||||
|
// XDG Surface listeners.
|
||||||
|
|
||||||
|
static void xdgSurfaceConfigure(void * data, struct xdg_surface * xdgSurface,
|
||||||
|
uint32_t serial)
|
||||||
|
{
|
||||||
|
if (state.configured)
|
||||||
|
{
|
||||||
|
state.resizeSerial = serial;
|
||||||
|
waylandNeedsResize();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
xdg_surface_ack_configure(xdgSurface, serial);
|
||||||
|
state.configured = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct xdg_surface_listener xdgSurfaceListener = {
|
||||||
|
.configure = xdgSurfaceConfigure,
|
||||||
|
};
|
||||||
|
|
||||||
|
// XDG Toplevel listeners.
|
||||||
|
|
||||||
|
static void xdgToplevelConfigure(void * data, struct xdg_toplevel * xdgToplevel,
|
||||||
|
int32_t width, int32_t height, struct wl_array * states)
|
||||||
|
{
|
||||||
|
state.width = width;
|
||||||
|
state.height = height;
|
||||||
|
state.fullscreen = false;
|
||||||
|
state.floating = true;
|
||||||
|
|
||||||
|
enum xdg_toplevel_state * s;
|
||||||
|
wl_array_for_each(s, states)
|
||||||
|
{
|
||||||
|
switch (*s)
|
||||||
|
{
|
||||||
|
case XDG_TOPLEVEL_STATE_FULLSCREEN:
|
||||||
|
state.fullscreen = true;
|
||||||
|
// fallthrough
|
||||||
|
case XDG_TOPLEVEL_STATE_MAXIMIZED:
|
||||||
|
case XDG_TOPLEVEL_STATE_TILED_LEFT:
|
||||||
|
case XDG_TOPLEVEL_STATE_TILED_RIGHT:
|
||||||
|
case XDG_TOPLEVEL_STATE_TILED_TOP:
|
||||||
|
case XDG_TOPLEVEL_STATE_TILED_BOTTOM:
|
||||||
|
state.floating = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xdgToplevelClose(void * data, struct xdg_toplevel * xdgToplevel)
|
||||||
|
{
|
||||||
|
app_handleCloseEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct xdg_toplevel_listener xdgToplevelListener = {
|
||||||
|
.configure = xdgToplevelConfigure,
|
||||||
|
.close = xdgToplevelClose,
|
||||||
|
};
|
||||||
|
|
||||||
|
bool xdg_shellInit(struct wl_display * display, struct wl_surface * surface,
|
||||||
|
const char * title, bool fullscreen, bool maximize, bool borderless,
|
||||||
|
bool resizable)
|
||||||
|
{
|
||||||
|
if (!state.wmBase)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Compositor missing xdg_wm_base, will not proceed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
xdg_wm_base_add_listener(state.wmBase, &xdgWmBaseListener, NULL);
|
||||||
|
|
||||||
|
state.surface = xdg_wm_base_get_xdg_surface(state.wmBase, surface);
|
||||||
|
xdg_surface_add_listener(state.surface, &xdgSurfaceListener, NULL);
|
||||||
|
|
||||||
|
state.toplevel = xdg_surface_get_toplevel(state.surface);
|
||||||
|
xdg_toplevel_add_listener(state.toplevel, &xdgToplevelListener, NULL);
|
||||||
|
xdg_toplevel_set_title(state.toplevel, title);
|
||||||
|
xdg_toplevel_set_app_id(state.toplevel, "looking-glass-client");
|
||||||
|
|
||||||
|
if (fullscreen)
|
||||||
|
xdg_toplevel_set_fullscreen(state.toplevel, NULL);
|
||||||
|
|
||||||
|
if (maximize)
|
||||||
|
xdg_toplevel_set_maximized(state.toplevel);
|
||||||
|
|
||||||
|
if (state.decorationManager)
|
||||||
|
{
|
||||||
|
state.toplevelDecoration = zxdg_decoration_manager_v1_get_toplevel_decoration(
|
||||||
|
state.decorationManager, state.toplevel);
|
||||||
|
if (state.toplevelDecoration)
|
||||||
|
{
|
||||||
|
zxdg_toplevel_decoration_v1_set_mode(state.toplevelDecoration,
|
||||||
|
borderless ?
|
||||||
|
ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE :
|
||||||
|
ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xdg_shellAckConfigureIfNeeded(void)
|
||||||
|
{
|
||||||
|
if (state.resizeSerial)
|
||||||
|
{
|
||||||
|
xdg_surface_ack_configure(state.surface, state.resizeSerial);
|
||||||
|
state.resizeSerial = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xdg_setFullscreen(bool fs)
|
||||||
|
{
|
||||||
|
if (fs)
|
||||||
|
xdg_toplevel_set_fullscreen(state.toplevel, NULL);
|
||||||
|
else
|
||||||
|
xdg_toplevel_unset_fullscreen(state.toplevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool xdg_getFullscreen(void)
|
||||||
|
{
|
||||||
|
return state.fullscreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xdg_minimize(void)
|
||||||
|
{
|
||||||
|
xdg_toplevel_set_minimized(state.toplevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xdg_shellResize(int w, int h)
|
||||||
|
{
|
||||||
|
if (!state.floating)
|
||||||
|
return;
|
||||||
|
|
||||||
|
state.width = w;
|
||||||
|
state.height = h;
|
||||||
|
xdg_surface_set_window_geometry(state.surface, 0, 0, w, h);
|
||||||
|
|
||||||
|
waylandNeedsResize();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xdg_setSize(int w, int h)
|
||||||
|
{
|
||||||
|
state.width = w;
|
||||||
|
state.height = h;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xdg_getSize(int * w, int * h)
|
||||||
|
{
|
||||||
|
*w = state.width;
|
||||||
|
*h = state.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool xdg_registryGlobalHandler(void * data,
|
||||||
|
struct wl_registry * registry, uint32_t name, const char * interface,
|
||||||
|
uint32_t version)
|
||||||
|
{
|
||||||
|
if (!strcmp(interface, xdg_wm_base_interface.name))
|
||||||
|
{
|
||||||
|
state.wmBase = wl_registry_bind(registry, name,
|
||||||
|
&xdg_wm_base_interface, 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp(interface, zxdg_decoration_manager_v1_interface.name))
|
||||||
|
{
|
||||||
|
state.decorationManager = wl_registry_bind(registry, name,
|
||||||
|
&zxdg_decoration_manager_v1_interface, 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void waylandDisplayCallback(uint32_t events, void * opaque)
|
||||||
|
{
|
||||||
|
struct wl_display * display = (struct wl_display *)opaque;
|
||||||
|
if (events & EPOLLERR)
|
||||||
|
wl_display_cancel_read(display);
|
||||||
|
else
|
||||||
|
wl_display_read_events(display);
|
||||||
|
wl_display_dispatch_pending(display);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool xdg_pollInit(struct wl_display * display)
|
||||||
|
{
|
||||||
|
state.displayFd = wl_display_get_fd(display);
|
||||||
|
if (!waylandPollRegister(state.displayFd, waylandDisplayCallback,
|
||||||
|
display, EPOLLIN))
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed register display to epoll: %s", strerror(errno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void xdg_pollWait(struct wl_display * display, int epollFd,
|
||||||
|
unsigned int time)
|
||||||
|
{
|
||||||
|
while (wl_display_prepare_read(display))
|
||||||
|
wl_display_dispatch_pending(display);
|
||||||
|
wl_display_flush(display);
|
||||||
|
|
||||||
|
struct epoll_event events[MAX_EPOLL_EVENTS];
|
||||||
|
int count;
|
||||||
|
if ((count = epoll_wait(epollFd, events, MAX_EPOLL_EVENTS, time)) < 0)
|
||||||
|
{
|
||||||
|
if (errno != EINTR)
|
||||||
|
DEBUG_INFO("epoll failed: %s", strerror(errno));
|
||||||
|
wl_display_cancel_read(display);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sawDisplay = false;
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
struct WaylandPoll * poll = events[i].data.ptr;
|
||||||
|
if (!poll->removed)
|
||||||
|
poll->callback(events[i].events, poll->opaque);
|
||||||
|
if (poll->fd == state.displayFd)
|
||||||
|
sawDisplay = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sawDisplay)
|
||||||
|
wl_display_cancel_read(display);
|
||||||
|
}
|
||||||
|
|
||||||
|
WL_DesktopOps WLD_xdg =
|
||||||
|
{
|
||||||
|
.name = "xdg",
|
||||||
|
.compositor = "",
|
||||||
|
.shellInit = xdg_shellInit,
|
||||||
|
.shellAckConfigureIfNeeded = xdg_shellAckConfigureIfNeeded,
|
||||||
|
.setFullscreen = xdg_setFullscreen,
|
||||||
|
.getFullscreen = xdg_getFullscreen,
|
||||||
|
.minimize = xdg_minimize,
|
||||||
|
.shellResize = xdg_shellResize,
|
||||||
|
.setSize = xdg_setSize,
|
||||||
|
.getSize = xdg_getSize,
|
||||||
|
.registryGlobalHandler = xdg_registryGlobalHandler,
|
||||||
|
.pollInit = xdg_pollInit,
|
||||||
|
.pollWait = xdg_pollWait
|
||||||
|
};
|
@ -89,18 +89,21 @@ void waylandEGLSwapBuffers(EGLDisplay display, EGLSurface surface, const struct
|
|||||||
if (wlWm.needsResize)
|
if (wlWm.needsResize)
|
||||||
{
|
{
|
||||||
bool skipResize = false;
|
bool skipResize = false;
|
||||||
wl_egl_window_resize(wlWm.eglWindow, wl_fixed_to_int(wlWm.width * wlWm.scale),
|
|
||||||
wl_fixed_to_int(wlWm.height * wlWm.scale), 0, 0);
|
|
||||||
|
|
||||||
if (wlWm.width == 0 || wlWm.height == 0)
|
int width, height;
|
||||||
|
wlWm.desktop->getSize(&width, &height);
|
||||||
|
wl_egl_window_resize(wlWm.eglWindow, wl_fixed_to_int(width * wlWm.scale),
|
||||||
|
wl_fixed_to_int(height * wlWm.scale), 0, 0);
|
||||||
|
|
||||||
|
if (width == 0 || height == 0)
|
||||||
skipResize = true;
|
skipResize = true;
|
||||||
else if (wlWm.fractionalScale)
|
else if (wlWm.fractionalScale)
|
||||||
{
|
{
|
||||||
wl_surface_set_buffer_scale(wlWm.surface, 1);
|
wl_surface_set_buffer_scale(wlWm.surface, 1);
|
||||||
if (!wlWm.viewport)
|
if (!wlWm.viewport)
|
||||||
wlWm.viewport = wp_viewporter_get_viewport(wlWm.viewporter, wlWm.surface);
|
wlWm.viewport = wp_viewporter_get_viewport(wlWm.viewporter, wlWm.surface);
|
||||||
wp_viewport_set_source(wlWm.viewport, 0, 0, wlWm.width * wlWm.scale, wlWm.height * wlWm.scale);
|
wp_viewport_set_source(wlWm.viewport, 0, 0, width * wlWm.scale, height * wlWm.scale);
|
||||||
wp_viewport_set_destination(wlWm.viewport, wlWm.width, wlWm.height);
|
wp_viewport_set_destination(wlWm.viewport, width, height);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -120,18 +123,18 @@ void waylandEGLSwapBuffers(EGLDisplay display, EGLSurface surface, const struct
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct wl_region * region = wl_compositor_create_region(wlWm.compositor);
|
struct wl_region * region = wl_compositor_create_region(wlWm.compositor);
|
||||||
wl_region_add(region, 0, 0, wlWm.width, wlWm.height);
|
wl_region_add(region, 0, 0, width, height);
|
||||||
wl_surface_set_opaque_region(wlWm.surface, region);
|
wl_surface_set_opaque_region(wlWm.surface, region);
|
||||||
wl_region_destroy(region);
|
wl_region_destroy(region);
|
||||||
|
|
||||||
app_handleResizeEvent(wlWm.width, wlWm.height, wl_fixed_to_double(wlWm.scale),
|
app_handleResizeEvent(width, height, wl_fixed_to_double(wlWm.scale),
|
||||||
(struct Border) {0, 0, 0, 0});
|
(struct Border) {0, 0, 0, 0});
|
||||||
app_invalidateWindow(true);
|
app_invalidateWindow(true);
|
||||||
waylandStopWaitFrame();
|
waylandStopWaitFrame();
|
||||||
wlWm.needsResize = skipResize;
|
wlWm.needsResize = skipResize;
|
||||||
}
|
}
|
||||||
|
|
||||||
waylandShellAckConfigureIfNeeded();
|
wlWm.desktop->shellAckConfigureIfNeeded();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -590,10 +590,13 @@ void waylandWarpPointer(int x, int y, bool exiting)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int width, height;
|
||||||
|
wlWm.desktop->getSize(&width, &height);
|
||||||
|
|
||||||
if (x < 0) x = 0;
|
if (x < 0) x = 0;
|
||||||
else if (x >= wlWm.width) x = wlWm.width - 1;
|
else if (x >= width) x = width - 1;
|
||||||
if (y < 0) y = 0;
|
if (y < 0) y = 0;
|
||||||
else if (y >= wlWm.height) y = wlWm.height - 1;
|
else if (y >= height) y = height - 1;
|
||||||
|
|
||||||
struct wl_region * region = wl_compositor_create_region(wlWm.compositor);
|
struct wl_region * region = wl_compositor_create_region(wlWm.compositor);
|
||||||
wl_region_add(region, x, y, 1, 1);
|
wl_region_add(region, x, y, 1, 1);
|
||||||
|
64
client/displayservers/Wayland/interface/desktop.h
Normal file
64
client/displayservers/Wayland/interface/desktop.h
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
/**
|
||||||
|
* Looking Glass
|
||||||
|
* Copyright © 2017-2023 The Looking Glass Authors
|
||||||
|
* https://looking-glass.io
|
||||||
|
*
|
||||||
|
* This program 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 2 of the License, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program 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 this program; if not, write to the Free Software Foundation, Inc., 59
|
||||||
|
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _H_WAYLAND_DESKTOP_H_
|
||||||
|
#define _H_WAYLAND_DESKTOP_H_
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <wayland-client.h>
|
||||||
|
|
||||||
|
typedef struct WL_DesktopOps
|
||||||
|
{
|
||||||
|
// the friendly name
|
||||||
|
const char * name;
|
||||||
|
|
||||||
|
// the compositor process name to match
|
||||||
|
const char * compositor;
|
||||||
|
|
||||||
|
bool (*shellInit)(
|
||||||
|
struct wl_display * display, struct wl_surface * surface,
|
||||||
|
const char * title, bool fullscreen, bool maximize,
|
||||||
|
bool borderless, bool resizable);
|
||||||
|
|
||||||
|
void (*shellAckConfigureIfNeeded)(void);
|
||||||
|
|
||||||
|
void (*setFullscreen)(bool fs);
|
||||||
|
|
||||||
|
bool (*getFullscreen)(void);
|
||||||
|
|
||||||
|
void (*minimize)(void);
|
||||||
|
|
||||||
|
void (*shellResize)(int w, int h);
|
||||||
|
|
||||||
|
void (*setSize)(int w, int h);
|
||||||
|
|
||||||
|
void (*getSize)(int * w, int * h);
|
||||||
|
|
||||||
|
bool (*registryGlobalHandler)(
|
||||||
|
void * data, struct wl_registry * registry,
|
||||||
|
uint32_t name, const char * interface, uint32_t version);
|
||||||
|
|
||||||
|
bool (*pollInit)(struct wl_display * display);
|
||||||
|
|
||||||
|
void (*pollWait)(struct wl_display * display, int epollFd, unsigned int time);
|
||||||
|
}
|
||||||
|
WL_DesktopOps;
|
||||||
|
|
||||||
|
#endif
|
@ -30,23 +30,6 @@
|
|||||||
#include "common/debug.h"
|
#include "common/debug.h"
|
||||||
#include "common/locking.h"
|
#include "common/locking.h"
|
||||||
|
|
||||||
#ifdef ENABLE_LIBDECOR
|
|
||||||
#include <libdecor.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define EPOLL_EVENTS 10 // Maximum number of fds we can process at once in waylandWait
|
|
||||||
|
|
||||||
#ifndef ENABLE_LIBDECOR
|
|
||||||
static void waylandDisplayCallback(uint32_t events, void * opaque)
|
|
||||||
{
|
|
||||||
if (events & EPOLLERR)
|
|
||||||
wl_display_cancel_read(wlWm.display);
|
|
||||||
else
|
|
||||||
wl_display_read_events(wlWm.display);
|
|
||||||
wl_display_dispatch_pending(wlWm.display);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool waylandPollInit(void)
|
bool waylandPollInit(void)
|
||||||
{
|
{
|
||||||
wlWm.epollFd = epoll_create1(EPOLL_CLOEXEC);
|
wlWm.epollFd = epoll_create1(EPOLL_CLOEXEC);
|
||||||
@ -61,58 +44,12 @@ bool waylandPollInit(void)
|
|||||||
LG_LOCK_INIT(wlWm.pollLock);
|
LG_LOCK_INIT(wlWm.pollLock);
|
||||||
LG_LOCK_INIT(wlWm.pollFreeLock);
|
LG_LOCK_INIT(wlWm.pollFreeLock);
|
||||||
|
|
||||||
#ifndef ENABLE_LIBDECOR
|
return wlWm.desktop->pollInit(wlWm.display);
|
||||||
wlWm.displayFd = wl_display_get_fd(wlWm.display);
|
|
||||||
if (!waylandPollRegister(wlWm.displayFd, waylandDisplayCallback, NULL, EPOLLIN))
|
|
||||||
{
|
|
||||||
DEBUG_ERROR("Failed register display to epoll: %s", strerror(errno));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void waylandWait(unsigned int time)
|
void waylandWait(unsigned int time)
|
||||||
{
|
{
|
||||||
#ifdef ENABLE_LIBDECOR
|
wlWm.desktop->pollWait(wlWm.display, wlWm.epollFd, time);
|
||||||
libdecor_dispatch(wlWm.libdecor, 0);
|
|
||||||
#else
|
|
||||||
while (wl_display_prepare_read(wlWm.display))
|
|
||||||
wl_display_dispatch_pending(wlWm.display);
|
|
||||||
wl_display_flush(wlWm.display);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct epoll_event events[EPOLL_EVENTS];
|
|
||||||
int count;
|
|
||||||
if ((count = epoll_wait(wlWm.epollFd, events, EPOLL_EVENTS, time)) < 0)
|
|
||||||
{
|
|
||||||
if (errno != EINTR)
|
|
||||||
DEBUG_INFO("epoll failed: %s", strerror(errno));
|
|
||||||
#ifndef ENABLE_LIBDECOR
|
|
||||||
wl_display_cancel_read(wlWm.display);
|
|
||||||
#endif
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef ENABLE_LIBDECOR
|
|
||||||
bool sawDisplay = false;
|
|
||||||
#endif
|
|
||||||
for (int i = 0; i < count; ++i) {
|
|
||||||
struct WaylandPoll * poll = events[i].data.ptr;
|
|
||||||
if (!poll->removed)
|
|
||||||
poll->callback(events[i].events, poll->opaque);
|
|
||||||
#ifndef ENABLE_LIBDECOR
|
|
||||||
if (poll->fd == wlWm.displayFd)
|
|
||||||
sawDisplay = true;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef ENABLE_LIBDECOR
|
|
||||||
if (!sawDisplay)
|
|
||||||
wl_display_cancel_read(wlWm.display);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
INTERLOCKED_SECTION(wlWm.pollFreeLock,
|
INTERLOCKED_SECTION(wlWm.pollFreeLock,
|
||||||
{
|
{
|
||||||
struct WaylandPoll * node;
|
struct WaylandPoll * node;
|
||||||
|
71
client/displayservers/Wayland/protocol/CMakeLists.txt
Normal file
71
client/displayservers/Wayland/protocol/CMakeLists.txt
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.0)
|
||||||
|
project(wayland_protocol LANGUAGES C)
|
||||||
|
|
||||||
|
find_package(PkgConfig)
|
||||||
|
pkg_check_modules(WAYLAND REQUIRED IMPORTED_TARGET
|
||||||
|
wayland-client
|
||||||
|
wayland-cursor
|
||||||
|
xkbcommon
|
||||||
|
)
|
||||||
|
|
||||||
|
find_program(WAYLAND_SCANNER_EXECUTABLE NAMES wayland-scanner)
|
||||||
|
|
||||||
|
add_library(wayland_protocol STATIC
|
||||||
|
)
|
||||||
|
|
||||||
|
macro(wayland_generate protocol_file output_file)
|
||||||
|
add_custom_command(OUTPUT "${output_file}.h"
|
||||||
|
COMMAND "${WAYLAND_SCANNER_EXECUTABLE}" client-header "${protocol_file}" "${output_file}.h"
|
||||||
|
DEPENDS "${protocol_file}"
|
||||||
|
VERBATIM)
|
||||||
|
|
||||||
|
add_custom_command(OUTPUT "${output_file}.c"
|
||||||
|
COMMAND "${WAYLAND_SCANNER_EXECUTABLE}" private-code "${protocol_file}" "${output_file}.c"
|
||||||
|
DEPENDS "${protocol_file}"
|
||||||
|
VERBATIM)
|
||||||
|
|
||||||
|
target_sources(wayland_protocol PRIVATE "${output_file}.h" "${output_file}.c")
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
set(WAYLAND_PROTOCOLS_BASE "${PROJECT_TOP}/repos/wayland-protocols")
|
||||||
|
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/wayland")
|
||||||
|
include_directories("${CMAKE_BINARY_DIR}/wayland")
|
||||||
|
wayland_generate(
|
||||||
|
"${WAYLAND_PROTOCOLS_BASE}/stable/xdg-shell/xdg-shell.xml"
|
||||||
|
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-shell-client-protocol")
|
||||||
|
wayland_generate(
|
||||||
|
"${WAYLAND_PROTOCOLS_BASE}/stable/presentation-time/presentation-time.xml"
|
||||||
|
"${CMAKE_BINARY_DIR}/wayland/wayland-presentation-time-client-protocol")
|
||||||
|
wayland_generate(
|
||||||
|
"${WAYLAND_PROTOCOLS_BASE}/stable/viewporter/viewporter.xml"
|
||||||
|
"${CMAKE_BINARY_DIR}/wayland/wayland-viewporter-client-protocol")
|
||||||
|
wayland_generate(
|
||||||
|
"${WAYLAND_PROTOCOLS_BASE}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml"
|
||||||
|
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-decoration-unstable-v1-client-protocol")
|
||||||
|
wayland_generate(
|
||||||
|
"${WAYLAND_PROTOCOLS_BASE}/unstable/relative-pointer/relative-pointer-unstable-v1.xml"
|
||||||
|
"${CMAKE_BINARY_DIR}/wayland/wayland-relative-pointer-unstable-v1-client-protocol")
|
||||||
|
wayland_generate(
|
||||||
|
"${WAYLAND_PROTOCOLS_BASE}/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml"
|
||||||
|
"${CMAKE_BINARY_DIR}/wayland/wayland-pointer-constraints-unstable-v1-client-protocol")
|
||||||
|
wayland_generate(
|
||||||
|
"${WAYLAND_PROTOCOLS_BASE}/unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml"
|
||||||
|
"${CMAKE_BINARY_DIR}/wayland/wayland-keyboard-shortcuts-inhibit-unstable-v1-client-protocol")
|
||||||
|
wayland_generate(
|
||||||
|
"${WAYLAND_PROTOCOLS_BASE}/unstable/idle-inhibit/idle-inhibit-unstable-v1.xml"
|
||||||
|
"${CMAKE_BINARY_DIR}/wayland/wayland-idle-inhibit-unstable-v1-client-protocol")
|
||||||
|
wayland_generate(
|
||||||
|
"${WAYLAND_PROTOCOLS_BASE}/unstable/xdg-output/xdg-output-unstable-v1.xml"
|
||||||
|
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-output-unstable-v1-client-protocol")
|
||||||
|
wayland_generate(
|
||||||
|
"${WAYLAND_PROTOCOLS_BASE}/staging/xdg-activation/xdg-activation-v1.xml"
|
||||||
|
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-activation-v1-client-protocol")
|
||||||
|
|
||||||
|
target_link_libraries(wayland_protocol
|
||||||
|
PkgConfig::WAYLAND
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(wayland_protocol
|
||||||
|
PUBLIC
|
||||||
|
"${CMAKE_BINARY_DIR}/wayland"
|
||||||
|
)
|
@ -40,13 +40,6 @@ static void registryGlobalHandler(void * data, struct wl_registry * registry,
|
|||||||
wlWm.compositor = wl_registry_bind(wlWm.registry, name,
|
wlWm.compositor = wl_registry_bind(wlWm.registry, name,
|
||||||
// we only need v3 to run, but v4 can use eglSwapBuffersWithDamageKHR
|
// we only need v3 to run, but v4 can use eglSwapBuffersWithDamageKHR
|
||||||
&wl_compositor_interface, version > 4 ? 4 : version);
|
&wl_compositor_interface, version > 4 ? 4 : version);
|
||||||
#ifndef ENABLE_LIBDECOR
|
|
||||||
else if (!strcmp(interface, xdg_wm_base_interface.name))
|
|
||||||
wlWm.xdgWmBase = wl_registry_bind(wlWm.registry, name, &xdg_wm_base_interface, 1);
|
|
||||||
else if (!strcmp(interface, zxdg_decoration_manager_v1_interface.name))
|
|
||||||
wlWm.xdgDecorationManager = wl_registry_bind(wlWm.registry, name,
|
|
||||||
&zxdg_decoration_manager_v1_interface, 1);
|
|
||||||
#endif
|
|
||||||
else if (!strcmp(interface, wp_presentation_interface.name))
|
else if (!strcmp(interface, wp_presentation_interface.name))
|
||||||
wlWm.presentation = wl_registry_bind(wlWm.registry, name,
|
wlWm.presentation = wl_registry_bind(wlWm.registry, name,
|
||||||
&wp_presentation_interface, 1);
|
&wp_presentation_interface, 1);
|
||||||
@ -75,6 +68,9 @@ static void registryGlobalHandler(void * data, struct wl_registry * registry,
|
|||||||
else if (!strcmp(interface, xdg_activation_v1_interface.name))
|
else if (!strcmp(interface, xdg_activation_v1_interface.name))
|
||||||
wlWm.xdgActivation = wl_registry_bind(wlWm.registry, name,
|
wlWm.xdgActivation = wl_registry_bind(wlWm.registry, name,
|
||||||
&xdg_activation_v1_interface, 1);
|
&xdg_activation_v1_interface, 1);
|
||||||
|
else if (wlWm.desktop->registryGlobalHandler(
|
||||||
|
data, registry, name, interface, version))
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void registryGlobalRemoveHandler(void * data,
|
static void registryGlobalRemoveHandler(void * data,
|
||||||
|
@ -1,178 +0,0 @@
|
|||||||
/**
|
|
||||||
* Looking Glass
|
|
||||||
* Copyright © 2017-2023 The Looking Glass Authors
|
|
||||||
* https://looking-glass.io
|
|
||||||
*
|
|
||||||
* This program 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 2 of the License, or (at your option)
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program 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 this program; if not, write to the Free Software Foundation, Inc., 59
|
|
||||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "wayland.h"
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/epoll.h>
|
|
||||||
|
|
||||||
#include <libdecor.h>
|
|
||||||
#include <wayland-client.h>
|
|
||||||
|
|
||||||
#include "app.h"
|
|
||||||
#include "common/debug.h"
|
|
||||||
|
|
||||||
struct libdecor_configuration {
|
|
||||||
uint32_t serial;
|
|
||||||
|
|
||||||
bool has_window_state;
|
|
||||||
enum libdecor_window_state window_state;
|
|
||||||
|
|
||||||
bool has_size;
|
|
||||||
int window_width;
|
|
||||||
int window_height;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void libdecorHandleError(struct libdecor * context, enum libdecor_error error,
|
|
||||||
const char *message)
|
|
||||||
{
|
|
||||||
DEBUG_ERROR("Got libdecor error (%d): %s", error, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void libdecorFrameConfigure(struct libdecor_frame * frame,
|
|
||||||
struct libdecor_configuration * configuration, void * opaque)
|
|
||||||
{
|
|
||||||
if (!wlWm.configured)
|
|
||||||
{
|
|
||||||
xdg_surface_ack_configure(libdecor_frame_get_xdg_surface(frame), configuration->serial);
|
|
||||||
wlWm.configured = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int width, height;
|
|
||||||
if (libdecor_configuration_get_content_size(configuration, frame, &width, &height))
|
|
||||||
{
|
|
||||||
wlWm.width = width;
|
|
||||||
wlWm.height = height;
|
|
||||||
|
|
||||||
struct libdecor_state * state = libdecor_state_new(wlWm.width, wlWm.height);
|
|
||||||
libdecor_frame_commit(wlWm.libdecorFrame, state, NULL);
|
|
||||||
libdecor_state_free(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum libdecor_window_state windowState;
|
|
||||||
if (libdecor_configuration_get_window_state(configuration, &windowState))
|
|
||||||
wlWm.fullscreen = windowState & LIBDECOR_WINDOW_STATE_FULLSCREEN;
|
|
||||||
|
|
||||||
wlWm.needsResize = true;
|
|
||||||
wlWm.resizeSerial = configuration->serial;
|
|
||||||
app_invalidateWindow(true);
|
|
||||||
waylandStopWaitFrame();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void libdecorFrameClose(struct libdecor_frame * frame, void * opaque)
|
|
||||||
{
|
|
||||||
app_handleCloseEvent();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void libdecorFrameCommit(struct libdecor_frame * frame, void * opaque)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma GCC diagnostic push
|
|
||||||
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
|
|
||||||
static struct libdecor_interface libdecorListener = {
|
|
||||||
libdecorHandleError,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct libdecor_frame_interface libdecorFrameListener = {
|
|
||||||
libdecorFrameConfigure,
|
|
||||||
libdecorFrameClose,
|
|
||||||
libdecorFrameCommit,
|
|
||||||
};
|
|
||||||
#pragma GCC diagnostic pop
|
|
||||||
|
|
||||||
static void libdecorCallback(uint32_t events, void * opaque)
|
|
||||||
{
|
|
||||||
libdecor_dispatch(wlWm.libdecor, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool waylandShellInit(const char * title, bool fullscreen, bool maximize, bool borderless, bool resizable)
|
|
||||||
{
|
|
||||||
wlWm.libdecor = libdecor_new(wlWm.display, &libdecorListener);
|
|
||||||
wlWm.libdecorFrame = libdecor_decorate(wlWm.libdecor, wlWm.surface, &libdecorFrameListener, NULL);
|
|
||||||
|
|
||||||
libdecor_frame_set_app_id(wlWm.libdecorFrame, "looking-glass-client");
|
|
||||||
libdecor_frame_set_title(wlWm.libdecorFrame, title);
|
|
||||||
libdecor_frame_map(wlWm.libdecorFrame);
|
|
||||||
|
|
||||||
if (resizable)
|
|
||||||
libdecor_frame_set_capabilities(wlWm.libdecorFrame, LIBDECOR_ACTION_RESIZE);
|
|
||||||
else
|
|
||||||
libdecor_frame_unset_capabilities(wlWm.libdecorFrame, LIBDECOR_ACTION_RESIZE);
|
|
||||||
|
|
||||||
while (!wlWm.configured)
|
|
||||||
libdecor_dispatch(wlWm.libdecor, 0);
|
|
||||||
|
|
||||||
if (!waylandPollRegister(libdecor_get_fd(wlWm.libdecor), libdecorCallback, NULL, EPOLLIN))
|
|
||||||
{
|
|
||||||
DEBUG_ERROR("Failed register display to epoll: %s", strerror(errno));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void waylandShellAckConfigureIfNeeded(void)
|
|
||||||
{
|
|
||||||
if (wlWm.resizeSerial)
|
|
||||||
{
|
|
||||||
xdg_surface_ack_configure(libdecor_frame_get_xdg_surface(wlWm.libdecorFrame), wlWm.resizeSerial);
|
|
||||||
wlWm.resizeSerial = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void waylandSetFullscreen(bool fs)
|
|
||||||
{
|
|
||||||
if (fs)
|
|
||||||
libdecor_frame_set_fullscreen(wlWm.libdecorFrame, NULL);
|
|
||||||
else
|
|
||||||
libdecor_frame_unset_fullscreen(wlWm.libdecorFrame);
|
|
||||||
|
|
||||||
libdecor_frame_set_visibility(wlWm.libdecorFrame, !fs);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool waylandGetFullscreen(void)
|
|
||||||
{
|
|
||||||
return wlWm.fullscreen;
|
|
||||||
}
|
|
||||||
|
|
||||||
void waylandMinimize(void)
|
|
||||||
{
|
|
||||||
libdecor_frame_set_minimized(wlWm.libdecorFrame);
|
|
||||||
}
|
|
||||||
|
|
||||||
void waylandShellResize(int w, int h)
|
|
||||||
{
|
|
||||||
if (!libdecor_frame_is_floating(wlWm.libdecorFrame))
|
|
||||||
return;
|
|
||||||
|
|
||||||
wlWm.width = w;
|
|
||||||
wlWm.height = h;
|
|
||||||
|
|
||||||
struct libdecor_state * state = libdecor_state_new(w, h);
|
|
||||||
libdecor_frame_commit(wlWm.libdecorFrame, state, NULL);
|
|
||||||
libdecor_state_free(state);
|
|
||||||
|
|
||||||
wlWm.needsResize = true;
|
|
||||||
app_invalidateWindow(true);
|
|
||||||
waylandStopWaitFrame();
|
|
||||||
}
|
|
@ -1,184 +0,0 @@
|
|||||||
/**
|
|
||||||
* Looking Glass
|
|
||||||
* Copyright © 2017-2023 The Looking Glass Authors
|
|
||||||
* https://looking-glass.io
|
|
||||||
*
|
|
||||||
* This program 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 2 of the License, or (at your option)
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program 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 this program; if not, write to the Free Software Foundation, Inc., 59
|
|
||||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "wayland.h"
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <wayland-client.h>
|
|
||||||
|
|
||||||
#include "app.h"
|
|
||||||
#include "common/debug.h"
|
|
||||||
|
|
||||||
// XDG WM base listeners.
|
|
||||||
|
|
||||||
static void xdgWmBasePing(void * data, struct xdg_wm_base * xdgWmBase, uint32_t serial)
|
|
||||||
{
|
|
||||||
xdg_wm_base_pong(xdgWmBase, serial);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct xdg_wm_base_listener xdgWmBaseListener = {
|
|
||||||
.ping = xdgWmBasePing,
|
|
||||||
};
|
|
||||||
|
|
||||||
// XDG Surface listeners.
|
|
||||||
|
|
||||||
static void xdgSurfaceConfigure(void * data, struct xdg_surface * xdgSurface,
|
|
||||||
uint32_t serial)
|
|
||||||
{
|
|
||||||
if (wlWm.configured)
|
|
||||||
{
|
|
||||||
wlWm.needsResize = true;
|
|
||||||
wlWm.resizeSerial = serial;
|
|
||||||
app_invalidateWindow(true);
|
|
||||||
waylandStopWaitFrame();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
xdg_surface_ack_configure(xdgSurface, serial);
|
|
||||||
wlWm.configured = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct xdg_surface_listener xdgSurfaceListener = {
|
|
||||||
.configure = xdgSurfaceConfigure,
|
|
||||||
};
|
|
||||||
|
|
||||||
// XDG Toplevel listeners.
|
|
||||||
|
|
||||||
static void xdgToplevelConfigure(void * data, struct xdg_toplevel * xdgToplevel,
|
|
||||||
int32_t width, int32_t height, struct wl_array * states)
|
|
||||||
{
|
|
||||||
wlWm.width = width;
|
|
||||||
wlWm.height = height;
|
|
||||||
wlWm.fullscreen = false;
|
|
||||||
wlWm.floating = true;
|
|
||||||
|
|
||||||
enum xdg_toplevel_state * state;
|
|
||||||
wl_array_for_each(state, states)
|
|
||||||
{
|
|
||||||
switch (*state)
|
|
||||||
{
|
|
||||||
case XDG_TOPLEVEL_STATE_FULLSCREEN:
|
|
||||||
wlWm.fullscreen = true;
|
|
||||||
// fallthrough
|
|
||||||
case XDG_TOPLEVEL_STATE_MAXIMIZED:
|
|
||||||
case XDG_TOPLEVEL_STATE_TILED_LEFT:
|
|
||||||
case XDG_TOPLEVEL_STATE_TILED_RIGHT:
|
|
||||||
case XDG_TOPLEVEL_STATE_TILED_TOP:
|
|
||||||
case XDG_TOPLEVEL_STATE_TILED_BOTTOM:
|
|
||||||
wlWm.floating = false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void xdgToplevelClose(void * data, struct xdg_toplevel * xdgToplevel)
|
|
||||||
{
|
|
||||||
app_handleCloseEvent();
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct xdg_toplevel_listener xdgToplevelListener = {
|
|
||||||
.configure = xdgToplevelConfigure,
|
|
||||||
.close = xdgToplevelClose,
|
|
||||||
};
|
|
||||||
|
|
||||||
bool waylandShellInit(const char * title, bool fullscreen, bool maximize, bool borderless, bool resizable)
|
|
||||||
{
|
|
||||||
if (!wlWm.xdgWmBase)
|
|
||||||
{
|
|
||||||
DEBUG_ERROR("Compositor missing xdg_wm_base, will not proceed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
xdg_wm_base_add_listener(wlWm.xdgWmBase, &xdgWmBaseListener, NULL);
|
|
||||||
|
|
||||||
wlWm.xdgSurface = xdg_wm_base_get_xdg_surface(wlWm.xdgWmBase, wlWm.surface);
|
|
||||||
xdg_surface_add_listener(wlWm.xdgSurface, &xdgSurfaceListener, NULL);
|
|
||||||
|
|
||||||
wlWm.xdgToplevel = xdg_surface_get_toplevel(wlWm.xdgSurface);
|
|
||||||
xdg_toplevel_add_listener(wlWm.xdgToplevel, &xdgToplevelListener, NULL);
|
|
||||||
xdg_toplevel_set_title(wlWm.xdgToplevel, title);
|
|
||||||
xdg_toplevel_set_app_id(wlWm.xdgToplevel, "looking-glass-client");
|
|
||||||
|
|
||||||
if (fullscreen)
|
|
||||||
xdg_toplevel_set_fullscreen(wlWm.xdgToplevel, NULL);
|
|
||||||
|
|
||||||
if (maximize)
|
|
||||||
xdg_toplevel_set_maximized(wlWm.xdgToplevel);
|
|
||||||
|
|
||||||
if (wlWm.xdgDecorationManager)
|
|
||||||
{
|
|
||||||
wlWm.xdgToplevelDecoration = zxdg_decoration_manager_v1_get_toplevel_decoration(
|
|
||||||
wlWm.xdgDecorationManager, wlWm.xdgToplevel);
|
|
||||||
if (wlWm.xdgToplevelDecoration)
|
|
||||||
{
|
|
||||||
zxdg_toplevel_decoration_v1_set_mode(wlWm.xdgToplevelDecoration,
|
|
||||||
borderless ?
|
|
||||||
ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE :
|
|
||||||
ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void waylandShellAckConfigureIfNeeded(void)
|
|
||||||
{
|
|
||||||
if (wlWm.resizeSerial)
|
|
||||||
{
|
|
||||||
xdg_surface_ack_configure(wlWm.xdgSurface, wlWm.resizeSerial);
|
|
||||||
wlWm.resizeSerial = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void waylandSetFullscreen(bool fs)
|
|
||||||
{
|
|
||||||
if (fs)
|
|
||||||
xdg_toplevel_set_fullscreen(wlWm.xdgToplevel, NULL);
|
|
||||||
else
|
|
||||||
xdg_toplevel_unset_fullscreen(wlWm.xdgToplevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool waylandGetFullscreen(void)
|
|
||||||
{
|
|
||||||
return wlWm.fullscreen;
|
|
||||||
}
|
|
||||||
|
|
||||||
void waylandMinimize(void)
|
|
||||||
{
|
|
||||||
xdg_toplevel_set_minimized(wlWm.xdgToplevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
void waylandShellResize(int w, int h)
|
|
||||||
{
|
|
||||||
if (!wlWm.floating)
|
|
||||||
return;
|
|
||||||
|
|
||||||
wlWm.width = w;
|
|
||||||
wlWm.height = h;
|
|
||||||
xdg_surface_set_window_geometry(wlWm.xdgSurface, 0, 0, w, h);
|
|
||||||
|
|
||||||
wlWm.needsResize = true;
|
|
||||||
app_invalidateWindow(true);
|
|
||||||
waylandStopWaitFrame();
|
|
||||||
}
|
|
@ -24,10 +24,13 @@
|
|||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
#include "common/debug.h"
|
#include "common/debug.h"
|
||||||
#include "common/option.h"
|
#include "common/option.h"
|
||||||
|
|
||||||
|
#include "dynamic/wayland_desktops.h"
|
||||||
|
|
||||||
static struct Option waylandOptions[] =
|
static struct Option waylandOptions[] =
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
@ -68,22 +71,66 @@ static bool waylandProbe(void)
|
|||||||
return getenv("WAYLAND_DISPLAY") != NULL;
|
return getenv("WAYLAND_DISPLAY") != NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool getCompositor(char * dst, size_t size)
|
||||||
|
{
|
||||||
|
int fd = wl_display_get_fd(wlWm.display);
|
||||||
|
struct ucred ucred;
|
||||||
|
socklen_t len = sizeof(struct ucred);
|
||||||
|
|
||||||
|
if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed to get the pid of the socket");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char path[64];
|
||||||
|
snprintf(path, sizeof(path), "/proc/%d/comm", ucred.pid);
|
||||||
|
FILE *fp = fopen(path, "r");
|
||||||
|
if (!fp)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed to open %s", path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fgets(dst, size, fp))
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed to read %s", path);
|
||||||
|
fclose(fp);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
dst[strlen(dst) - 1] = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static bool waylandInit(const LG_DSInitParams params)
|
static bool waylandInit(const LG_DSInitParams params)
|
||||||
{
|
{
|
||||||
memset(&wlWm, 0, sizeof(wlWm));
|
memset(&wlWm, 0, sizeof(wlWm));
|
||||||
|
wlWm.desktop = WL_Desktops[0];
|
||||||
|
|
||||||
wlWm.display = wl_display_connect(NULL);
|
wlWm.display = wl_display_connect(NULL);
|
||||||
if (!wlWm.display)
|
if (!wlWm.display)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
// select the desktop interface based on the compositor process name
|
||||||
|
char compositor[1024];
|
||||||
|
if (getCompositor(compositor, sizeof(compositor)))
|
||||||
|
{
|
||||||
|
for(int i = 0; i < WL_DESKTOP_COUNT; ++i)
|
||||||
|
if (strcmp(WL_Desktops[i]->compositor, compositor) == 0)
|
||||||
|
{
|
||||||
|
wlWm.desktop = WL_Desktops[0];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DEBUG_INFO("Using %s", wlWm.desktop->name);
|
||||||
|
|
||||||
wl_list_init(&wlWm.surfaceOutputs);
|
wl_list_init(&wlWm.surfaceOutputs);
|
||||||
|
|
||||||
wlWm.warpSupport = option_get_bool("wayland", "warpSupport");
|
wlWm.warpSupport = option_get_bool("wayland", "warpSupport");
|
||||||
wlWm.useFractionalScale = option_get_bool("wayland", "fractionScale");
|
wlWm.useFractionalScale = option_get_bool("wayland", "fractionScale");
|
||||||
|
|
||||||
wlWm.width = params.w;
|
|
||||||
wlWm.height = params.h;
|
|
||||||
|
|
||||||
if (!waylandPollInit())
|
if (!waylandPollInit())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -108,7 +155,9 @@ static bool waylandInit(const LG_DSInitParams params)
|
|||||||
if (!waylandInputInit())
|
if (!waylandInputInit())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!waylandWindowInit(params.title, params.fullscreen, params.maximize, params.borderless, params.resizable))
|
wlWm.desktop->setSize(params.w, params.h);
|
||||||
|
if (!waylandWindowInit(params.title, params.fullscreen, params.maximize,
|
||||||
|
params.borderless, params.resizable))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!waylandEGLInit(params.w, params.h))
|
if (!waylandEGLInit(params.w, params.h))
|
||||||
@ -119,9 +168,6 @@ static bool waylandInit(const LG_DSInitParams params)
|
|||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
wlWm.width = params.w;
|
|
||||||
wlWm.height = params.h;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,6 +202,28 @@ static bool waylandGetProp(LG_DSProperty prop, void * ret)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void waylandNeedsResize(void)
|
||||||
|
{
|
||||||
|
wlWm.needsResize = true;
|
||||||
|
app_invalidateWindow(true);
|
||||||
|
waylandStopWaitFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void waylandSetFullscreen(bool fs)
|
||||||
|
{
|
||||||
|
wlWm.desktop->setFullscreen(fs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool waylandGetFullscreen(void)
|
||||||
|
{
|
||||||
|
return wlWm.desktop->getFullscreen();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void waylandMinimize(void)
|
||||||
|
{
|
||||||
|
wlWm.desktop->minimize();
|
||||||
|
}
|
||||||
|
|
||||||
struct LG_DisplayServerOps LGDS_Wayland =
|
struct LG_DisplayServerOps LGDS_Wayland =
|
||||||
{
|
{
|
||||||
.name = "Wayland",
|
.name = "Wayland",
|
||||||
|
@ -37,11 +37,10 @@
|
|||||||
#include "common/countedbuffer.h"
|
#include "common/countedbuffer.h"
|
||||||
#include "common/ringbuffer.h"
|
#include "common/ringbuffer.h"
|
||||||
#include "interface/displayserver.h"
|
#include "interface/displayserver.h"
|
||||||
|
#include "interface/desktop.h"
|
||||||
|
|
||||||
#include "wayland-xdg-shell-client-protocol.h"
|
|
||||||
#include "wayland-presentation-time-client-protocol.h"
|
#include "wayland-presentation-time-client-protocol.h"
|
||||||
#include "wayland-viewporter-client-protocol.h"
|
#include "wayland-viewporter-client-protocol.h"
|
||||||
#include "wayland-xdg-decoration-unstable-v1-client-protocol.h"
|
|
||||||
#include "wayland-keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h"
|
#include "wayland-keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h"
|
||||||
#include "wayland-pointer-constraints-unstable-v1-client-protocol.h"
|
#include "wayland-pointer-constraints-unstable-v1-client-protocol.h"
|
||||||
#include "wayland-relative-pointer-unstable-v1-client-protocol.h"
|
#include "wayland-relative-pointer-unstable-v1-client-protocol.h"
|
||||||
@ -100,6 +99,8 @@ struct WaylandDSState
|
|||||||
bool pointerInSurface;
|
bool pointerInSurface;
|
||||||
bool focusedOnSurface;
|
bool focusedOnSurface;
|
||||||
|
|
||||||
|
WL_DesktopOps * desktop;
|
||||||
|
|
||||||
struct wl_display * display;
|
struct wl_display * display;
|
||||||
struct wl_surface * surface;
|
struct wl_surface * surface;
|
||||||
struct wl_registry * registry;
|
struct wl_registry * registry;
|
||||||
@ -107,13 +108,9 @@ struct WaylandDSState
|
|||||||
struct wl_shm * shm;
|
struct wl_shm * shm;
|
||||||
struct wl_compositor * compositor;
|
struct wl_compositor * compositor;
|
||||||
|
|
||||||
int32_t width, height;
|
|
||||||
wl_fixed_t scale;
|
wl_fixed_t scale;
|
||||||
bool fractionalScale;
|
bool fractionalScale;
|
||||||
bool needsResize;
|
bool needsResize;
|
||||||
bool fullscreen;
|
|
||||||
bool floating;
|
|
||||||
uint32_t resizeSerial;
|
|
||||||
bool configured;
|
bool configured;
|
||||||
bool warpSupport;
|
bool warpSupport;
|
||||||
double cursorX, cursorY;
|
double cursorX, cursorY;
|
||||||
@ -134,17 +131,6 @@ struct WaylandDSState
|
|||||||
RingBuffer photonTimings;
|
RingBuffer photonTimings;
|
||||||
GraphHandle photonGraph;
|
GraphHandle photonGraph;
|
||||||
|
|
||||||
#ifdef ENABLE_LIBDECOR
|
|
||||||
struct libdecor * libdecor;
|
|
||||||
struct libdecor_frame * libdecorFrame;
|
|
||||||
#else
|
|
||||||
struct xdg_wm_base * xdgWmBase;
|
|
||||||
struct xdg_surface * xdgSurface;
|
|
||||||
struct xdg_toplevel * xdgToplevel;
|
|
||||||
struct zxdg_decoration_manager_v1 * xdgDecorationManager;
|
|
||||||
struct zxdg_toplevel_decoration_v1 * xdgToplevelDecoration;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const char * cursorThemeName;
|
const char * cursorThemeName;
|
||||||
int cursorSize;
|
int cursorSize;
|
||||||
int cursorScale;
|
int cursorScale;
|
||||||
@ -314,14 +300,6 @@ void waylandPresentationFree(void);
|
|||||||
bool waylandRegistryInit(void);
|
bool waylandRegistryInit(void);
|
||||||
void waylandRegistryFree(void);
|
void waylandRegistryFree(void);
|
||||||
|
|
||||||
// shell module
|
|
||||||
bool waylandShellInit(const char * title, bool fullscreen, bool maximize, bool borderless, bool resizable);
|
|
||||||
void waylandShellAckConfigureIfNeeded(void);
|
|
||||||
void waylandSetFullscreen(bool fs);
|
|
||||||
bool waylandGetFullscreen(void);
|
|
||||||
void waylandMinimize(void);
|
|
||||||
void waylandShellResize(int w, int h);
|
|
||||||
|
|
||||||
// window module
|
// window module
|
||||||
bool waylandWindowInit(const char * title, bool fullscreen, bool maximize, bool borderless, bool resizable);
|
bool waylandWindowInit(const char * title, bool fullscreen, bool maximize, bool borderless, bool resizable);
|
||||||
void waylandWindowFree(void);
|
void waylandWindowFree(void);
|
||||||
@ -331,3 +309,4 @@ bool waylandIsValidPointerPos(int x, int y);
|
|||||||
bool waylandWaitFrame(void);
|
bool waylandWaitFrame(void);
|
||||||
void waylandSkipFrame(void);
|
void waylandSkipFrame(void);
|
||||||
void waylandStopWaitFrame(void);
|
void waylandStopWaitFrame(void);
|
||||||
|
void waylandNeedsResize(void);
|
||||||
|
@ -112,7 +112,8 @@ bool waylandWindowInit(const char * title, bool fullscreen, bool maximize, bool
|
|||||||
|
|
||||||
wl_surface_add_listener(wlWm.surface, &wlSurfaceListener, NULL);
|
wl_surface_add_listener(wlWm.surface, &wlSurfaceListener, NULL);
|
||||||
|
|
||||||
if (!waylandShellInit(title, fullscreen, maximize, borderless, resizable))
|
if (!wlWm.desktop->shellInit(wlWm.display, wlWm.surface,
|
||||||
|
title, fullscreen, maximize, borderless, resizable))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
wl_surface_commit(wlWm.surface);
|
wl_surface_commit(wlWm.surface);
|
||||||
@ -127,12 +128,14 @@ void waylandWindowFree(void)
|
|||||||
|
|
||||||
void waylandSetWindowSize(int x, int y)
|
void waylandSetWindowSize(int x, int y)
|
||||||
{
|
{
|
||||||
waylandShellResize(x, y);
|
wlWm.desktop->shellResize(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool waylandIsValidPointerPos(int x, int y)
|
bool waylandIsValidPointerPos(int x, int y)
|
||||||
{
|
{
|
||||||
return x >= 0 && x < wlWm.width && y >= 0 && y < wlWm.height;
|
int width, height;
|
||||||
|
wlWm.desktop->getSize(&width, &height);
|
||||||
|
return x >= 0 && x < width && y >= 0 && y < height;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void frameHandler(void * opaque, struct wl_callback * callback, unsigned int data)
|
static void frameHandler(void * opaque, struct wl_callback * callback, unsigned int data)
|
||||||
|
Loading…
Reference in New Issue
Block a user