Integrate UWAC in to the FreeRDP source tree

So the wayland client can still be built without installing UWAC as a dependency.
This commit is contained in:
David FORT 2016-01-18 15:55:33 +01:00
parent bc31c00865
commit 87d6caa69a
22 changed files with 4550 additions and 29 deletions

View File

@ -789,6 +789,10 @@ include_directories("${CMAKE_BINARY_DIR}/rdtk/include")
add_subdirectory(rdtk)
if(WAYLAND_FOUND)
add_subdirectory(uwac)
endif()
if(BSD)
if(IS_DIRECTORY /usr/local/include)
include_directories(/usr/local/include)

View File

@ -19,7 +19,7 @@
set(MODULE_NAME "wlfreerdp")
set(MODULE_PREFIX "FREERDP_CLIENT_WAYLAND")
include_directories(${UWAC_INCLUDE_DIRS})
include_directories(${CMAKE_SOURCE_DIR}/uwac/include)
set(${MODULE_PREFIX}_SRCS
wlfreerdp.c
@ -31,7 +31,7 @@ set(${MODULE_PREFIX}_SRCS
add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${CMAKE_DL_LIBS})
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${UWAC_LIBS} freerdp-client freerdp)
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} freerdp-client freerdp uwac)
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
install(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT client)

View File

@ -1,10 +1,12 @@
# - Finds UWAC
# Find the UWAC libraries and its dependencies
# - Finds Wayland
# Find the Wayland libraries that are needed for UWAC
#
# This module defines the following variables:
# UWAC_FOUND - true if UWAC has been found
# UWAC_LIBS - Set to the full path to UWAC libraries and its dependencies
# UWAC_INCLUDE_DIRS - Set to the include directories for UWAC
# WAYLAND_FOUND - true if UWAC has been found
# WAYLAND_LIBS - Set to the full path to wayland client libraries
# WAYLAND_INCLUDE_DIR - Set to the include directories for wayland
# XKBCOMMON_LIBS - Set to the full path to xkbcommon libraries
# XKBCOMMON_INCLUDE_DIR - Set to the include directories for xkbcommon
#
#=============================================================================
@ -26,28 +28,32 @@
include(FindPkgConfig)
if(PKG_CONFIG_FOUND)
pkg_check_modules(UWAC uwac)
# find the complete path to each dependant library
set(UWAC_LIBS, "")
foreach(libname ${UWAC_LIBRARIES})
find_library(FOUND_LIB NAMES ${libname}
PATHS ${UWAC_LIBRARY_DIRS}
)
if (FOUND_LIB)
list(APPEND UWAC_LIBS ${FOUND_LIB})
endif()
unset(FOUND_LIB CACHE)
endforeach()
find_path(UWAC_INCLUDE_DIR NAMES uwac/uwac.h
PATHS ${UWAC_INCLUDE_DIRS}
DOC "The UWAC include directory"
)
pkg_check_modules(WAYLAND_SCANNER_PKG wayland-scanner)
pkg_check_modules(WAYLAND_CLIENT wayland-client)
pkg_check_modules(XKBCOMMON xkbcommon)
endif()
find_program(WAYLAND_SCANNER wayland-scanner
HINTS "${WAYLAND_SCANNER_PREFIX}/bin"
)
find_path(WAYLAND_INCLUDE_DIR wayland-client.h
HINTS ${WAYLAND_CLIENT_INCLUDE_DIRS}
)
find_library(WAYLAND_LIBS
NAMES "wayland-client"
HINTS "${WAYLAND_CLIENT_LIBRARIES}"
)
find_path(XKBCOMMON_INCLUDE_DIR xkbcommon/xkbcommon.h
HINTS ${XKBCOMMON_INCLUDE_DIRS}
)
find_library(XKBCOMMON_LIBS
NAMES xkbcommon
HINTS "${XKBCOMMON_LIBRARIES}"
)
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND DEFAULT_MSG UWAC_LIBS UWAC_INCLUDE_DIR)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND DEFAULT_MSG WAYLAND_SCANNER WAYLAND_INCLUDE_DIR WAYLAND_LIBS XKBCOMMON_INCLUDE_DIR XKBCOMMON_LIBS)

20
uwac/CMakeLists.txt Normal file
View File

@ -0,0 +1,20 @@
# UWAC: Using Wayland As Client
# cmake build script
#
# Copyright 2015 David FORT <contact@hardening-consulting.com>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
add_subdirectory(include)
add_subdirectory(libuwac)

View File

@ -0,0 +1,20 @@
# UWAC: Using Wayland As Client
# cmake build script
#
# Copyright 2015 David FORT <contact@hardening-consulting.com>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
file(GLOB UWAC_HEADERS "uwac/*.h")
install(FILES ${UWAC_HEADERS} DESTINATION include/uwac COMPONENT headers)

View File

@ -0,0 +1,60 @@
/*
* Copyright © 2015 David FORT <contact@hardening-consulting.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting documentation, and
* that the name of the copyright holders not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no representations
* about the suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*/
#ifndef __UWAC_TOOLS_H_
#define __UWAC_TOOLS_H_
#include <stdbool.h>
#include <uwac/uwac.h>
/** @brief */
struct uwac_touch_point {
uint32_t id;
wl_fixed_t x, y;
};
typedef struct uwac_touch_point UwacTouchPoint;
struct uwac_touch_automata;
typedef struct uwac_touch_automata UwacTouchAutomata;
/**
*
* @param automata
*/
void UwacTouchAutomataInit(UwacTouchAutomata *automata);
/**
*
* @param automata
*/
void UwacTouchAutomataReset(UwacTouchAutomata *automata);
/**
*
* @param automata
* @param event
* @return
*/
bool UwacTouchAutomataInjectEvent(UwacTouchAutomata *automata, UwacEvent *event);
#endif /* __UWAC_TOOLS_H_ */

480
uwac/include/uwac/uwac.h Normal file
View File

@ -0,0 +1,480 @@
/*
* Copyright © 2014-2015 David FORT <contact@hardening-consulting.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting documentation, and
* that the name of the copyright holders not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no representations
* about the suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*/
#ifndef __UWAC_H_
#define __UWAC_H_
#include <wayland-client.h>
#include <stdbool.h>
typedef struct uwac_size UwacSize;
typedef struct uwac_display UwacDisplay;
typedef struct uwac_output UwacOutput;
typedef struct uwac_window UwacWindow;
typedef struct uwac_seat UwacSeat;
/** @brief error codes */
typedef enum {
UWAC_SUCCESS = 0,
UWAC_ERROR_NOMEMORY,
UWAC_ERROR_UNABLE_TO_CONNECT,
UWAC_ERROR_INVALID_DISPLAY,
UWAC_NOT_ENOUGH_RESOURCES,
UWAC_TIMEDOUT,
UWAC_NOT_FOUND,
UWAC_ERROR_CLOSED,
UWAC_ERROR_INTERNAL,
UWAC_ERROR_LAST,
} UwacReturnCode;
/** @brief input modifiers */
enum {
UWAC_MOD_SHIFT_MASK = 0x01,
UWAC_MOD_ALT_MASK = 0x02,
UWAC_MOD_CONTROL_MASK = 0x04,
};
/** @brief a rectangle size measure */
struct uwac_size {
int width;
int height;
};
/** @brief event types */
enum {
UWAC_EVENT_NEW_SEAT = 0,
UWAC_EVENT_REMOVED_SEAT,
UWAC_EVENT_NEW_OUTPUT,
UWAC_EVENT_CONFIGURE,
UWAC_EVENT_POINTER_ENTER,
UWAC_EVENT_POINTER_LEAVE,
UWAC_EVENT_POINTER_MOTION,
UWAC_EVENT_POINTER_BUTTONS,
UWAC_EVENT_POINTER_AXIS,
UWAC_EVENT_KEYBOARD_ENTER,
UWAC_EVENT_KEY,
UWAC_EVENT_TOUCH_FRAME_BEGIN,
UWAC_EVENT_TOUCH_UP,
UWAC_EVENT_TOUCH_DOWN,
UWAC_EVENT_TOUCH_MOTION,
UWAC_EVENT_TOUCH_CANCEL,
UWAC_EVENT_TOUCH_FRAME_END,
UWAC_EVENT_FRAME_DONE,
UWAC_EVENT_CLOSE,
};
/** @brief window states */
enum {
UWAC_WINDOW_MAXIMIZED = 0x1,
UWAC_WINDOW_RESIZING = 0x2,
UWAC_WINDOW_FULLSCREEN = 0x4,
UWAC_WINDOW_ACTIVATED = 0x8,
};
struct uwac_new_output_event {
int type;
UwacOutput *output;
};
typedef struct uwac_new_output_event UwacOutputNewEvent;
struct uwac_new_seat_event {
int type;
UwacSeat *seat;
};
typedef struct uwac_new_seat_event UwacSeatNewEvent;
typedef struct uwac_new_seat_event UwacSeatRemovedEvent;
struct uwac_keyboard_enter_event {
int type;
UwacWindow *window;
UwacSeat *seat;
};
typedef struct uwac_keyboard_enter_event UwacKeyboardEnterLeaveEvent;
struct uwac_pointer_enter_event {
int type;
UwacWindow *window;
UwacSeat *seat;
uint32_t x, y;
};
typedef struct uwac_pointer_enter_event UwacPointerEnterLeaveEvent;
struct uwac_pointer_motion_event {
int type;
UwacWindow *window;
UwacSeat *seat;
uint32_t x, y;
};
typedef struct uwac_pointer_motion_event UwacPointerMotionEvent;
struct uwac_pointer_button_event {
int type;
UwacWindow *window;
UwacSeat *seat;
uint32_t x, y;
uint32_t button;
enum wl_pointer_button_state state;
};
typedef struct uwac_pointer_button_event UwacPointerButtonEvent;
struct uwac_pointer_axis_event {
int type;
UwacWindow *window;
UwacSeat *seat;
uint32_t x, y;
uint32_t axis;
wl_fixed_t value;
};
typedef struct uwac_pointer_axis_event UwacPointerAxisEvent;
struct uwac_touch_frame_event {
int type;
UwacWindow *window;
UwacSeat *seat;
};
typedef struct uwac_touch_frame_event UwacTouchFrameBegin;
typedef struct uwac_touch_frame_event UwacTouchFrameEnd;
typedef struct uwac_touch_frame_event UwacTouchCancel;
struct uwac_touch_data {
int type;
UwacWindow *window;
UwacSeat *seat;
int32_t id;
wl_fixed_t x;
wl_fixed_t y;
};
typedef struct uwac_touch_data UwacTouchUp;
typedef struct uwac_touch_data UwacTouchDown;
typedef struct uwac_touch_data UwacTouchMotion;
struct uwac_frame_done_event {
int type;
UwacWindow *window;
};
typedef struct uwac_frame_done_event UwacFrameDoneEvent;
struct uwac_configure_event {
int type;
UwacWindow *window;
int32_t width;
int32_t height;
int states;
};
typedef struct uwac_configure_event UwacConfigureEvent;
struct uwac_key_event {
int type;
UwacWindow *window;
uint32_t raw_key;
uint32_t sym;
bool pressed;
};
typedef struct uwac_key_event UwacKeyEvent;
struct uwac_close_event {
int type;
UwacWindow *window;
};
typedef struct uwac_close_event UwacCloseEvent;
/** @brief */
union uwac_event {
int type;
UwacOutputNewEvent output_new;
UwacSeatNewEvent seat_new;
UwacPointerEnterLeaveEvent mouse_enter_leave;
UwacPointerMotionEvent mouse_motion;
UwacPointerButtonEvent mouse_button;
UwacPointerAxisEvent mouse_axis;
UwacKeyboardEnterLeaveEvent keyboard_enter_leave;
UwacKeyEvent key;
UwacTouchFrameBegin touchFrameBegin;
UwacTouchUp touchUp;
UwacTouchDown touchDown;
UwacTouchMotion touchMotion;
UwacTouchFrameEnd touchFrameEnd;
UwacTouchCancel touchCancel;
UwacFrameDoneEvent frame_done;
UwacConfigureEvent configure;
UwacCloseEvent close;
};
typedef union uwac_event UwacEvent;
typedef bool (*UwacErrorHandler)(UwacDisplay *d, UwacReturnCode code, const char *msg, ...);
#ifdef __cplusplus
extern "C" {
#endif
/**
* install a handler that will be called when UWAC encounter internal errors. The
* handler is supposed to answer if the execution can continue. I can also be used
* to log things.
*
* @param handler
*/
void UwacInstallErrorHandler(UwacErrorHandler handler);
/**
* Opens the corresponding wayland display, using NULL you will open the default
* display.
*
* @param name the name of the display to open
* @return the created UwacDisplay object
*/
UwacDisplay *UwacOpenDisplay(const char *name, UwacReturnCode *err);
/**
* closes the corresponding UwacDisplay
*
* @param pdisplay a pointer on the display to close
* @return UWAC_SUCCESS if the operation was successful, the corresponding error otherwise
*/
UwacReturnCode UwacCloseDisplay(UwacDisplay **pdisplay);
/**
* Returns the file descriptor associated with the UwacDisplay, this is useful when
* you want to poll that file descriptor for activity.
*
* @param display an opened UwacDisplay
* @return the corresponding descriptor
*/
int UwacDisplayGetFd(UwacDisplay *display);
/**
* Returns a human readable form of a Uwac error code
*
* @param error the error number
* @return the associated string
*/
const char *UwacErrorString(UwacReturnCode error);
/**
* returns the last error that occurred on a display
*
* @param display the display
* @return the last error that have been set for this display
*/
UwacReturnCode UwacDisplayGetLastError(const UwacDisplay *display);
/**
* retrieves the version of a given interface
*
* @param display the display connection
* @param name the name of the interface
* @param version the output variable for the version
* @return UWAC_SUCCESS if the interface was found, UWAC_NOT_FOUND otherwise
*/
UwacReturnCode UwacDisplayQueryInterfaceVersion(const UwacDisplay *display, const char *name, uint32_t *version);
/**
* returns the number SHM formats that have been reported by the compositor
*
* @param display a connected UwacDisplay
* @return the number of SHM formats supported
*/
uint32_t UwacDisplayQueryGetNbShmFormats(UwacDisplay *display);
/**
* returns the supported ShmFormats
*
* @param display a connected UwacDisplay
* @param formats a pointer on an array of wl_shm_format with enough place for formats_size items
* @param formats_size the size of the formats array
* @param filled the number of filled entries in the formats array
* @return UWAC_SUCCESS on success, an error otherwise
*/
UwacReturnCode UwacDisplayQueryShmFormats(const UwacDisplay *display, enum wl_shm_format *formats, int formats_size, int *filled);
/**
* returns the number of registered outputs
*
* @param display the display to query
* @return the number of outputs
*/
uint32_t UwacDisplayGetNbOutputs(UwacDisplay *display);
/**
* retrieve a particular UwacOutput object
*
* @param display the display to query
* @param index index of the output
* @return the given UwacOutput, NULL if something failed (so you should query UwacDisplayGetLastError() to have the reason)
*/
UwacOutput *UwacDisplayGetOutput(UwacDisplay *display, int index);
/**
* retrieve the resolution of a given UwacOutput
*
* @param output the UwacOutput
* @param resolution a pointer on the
* @return UWAC_SUCCESS on success
*/
UwacReturnCode UwacOutputGetResolution(UwacOutput *output, UwacSize *resolution);
/**
* creates a window using a SHM surface
*
* @param display the display to attach the window to
* @param width the width of the window
* @param height the heigh of the window
* @param format format to use for the SHM surface
* @return the created UwacWindow, NULL if something failed (use UwacDisplayGetLastError() to know more about this)
*/
UwacWindow *UwacCreateWindowShm(UwacDisplay *display, uint32_t width, uint32_t height, enum wl_shm_format format);
/**
* destroys the corresponding UwacWindow
*
* @param window a pointer on the UwacWindow to destroy
* @return if the operation completed successfully
*/
UwacReturnCode UwacDestroyWindow(UwacWindow **window);
/**
* Sets the region that should be considered opaque to the compositor.
*
* @param window the UwacWindow
* @param x
* @param y
* @param width
* @param height
* @return UWAC_SUCCESS on success, an error otherwise
*/
UwacReturnCode UwacWindowSetOpaqueRegion(UwacWindow *window, uint32_t x, uint32_t y, uint32_t width, uint32_t height);
/**
* Sets the region of the window that can trigger input events
*
* @param window the UwacWindow
* @param x
* @param y
* @param width
* @param height
* @return
*/
UwacReturnCode UwacWindowSetInputRegion(UwacWindow *window, uint32_t x, uint32_t y, uint32_t width, uint32_t height);
/**
* retrieves a pointer on the current window content to draw a frame
* @param window the UwacWindow
* @return a pointer on the current window content
*/
void *UwacWindowGetDrawingBuffer(UwacWindow *window);
/**
* sets a rectangle as dirty for the next frame of a window
*
* @param window the UwacWindow
* @param x left coordinate
* @param y top coordinate
* @param width the width of the dirty rectangle
* @param height the height of the dirty rectangle
* @return UWAC_SUCCESS on success, an Uwac error otherwise
*/
UwacReturnCode UwacWindowAddDamage(UwacWindow *window, uint32_t x, uint32_t y, uint32_t width, uint32_t height);
/**
* Sends a frame to the compositor with the content of the drawing buffer
*
* @param window the UwacWindow to refresh
* @param copyContentForNextFrame if true the content to display is copied in the next drawing buffer
* @return UWAC_SUCCESS if the operation was successful
*/
UwacReturnCode UwacWindowSubmitBuffer(UwacWindow *window, bool copyContentForNextFrame);
/**
* returns the geometry of the given UwacWindows
*
* @param window the UwacWindow
* @param geometry the geometry to fill
* @return UWAC_SUCCESS on success, an Uwac error otherwise
*/
UwacReturnCode UwacWindowGetGeometry(UwacWindow *window, UwacSize *geometry);
/**
* Sets or unset the fact that the window is set fullscreen. After this call the
* application should get prepared to receive a configure event. The output is used
* only when going fullscreen, it is optional and not used when exiting fullscreen.
*
* @param window the UwacWindow
* @param output an optional UwacOutput to put the window fullscreen on
* @param isFullscreen set or unset fullscreen
* @return UWAC_SUCCESS if the operation was a success
*/
UwacReturnCode UwacWindowSetFullscreenState(UwacWindow *window, UwacOutput *output, bool isFullscreen);
/**
* When possible (depending on the shell) sets the title of the UwacWindow
*
* @param window the UwacWindow
* @param name title
*/
void UwacWindowSetTitle(UwacWindow *window, const char *name);
/**
*
* @param display
* @param timeout
* @return
*/
int UwacDisplayDispatch(UwacDisplay *display, int timeout);
/**
* Returns if you have some pending events, and you can UwacNextEvent() without blocking
*
* @param display the UwacDisplay
* @return if there's some pending events
*/
bool UwacHasEvent(UwacDisplay *display);
/** Waits until an event occurs, and when it's there copy the event from the queue to
* event.
*
* @param display the Uwac display
* @param event the event to fill
* @return if the operation completed successfully
*/
UwacReturnCode UwacNextEvent(UwacDisplay *display, UwacEvent *event);
/**
* returns the name of the given UwacSeat
*
* @param seat the UwacSeat
* @return the name of the seat
*/
const char *UwacSeatGetName(const UwacSeat *seat);
#ifdef __cplusplus
}
#endif
#endif /* __UWAC_H_ */

7
uwac/libuwac/.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
fullscreen-shell-client-protocol.h
fullscreen-shell-protocol.c
ivi-application-client-protocol.h
ivi-application-protocol.c
xdg-shell-client-protocol.h
xdg-shell-protocol.c

View File

@ -0,0 +1,79 @@
# UWAC: Using Wayland As Client
#
# Copyright 2015 David FORT <contact@hardening-consulting.com>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set(MODULE_NAME "uwac")
set(MODULE_PREFIX "UWAC")
set(GENERATED_SOURCES "")
macro(generate_protocol_file PROTO)
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${PROTO}-protocol.c"
COMMAND ${WAYLAND_SCANNER} code < ${CMAKE_SOURCE_DIR}/uwac/protocols/${PROTO}.xml > ${CMAKE_CURRENT_BINARY_DIR}/${PROTO}-protocol.c
DEPENDS ${CMAKE_SOURCE_DIR}/uwac/protocols/${PROTO}.xml
)
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${PROTO}-client-protocol.h"
COMMAND ${WAYLAND_SCANNER} client-header < ${CMAKE_SOURCE_DIR}/uwac/protocols/${PROTO}.xml > ${CMAKE_CURRENT_BINARY_DIR}/${PROTO}-client-protocol.h
DEPENDS ${CMAKE_SOURCE_DIR}/uwac/protocols/${PROTO}.xml
)
list(APPEND GENERATED_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/${PROTO}-client-protocol.h)
list(APPEND GENERATED_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/${PROTO}-protocol.c)
endmacro()
generate_protocol_file(xdg-shell)
generate_protocol_file(ivi-application)
generate_protocol_file(fullscreen-shell)
include_directories(${WAYLAND_INCLUDE_DIR})
include_directories(${XKBCOMMON_INCLUDE_DIR})
include_directories("${CMAKE_SOURCE_DIR}/uwac/include")
include_directories("${CMAKE_BINARY_DIR}/uwac/include")
add_definitions(-DBUILD_IVI -DBUILD_FULLSCREEN_SHELL -DENABLE_XKBCOMMON)
set(${MODULE_PREFIX}_SRCS
${GENERATED_SOURCES}
uwac-display.c
uwac-input.c
uwac-os.c
uwac-os.h
uwac-output.c
uwac-priv.h
uwac-tools.c
uwac-utils.c
uwac-window.c)
add_library(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
if (WITH_LIBRARY_VERSIONING)
#set_target_properties(${MODULE_NAME} PROPERTIES VERSION ${UWAC_VERSION} SOVERSION ${UWAC_API_VERSION})
endif()
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS} ${WAYLAND_LIBS} ${XKBCOMMON_LIBS})
install(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT Uwac)
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "uwac")
if(BUILD_TESTING)
# add_subdirectory(test)
endif()

631
uwac/libuwac/uwac-display.c Normal file
View File

@ -0,0 +1,631 @@
/*
* Copyright © 2014 David FORT <contact@hardening-consulting.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting documentation, and
* that the name of the copyright holders not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no representations
* about the suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*/
#include "uwac-priv.h"
#include "uwac-utils.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <assert.h>
#include <errno.h>
#include <time.h>
#include <unistd.h>
#include <sys/epoll.h>
#include "uwac-os.h"
#define TARGET_COMPOSITOR_INTERFACE 3
#define TARGET_SHM_INTERFACE 1
#define TARGET_SHELL_INTERFACE 1
#define TARGET_DDM_INTERFACE 1
#define TARGET_SEAT_INTERFACE 5
#define TARGET_XDG_VERSION 5 /* The version of xdg-shell that we implement */
static const char *event_names[] = {
"new seat",
"removed seat",
"new output",
"configure",
"pointer enter",
"pointer leave",
"pointer motion",
"pointer buttons",
"pointer axis",
"keyboard enter",
"key",
"touch frame begin",
"touch up",
"touch down",
"touch motion",
"touch cancel",
"touch frame end",
"frame done",
"close",
NULL
};
bool uwac_default_error_handler(UwacDisplay *display, UwacReturnCode code, const char *msg, ...) {
va_list args;
va_start(args, msg);
vfprintf(stderr, "%s", args);
return false;
}
UwacErrorHandler uwacErrorHandler = uwac_default_error_handler;
void UwacInstallErrorHandler(UwacErrorHandler handler) {
if (handler)
uwacErrorHandler = handler;
else
uwacErrorHandler = uwac_default_error_handler;
}
static void cb_shm_format(void *data, struct wl_shm *wl_shm, uint32_t format)
{
UwacDisplay *d = data;
if (format == WL_SHM_FORMAT_RGB565)
d->has_rgb565 = true;
d->shm_formats_nb++;
d->shm_formats = xrealloc((void *)d->shm_formats, sizeof(enum wl_shm_format) * d->shm_formats_nb);
d->shm_formats[d->shm_formats_nb - 1] = format;
}
struct wl_shm_listener shm_listener = {
cb_shm_format
};
static void xdg_shell_ping(void *data, struct xdg_shell *shell, uint32_t serial)
{
xdg_shell_pong(shell, serial);
}
static const struct xdg_shell_listener xdg_shell_listener = {
xdg_shell_ping,
};
#ifdef BUILD_FULLSCREEN_SHELL
static void fullscreen_capability(void *data, struct _wl_fullscreen_shell *_wl_fullscreen_shell,
uint32_t capabilty)
{
}
static const struct _wl_fullscreen_shell_listener fullscreen_shell_listener = {
fullscreen_capability,
};
#endif
static UwacSeat *display_destroy_seat(UwacDisplay *d, uint32_t name)
{
UwacSeat *seat;
wl_list_for_each(seat, &d->seats, link) {
if (seat->seat_id == name) {
UwacSeatDestroy(seat);
return seat;
}
}
return NULL;
}
static void registry_handle_global(void *data, struct wl_registry *registry, uint32_t id,
const char *interface, uint32_t version)
{
UwacDisplay *d = data;
UwacGlobal *global;
global = xmalloc(sizeof *global);
global->name = id;
global->interface = xstrdup(interface);
global->version = version;
wl_list_insert(d->globals.prev, &global->link);
if (strcmp(interface, "wl_compositor") == 0) {
d->compositor = wl_registry_bind(registry, id, &wl_compositor_interface, min(TARGET_COMPOSITOR_INTERFACE, version));
} else if (strcmp(interface, "wl_shm") == 0) {
d->shm = wl_registry_bind(registry, id, &wl_shm_interface, min(TARGET_SHM_INTERFACE, version));
wl_shm_add_listener(d->shm, &shm_listener, d);
} else if (strcmp(interface, "wl_output") == 0) {
UwacOutput *output;
UwacOutputNewEvent *ev;
output = UwacCreateOutput(d, id, version);
if (!output) {
assert(uwacErrorHandler(d, UWAC_ERROR_NOMEMORY, "unable to create output\n"));
return;
}
ev = (UwacOutputNewEvent *)UwacDisplayNewEvent(d, UWAC_EVENT_NEW_OUTPUT);
if (ev)
ev->output = output;
} else if (strcmp(interface, "wl_seat") == 0) {
UwacSeatNewEvent *ev;
UwacSeat *seat;
seat = UwacSeatNew(d, id, min(version, TARGET_SEAT_INTERFACE));
if (!seat) {
assert(uwacErrorHandler(d, UWAC_ERROR_NOMEMORY, "unable to create new seat\n"));
return;
}
ev = (UwacSeatNewEvent *)UwacDisplayNewEvent(d, UWAC_EVENT_NEW_SEAT);
if (!ev) {
assert(uwacErrorHandler(d, UWAC_ERROR_NOMEMORY, "unable to create new seat event\n"));
return;
}
ev->seat = seat;
} else if (strcmp(interface, "wl_data_device_manager") == 0) {
d->data_device_manager = wl_registry_bind(registry, id, &wl_data_device_manager_interface, min(TARGET_DDM_INTERFACE, version));
} else if (strcmp(interface, "wl_shell") == 0) {
d->shell = wl_registry_bind(registry, id, &wl_shell_interface, min(TARGET_SHELL_INTERFACE, version));
} else if (strcmp(interface, "xdg_shell") == 0) {
d->xdg_shell = wl_registry_bind(registry, id, &xdg_shell_interface, 1);
xdg_shell_use_unstable_version(d->xdg_shell, TARGET_XDG_VERSION);
xdg_shell_add_listener(d->xdg_shell, &xdg_shell_listener, d);
#if BUILD_IVI
} else if (strcmp(interface, "ivi_application") == 0) {
d->ivi_application = wl_registry_bind(registry, id, &ivi_application_interface, 1);
#endif
#if BUILD_FULLSCREEN_SHELL
} else if (strcmp(interface, "_wl_fullscreen_shell") == 0) {
d->fullscreen_shell = wl_registry_bind(registry, id, &_wl_fullscreen_shell_interface, 1);
_wl_fullscreen_shell_add_listener(d->fullscreen_shell, &fullscreen_shell_listener, d);
#endif
#if 0
} else if (strcmp(interface, "text_cursor_position") == 0) {
d->text_cursor_position = wl_registry_bind(registry, id, &text_cursor_position_interface, 1);
} else if (strcmp(interface, "workspace_manager") == 0) {
//init_workspace_manager(d, id);
} else if (strcmp(interface, "wl_subcompositor") == 0) {
d->subcompositor = wl_registry_bind(registry, id, &wl_subcompositor_interface, 1);
#endif
}
}
static void registry_handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) {
UwacDisplay *d = data;
UwacGlobal *global;
UwacGlobal *tmp;
wl_list_for_each_safe(global, tmp, &d->globals, link) {
if (global->name != name)
continue;
#if 0
if (strcmp(global->interface, "wl_output") == 0)
display_destroy_output(d, name);
#endif
if (strcmp(global->interface, "wl_seat") == 0) {
UwacSeatRemovedEvent *ev;
UwacSeat *seat;
seat = display_destroy_seat(d, name);
ev = (UwacSeatRemovedEvent *)UwacDisplayNewEvent(d, UWAC_EVENT_REMOVED_SEAT);
if (ev)
ev->seat = seat;
}
wl_list_remove(&global->link);
free(global->interface);
free(global);
}
}
void UwacDestroyGlobal(UwacGlobal *global) {
free(global->interface);
wl_list_remove(&global->link);
free(global);
}
void *display_bind(UwacDisplay *display, uint32_t name, const struct wl_interface *interface, uint32_t version) {
return wl_registry_bind(display->registry, name, interface, version);
}
static const struct wl_registry_listener registry_listener = {
registry_handle_global,
registry_handle_global_remove
};
int UwacDisplayWatchFd(UwacDisplay *display, int fd, uint32_t events, UwacTask *task) {
struct epoll_event ep;
ep.events = events;
ep.data.ptr = task;
return epoll_ctl(display->epoll_fd, EPOLL_CTL_ADD, fd, &ep);
}
void UwacDisplayUnwatchFd(UwacDisplay *display, int fd) {
epoll_ctl(display->epoll_fd, EPOLL_CTL_DEL, fd, NULL);
}
static void display_exit(UwacDisplay *display) {
display->running = false;
}
static void display_dispatch_events(UwacTask *task, uint32_t events)
{
UwacDisplay *display = container_of(task, UwacDisplay, dispatch_fd_task);
struct epoll_event ep;
int ret;
display->display_fd_events = events;
if ((events & EPOLLERR) || (events & EPOLLHUP)) {
display_exit(display);
return;
}
if (events & EPOLLIN) {
ret = wl_display_dispatch(display->display);
if (ret == -1) {
display_exit(display);
return;
}
}
if (events & EPOLLOUT) {
ret = wl_display_flush(display->display);
if (ret == 0) {
ep.events = EPOLLIN | EPOLLERR | EPOLLHUP;
ep.data.ptr = &display->dispatch_fd_task;
epoll_ctl(display->epoll_fd, EPOLL_CTL_MOD, display->display_fd, &ep);
} else if (ret == -1 && errno != EAGAIN) {
display_exit(display);
return;
}
}
}
UwacDisplay *UwacOpenDisplay(const char *name, UwacReturnCode *err) {
UwacDisplay *ret;
ret = (UwacDisplay *)calloc(1, sizeof(*ret));
if (!ret) {
*err = UWAC_ERROR_NOMEMORY;
return NULL;
}
wl_list_init(&ret->globals);
wl_list_init(&ret->seats);
wl_list_init(&ret->outputs);
wl_list_init(&ret->windows);
ret->display = wl_display_connect(name);
if (ret->display == NULL) {
fprintf(stderr, "failed to connect to Wayland display %s: %m\n", name);
*err = UWAC_ERROR_UNABLE_TO_CONNECT;
goto out_free;
}
ret->epoll_fd = uwac_os_epoll_create_cloexec();
if (ret->epoll_fd < 0) {
*err = UWAC_NOT_ENOUGH_RESOURCES;
goto out_disconnect;
}
ret->display_fd = wl_display_get_fd(ret->display);
ret->registry = wl_display_get_registry(ret->display);
if (!ret->registry) {
*err = UWAC_ERROR_NOMEMORY;
goto out_close_epoll;
}
wl_registry_add_listener(ret->registry, &registry_listener, ret);
if ((wl_display_roundtrip(ret->display) < 0) || (wl_display_roundtrip(ret->display) < 0)) {
uwacErrorHandler(ret, UWAC_ERROR_UNABLE_TO_CONNECT, "Failed to process Wayland connection: %m\n");
*err = UWAC_ERROR_UNABLE_TO_CONNECT;
goto out_free_registry;
}
ret->dispatch_fd_task.run = display_dispatch_events;
if (UwacDisplayWatchFd(ret, ret->display_fd, EPOLLIN | EPOLLERR | EPOLLHUP, &ret->dispatch_fd_task) < 0) {
uwacErrorHandler(ret, UWAC_ERROR_INTERNAL, "unable to watch display fd: %m\n");
*err = UWAC_ERROR_INTERNAL;
goto out_free_registry;
}
ret->running = true;
ret->last_error = *err = UWAC_SUCCESS;
return ret;
out_free_registry:
wl_registry_destroy(ret->registry);
out_close_epoll:
close(ret->epoll_fd);
out_disconnect:
wl_display_disconnect(ret->display);
out_free:
free(ret);
return NULL;
}
int UwacDisplayDispatch(UwacDisplay *display, int timeout) {
int ret, count, i;
UwacTask *task;
struct epoll_event ep[16];
wl_display_dispatch_pending(display->display);
if (!display->running)
return 0;
ret = wl_display_flush(display->display);
if (ret < 0 && errno == EAGAIN) {
ep[0].events = (EPOLLIN | EPOLLOUT | EPOLLERR | EPOLLHUP);
ep[0].data.ptr = &display->dispatch_fd_task;
epoll_ctl(display->epoll_fd, EPOLL_CTL_MOD, display->display_fd, &ep[0]);
} else if (ret < 0) {
return -1;
}
count = epoll_wait(display->epoll_fd, ep, ARRAY_LENGTH(ep), timeout);
for (i = 0; i < count; i++) {
task = ep[i].data.ptr;
task->run(task, ep[i].events);
}
return 1;
}
UwacReturnCode UwacDisplayGetLastError(const UwacDisplay *display) {
return display->last_error;
}
UwacReturnCode UwacCloseDisplay(UwacDisplay **pdisplay) {
UwacDisplay *display;
UwacSeat *seat, *tmpSeat;
UwacWindow *window, *tmpWindow;
UwacOutput *output, *tmpOutput;
UwacGlobal *global, *tmpGlobal;
assert(pdisplay);
display = *pdisplay;
if (!display)
return UWAC_ERROR_INVALID_DISPLAY;
/* destroy windows */
wl_list_for_each_safe(window, tmpWindow, &display->windows, link) {
UwacDestroyWindow(&window);
}
/* destroy seats */
wl_list_for_each_safe(seat, tmpSeat, &display->seats, link) {
UwacSeatDestroy(seat);
}
/* destroy output */
wl_list_for_each_safe(output, tmpOutput, &display->outputs, link) {
UwacDestroyOutput(output);
}
/* destroy globals */
wl_list_for_each_safe(global, tmpGlobal, &display->globals, link) {
UwacDestroyGlobal(global);
}
if (display->compositor)
wl_compositor_destroy(display->compositor);
#ifdef BUILD_FULLSCREEN_SHELL
if (display->fullscreen_shell)
_wl_fullscreen_shell_destroy(display->fullscreen_shell);
#endif
#ifdef BUILD_IVI
if (display->ivi_application)
ivi_application_destroy(display->ivi_application);
#endif
if (display->xdg_shell)
xdg_shell_destroy(display->xdg_shell);
if (display->shell)
wl_shell_destroy(display->shell);
if (display->shm)
wl_shm_destroy(display->shm);
if (display->subcompositor)
wl_subcompositor_destroy(display->subcompositor);
if (display->data_device_manager)
wl_data_device_manager_destroy(display->data_device_manager);
free(display->shm_formats);
wl_registry_destroy(display->registry);
close(display->epoll_fd);
wl_display_disconnect(display->display);
/* cleanup the event queue */
while (display->push_queue) {
UwacEventListItem *item = display->push_queue;
display->push_queue = item->tail;
free(item);
}
free(display);
*pdisplay = NULL;
return UWAC_SUCCESS;
}
int UwacDisplayGetFd(UwacDisplay *display) {
return display->epoll_fd;
}
static const char *errorStrings[] = {
"success",
"out of memory error",
"unable to connect to wayland display",
"invalid UWAC display",
"not enough resources",
"timed out",
"not found",
"closed connection",
"internal error",
};
const char *UwacErrorString(UwacReturnCode error) {
if (error < 0 || error >= UWAC_ERROR_LAST)
return "invalid error code";
return errorStrings[error];
}
UwacReturnCode UwacDisplayQueryInterfaceVersion(const UwacDisplay *display, const char *name, uint32_t *version) {
const UwacGlobal *global;
if (!display)
return UWAC_ERROR_INVALID_DISPLAY;
wl_list_for_each(global, &display->globals, link) {
if (strcmp(global->interface, name) == 0) {
if (version)
*version = global->version;
return UWAC_SUCCESS;
}
}
return UWAC_NOT_FOUND;
}
uint32_t UwacDisplayQueryGetNbShmFormats(UwacDisplay *display) {
if (!display) {
display->last_error = UWAC_ERROR_INVALID_DISPLAY;
return 0;
}
if (!display->shm) {
display->last_error = UWAC_NOT_FOUND;
return 0;
}
display->last_error = UWAC_SUCCESS;
return display->shm_formats_nb;
}
UwacReturnCode UwacDisplayQueryShmFormats(const UwacDisplay *display, enum wl_shm_format *formats, int formats_size, int *filled) {
if (!display)
return UWAC_ERROR_INVALID_DISPLAY;
*filled = min(display->shm_formats_nb, formats_size);
memcpy(formats, (const void *)display->shm_formats, *filled * sizeof(enum wl_shm_format));
return UWAC_SUCCESS;
}
uint32_t UwacDisplayGetNbOutputs(UwacDisplay *display) {
return wl_list_length(&display->outputs);
}
UwacOutput *UwacDisplayGetOutput(UwacDisplay *display, int index) {
struct wl_list *l;
int i;
for (i = 0, l = &display->outputs; l && i < index; i++, l = l->next)
;
if (!l) {
display->last_error = UWAC_NOT_FOUND;
return NULL;
}
display->last_error = UWAC_SUCCESS;
return container_of(l, UwacOutput, link);
}
UwacReturnCode UwacOutputGetResolution(UwacOutput *output, UwacSize *resolution) {
*resolution = output->resolution;
return UWAC_SUCCESS;
}
UwacEvent *UwacDisplayNewEvent(UwacDisplay *display, int type) {
UwacEventListItem *ret;
if (!display) {
display->last_error = UWAC_ERROR_INVALID_DISPLAY;
return 0;
}
ret = zalloc(sizeof(UwacEventListItem));
if (!ret) {
assert(uwacErrorHandler(display, UWAC_ERROR_NOMEMORY, "unable to allocate a '%s' event", event_names[type]));
display->last_error = UWAC_ERROR_NOMEMORY;
return 0;
}
ret->event.type = type;
ret->tail = display->push_queue;
if (ret->tail)
ret->tail->head = ret;
else
display->pop_queue = ret;
display->push_queue = ret;
return &ret->event;
}
bool UwacHasEvent(UwacDisplay *display) {
return display->pop_queue != NULL;
}
UwacReturnCode UwacNextEvent(UwacDisplay *display, UwacEvent *event) {
UwacEventListItem *prevItem;
int ret;
if (!display)
return UWAC_ERROR_INVALID_DISPLAY;
while (!display->pop_queue) {
ret = UwacDisplayDispatch(display, 1 * 1000);
if (ret < 0)
return UWAC_ERROR_INTERNAL;
else if (ret == 0)
return UWAC_ERROR_CLOSED;
}
prevItem = display->pop_queue->head;
*event = display->pop_queue->event;
free(display->pop_queue);
display->pop_queue = prevItem;
if (prevItem)
prevItem->tail = NULL;
else
display->push_queue = NULL;
return UWAC_SUCCESS;
}

831
uwac/libuwac/uwac-input.c Normal file
View File

@ -0,0 +1,831 @@
/*
* Copyright © 2014-2015 David FORT <contact@hardening-consulting.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting documentation, and
* that the name of the copyright holders not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no representations
* about the suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*/
#include "uwac-priv.h"
#include "uwac-utils.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <time.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/timerfd.h>
#include <sys/epoll.h>
static void keyboard_repeat_func(UwacTask *task, uint32_t events)
{
UwacSeat *input = container_of(task, UwacSeat, repeat_task);
UwacWindow *window = input->keyboard_focus;
uint64_t exp;
if (read(input->repeat_timer_fd, &exp, sizeof exp) != sizeof exp)
/* If we change the timer between the fd becoming
* readable and getting here, there'll be nothing to
* read and we get EAGAIN. */
return;
if (window) {
UwacKeyEvent *key;
key = (UwacKeyEvent *)UwacDisplayNewEvent(input->display, UWAC_EVENT_KEY);
if (!key)
return;
key->window = window;
key->sym = input->repeat_sym;
key->pressed = true;
}
}
static void keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard,
uint32_t format, int fd, uint32_t size)
{
UwacSeat *input = data;
struct xkb_keymap *keymap;
struct xkb_state *state;
char *map_str;
if (!data) {
close(fd);
return;
}
if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
close(fd);
return;
}
map_str = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
if (map_str == MAP_FAILED) {
close(fd);
return;
}
keymap = xkb_keymap_new_from_string(input->xkb_context, map_str, XKB_KEYMAP_FORMAT_TEXT_V1, 0);
munmap(map_str, size);
close(fd);
if (!keymap) {
assert(uwacErrorHandler(input->display, UWAC_ERROR_INTERNAL, "failed to compile keymap\n"));
return;
}
state = xkb_state_new(keymap);
if (!state) {
assert(uwacErrorHandler(input->display, UWAC_ERROR_NOMEMORY, "failed to create XKB state\n"));
xkb_keymap_unref(keymap);
return;
}
xkb_keymap_unref(input->xkb.keymap);
xkb_state_unref(input->xkb.state);
input->xkb.keymap = keymap;
input->xkb.state = state;
input->xkb.control_mask = 1 << xkb_keymap_mod_get_index(input->xkb.keymap, "Control");
input->xkb.alt_mask = 1 << xkb_keymap_mod_get_index(input->xkb.keymap, "Mod1");
input->xkb.shift_mask = 1 << xkb_keymap_mod_get_index(input->xkb.keymap, "Shift");
}
static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
uint32_t serial, uint32_t time, uint32_t key,
uint32_t state_w);
static void keyboard_handle_enter(void *data, struct wl_keyboard *keyboard, uint32_t serial,
struct wl_surface *surface, struct wl_array *keys)
{
uint32_t *key, *pressedKey;
UwacSeat *input = (UwacSeat *)data;
int i, found;
UwacKeyboardEnterLeaveEvent *event;
event = (UwacKeyboardEnterLeaveEvent *)UwacDisplayNewEvent(input->display, UWAC_EVENT_KEYBOARD_ENTER);
if (!event)
return;
event->window = input->keyboard_focus = (UwacWindow *)wl_surface_get_user_data(surface);
/* look for keys that have been released */
found = false;
for (pressedKey = input->pressed_keys.data, i = 0; i < input->pressed_keys.size; i += sizeof(uint32_t)) {
wl_array_for_each(key, keys) {
if (*key == *pressedKey) {
found = true;
break;
}
}
if (!found) {
keyboard_handle_key(data, keyboard, serial, 0, *pressedKey, WL_KEYBOARD_KEY_STATE_RELEASED);
} else {
pressedKey++;
}
}
/* handle keys that are now pressed */
wl_array_for_each(key, keys) {
keyboard_handle_key(data, keyboard, serial, 0, *key, WL_KEYBOARD_KEY_STATE_PRESSED);
}
}
static void keyboard_handle_leave(void *data, struct wl_keyboard *keyboard, uint32_t serial,
struct wl_surface *surface)
{
struct itimerspec its;
UwacSeat *input;
UwacPointerEnterLeaveEvent *event;
input = (UwacSeat *)data;
its.it_interval.tv_sec = 0;
its.it_interval.tv_nsec = 0;
its.it_value.tv_sec = 0;
its.it_value.tv_nsec = 0;
timerfd_settime(input->repeat_timer_fd, 0, &its, NULL);
event = (UwacPointerEnterLeaveEvent *)UwacDisplayNewEvent(input->display, UWAC_EVENT_POINTER_LEAVE);
if (!event)
return;
event->window = input->keyboard_focus;
}
static int update_key_pressed(UwacSeat *seat, uint32_t key) {
uint32_t *keyPtr;
/* check if the key is not already pressed */
wl_array_for_each(keyPtr, &seat->pressed_keys) {
if (*keyPtr == key)
return 1;
}
keyPtr = wl_array_add(&seat->pressed_keys, sizeof(uint32_t));
if (!keyPtr)
return -1;
*keyPtr = key;
return 0;
}
static int update_key_released(UwacSeat *seat, uint32_t key) {
uint32_t *keyPtr;
int i, toMove;
bool found = false;
for (i = 0, keyPtr = seat->pressed_keys.data; i < seat->pressed_keys.size; i++, keyPtr++) {
if (*keyPtr == key) {
found = true;
break;
}
}
if (found) {
toMove = seat->pressed_keys.size - ((i + 1) * sizeof(uint32_t));
if (toMove)
memmove(keyPtr, keyPtr+1, toMove);
seat->pressed_keys.size -= sizeof(uint32_t);
}
return 1;
}
static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
uint32_t serial, uint32_t time, uint32_t key,
uint32_t state_w)
{
UwacSeat *input = (UwacSeat *)data;
UwacWindow *window = input->keyboard_focus;
UwacKeyEvent *keyEvent;
uint32_t code, num_syms;
enum wl_keyboard_key_state state = state_w;
const xkb_keysym_t *syms;
xkb_keysym_t sym;
struct itimerspec its;
if (state_w == WL_KEYBOARD_KEY_STATE_PRESSED)
update_key_pressed(input, key);
else
update_key_released(input, key);
input->display->serial = serial;
code = key + 8;
if (!window || !input->xkb.state)
return;
/* We only use input grabs for pointer events for now, so just
* ignore key presses if a grab is active. We expand the key
* event delivery mechanism to route events to widgets to
* properly handle key grabs. In the meantime, this prevents
* key event delivery while a grab is active. */
/*if (input->grab && input->grab_button == 0)
return;*/
num_syms = xkb_state_key_get_syms(input->xkb.state, code, &syms);
sym = XKB_KEY_NoSymbol;
if (num_syms == 1)
sym = syms[0];
if (state == WL_KEYBOARD_KEY_STATE_RELEASED && key == input->repeat_key) {
its.it_interval.tv_sec = 0;
its.it_interval.tv_nsec = 0;
its.it_value.tv_sec = 0;
its.it_value.tv_nsec = 0;
timerfd_settime(input->repeat_timer_fd, 0, &its, NULL);
} else if (state == WL_KEYBOARD_KEY_STATE_PRESSED && xkb_keymap_key_repeats(input->xkb.keymap, code)) {
input->repeat_sym = sym;
input->repeat_key = key;
input->repeat_time = time;
its.it_interval.tv_sec = input->repeat_rate_sec;
its.it_interval.tv_nsec = input->repeat_rate_nsec;
its.it_value.tv_sec = input->repeat_delay_sec;
its.it_value.tv_nsec = input->repeat_delay_nsec;
timerfd_settime(input->repeat_timer_fd, 0, &its, NULL);
}
keyEvent = (UwacKeyEvent *)UwacDisplayNewEvent(input->display, UWAC_EVENT_KEY);
if (!keyEvent)
return;
keyEvent->window = window;
keyEvent->sym = sym;
keyEvent->raw_key = key;
keyEvent->pressed = (state == WL_KEYBOARD_KEY_STATE_PRESSED);
}
static void keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard, uint32_t serial,
uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group)
{
UwacSeat *input = data;
xkb_mod_mask_t mask;
/* If we're not using a keymap, then we don't handle PC-style modifiers */
if (!input->xkb.keymap)
return;
xkb_state_update_mask(input->xkb.state, mods_depressed, mods_latched, mods_locked, 0, 0, group);
mask = xkb_state_serialize_mods(input->xkb.state, XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED);
input->modifiers = 0;
if (mask & input->xkb.control_mask)
input->modifiers |= UWAC_MOD_CONTROL_MASK;
if (mask & input->xkb.alt_mask)
input->modifiers |= UWAC_MOD_ALT_MASK;
if (mask & input->xkb.shift_mask)
input->modifiers |= UWAC_MOD_SHIFT_MASK;
}
static void set_repeat_info(UwacSeat *input, int32_t rate, int32_t delay)
{
input->repeat_rate_sec = input->repeat_rate_nsec = 0;
input->repeat_delay_sec = input->repeat_delay_nsec = 0;
/* a rate of zero disables any repeating, regardless of the delay's
* value */
if (rate == 0)
return;
if (rate == 1)
input->repeat_rate_sec = 1;
else
input->repeat_rate_nsec = 1000000000 / rate;
input->repeat_delay_sec = delay / 1000;
delay -= (input->repeat_delay_sec * 1000);
input->repeat_delay_nsec = delay * 1000 * 1000;
}
static void keyboard_handle_repeat_info(void *data, struct wl_keyboard *keyboard,
int32_t rate, int32_t delay)
{
UwacSeat *input = data;
set_repeat_info(input, rate, delay);
}
static const struct wl_keyboard_listener keyboard_listener = {
keyboard_handle_keymap,
keyboard_handle_enter,
keyboard_handle_leave,
keyboard_handle_key,
keyboard_handle_modifiers,
keyboard_handle_repeat_info
};
static bool touch_send_start_frame(UwacSeat *seat) {
UwacTouchFrameBegin *ev;
ev = (UwacTouchFrameBegin *)UwacDisplayNewEvent(seat->display, UWAC_EVENT_TOUCH_FRAME_BEGIN);
if (!ev)
return false;
seat->touch_frame_started = true;
return true;
}
static void touch_handle_down(void *data, struct wl_touch *wl_touch,
uint32_t serial, uint32_t time, struct wl_surface *surface,
int32_t id, wl_fixed_t x_w, wl_fixed_t y_w)
{
UwacSeat *seat = data;
UwacTouchDown *tdata;
seat->display->serial = serial;
if (!seat->touch_frame_started && !touch_send_start_frame(seat))
return;
tdata = (UwacTouchDown *)UwacDisplayNewEvent(seat->display, UWAC_EVENT_TOUCH_DOWN);
if (!tdata)
return;
tdata->seat = seat;
tdata->id = id;
tdata->x = x_w;
tdata->y = y_w;
#if 0
struct widget *widget;
float sx = wl_fixed_to_double(x);
float sy = wl_fixed_to_double(y);
input->touch_focus = wl_surface_get_user_data(surface);
if (!input->touch_focus) {
DBG("Failed to find to touch focus for surface %p\n", surface);
return;
}
if (surface != input->touch_focus->main_surface->surface) {
DBG("Ignoring input event from subsurface %p\n", surface);
input->touch_focus = NULL;
return;
}
if (input->grab)
widget = input->grab;
else
widget = window_find_widget(input->touch_focus,
wl_fixed_to_double(x),
wl_fixed_to_double(y));
if (widget) {
struct touch_point *tp = xmalloc(sizeof *tp);
if (tp) {
tp->id = id;
tp->widget = widget;
tp->x = sx;
tp->y = sy;
wl_list_insert(&input->touch_point_list, &tp->link);
if (widget->touch_down_handler)
(*widget->touch_down_handler)(widget, input,
serial, time, id,
sx, sy,
widget->user_data);
}
}
#endif
}
static void touch_handle_up(void *data, struct wl_touch *wl_touch,
uint32_t serial, uint32_t time, int32_t id)
{
UwacSeat *seat = data;
UwacTouchUp *tdata;
if (!seat->touch_frame_started && !touch_send_start_frame(seat))
return;
tdata = (UwacTouchUp *)UwacDisplayNewEvent(seat->display, UWAC_EVENT_TOUCH_UP);
if (!tdata)
return;
tdata->seat = seat;
tdata->id = id;
#if 0
struct touch_point *tp, *tmp;
if (!input->touch_focus) {
DBG("No touch focus found for touch up event!\n");
return;
}
wl_list_for_each_safe(tp, tmp, &input->touch_point_list, link) {
if (tp->id != id)
continue;
if (tp->widget->touch_up_handler)
(*tp->widget->touch_up_handler)(tp->widget, input, serial,
time, id,
tp->widget->user_data);
wl_list_remove(&tp->link);
free(tp);
return;
}
#endif
}
static void touch_handle_motion(void *data, struct wl_touch *wl_touch,
uint32_t time, int32_t id, wl_fixed_t x_w, wl_fixed_t y_w)
{
UwacSeat *seat = data;
UwacTouchMotion *tdata;
if (!seat->touch_frame_started && !touch_send_start_frame(seat))
return;
tdata = (UwacTouchMotion *)UwacDisplayNewEvent(seat->display, UWAC_EVENT_TOUCH_MOTION);
if (!tdata)
return;
tdata->seat = seat;
tdata->id = id;
tdata->x = x_w;
tdata->y = y_w;
#if 0
struct touch_point *tp;
float sx = wl_fixed_to_double(x);
float sy = wl_fixed_to_double(y);
DBG("touch_handle_motion: %i %i\n", id, wl_list_length(&seat->touch_point_list));
if (!seat->touch_focus) {
DBG("No touch focus found for touch motion event!\n");
return;
}
wl_list_for_each(tp, &seat->touch_point_list, link) {
if (tp->id != id)
continue;
tp->x = sx;
tp->y = sy;
if (tp->widget->touch_motion_handler)
(*tp->widget->touch_motion_handler)(tp->widget, seat, time,
id, sx, sy,
tp->widget->user_data);
return;
}
#endif
}
static void touch_handle_frame(void *data, struct wl_touch *wl_touch)
{
UwacSeat *seat = data;
UwacTouchFrameEnd *ev;
ev = (UwacTouchFrameEnd *)UwacDisplayNewEvent(seat->display, UWAC_EVENT_TOUCH_FRAME_END);
if (!ev)
return;
ev->seat = seat;
seat->touch_frame_started = false;
}
static void touch_handle_cancel(void *data, struct wl_touch *wl_touch)
{
UwacSeat *seat = data;
UwacTouchCancel *ev;
ev = (UwacTouchCancel *)UwacDisplayNewEvent(seat->display, UWAC_EVENT_TOUCH_CANCEL);
if (!ev)
return;
ev->seat = seat;
seat->touch_frame_started = false;
#if 0
struct touch_point *tp, *tmp;
DBG("touch_handle_cancel\n");
if (!input->touch_focus) {
DBG("No touch focus found for touch cancel event!\n");
return;
}
wl_list_for_each_safe(tp, tmp, &input->touch_point_list, link) {
if (tp->widget->touch_cancel_handler)
(*tp->widget->touch_cancel_handler)(tp->widget, input,
tp->widget->user_data);
wl_list_remove(&tp->link);
free(tp);
}
#endif
}
static const struct wl_touch_listener touch_listener = {
touch_handle_down,
touch_handle_up,
touch_handle_motion,
touch_handle_frame,
touch_handle_cancel,
};
static void pointer_handle_enter(void *data, struct wl_pointer *pointer, uint32_t serial,
struct wl_surface *surface, wl_fixed_t sx_w, wl_fixed_t sy_w)
{
UwacSeat *input = data;
UwacWindow *window;
UwacPointerEnterLeaveEvent *event;
float sx = wl_fixed_to_double(sx_w);
float sy = wl_fixed_to_double(sy_w);
if (!surface) {
/* enter event for a window we've just destroyed */
return;
}
input->display->serial = serial;
window = wl_surface_get_user_data(surface);
if (window)
window->pointer_enter_serial = serial;
input->pointer_focus = window;
input->sx = sx;
input->sy = sy;
event = (UwacPointerEnterLeaveEvent *)UwacDisplayNewEvent(input->display, UWAC_EVENT_POINTER_ENTER);
if (!event)
return;
event->seat = input;
event->window = window;
event->x = sx;
event->y = sy;
}
static void pointer_handle_leave(void *data, struct wl_pointer *pointer, uint32_t serial,
struct wl_surface *surface)
{
UwacPointerEnterLeaveEvent *event;
UwacWindow *window;
UwacSeat *input = data;
input->display->serial = serial;
event = (UwacPointerEnterLeaveEvent *)UwacDisplayNewEvent(input->display, UWAC_EVENT_POINTER_LEAVE);
if (!event)
return;
window = wl_surface_get_user_data(surface);
event->seat = input;
event->window = window;
#if 0
input_remove_pointer_focus(input);
#endif
}
static void pointer_handle_motion(void *data, struct wl_pointer *pointer, uint32_t time,
wl_fixed_t sx_w, wl_fixed_t sy_w)
{
UwacPointerMotionEvent *motion_event;
UwacSeat *input = data;
UwacWindow *window = input->pointer_focus;
float sx = wl_fixed_to_double(sx_w);
float sy = wl_fixed_to_double(sy_w);
if (!window)
return;
input->sx = sx;
input->sy = sy;
motion_event = (UwacPointerMotionEvent *)UwacDisplayNewEvent(input->display, UWAC_EVENT_POINTER_MOTION);
if (!motion_event)
return;
motion_event->seat = input;
motion_event->window = window;
motion_event->x = wl_fixed_to_int(sx_w);
motion_event->y = wl_fixed_to_int(sy_w);
}
static void pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial,
uint32_t time, uint32_t button, uint32_t state_w)
{
UwacPointerButtonEvent *event;
UwacSeat *seat = data;
UwacWindow *window = seat->pointer_focus;
seat->display->serial = serial;
event = (UwacPointerButtonEvent *)UwacDisplayNewEvent(seat->display, UWAC_EVENT_POINTER_BUTTONS);
if (!event)
return;
event->seat = seat;
event->window = window;
event->x = seat->sx;
event->y = seat->sy;
event->button = button;
event->state = (enum wl_pointer_button_state)state_w;
}
static void pointer_handle_axis(void *data, struct wl_pointer *pointer, uint32_t time,
uint32_t axis, wl_fixed_t value)
{
UwacPointerAxisEvent *event;
UwacSeat *seat = data;
UwacWindow *window = seat->pointer_focus;
if (!window)
return;
event = (UwacPointerAxisEvent *)UwacDisplayNewEvent(seat->display, UWAC_EVENT_POINTER_AXIS);
if (!event)
return;
event->seat = seat;
event->window = window;
event->x = seat->sx;
event->y = seat->sy;
event->axis = axis;
event->value = value;
}
static const struct wl_pointer_listener pointer_listener = {
pointer_handle_enter,
pointer_handle_leave,
pointer_handle_motion,
pointer_handle_button,
pointer_handle_axis,
};
static void seat_handle_capabilities(void *data, struct wl_seat *seat, enum wl_seat_capability caps)
{
UwacSeat *input = data;
if ((caps & WL_SEAT_CAPABILITY_POINTER) && !input->pointer) {
input->pointer = wl_seat_get_pointer(seat);
wl_pointer_set_user_data(input->pointer, input);
wl_pointer_add_listener(input->pointer, &pointer_listener, input);
} else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && input->pointer) {
if (input->seat_version >= WL_POINTER_RELEASE_SINCE_VERSION)
wl_pointer_release(input->pointer);
else
wl_pointer_destroy(input->pointer);
input->pointer = NULL;
}
if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !input->keyboard) {
input->keyboard = wl_seat_get_keyboard(seat);
wl_keyboard_set_user_data(input->keyboard, input);
wl_keyboard_add_listener(input->keyboard, &keyboard_listener, input);
} else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && input->keyboard) {
if (input->seat_version >= WL_KEYBOARD_RELEASE_SINCE_VERSION)
wl_keyboard_release(input->keyboard);
else
wl_keyboard_destroy(input->keyboard);
input->keyboard = NULL;
}
if ((caps & WL_SEAT_CAPABILITY_TOUCH) && !input->touch) {
input->touch = wl_seat_get_touch(seat);
wl_touch_set_user_data(input->touch, input);
wl_touch_add_listener(input->touch, &touch_listener, input);
} else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && input->touch) {
if (input->seat_version >= WL_TOUCH_RELEASE_SINCE_VERSION)
wl_touch_release(input->touch);
else
wl_touch_destroy(input->touch);
input->touch = NULL;
}
}
static void
seat_handle_name(void *data, struct wl_seat *seat, const char *name)
{
UwacSeat *input = data;
if (input->name)
free(input->name);
input->name = strdup(name);
}
static const struct wl_seat_listener seat_listener = {
seat_handle_capabilities,
seat_handle_name,
};
UwacSeat *UwacSeatNew(UwacDisplay *d, uint32_t id, uint32_t version) {
UwacSeat *ret;
ret = zalloc(sizeof(UwacSeat));
ret->display = d;
ret->seat_id = id;
ret->seat_version = version;
wl_array_init(&ret->pressed_keys);
ret->xkb_context = xkb_context_new(0);
if (!ret->xkb_context) {
fprintf(stderr, "%s: unable to allocate a xkb_context\n", __FUNCTION__);
goto error_xkb_context;
}
ret->seat = wl_registry_bind(d->registry, id, &wl_seat_interface, version);
wl_seat_add_listener(ret->seat, &seat_listener, ret);
wl_seat_set_user_data(ret->seat, ret);
ret->repeat_timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK);
if (ret->repeat_timer_fd < 0) {
fprintf(stderr, "%s: error creating repeat timer\n", __FUNCTION__);
goto error_timer_fd;
}
ret->repeat_task.run = keyboard_repeat_func;
if (UwacDisplayWatchFd(d, ret->repeat_timer_fd, EPOLLIN, &ret->repeat_task) < 0) {
fprintf(stderr, "%s: error polling repeat timer\n", __FUNCTION__);
goto error_watch_timerfd;
}
wl_list_insert(d->seats.prev, &ret->link);
return ret;
error_watch_timerfd:
close(ret->repeat_timer_fd);
error_timer_fd:
wl_seat_destroy(ret->seat);
error_xkb_context:
free(ret);
return NULL;
}
void UwacSeatDestroy(UwacSeat *s) {
if (s->seat) {
if (s->seat_version >= WL_SEAT_RELEASE_SINCE_VERSION)
wl_seat_release(s->seat);
else
wl_seat_destroy(s->seat);
}
s->seat = NULL;
free(s->name);
wl_array_release(&s->pressed_keys);
xkb_state_unref(s->xkb.state);
xkb_context_unref(s->xkb_context);
if (s->pointer) {
if (s->seat_version >= WL_POINTER_RELEASE_SINCE_VERSION)
wl_pointer_release(s->pointer);
else
wl_pointer_destroy(s->pointer);
}
if (s->touch) {
if (s->seat_version >= WL_TOUCH_RELEASE_SINCE_VERSION)
wl_touch_release(s->touch);
else
wl_touch_destroy(s->touch);
}
if (s->keyboard) {
if (s->seat_version >= WL_KEYBOARD_RELEASE_SINCE_VERSION)
wl_keyboard_release(s->keyboard);
else
wl_keyboard_destroy(s->keyboard);
}
wl_list_remove(&s->link);
free(s);
}
const char *UwacSeatGetName(const UwacSeat *seat) {
return seat->name;
}

236
uwac/libuwac/uwac-os.c Normal file
View File

@ -0,0 +1,236 @@
/*
* Copyright © 2012 Collabora, Ltd.
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting documentation, and
* that the name of the copyright holders not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no representations
* about the suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*/
/*
* This file is an adaptation of src/wayland-os.h from the wayland project and
* shared/os-compatiblity.h from the weston project.
*
* Functions have been renamed just to prevent name clashes.
*/
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include "../config.h"
#include "uwac-os.h"
static int set_cloexec_or_close(int fd)
{
long flags;
if (fd == -1)
return -1;
flags = fcntl(fd, F_GETFD);
if (flags == -1)
goto err;
if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
goto err;
return fd;
err:
close(fd);
return -1;
}
int uwac_os_socket_cloexec(int domain, int type, int protocol)
{
int fd;
fd = socket(domain, type | SOCK_CLOEXEC, protocol);
if (fd >= 0)
return fd;
if (errno != EINVAL)
return -1;
fd = socket(domain, type, protocol);
return set_cloexec_or_close(fd);
}
int uwac_os_dupfd_cloexec(int fd, long minfd)
{
int newfd;
newfd = fcntl(fd, F_DUPFD_CLOEXEC, minfd);
if (newfd >= 0)
return newfd;
if (errno != EINVAL)
return -1;
newfd = fcntl(fd, F_DUPFD, minfd);
return set_cloexec_or_close(newfd);
}
static ssize_t recvmsg_cloexec_fallback(int sockfd, struct msghdr *msg, int flags)
{
ssize_t len;
struct cmsghdr *cmsg;
unsigned char *data;
int *fd;
int *end;
len = recvmsg(sockfd, msg, flags);
if (len == -1)
return -1;
if (!msg->msg_control || msg->msg_controllen == 0)
return len;
cmsg = CMSG_FIRSTHDR(msg);
for (; cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) {
if (cmsg->cmsg_level != SOL_SOCKET ||
cmsg->cmsg_type != SCM_RIGHTS)
continue;
data = CMSG_DATA(cmsg);
end = (int *)(data + cmsg->cmsg_len - CMSG_LEN(0));
for (fd = (int *)data; fd < end; ++fd)
*fd = set_cloexec_or_close(*fd);
}
return len;
}
ssize_t uwac_os_recvmsg_cloexec(int sockfd, struct msghdr *msg, int flags)
{
ssize_t len;
len = recvmsg(sockfd, msg, flags | MSG_CMSG_CLOEXEC);
if (len >= 0)
return len;
if (errno != EINVAL)
return -1;
return recvmsg_cloexec_fallback(sockfd, msg, flags);
}
int uwac_os_epoll_create_cloexec(void)
{
int fd;
#ifdef EPOLL_CLOEXEC
fd = epoll_create1(EPOLL_CLOEXEC);
if (fd >= 0)
return fd;
if (errno != EINVAL)
return -1;
#endif
fd = epoll_create(1);
return set_cloexec_or_close(fd);
}
static int create_tmpfile_cloexec(char *tmpname)
{
int fd;
#ifdef HAVE_MKOSTEMP
fd = mkostemp(tmpname, O_CLOEXEC);
if (fd >= 0)
unlink(tmpname);
#else
fd = mkstemp(tmpname);
if (fd >= 0) {
fd = set_cloexec_or_close(fd);
unlink(tmpname);
}
#endif
return fd;
}
/*
* Create a new, unique, anonymous file of the given size, and
* return the file descriptor for it. The file descriptor is set
* CLOEXEC. The file is immediately suitable for mmap()'ing
* the given size at offset zero.
*
* The file should not have a permanent backing store like a disk,
* but may have if XDG_RUNTIME_DIR is not properly implemented in OS.
*
* The file name is deleted from the file system.
*
* The file is suitable for buffer sharing between processes by
* transmitting the file descriptor over Unix sockets using the
* SCM_RIGHTS methods.
*
* If the C library implements posix_fallocate(), it is used to
* guarantee that disk space is available for the file at the
* given size. If disk space is insufficient, errno is set to ENOSPC.
* If posix_fallocate() is not supported, program may receive
* SIGBUS on accessing mmap()'ed file contents instead.
*/
int uwac_create_anonymous_file(off_t size)
{
static const char template[] = "/weston-shared-XXXXXX";
const char *path;
char *name;
int fd;
int ret;
path = getenv("XDG_RUNTIME_DIR");
if (!path) {
errno = ENOENT;
return -1;
}
name = malloc(strlen(path) + sizeof(template));
if (!name)
return -1;
strcpy(name, path);
strcat(name, template);
fd = create_tmpfile_cloexec(name);
free(name);
if (fd < 0)
return -1;
#ifdef HAVE_POSIX_FALLOCATE
ret = posix_fallocate(fd, 0, size);
if (ret != 0) {
close(fd);
errno = ret;
return -1;
}
#else
ret = ftruncate(fd, size);
if (ret < 0) {
close(fd);
return -1;
}
#endif
return fd;
}

45
uwac/libuwac/uwac-os.h Normal file
View File

@ -0,0 +1,45 @@
/*
* Copyright © 2012 Collabora, Ltd.
* Copyright © 2014 David FORT <contact@hardening-consulting.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting documentation, and
* that the name of the copyright holders not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no representations
* about the suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*/
/*
* This file is an adaptation of src/wayland-os.h from the wayland project and
* shared/os-compatiblity.h from the weston project.
*
* Functions have been renamed just to prevent name clashes.
*/
#ifndef __UWAC_OS_H
#define __UWAC_OS_H
#include <sys/socket.h>
int uwac_os_socket_cloexec(int domain, int type, int protocol);
int uwac_os_dupfd_cloexec(int fd, long minfd);
ssize_t uwac_os_recvmsg_cloexec(int sockfd, struct msghdr *msg, int flags);
int uwac_os_epoll_create_cloexec(void);
int uwac_create_anonymous_file(off_t size);
#endif /* __UWAC_OS_H */

126
uwac/libuwac/uwac-output.c Normal file
View File

@ -0,0 +1,126 @@
/*
* Copyright © 2014 David FORT <contact@hardening-consulting.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting documentation, and
* that the name of the copyright holders not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no representations
* about the suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*/
#include "uwac-priv.h"
#include "uwac-utils.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#define TARGET_OUTPUT_INTERFACE 2
static void output_handle_geometry(void *data, struct wl_output *wl_output, int x, int y,
int physical_width, int physical_height, int subpixel,
const char *make, const char *model, int transform)
{
UwacOutput *output = data;
/* output->allocation.x = x;
output->allocation.y = y;*/
output->transform = transform;
if (output->make)
free(output->make);
output->make = strdup(make);
if (!output->make) {
assert(uwacErrorHandler(output->display, UWAC_ERROR_NOMEMORY, "%s: unable to strdup make\n", __FUNCTION__));
}
if (output->model)
free(output->model);
output->model = strdup(model);
if (!output->model) {
assert(uwacErrorHandler(output->display, UWAC_ERROR_NOMEMORY, "%s: unable to strdup model\n", __FUNCTION__));
}
}
static void output_handle_done(void *data, struct wl_output *wl_output)
{
UwacOutput *output = data;
output->doneReceived = true;
}
static void output_handle_scale(void *data, struct wl_output *wl_output, int32_t scale)
{
UwacOutput *output = data;
output->scale = scale;
}
static void output_handle_mode(void *data, struct wl_output *wl_output, uint32_t flags,
int width, int height, int refresh)
{
UwacOutput *output = data;
//UwacDisplay *display = output->display;
if (output->doneNeeded && output->doneReceived) {
/* TODO: we should clear the mode list */
}
if (flags & WL_OUTPUT_MODE_CURRENT) {
output->resolution.width = width;
output->resolution.height = height;
/* output->allocation.width = width;
output->allocation.height = height;
if (display->output_configure_handler)
(*display->output_configure_handler)(
output, display->user_data);*/
}
}
static const struct wl_output_listener output_listener = {
output_handle_geometry,
output_handle_mode,
output_handle_done,
output_handle_scale
};
UwacOutput *UwacCreateOutput(UwacDisplay *d, uint32_t id, uint32_t version) {
UwacOutput *o;
o = zalloc(sizeof *o);
if (!o)
return NULL;
o->display = d;
o->server_output_id = id;
o->doneNeeded = (version > 1);
o->doneReceived = false;
o->output = wl_registry_bind(d->registry, id, &wl_output_interface, min(TARGET_OUTPUT_INTERFACE, version));
wl_output_add_listener(o->output, &output_listener, o);
wl_list_insert(d->outputs.prev, &o->link);
return o;
}
int UwacDestroyOutput(UwacOutput *output) {
free(output->make);
free(output->model);
wl_output_destroy(output->output);
wl_list_remove(&output->link);
free(output);
return UWAC_SUCCESS;
}

237
uwac/libuwac/uwac-priv.h Normal file
View File

@ -0,0 +1,237 @@
/*
* Copyright © 2014 David FORT <contact@hardening-consulting.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting documentation, and
* that the name of the copyright holders not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no representations
* about the suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*/
#ifndef __UWAC_PRIV_H_
#define __UWAC_PRIV_H_
#include "config.h"
#include <stdbool.h>
#include <wayland-client.h>
#include "xdg-shell-client-protocol.h"
#ifdef BUILD_IVI
#include "ivi-application-client-protocol.h"
#endif
#ifdef BUILD_FULLSCREEN_SHELL
#include "fullscreen-shell-client-protocol.h"
#endif
#ifdef HAVE_PIXMAN_REGION
#include <pixman-1/pixman.h>
#else
#include <freerdp/codec/region.h>
#endif
#include <xkbcommon/xkbcommon.h>
#include <uwac/uwac.h>
extern UwacErrorHandler uwacErrorHandler;
typedef struct uwac_task UwacTask;
/** @brief */
struct uwac_task {
void (*run)(UwacTask *task, uint32_t events);
struct wl_list link;
};
/** @brief a global registry object */
struct uwac_global {
uint32_t name;
char *interface;
uint32_t version;
struct wl_list link;
};
typedef struct uwac_global UwacGlobal;
struct uwac_event_list_item;
typedef struct uwac_event_list_item UwacEventListItem;
/** @brief */
struct uwac_event_list_item {
UwacEvent event;
UwacEventListItem *tail, *head;
};
/** @brief main connection object to a wayland display */
struct uwac_display {
struct wl_list globals;
struct wl_display *display;
struct wl_registry *registry;
struct wl_compositor *compositor;
struct wl_subcompositor *subcompositor;
struct wl_shell *shell;
struct xdg_shell *xdg_shell;
#ifdef BUILD_IVI
struct ivi_application *ivi_application;
#endif
#ifdef BUILD_FULLSCREEN_SHELL
struct _wl_fullscreen_shell *fullscreen_shell;
#endif
struct wl_shm *shm;
enum wl_shm_format *shm_formats;
uint32_t shm_formats_nb;
bool has_rgb565;
struct wl_data_device_manager *data_device_manager;
struct text_cursor_position *text_cursor_position;
struct workspace_manager *workspace_manager;
struct wl_list seats;
int display_fd;
UwacReturnCode last_error;
uint32_t display_fd_events;
int epoll_fd;
bool running;
UwacTask dispatch_fd_task;
uint32_t serial;
struct wl_cursor_theme *cursor_theme;
struct wl_cursor **cursors;
struct wl_list windows;
struct wl_list outputs;
UwacEventListItem *push_queue, *pop_queue;
};
/** @brief an output on a wayland display */
struct uwac_output {
UwacDisplay *display;
bool doneNeeded;
bool doneReceived;
UwacSize resolution;
int transform;
int scale;
char *make;
char *model;
uint32_t server_output_id;
struct wl_output *output;
struct wl_list link;
};
/** @brief a seat attached to a wayland display */
struct uwac_seat {
UwacDisplay *display;
char *name;
struct wl_seat *seat;
uint32_t seat_id;
uint32_t seat_version;
struct wl_pointer *pointer;
struct wl_keyboard *keyboard;
struct wl_touch *touch;
struct xkb_context *xkb_context;
struct {
struct xkb_keymap *keymap;
struct xkb_state *state;
xkb_mod_mask_t control_mask;
xkb_mod_mask_t alt_mask;
xkb_mod_mask_t shift_mask;
} xkb;
uint32_t modifiers;
int32_t repeat_rate_sec, repeat_rate_nsec;
int32_t repeat_delay_sec, repeat_delay_nsec;
uint32_t repeat_sym, repeat_key, repeat_time;
struct wl_array pressed_keys;
UwacWindow *pointer_focus;
UwacWindow *keyboard_focus;
UwacWindow *touch_focus;
bool touch_frame_started;
int repeat_timer_fd;
UwacTask repeat_task;
float sx, sy;
struct wl_list link;
};
/** @brief a buffer used for drawing a surface frame */
struct uwac_buffer {
bool used;
#ifdef HAVE_PIXMAN_REGION
pixman_region32_t damage;
#else
REGION16 damage;
#endif
struct wl_buffer *wayland_buffer;
void *data;
};
typedef struct uwac_buffer UwacBuffer;
/** @brief a window */
struct uwac_window {
UwacDisplay *display;
int width, height, stride;
int surfaceStates;
enum wl_shm_format format;
int nbuffers;
UwacBuffer *buffers;
struct wl_region *opaque_region;
struct wl_region *input_region;
struct wl_callback *frame_callback;
UwacBuffer *drawingBuffer, *pendingBuffer;
struct wl_surface *surface;
struct wl_shell_surface *shell_surface;
struct xdg_surface *xdg_surface;
#ifdef BUILD_IVI
struct ivi_surface *ivi_surface;
#endif
struct wl_list link;
uint32_t pointer_enter_serial;
uint32_t pointer_cursor_serial;
int pointer_current_cursor;
};
/* in uwa-display.c */
UwacEvent *UwacDisplayNewEvent(UwacDisplay *d, int type);
int UwacDisplayWatchFd(UwacDisplay *display, int fd, uint32_t events, UwacTask *task);
/* in uwac-input.c */
UwacSeat *UwacSeatNew(UwacDisplay *d, uint32_t id, uint32_t version);
void UwacSeatDestroy(UwacSeat *s);
/* in uwac-output.c */
UwacOutput *UwacCreateOutput(UwacDisplay *d, uint32_t id, uint32_t version);
int UwacDestroyOutput(UwacOutput *output);
#endif /* __UWAC_PRIV_H_ */

93
uwac/libuwac/uwac-tools.c Normal file
View File

@ -0,0 +1,93 @@
/*
* Copyright © 2015 David FORT <contact@hardening-consulting.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting documentation, and
* that the name of the copyright holders not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no representations
* about the suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*/
#include <wayland-util.h>
#include <string.h>
#include <uwac/uwac-tools.h>
/** @brief */
struct uwac_touch_automata {
struct wl_array tp;
};
void UwacTouchAutomataInit(UwacTouchAutomata *automata) {
wl_array_init(&automata->tp);
}
void UwacTouchAutomataReset(UwacTouchAutomata *automata) {
automata->tp.size = 0;
}
bool UwacTouchAutomataInjectEvent(UwacTouchAutomata *automata, UwacEvent *event) {
UwacTouchPoint *tp;
switch (event->type) {
case UWAC_EVENT_TOUCH_FRAME_BEGIN:
break;
case UWAC_EVENT_TOUCH_UP: {
UwacTouchUp *touchUp = &event->touchUp;
int toMove = automata->tp.size - sizeof(UwacTouchPoint);
wl_array_for_each(tp, &automata->tp) {
if (tp->id == touchUp->id) {
if (toMove)
memmove(tp, tp+1, toMove);
return true;
}
toMove -= sizeof(UwacTouchPoint);
}
break;
}
case UWAC_EVENT_TOUCH_DOWN: {
UwacTouchDown *touchDown = &event->touchDown;
wl_array_for_each(tp, &automata->tp) {
if (tp->id == touchDown->id) {
tp->x = touchDown->x;
tp->y = touchDown->y;
return true;
}
}
tp = wl_array_add(&automata->tp, sizeof(UwacTouchPoint));
if (!tp)
return false;
tp->id = touchDown->id;
tp->x = touchDown->x;
tp->y = touchDown->y;
break;
}
case UWAC_EVENT_TOUCH_FRAME_END:
break;
default:
break;
}
return true;
}

63
uwac/libuwac/uwac-utils.c Normal file
View File

@ -0,0 +1,63 @@
/*
* Copyright © 2012 Collabora, Ltd.
* Copyright © 2008 Kristian Høgsberg
* Copyright © 2014 David FORT <contact@hardening-consulting.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting documentation, and
* that the name of the copyright holders not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no representations
* about the suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "uwac-utils.h"
/*
* This part is an adaptation of client/window.c from the weston project.
*/
void *fail_on_null(void *p) {
if (p == NULL) {
fprintf(stderr, "out of memory\n");
exit(EXIT_FAILURE);
}
return p;
}
void *xmalloc(size_t s) {
return fail_on_null(malloc(s));
}
void *xzalloc(size_t s) {
return fail_on_null(zalloc(s));
}
char *xstrdup(const char *s) {
return fail_on_null(strdup(s));
}
void *xrealloc(char *p, size_t s) {
return fail_on_null(realloc(p, s));
}

53
uwac/libuwac/uwac-utils.h Normal file
View File

@ -0,0 +1,53 @@
/*
* Copyright © 2014 David FORT <contact@hardening-consulting.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting documentation, and
* that the name of the copyright holders not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no representations
* about the suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*/
#ifndef __UWAC_UTILS_H_
#define __UWAC_UTILS_H_
#include <stdlib.h>
#define min(a,b) \
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a < _b ? _a : _b; })
#define container_of(ptr, type, member) ({ \
const __typeof__( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
void *xmalloc(size_t s);
static inline void *zalloc(size_t size) {
return calloc(1, size);
}
void *xzalloc(size_t s);
char *xstrdup(const char *s);
void *xrealloc(char *p, size_t s);
#endif /* __UWAC_UTILS_H_ */

616
uwac/libuwac/uwac-window.c Normal file
View File

@ -0,0 +1,616 @@
/*
* Copyright © 2014 David FORT <contact@hardening-consulting.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting documentation, and
* that the name of the copyright holders not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no representations
* about the suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>
#include <sys/mman.h>
#include "uwac-priv.h"
#include "uwac-utils.h"
#include "uwac-os.h"
#define UWAC_INITIAL_BUFFERS 3
static int bppFromShmFormat(enum wl_shm_format format) {
switch (format) {
case WL_SHM_FORMAT_ARGB8888:
case WL_SHM_FORMAT_XRGB8888:
default:
return 4;
}
}
static void buffer_release(void *data, struct wl_buffer *buffer) {
UwacBuffer *uwacBuffer = (UwacBuffer *)data;
uwacBuffer->used = false;
}
static const struct wl_buffer_listener buffer_listener = {
buffer_release
};
void UwacWindowDestroyBuffers(UwacWindow *w) {
int i;
for (i = 0; i < w->nbuffers; i++) {
UwacBuffer *buffer = &w->buffers[i];
#ifdef HAVE_PIXMAN_REGION
pixman_region32_fini(&buffer->damage);
#else
region16_uninit(&buffer->damage);
#endif
wl_buffer_destroy(buffer->wayland_buffer);
}
w->nbuffers = 0;
free(w->buffers);
w->buffers = NULL;
}
int UwacWindowShmAllocBuffers(UwacWindow *w, int nbuffers, int allocSize, uint32_t width,
uint32_t height, enum wl_shm_format format);
static void xdg_handle_configure(void *data, struct xdg_surface *surface,
int32_t width, int32_t height,
struct wl_array *states, uint32_t serial)
{
UwacWindow *window = (UwacWindow *)data;
UwacConfigureEvent *event;
int ret, surfaceState;
enum xdg_surface_state *state;
surfaceState = 0;
wl_array_for_each(state, states) {
switch (*state) {
case XDG_SURFACE_STATE_MAXIMIZED:
surfaceState |= UWAC_WINDOW_MAXIMIZED;
break;
case XDG_SURFACE_STATE_FULLSCREEN:
surfaceState |= UWAC_WINDOW_FULLSCREEN;
break;
case XDG_SURFACE_STATE_ACTIVATED:
surfaceState |= UWAC_WINDOW_ACTIVATED;
break;
case XDG_SURFACE_STATE_RESIZING:
surfaceState |= UWAC_WINDOW_RESIZING;
break;
default:
break;
}
}
window->surfaceStates = surfaceState;
event = (UwacConfigureEvent *)UwacDisplayNewEvent(window->display, UWAC_EVENT_CONFIGURE);
if(!event) {
assert(uwacErrorHandler(window->display, UWAC_ERROR_NOMEMORY, "failed to allocate a configure event\n"));
goto ack;
}
event->window = window;
event->states = surfaceState;
if (width && height) {
event->width = width;
event->height = height;
UwacWindowDestroyBuffers(window);
window->width = width;
window->stride = width * bppFromShmFormat(window->format);
window->height = height;
ret = UwacWindowShmAllocBuffers(window, UWAC_INITIAL_BUFFERS, window->stride * height,
width, height, window->format);
if (ret != UWAC_SUCCESS) {
assert(uwacErrorHandler(window->display, ret, "failed to reallocate a wayland buffers\n"));
window->drawingBuffer = window->pendingBuffer = NULL;
goto ack;
}
window->drawingBuffer = window->pendingBuffer = &window->buffers[0];
} else {
event->width = window->width;
event->height = window->height;
}
ack:
xdg_surface_ack_configure(surface, serial);
}
static void xdg_handle_close(void *data, struct xdg_surface *xdg_surface)
{
UwacCloseEvent *event;
UwacWindow *window = (UwacWindow *)data;
event = (UwacCloseEvent *)UwacDisplayNewEvent(window->display, UWAC_EVENT_CLOSE);
if(!event) {
assert(uwacErrorHandler(window->display, UWAC_ERROR_INTERNAL, "failed to allocate a close event\n"));
return;
}
event->window = window;
}
static const struct xdg_surface_listener xdg_surface_listener = {
xdg_handle_configure,
xdg_handle_close,
};
#if BUILD_IVI
static void ivi_handle_configure(void *data, struct ivi_surface *surface,
int32_t width, int32_t height)
{
UwacWindow *window = (UwacWindow *)data;
UwacConfigureEvent *event;
int ret;
event = (UwacConfigureEvent *)UwacDisplayNewEvent(window->display, UWAC_EVENT_CONFIGURE);
if(!event) {
assert(uwacErrorHandler(window->display, UWAC_ERROR_NOMEMORY, "failed to allocate a configure event\n"));
return;
}
event->window = window;
event->states = 0;
if (width && height) {
event->width = width;
event->height = height;
UwacWindowDestroyBuffers(window);
window->width = width;
window->stride = width * bppFromShmFormat(window->format);
window->height = height;
ret = UwacWindowShmAllocBuffers(window, UWAC_INITIAL_BUFFERS, window->stride * height,
width, height, window->format);
if (ret != UWAC_SUCCESS) {
assert(uwacErrorHandler(window->display, ret, "failed to reallocate a wayland buffers\n"));
window->drawingBuffer = window->pendingBuffer = NULL;
return;
}
window->drawingBuffer = window->pendingBuffer = &window->buffers[0];
} else {
event->width = window->width;
event->height = window->height;
}
}
static const struct ivi_surface_listener ivi_surface_listener = {
ivi_handle_configure,
};
#endif
void shell_ping(void *data, struct wl_shell_surface *surface, uint32_t serial)
{
wl_shell_surface_pong(surface, serial);
}
void shell_configure(void *data, struct wl_shell_surface *surface, uint32_t edges,
int32_t width, int32_t height)
{
UwacWindow *window = (UwacWindow *)data;
UwacConfigureEvent *event;
int ret;
event = (UwacConfigureEvent *)UwacDisplayNewEvent(window->display, UWAC_EVENT_CONFIGURE);
if(!event) {
assert(uwacErrorHandler(window->display, UWAC_ERROR_NOMEMORY, "failed to allocate a configure event\n"));
return;
}
event->window = window;
event->states = 0;
if (width && height) {
event->width = width;
event->height = height;
UwacWindowDestroyBuffers(window);
window->width = width;
window->stride = width * bppFromShmFormat(window->format);
window->height = height;
ret = UwacWindowShmAllocBuffers(window, UWAC_INITIAL_BUFFERS, window->stride * height,
width, height, window->format);
if (ret != UWAC_SUCCESS) {
assert(uwacErrorHandler(window->display, ret, "failed to reallocate a wayland buffers\n"));
window->drawingBuffer = window->pendingBuffer = NULL;
return;
}
window->drawingBuffer = window->pendingBuffer = &window->buffers[0];
} else {
event->width = window->width;
event->height = window->height;
}
}
void shell_popup_done(void *data, struct wl_shell_surface *surface)
{
}
static const struct wl_shell_surface_listener shell_listener = {
shell_ping,
shell_configure,
shell_popup_done
};
int UwacWindowShmAllocBuffers(UwacWindow *w, int nbuffers, int allocSize, uint32_t width,
uint32_t height, enum wl_shm_format format)
{
int ret = UWAC_SUCCESS;
UwacBuffer *newBuffers;
int i, fd;
void *data;
struct wl_shm_pool *pool;
newBuffers = realloc(w->buffers, (w->nbuffers + nbuffers) * sizeof(UwacBuffer));
if (!newBuffers)
return UWAC_ERROR_NOMEMORY;
w->buffers = newBuffers;
memset(w->buffers + w->nbuffers, 0, sizeof(UwacBuffer) * nbuffers);
fd = uwac_create_anonymous_file(allocSize * nbuffers);
if (fd < 0) {
return UWAC_ERROR_INTERNAL;
}
data = mmap(NULL, allocSize * nbuffers, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (data == MAP_FAILED) {
ret = UWAC_ERROR_NOMEMORY;
goto error_mmap;
}
pool = wl_shm_create_pool(w->display->shm, fd, allocSize * nbuffers);
for (i = 0; i < nbuffers; i++) {
UwacBuffer *buffer = &w->buffers[w->nbuffers + i];
#ifdef HAVE_PIXMAN_REGION
pixman_region32_init(&buffer->damage);
#else
region16_init(&buffer->damage);
#endif
buffer->data = data + (allocSize * i);
buffer->wayland_buffer = wl_shm_pool_create_buffer(pool, allocSize * i, width, height, w->stride, format);
wl_buffer_add_listener(buffer->wayland_buffer, &buffer_listener, buffer);
}
wl_shm_pool_destroy(pool);
w->nbuffers += nbuffers;
error_mmap:
close(fd);
return ret;
}
UwacBuffer *UwacWindowFindFreeBuffer(UwacWindow *w) {
int i, ret;
for (i = 0; i < w->nbuffers; i++) {
if (!w->buffers[i].used) {
w->buffers[i].used = true;
return &w->buffers[i];
}
}
ret = UwacWindowShmAllocBuffers(w, 2, w->stride * w->height, w->width, w->height, w->format);
if (ret != UWAC_SUCCESS) {
w->display->last_error = ret;
return NULL;
}
w->buffers[i].used = true;
return &w->buffers[i];
}
UwacWindow *UwacCreateWindowShm(UwacDisplay *display, uint32_t width, uint32_t height, enum wl_shm_format format) {
UwacWindow *w;
int allocSize, ret;
if (!display) {
display->last_error = UWAC_ERROR_INVALID_DISPLAY;
return NULL;
}
w = zalloc(sizeof(*w));
if (!w) {
display->last_error = UWAC_ERROR_NOMEMORY;
return NULL;
}
w->display = display;
w->format = format;
w->width = width;
w->height = height;
w->stride = width * bppFromShmFormat(format);
allocSize = w->stride * height;
ret = UwacWindowShmAllocBuffers(w, UWAC_INITIAL_BUFFERS, allocSize, width, height, format);
if (ret != UWAC_SUCCESS) {
display->last_error = ret;
goto out_error_free;
}
w->buffers[0].used = true;
w->drawingBuffer = &w->buffers[0];
w->surface = wl_compositor_create_surface(display->compositor);
if (!w->surface) {
display->last_error = UWAC_ERROR_NOMEMORY;
goto out_error_surface;
}
wl_surface_set_user_data(w->surface, w);
if (display->xdg_shell) {
w->xdg_surface = xdg_shell_get_xdg_surface(display->xdg_shell, w->surface);
if (!w->xdg_surface) {
display->last_error = UWAC_ERROR_NOMEMORY;
goto out_error_shell;
}
assert(w->xdg_surface);
xdg_surface_add_listener(w->xdg_surface, &xdg_surface_listener, w);
#if BUILD_IVI
} else if (display->ivi_application) {
w->ivi_surface = ivi_application_surface_create(display->ivi_application, 1, w->surface);
assert (w->ivi_surface);
ivi_surface_add_listener(w->ivi_surface, &ivi_surface_listener, w);
#endif
#if BUILD_FULLSCREEN_SHELL
} else if (display->fullscreen_shell) {
_wl_fullscreen_shell_present_surface(display->fullscreen_shell, w->surface,
_WL_FULLSCREEN_SHELL_PRESENT_METHOD_CENTER, NULL);
#endif
} else {
w->shell_surface = wl_shell_get_shell_surface(display->shell, w->surface);
assert(w->shell_surface);
wl_shell_surface_add_listener(w->shell_surface, &shell_listener, w);
wl_shell_surface_set_toplevel(w->shell_surface);
}
wl_list_insert(display->windows.prev, &w->link);
display->last_error = UWAC_SUCCESS;
return w;
out_error_shell:
wl_surface_destroy(w->surface);
out_error_surface:
UwacWindowDestroyBuffers(w);
out_error_free:
free(w);
return NULL;
}
UwacReturnCode UwacDestroyWindow(UwacWindow **pwindow) {
UwacWindow *w;
assert (pwindow);
w = *pwindow;
UwacWindowDestroyBuffers(w);
if (w->frame_callback)
wl_callback_destroy(w->frame_callback);
if (w->xdg_surface)
xdg_surface_destroy(w->xdg_surface);
#if BUILD_IVI
if (w->ivi_surface)
ivi_surface_destroy(w->ivi_surface);
#endif
if (w->opaque_region)
wl_region_destroy(w->opaque_region);
if (w->input_region)
wl_region_destroy(w->opaque_region);
wl_surface_destroy(w->surface);
wl_list_remove(&w->link);
free(w);
*pwindow = NULL;
return UWAC_SUCCESS;
}
UwacReturnCode UwacWindowSetOpaqueRegion(UwacWindow *window, uint32_t x, uint32_t y, uint32_t width,
uint32_t height)
{
assert(window);
if (window->opaque_region)
wl_region_destroy(window->opaque_region);
window->opaque_region = wl_compositor_create_region(window->display->compositor);
if (!window->opaque_region)
return UWAC_ERROR_NOMEMORY;
wl_region_add(window->opaque_region, x, y, width, height);
wl_surface_set_opaque_region(window->surface, window->opaque_region);
return UWAC_SUCCESS;
}
UwacReturnCode UwacWindowSetInputRegion(UwacWindow *window, uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
assert(window);
if (window->input_region)
wl_region_destroy(window->input_region);
window->input_region = wl_compositor_create_region(window->display->compositor);
if (!window->input_region)
return UWAC_ERROR_NOMEMORY;
wl_region_add(window->input_region, x, y, width, height);
wl_surface_set_input_region(window->surface, window->input_region);
return UWAC_SUCCESS;
}
void *UwacWindowGetDrawingBuffer(UwacWindow *window) {
return window->drawingBuffer->data;
}
static void frame_done_cb(void *data, struct wl_callback *callback, uint32_t time);
static const struct wl_callback_listener frame_listener = {
frame_done_cb
};
static void UwacSubmitBufferPtr(UwacWindow *window, UwacBuffer *buffer) {
int nrects, i;
#ifdef HAVE_PIXMAN_REGION
const pixman_box32_t *box;
#else
const RECTANGLE_16 *box;
#endif
wl_surface_attach(window->surface, buffer->wayland_buffer, 0, 0);
#ifdef HAVE_PIXMAN_REGION
box = pixman_region32_rectangles(&buffer->damage, &nrects);
for (i = 0; i < nrects; i++, box++)
wl_surface_damage(window->surface, box->x1, box->y1, (box->x2 - box->x1), (box->y2 - box->y1));
#else
box = region16_rects(&buffer->damage, &nrects);
for (i = 0; i < nrects; i++, box++)
wl_surface_damage(window->surface, box->left, box->top, (box->right - box->left), (box->bottom - box->top));
#endif
if (window->frame_callback)
wl_callback_destroy(window->frame_callback);
window->frame_callback = wl_surface_frame(window->surface);
wl_callback_add_listener(window->frame_callback, &frame_listener, window);
wl_surface_commit(window->surface);
#ifdef HAVE_PIXMAN_REGION
pixman_region32_clear(&buffer->damage);
#else
region16_clear(&buffer->damage);
#endif
}
static void frame_done_cb(void *data, struct wl_callback *callback, uint32_t time) {
UwacWindow *window = (UwacWindow *)data;
UwacFrameDoneEvent *event;
window->pendingBuffer = NULL;
event = (UwacFrameDoneEvent *)UwacDisplayNewEvent(window->display, UWAC_EVENT_FRAME_DONE);
if(event)
event->window = window;
}
UwacReturnCode UwacWindowAddDamage(UwacWindow *window, uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
#ifdef HAVE_PIXMAN_REGION
if (!pixman_region32_union_rect(&window->drawingBuffer->damage, &window->drawingBuffer->damage, x, y, width, height))
#else
RECTANGLE_16 box;
box.left = x; box.top = y;
box.right = x + width; box.bottom = y + height;
if (!region16_union_rect(&window->drawingBuffer->damage, &window->drawingBuffer->damage, &box))
#endif
return UWAC_ERROR_INTERNAL;
return UWAC_SUCCESS;
}
UwacReturnCode UwacWindowSubmitBuffer(UwacWindow *window, bool copyContentForNextFrame) {
UwacBuffer *drawingBuffer = window->drawingBuffer;
if (window->pendingBuffer) {
/* we already have a pending frame, don't do anything*/
return UWAC_SUCCESS;
}
UwacSubmitBufferPtr(window, drawingBuffer);
window->pendingBuffer = window->drawingBuffer;
window->drawingBuffer = UwacWindowFindFreeBuffer(window);
if (!window->drawingBuffer)
return UWAC_ERROR_NOMEMORY;
if (copyContentForNextFrame) {
memcpy(window->drawingBuffer->data, window->pendingBuffer->data, window->stride * window->height);
}
return UWAC_SUCCESS;
}
UwacReturnCode UwacWindowGetGeometry(UwacWindow *window, UwacSize *geometry) {
assert(window);
assert(geometry);
geometry->width = window->width;
geometry->height = window->height;
return UWAC_SUCCESS;
}
UwacReturnCode UwacWindowSetFullscreenState(UwacWindow *window, UwacOutput *output, bool isFullscreen) {
if (window->xdg_surface) {
if (isFullscreen) {
xdg_surface_set_fullscreen(window->xdg_surface, output ? output->output : NULL);
} else {
xdg_surface_unset_fullscreen(window->xdg_surface);
}
}
return UWAC_SUCCESS;
}
void UwacWindowSetTitle(UwacWindow *window, const char *name) {
if (window->xdg_surface)
xdg_surface_set_title(window->xdg_surface, name);
else if (window->shell_surface)
wl_shell_surface_set_title(window->shell_surface, name);
}

View File

@ -0,0 +1,206 @@
<protocol name="fullscreen_shell">
<interface name="_wl_fullscreen_shell" version="1">
<description summary="Displays a single surface per output">
Displays a single surface per output.
This interface provides a mechanism for a single client to display
simple full-screen surfaces. While there technically may be multiple
clients bound to this interface, only one of those clients should be
shown at a time.
To present a surface, the client uses either the present_surface or
present_surface_for_mode requests. Presenting a surface takes effect
on the next wl_surface.commit. See the individual requests for
details about scaling and mode switches.
The client can have at most one surface per output at any time.
Requesting a surface be presented on an output that already has a
surface replaces the previously presented surface. Presenting a null
surface removes its content and effectively disables the output.
Exactly what happens when an output is "disabled" is
compositor-specific. The same surface may be presented on multiple
outputs simultaneously.
Once a surface is presented on an output, it stays on that output
until either the client removes it or the compositor destroys the
output. This way, the client can update the output's contents by
simply attaching a new buffer.
</description>
<request name="release" type="destructor">
<description summary="release the wl_fullscreen_shell interface">
Release the binding from the wl_fullscreen_shell interface
This destroys the server-side object and frees this binding. If
the client binds to wl_fullscreen_shell multiple times, it may wish
to free some of those bindings.
</description>
</request>
<enum name="capability">
<description summary="capabilities advertised by the compositor">
Various capabilities that can be advertised by the compositor. They
are advertised one-at-a-time when the wl_fullscreen_shell interface is
bound. See the wl_fullscreen_shell.capability event for more details.
ARBITRARY_MODE:
This is a hint to the client that indicates that the compositor is
capable of setting practically any mode on its outputs. If this
capability is provided, wl_fullscreen_shell.present_surface_for_mode
will almost never fail and clients should feel free to set whatever
mode they like. If the compositor does not advertise this, it may
still support some modes that are not advertised through wl_global.mode
but it is less likely.
CURSOR_PLANE:
This is a hint to the client that indicates that the compositor can
handle a cursor surface from the client without actually compositing.
This may be because of a hardware cursor plane or some other mechanism.
If the compositor does not advertise this capability then setting
wl_pointer.cursor may degrade performance or be ignored entirely. If
CURSOR_PLANE is not advertised, it is recommended that the client draw
its own cursor and set wl_pointer.cursor(NULL).
</description>
<entry name="arbitrary_modes" value="1" summary="compositor is capable of almost any output mode"/>
<entry name="cursor_plane" value="2" summary="compositor has a separate cursor plane"/>
</enum>
<event name="capability">
<description summary="advertises a capability of the compositor">
Advertises a single capability of the compositor.
When the wl_fullscreen_shell interface is bound, this event is emitted
once for each capability advertised. Valid capabilities are given by
the wl_fullscreen_shell.capability enum. If clients want to take
advantage of any of these capabilities, they should use a
wl_display.sync request immediately after binding to ensure that they
receive all the capability events.
</description>
<arg name="capabilty" type="uint"/>
</event>
<enum name="present_method">
<description summary="different method to set the surface fullscreen">
Hints to indicate to the compositor how to deal with a conflict
between the dimensions of the surface and the dimensions of the
output. The compositor is free to ignore this parameter.
</description>
<entry name="default" value="0" summary="no preference, apply default policy"/>
<entry name="center" value="1" summary="center the surface on the output"/>
<entry name="zoom" value="2" summary="scale the surface, preserving aspect ratio, to the largest size that will fit on the output" />
<entry name="zoom_crop" value="3" summary="scale the surface, preserving aspect ratio, to fully fill the output cropping if needed" />
<entry name="stretch" value="4" summary="scale the surface to the size of the output ignoring aspect ratio" />
</enum>
<request name="present_surface">
<description summary="present surface for display">
Present a surface on the given output.
If the output is null, the compositor will present the surface on
whatever display (or displays) it thinks best. In particular, this
may replace any or all surfaces currently presented so it should
not be used in combination with placing surfaces on specific
outputs.
The method parameter is a hint to the compositor for how the surface
is to be presented. In particular, it tells the compositor how to
handle a size mismatch between the presented surface and the
output. The compositor is free to ignore this parameter.
The "zoom", "zoom_crop", and "stretch" methods imply a scaling
operation on the surface. This will override any kind of output
scaling, so the buffer_scale property of the surface is effectively
ignored.
</description>
<arg name="surface" type="object" interface="wl_surface" allow-null="true"/>
<arg name="method" type="uint"/>
<arg name="output" type="object" interface="wl_output" allow-null="true"/>
</request>
<request name="present_surface_for_mode">
<description summary="present surface for display at a particular mode">
Presents a surface on the given output for a particular mode.
If the current size of the output differs from that of the surface,
the compositor will attempt to change the size of the output to
match the surface. The result of the mode-switch operation will be
returned via the provided wl_fullscreen_shell_mode_feedback object.
If the current output mode matches the one requested or if the
compositor successfully switches the mode to match the surface,
then the mode_successful event will be sent and the output will
contain the contents of the given surface. If the compositor
cannot match the output size to the surface size, the mode_failed
will be sent and the output will contain the contents of the
previously presented surface (if any). If another surface is
presented on the given output before either of these has a chance
to happen, the present_cancelled event will be sent.
Due to race conditions and other issues unknown to the client, no
mode-switch operation is guaranteed to succeed. However, if the
mode is one advertised by wl_output.mode or if the compositor
advertises the ARBITRARY_MODES capability, then the client should
expect that the mode-switch operation will usually succeed.
If the size of the presented surface changes, the resulting output
is undefined. The compositor may attempt to change the output mode
to compensate. However, there is no guarantee that a suitable mode
will be found and the client has no way to be notified of success
or failure.
The framerate parameter specifies the desired framerate for the
output in mHz. The compositor is free to ignore this parameter. A
value of 0 indicates that the client has no preference.
If the value of wl_output.scale differs from wl_surface.buffer_scale,
then the compositor may choose a mode that matches either the buffer
size or the surface size. In either case, the surface will fill the
output.
</description>
<arg name="surface" type="object" interface="wl_surface"/>
<arg name="output" type="object" interface="wl_output"/>
<arg name="framerate" type="int"/>
<arg name="feedback" type="new_id" interface="_wl_fullscreen_shell_mode_feedback"/>
</request>
<enum name="error">
<description summary="wl_fullscreen_shell error values">
These errors can be emitted in response to wl_fullscreen_shell requests
</description>
<entry name="invalid_method" value="0" summary="present_method is not known"/>
</enum>
</interface>
<interface name="_wl_fullscreen_shell_mode_feedback" version="1">
<event name="mode_successful">
<description summary="mode switch succeeded">
This event indicates that the attempted mode switch operation was
successful. A surface of the size requested in the mode switch
will fill the output without scaling.
Upon receiving this event, the client should destroy the
wl_fullscreen_shell_mode_feedback object.
</description>
</event>
<event name="mode_failed">
<description summary="mode switch failed">
This event indicates that the attempted mode switch operation
failed. This may be because the requested output mode is not
possible or it may mean that the compositor does not want to allow it.
Upon receiving this event, the client should destroy the
wl_fullscreen_shell_mode_feedback object.
</description>
</event>
<event name="present_cancelled">
<description summary="mode switch cancelled">
This event indicates that the attempted mode switch operation was
cancelled. Most likely this is because the client requested a
second mode switch before the first one completed.
Upon receiving this event, the client should destroy the
wl_fullscreen_shell_mode_feedback object.
</description>
</event>
</interface>
</protocol>

View File

@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="ivi_application">
<copyright>
Copyright (C) 2013 DENSO CORPORATION
Copyright (c) 2013 BMW Car IT GmbH
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
</copyright>
<interface name="ivi_surface" version="1">
<description summary="application interface to surface in ivi compositor"/>
<request name="destroy" type="destructor">
<description summary="destroy ivi_surface">
This removes link from ivi_id to wl_surface and destroys ivi_surface.
The ID, ivi_id, is free and can be used for surface_create again.
</description>
</request>
<event name="configure">
<description summary="suggest resize">
The configure event asks the client to resize its surface.
The size is a hint, in the sense that the client is free to
ignore it if it doesn't resize, pick a smaller size (to
satisfy aspect ratio or resize in steps of NxM pixels).
The client is free to dismiss all but the last configure
event it received.
The width and height arguments specify the size of the window
in surface local coordinates.
</description>
<arg name="width" type="int"/>
<arg name="height" type="int"/>
</event>
</interface>
<interface name="ivi_application" version="1">
<description summary="create ivi-style surfaces">
This interface is exposed as a global singleton.
This interface is implemented by servers that provide IVI-style user interfaces.
It allows clients to associate a ivi_surface with wl_surface.
</description>
<enum name="error">
<entry name="role" value="0" summary="given wl_surface has another role"/>
<entry name="ivi_id" value="1" summary="given ivi_id is assigned to another wl_surface"/>
</enum>
<request name="surface_create">
<description summary="create ivi_surface with numeric ID in ivi compositor">
This request gives the wl_surface the role of an IVI Surface. Creating more than
one ivi_surface for a wl_surface is not allowed. Note, that this still allows the
following example:
1. create a wl_surface
2. create ivi_surface for the wl_surface
3. destroy the ivi_surface
4. create ivi_surface for the wl_surface (with the same or another ivi_id as before)
surface_create will create a interface:ivi_surface with numeric ID; ivi_id in
ivi compositor. These ivi_ids are defined as unique in the system to identify
it inside of ivi compositor. The ivi compositor implements business logic how to
set properties of the surface with ivi_id according to status of the system.
E.g. a unique ID for Car Navigation application is used for implementing special
logic of the application about where it shall be located.
The server regards following cases as protocol errors and disconnects the client.
- wl_surface already has an nother role.
- ivi_id is already assigned to an another wl_surface.
If client destroys ivi_surface or wl_surface which is assigne to the ivi_surface,
ivi_id which is assigned to the ivi_surface is free for reuse.
</description>
<arg name="ivi_id" type="uint"/>
<arg name="surface" type="object" interface="wl_surface"/>
<arg name="id" type="new_id" interface="ivi_surface"/>
</request>
</interface>
</protocol>

View File

@ -0,0 +1,608 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="xdg_shell">
<copyright>
Copyright © 2008-2013 Kristian Høgsberg
Copyright © 2013 Rafael Antognolli
Copyright © 2013 Jasper St. Pierre
Copyright © 2010-2013 Intel Corporation
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
</copyright>
<interface name="xdg_shell" version="1">
<description summary="create desktop-style surfaces">
xdg_shell allows clients to turn a wl_surface into a "real window"
which can be dragged, resized, stacked, and moved around by the
user. Everything about this interface is suited towards traditional
desktop environments.
</description>
<enum name="version">
<description summary="latest protocol version">
The 'current' member of this enum gives the version of the
protocol. Implementations can compare this to the version
they implement using static_assert to ensure the protocol and
implementation versions match.
</description>
<entry name="current" value="5" summary="Always the latest version"/>
</enum>
<enum name="error">
<entry name="role" value="0" summary="given wl_surface has another role"/>
<entry name="defunct_surfaces" value="1" summary="xdg_shell was destroyed before children"/>
<entry name="not_the_topmost_popup" value="2" summary="the client tried to map or destroy a non-topmost popup"/>
<entry name="invalid_popup_parent" value="3" summary="the client specified an invalid popup parent surface"/>
</enum>
<request name="destroy" type="destructor">
<description summary="destroy xdg_shell">
Destroy this xdg_shell object.
Destroying a bound xdg_shell object while there are surfaces
still alive created by this xdg_shell object instance is illegal
and will result in a protocol error.
</description>
</request>
<request name="use_unstable_version">
<description summary="enable use of this unstable version">
Negotiate the unstable version of the interface. This
mechanism is in place to ensure client and server agree on the
unstable versions of the protocol that they speak or exit
cleanly if they don't agree. This request will go away once
the xdg-shell protocol is stable.
</description>
<arg name="version" type="int"/>
</request>
<request name="get_xdg_surface">
<description summary="create a shell surface from a surface">
This creates an xdg_surface for the given surface and gives it the
xdg_surface role. A wl_surface can only be given an xdg_surface role
once. If get_xdg_surface is called with a wl_surface that already has
an active xdg_surface associated with it, or if it had any other role,
an error is raised.
See the documentation of xdg_surface for more details about what an
xdg_surface is and how it is used.
</description>
<arg name="id" type="new_id" interface="xdg_surface"/>
<arg name="surface" type="object" interface="wl_surface"/>
</request>
<request name="get_xdg_popup">
<description summary="create a popup for a surface">
This creates an xdg_popup for the given surface and gives it the
xdg_popup role. A wl_surface can only be given an xdg_popup role
once. If get_xdg_popup is called with a wl_surface that already has
an active xdg_popup associated with it, or if it had any other role,
an error is raised.
This request must be used in response to some sort of user action
like a button press, key press, or touch down event.
See the documentation of xdg_popup for more details about what an
xdg_popup is and how it is used.
</description>
<arg name="id" type="new_id" interface="xdg_popup"/>
<arg name="surface" type="object" interface="wl_surface"/>
<arg name="parent" type="object" interface="wl_surface"/>
<arg name="seat" type="object" interface="wl_seat" summary="the wl_seat of the user event"/>
<arg name="serial" type="uint" summary="the serial of the user event"/>
<arg name="x" type="int"/>
<arg name="y" type="int"/>
</request>
<event name="ping">
<description summary="check if the client is alive">
The ping event asks the client if it's still alive. Pass the
serial specified in the event back to the compositor by sending
a "pong" request back with the specified serial.
Compositors can use this to determine if the client is still
alive. It's unspecified what will happen if the client doesn't
respond to the ping request, or in what timeframe. Clients should
try to respond in a reasonable amount of time.
A compositor is free to ping in any way it wants, but a client must
always respond to any xdg_shell object it created.
</description>
<arg name="serial" type="uint" summary="pass this to the pong request"/>
</event>
<request name="pong">
<description summary="respond to a ping event">
A client must respond to a ping event with a pong request or
the client may be deemed unresponsive.
</description>
<arg name="serial" type="uint" summary="serial of the ping event"/>
</request>
</interface>
<interface name="xdg_surface" version="1">
<description summary="A desktop window">
An interface that may be implemented by a wl_surface, for
implementations that provide a desktop-style user interface.
It provides requests to treat surfaces like windows, allowing to set
properties like maximized, fullscreen, minimized, and to move and resize
them, and associate metadata like title and app id.
The client must call wl_surface.commit on the corresponding wl_surface
for the xdg_surface state to take effect. Prior to committing the new
state, it can set up initial configuration, such as maximizing or setting
a window geometry.
Even without attaching a buffer the compositor must respond to initial
committed configuration, for instance sending a configure event with
expected window geometry if the client maximized its surface during
initialization.
For a surface to be mapped by the compositor the client must have
committed both an xdg_surface state and a buffer.
</description>
<request name="destroy" type="destructor">
<description summary="Destroy the xdg_surface">
Unmap and destroy the window. The window will be effectively
hidden from the user's point of view, and all state like
maximization, fullscreen, and so on, will be lost.
</description>
</request>
<request name="set_parent">
<description summary="set the parent of this surface">
Set the "parent" of this surface. This window should be stacked
above a parent. The parent surface must be mapped as long as this
surface is mapped.
Parent windows should be set on dialogs, toolboxes, or other
"auxiliary" surfaces, so that the parent is raised when the dialog
is raised.
</description>
<arg name="parent" type="object" interface="xdg_surface" allow-null="true"/>
</request>
<request name="set_title">
<description summary="set surface title">
Set a short title for the surface.
This string may be used to identify the surface in a task bar,
window list, or other user interface elements provided by the
compositor.
The string must be encoded in UTF-8.
</description>
<arg name="title" type="string"/>
</request>
<request name="set_app_id">
<description summary="set application ID">
Set an application identifier for the surface.
The app ID identifies the general class of applications to which
the surface belongs. The compositor can use this to group multiple
surfaces together, or to determine how to launch a new application.
For D-Bus activatable applications, the app ID is used as the D-Bus
service name.
The compositor shell will try to group application surfaces together
by their app ID. As a best practice, it is suggested to select app
ID's that match the basename of the application's .desktop file.
For example, "org.freedesktop.FooViewer" where the .desktop file is
"org.freedesktop.FooViewer.desktop".
See the desktop-entry specification [0] for more details on
application identifiers and how they relate to well-known D-Bus
names and .desktop files.
[0] http://standards.freedesktop.org/desktop-entry-spec/
</description>
<arg name="app_id" type="string"/>
</request>
<request name="show_window_menu">
<description summary="show the window menu">
Clients implementing client-side decorations might want to show
a context menu when right-clicking on the decorations, giving the
user a menu that they can use to maximize or minimize the window.
This request asks the compositor to pop up such a window menu at
the given position, relative to the local surface coordinates of
the parent surface. There are no guarantees as to what menu items
the window menu contains.
This request must be used in response to some sort of user action
like a button press, key press, or touch down event.
</description>
<arg name="seat" type="object" interface="wl_seat" summary="the wl_seat of the user event"/>
<arg name="serial" type="uint" summary="the serial of the user event"/>
<arg name="x" type="int" summary="the x position to pop up the window menu at"/>
<arg name="y" type="int" summary="the y position to pop up the window menu at"/>
</request>
<request name="move">
<description summary="start an interactive move">
Start an interactive, user-driven move of the surface.
This request must be used in response to some sort of user action
like a button press, key press, or touch down event. The passed
serial is used to determine the type of interactive move (touch,
pointer, etc).
The server may ignore move requests depending on the state of
the surface (e.g. fullscreen or maximized), or if the passed serial
is no longer valid.
If triggered, the surface will lose the focus of the device
(wl_pointer, wl_touch, etc) used for the move. It is up to the
compositor to visually indicate that the move is taking place, such as
updating a pointer cursor, during the move. There is no guarantee
that the device focus will return when the move is completed.
</description>
<arg name="seat" type="object" interface="wl_seat" summary="the wl_seat of the user event"/>
<arg name="serial" type="uint" summary="the serial of the user event"/>
</request>
<enum name="resize_edge">
<description summary="edge values for resizing">
These values are used to indicate which edge of a surface
is being dragged in a resize operation.
</description>
<entry name="none" value="0"/>
<entry name="top" value="1"/>
<entry name="bottom" value="2"/>
<entry name="left" value="4"/>
<entry name="top_left" value="5"/>
<entry name="bottom_left" value="6"/>
<entry name="right" value="8"/>
<entry name="top_right" value="9"/>
<entry name="bottom_right" value="10"/>
</enum>
<request name="resize">
<description summary="start an interactive resize">
Start a user-driven, interactive resize of the surface.
This request must be used in response to some sort of user action
like a button press, key press, or touch down event. The passed
serial is used to determine the type of interactive resize (touch,
pointer, etc).
The server may ignore resize requests depending on the state of
the surface (e.g. fullscreen or maximized).
If triggered, the client will receive configure events with the
"resize" state enum value and the expected sizes. See the "resize"
enum value for more details about what is required. The client
must also acknowledge configure events using "ack_configure". After
the resize is completed, the client will receive another "configure"
event without the resize state.
If triggered, the surface also will lose the focus of the device
(wl_pointer, wl_touch, etc) used for the resize. It is up to the
compositor to visually indicate that the resize is taking place,
such as updating a pointer cursor, during the resize. There is no
guarantee that the device focus will return when the resize is
completed.
The edges parameter specifies how the surface should be resized,
and is one of the values of the resize_edge enum. The compositor
may use this information to update the surface position for
example when dragging the top left corner. The compositor may also
use this information to adapt its behavior, e.g. choose an
appropriate cursor image.
</description>
<arg name="seat" type="object" interface="wl_seat" summary="the wl_seat of the user event"/>
<arg name="serial" type="uint" summary="the serial of the user event"/>
<arg name="edges" type="uint" summary="which edge or corner is being dragged"/>
</request>
<enum name="state">
<description summary="types of state on the surface">
The different state values used on the surface. This is designed for
state values like maximized, fullscreen. It is paired with the
configure event to ensure that both the client and the compositor
setting the state can be synchronized.
States set in this way are double-buffered. They will get applied on
the next commit.
Desktop environments may extend this enum by taking up a range of
values and documenting the range they chose in this description.
They are not required to document the values for the range that they
chose. Ideally, any good extensions from a desktop environment should
make its way into standardization into this enum.
The current reserved ranges are:
0x0000 - 0x0FFF: xdg-shell core values, documented below.
0x1000 - 0x1FFF: GNOME
</description>
<entry name="maximized" value="1" summary="the surface is maximized">
The surface is maximized. The window geometry specified in the configure
event must be obeyed by the client.
</entry>
<entry name="fullscreen" value="2" summary="the surface is fullscreen">
The surface is fullscreen. The window geometry specified in the configure
event must be obeyed by the client.
</entry>
<entry name="resizing" value="3">
The surface is being resized. The window geometry specified in the
configure event is a maximum; the client cannot resize beyond it.
Clients that have aspect ratio or cell sizing configuration can use
a smaller size, however.
</entry>
<entry name="activated" value="4">
Client window decorations should be painted as if the window is
active. Do not assume this means that the window actually has
keyboard or pointer focus.
</entry>
</enum>
<event name="configure">
<description summary="suggest a surface change">
The configure event asks the client to resize its surface or to
change its state.
The width and height arguments specify a hint to the window
about how its surface should be resized in window geometry
coordinates. See set_window_geometry.
If the width or height arguments are zero, it means the client
should decide its own window dimension. This may happen when the
compositor need to configure the state of the surface but doesn't
have any information about any previous or expected dimension.
The states listed in the event specify how the width/height
arguments should be interpreted, and possibly how it should be
drawn.
Clients should arrange their surface for the new size and
states, and then send a ack_configure request with the serial
sent in this configure event at some point before committing
the new surface.
If the client receives multiple configure events before it
can respond to one, it is free to discard all but the last
event it received.
</description>
<arg name="width" type="int"/>
<arg name="height" type="int"/>
<arg name="states" type="array"/>
<arg name="serial" type="uint"/>
</event>
<request name="ack_configure">
<description summary="ack a configure event">
When a configure event is received, if a client commits the
surface in response to the configure event, then the client
must make a ack_configure request before the commit request,
passing along the serial of the configure event.
For instance, the compositor might use this information to move
a surface to the top left only when the client has drawn itself
for the maximized or fullscreen state.
If the client receives multiple configure events before it
can respond to one, it only has to ack the last configure event.
</description>
<arg name="serial" type="uint" summary="the serial from the configure event"/>
</request>
<request name="set_window_geometry">
<description summary="set the new window geometry">
The window geometry of a window is its "visible bounds" from the
user's perspective. Client-side decorations often have invisible
portions like drop-shadows which should be ignored for the
purposes of aligning, placing and constraining windows.
The window geometry is double buffered, and will be applied at the
time wl_surface.commit of the corresponding wl_surface is called.
Once the window geometry of the surface is set once, it is not
possible to unset it, and it will remain the same until
set_window_geometry is called again, even if a new subsurface or
buffer is attached.
If never set, the value is the full bounds of the surface,
including any subsurfaces. This updates dynamically on every
commit. This unset mode is meant for extremely simple clients.
If responding to a configure event, the window geometry in here
must respect the sizing negotiations specified by the states in
the configure event.
The arguments are given in the surface local coordinate space of
the wl_surface associated with this xdg_surface.
The width and height must be greater than zero.
</description>
<arg name="x" type="int"/>
<arg name="y" type="int"/>
<arg name="width" type="int"/>
<arg name="height" type="int"/>
</request>
<request name="set_maximized">
<description summary="maximize the window">
Maximize the surface.
After requesting that the surface should be maximized, the compositor
will respond by emitting a configure event with the "maximized" state
and the required window geometry. The client should then update its
content, drawing it in a maximized state, i.e. without shadow or other
decoration outside of the window geometry. The client must also
acknowledge the configure when committing the new content (see
ack_configure).
It is up to the compositor to decide how and where to maximize the
surface, for example which output and what region of the screen should
be used.
If the surface was already maximized, the compositor will still emit
a configure event with the "maximized" state.
</description>
</request>
<request name="unset_maximized">
<description summary="unmaximize the window">
Unmaximize the surface.
After requesting that the surface should be unmaximized, the compositor
will respond by emitting a configure event without the "maximized"
state. If available, the compositor will include the window geometry
dimensions the window had prior to being maximized in the configure
request. The client must then update its content, drawing it in a
regular state, i.e. potentially with shadow, etc. The client must also
acknowledge the configure when committing the new content (see
ack_configure).
It is up to the compositor to position the surface after it was
unmaximized; usually the position the surface had before maximizing, if
applicable.
If the surface was already not maximized, the compositor will still
emit a configure event without the "maximized" state.
</description>
</request>
<request name="set_fullscreen">
<description summary="set the window as fullscreen on a monitor">
Make the surface fullscreen.
You can specify an output that you would prefer to be fullscreen.
If this value is NULL, it's up to the compositor to choose which
display will be used to map this surface.
If the surface doesn't cover the whole output, the compositor will
position the surface in the center of the output and compensate with
black borders filling the rest of the output.
</description>
<arg name="output" type="object" interface="wl_output" allow-null="true"/>
</request>
<request name="unset_fullscreen" />
<request name="set_minimized">
<description summary="set the window as minimized">
Request that the compositor minimize your surface. There is no
way to know if the surface is currently minimized, nor is there
any way to unset minimization on this surface.
If you are looking to throttle redrawing when minimized, please
instead use the wl_surface.frame event for this, as this will
also work with live previews on windows in Alt-Tab, Expose or
similar compositor features.
</description>
</request>
<event name="close">
<description summary="surface wants to be closed">
The close event is sent by the compositor when the user
wants the surface to be closed. This should be equivalent to
the user clicking the close button in client-side decorations,
if your application has any...
This is only a request that the user intends to close your
window. The client may choose to ignore this request, or show
a dialog to ask the user to save their data...
</description>
</event>
</interface>
<interface name="xdg_popup" version="1">
<description summary="short-lived, popup surfaces for menus">
A popup surface is a short-lived, temporary surface that can be
used to implement menus. It takes an explicit grab on the surface
that will be dismissed when the user dismisses the popup. This can
be done by the user clicking outside the surface, using the keyboard,
or even locking the screen through closing the lid or a timeout.
When the popup is dismissed, a popup_done event will be sent out,
and at the same time the surface will be unmapped. The xdg_popup
object is now inert and cannot be reactivated, so clients should
destroy it. Explicitly destroying the xdg_popup object will also
dismiss the popup and unmap the surface.
Clients will receive events for all their surfaces during this
grab (which is an "owner-events" grab in X11 parlance). This is
done so that users can navigate through submenus and other
"nested" popup windows without having to dismiss the topmost
popup.
Clients that want to dismiss the popup when another surface of
their own is clicked should dismiss the popup using the destroy
request.
The parent surface must have either an xdg_surface or xdg_popup
role.
Specifying an xdg_popup for the parent means that the popups are
nested, with this popup now being the topmost popup. Nested
popups must be destroyed in the reverse order they were created
in, e.g. the only popup you are allowed to destroy at all times
is the topmost one.
If there is an existing popup when creating a new popup, the
parent must be the current topmost popup.
A parent surface must be mapped before the new popup is mapped.
When compositors choose to dismiss a popup, they will likely
dismiss every nested popup as well. When a compositor dismisses
popups, it will follow the same dismissing order as required
from the client.
The x and y arguments passed when creating the popup object specify
where the top left of the popup should be placed, relative to the
local surface coordinates of the parent surface. See
xdg_shell.get_xdg_popup.
The client must call wl_surface.commit on the corresponding wl_surface
for the xdg_popup state to take effect.
For a surface to be mapped by the compositor the client must have
committed both the xdg_popup state and a buffer.
</description>
<request name="destroy" type="destructor">
<description summary="remove xdg_popup interface">
This destroys the popup. Explicitly destroying the xdg_popup
object will also dismiss the popup, and unmap the surface.
If this xdg_popup is not the "topmost" popup, a protocol error
will be sent.
</description>
</request>
<event name="popup_done">
<description summary="popup interaction is done">
The popup_done event is sent out when a popup is dismissed by the
compositor. The client should destroy the xdg_popup object at this
point.
</description>
</event>
</interface>
</protocol>