From 87d6caa69a40c24a33669ea17dc767dcbf5ad186 Mon Sep 17 00:00:00 2001 From: David FORT Date: Mon, 18 Jan 2016 15:55:33 +0100 Subject: [PATCH] Integrate UWAC in to the FreeRDP source tree So the wayland client can still be built without installing UWAC as a dependency. --- CMakeLists.txt | 4 + client/Wayland/CMakeLists.txt | 4 +- cmake/FindWayland.cmake | 60 +-- uwac/CMakeLists.txt | 20 + uwac/include/CMakeLists.txt | 20 + uwac/include/uwac/uwac-tools.h | 60 +++ uwac/include/uwac/uwac.h | 480 +++++++++++++++++++++ uwac/libuwac/.gitignore | 7 + uwac/libuwac/CMakeLists.txt | 79 ++++ uwac/libuwac/uwac-display.c | 631 +++++++++++++++++++++++++++ uwac/libuwac/uwac-input.c | 831 ++++++++++++++++++++++++++++++++++++ uwac/libuwac/uwac-os.c | 236 ++++++++++ uwac/libuwac/uwac-os.h | 45 ++ uwac/libuwac/uwac-output.c | 126 ++++++ uwac/libuwac/uwac-priv.h | 237 ++++++++++ uwac/libuwac/uwac-tools.c | 93 ++++ uwac/libuwac/uwac-utils.c | 63 +++ uwac/libuwac/uwac-utils.h | 53 +++ uwac/libuwac/uwac-window.c | 616 ++++++++++++++++++++++++++ uwac/protocols/fullscreen-shell.xml | 206 +++++++++ uwac/protocols/ivi-application.xml | 100 +++++ uwac/protocols/xdg-shell.xml | 608 ++++++++++++++++++++++++++ 22 files changed, 4550 insertions(+), 29 deletions(-) create mode 100644 uwac/CMakeLists.txt create mode 100644 uwac/include/CMakeLists.txt create mode 100644 uwac/include/uwac/uwac-tools.h create mode 100644 uwac/include/uwac/uwac.h create mode 100644 uwac/libuwac/.gitignore create mode 100644 uwac/libuwac/CMakeLists.txt create mode 100644 uwac/libuwac/uwac-display.c create mode 100644 uwac/libuwac/uwac-input.c create mode 100644 uwac/libuwac/uwac-os.c create mode 100644 uwac/libuwac/uwac-os.h create mode 100644 uwac/libuwac/uwac-output.c create mode 100644 uwac/libuwac/uwac-priv.h create mode 100644 uwac/libuwac/uwac-tools.c create mode 100644 uwac/libuwac/uwac-utils.c create mode 100644 uwac/libuwac/uwac-utils.h create mode 100644 uwac/libuwac/uwac-window.c create mode 100644 uwac/protocols/fullscreen-shell.xml create mode 100644 uwac/protocols/ivi-application.xml create mode 100644 uwac/protocols/xdg-shell.xml diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e2ca5d..2982da5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/client/Wayland/CMakeLists.txt b/client/Wayland/CMakeLists.txt index 2329891..315be7d 100644 --- a/client/Wayland/CMakeLists.txt +++ b/client/Wayland/CMakeLists.txt @@ -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) diff --git a/cmake/FindWayland.cmake b/cmake/FindWayland.cmake index 0414bdf..e1a7030 100644 --- a/cmake/FindWayland.cmake +++ b/cmake/FindWayland.cmake @@ -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) diff --git a/uwac/CMakeLists.txt b/uwac/CMakeLists.txt new file mode 100644 index 0000000..cbfecb8 --- /dev/null +++ b/uwac/CMakeLists.txt @@ -0,0 +1,20 @@ +# UWAC: Using Wayland As Client +# cmake build script +# +# Copyright 2015 David FORT +# +# 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) + diff --git a/uwac/include/CMakeLists.txt b/uwac/include/CMakeLists.txt new file mode 100644 index 0000000..b6e7129 --- /dev/null +++ b/uwac/include/CMakeLists.txt @@ -0,0 +1,20 @@ +# UWAC: Using Wayland As Client +# cmake build script +# +# Copyright 2015 David FORT +# +# 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) + diff --git a/uwac/include/uwac/uwac-tools.h b/uwac/include/uwac/uwac-tools.h new file mode 100644 index 0000000..600727b --- /dev/null +++ b/uwac/include/uwac/uwac-tools.h @@ -0,0 +1,60 @@ +/* + * Copyright © 2015 David FORT + * + * 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 +#include + +/** @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_ */ diff --git a/uwac/include/uwac/uwac.h b/uwac/include/uwac/uwac.h new file mode 100644 index 0000000..39fafa6 --- /dev/null +++ b/uwac/include/uwac/uwac.h @@ -0,0 +1,480 @@ +/* + * Copyright © 2014-2015 David FORT + * + * 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 +#include + +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_ */ diff --git a/uwac/libuwac/.gitignore b/uwac/libuwac/.gitignore new file mode 100644 index 0000000..11b3543 --- /dev/null +++ b/uwac/libuwac/.gitignore @@ -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 + diff --git a/uwac/libuwac/CMakeLists.txt b/uwac/libuwac/CMakeLists.txt new file mode 100644 index 0000000..b8219bb --- /dev/null +++ b/uwac/libuwac/CMakeLists.txt @@ -0,0 +1,79 @@ +# UWAC: Using Wayland As Client +# +# Copyright 2015 David FORT +# +# 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() diff --git a/uwac/libuwac/uwac-display.c b/uwac/libuwac/uwac-display.c new file mode 100644 index 0000000..2fa20b4 --- /dev/null +++ b/uwac/libuwac/uwac-display.c @@ -0,0 +1,631 @@ +/* + * Copyright © 2014 David FORT + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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, ®istry_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; +} diff --git a/uwac/libuwac/uwac-input.c b/uwac/libuwac/uwac-input.c new file mode 100644 index 0000000..c7dbb83 --- /dev/null +++ b/uwac/libuwac/uwac-input.c @@ -0,0 +1,831 @@ +/* + * Copyright © 2014-2015 David FORT + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + + +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; +} diff --git a/uwac/libuwac/uwac-os.c b/uwac/libuwac/uwac-os.c new file mode 100644 index 0000000..ec9efe4 --- /dev/null +++ b/uwac/libuwac/uwac-os.c @@ -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 +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/uwac/libuwac/uwac-os.h b/uwac/libuwac/uwac-os.h new file mode 100644 index 0000000..be927df --- /dev/null +++ b/uwac/libuwac/uwac-os.h @@ -0,0 +1,45 @@ +/* + * Copyright © 2012 Collabora, Ltd. + * Copyright © 2014 David FORT + * + * 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 + +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 */ diff --git a/uwac/libuwac/uwac-output.c b/uwac/libuwac/uwac-output.c new file mode 100644 index 0000000..0da871c --- /dev/null +++ b/uwac/libuwac/uwac-output.c @@ -0,0 +1,126 @@ +/* + * Copyright © 2014 David FORT + * + * 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 +#include +#include +#include + +#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; +} diff --git a/uwac/libuwac/uwac-priv.h b/uwac/libuwac/uwac-priv.h new file mode 100644 index 0000000..8b0324d --- /dev/null +++ b/uwac/libuwac/uwac-priv.h @@ -0,0 +1,237 @@ +/* + * Copyright © 2014 David FORT + * + * 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 +#include +#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 +#else +#include +#endif + +#include + +#include + + +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_ */ diff --git a/uwac/libuwac/uwac-tools.c b/uwac/libuwac/uwac-tools.c new file mode 100644 index 0000000..73aff22 --- /dev/null +++ b/uwac/libuwac/uwac-tools.c @@ -0,0 +1,93 @@ +/* + * Copyright © 2015 David FORT + * + * 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 +#include +#include + +/** @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; +} diff --git a/uwac/libuwac/uwac-utils.c b/uwac/libuwac/uwac-utils.c new file mode 100644 index 0000000..c9c33f6 --- /dev/null +++ b/uwac/libuwac/uwac-utils.c @@ -0,0 +1,63 @@ +/* + * Copyright © 2012 Collabora, Ltd. + * Copyright © 2008 Kristian Høgsberg + * Copyright © 2014 David FORT + * + * 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 +#include +#include +#include + +#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)); +} + + diff --git a/uwac/libuwac/uwac-utils.h b/uwac/libuwac/uwac-utils.h new file mode 100644 index 0000000..e305073 --- /dev/null +++ b/uwac/libuwac/uwac-utils.h @@ -0,0 +1,53 @@ +/* + * Copyright © 2014 David FORT + * + * 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 + +#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_ */ diff --git a/uwac/libuwac/uwac-window.c b/uwac/libuwac/uwac-window.c new file mode 100644 index 0000000..fa3b860 --- /dev/null +++ b/uwac/libuwac/uwac-window.c @@ -0,0 +1,616 @@ +/* + * Copyright © 2014 David FORT + * + * 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 +#include +#include +#include +#include +#include +#include + +#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); +} diff --git a/uwac/protocols/fullscreen-shell.xml b/uwac/protocols/fullscreen-shell.xml new file mode 100644 index 0000000..e2b994b --- /dev/null +++ b/uwac/protocols/fullscreen-shell.xml @@ -0,0 +1,206 @@ + + + + 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. + + + + + 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. + + + + + + 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). + + + + + + + + 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. + + + + + + + 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. + + + + + + + + + + + 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. + + + + + + + + + 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. + + + + + + + + + + These errors can be emitted in response to wl_fullscreen_shell requests + + + + + + + + + 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. + + + + + 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. + + + + + 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. + + + + diff --git a/uwac/protocols/ivi-application.xml b/uwac/protocols/ivi-application.xml new file mode 100644 index 0000000..8f24226 --- /dev/null +++ b/uwac/protocols/ivi-application.xml @@ -0,0 +1,100 @@ + + + + + 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. + + + + + + + + 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. + + + + + + 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. + + + + + + + + + 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. + + + + + + + + + + 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. + + + + + + + + + diff --git a/uwac/protocols/xdg-shell.xml b/uwac/protocols/xdg-shell.xml new file mode 100644 index 0000000..f98e760 --- /dev/null +++ b/uwac/protocols/xdg-shell.xml @@ -0,0 +1,608 @@ + + + + + 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. + + + + + 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. + + + + + 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. + + + + + + + + + + + + + + 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. + + + + + + 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. + + + + + + + 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. + + + + + + + + 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. + + + + + + + + + + + + + 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. + + + + + + + A client must respond to a ping event with a pong request or + the client may be deemed unresponsive. + + + + + + + + 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. + + + + + 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. + + + + + + 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. + + + + + + + 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. + + + + + + + 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/ + + + + + + + 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. + + + + + + + + + + + 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. + + + + + + + + These values are used to indicate which edge of a surface + is being dragged in a resize operation. + + + + + + + + + + + + + + + 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. + + + + + + + + + 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 + + + The surface is maximized. The window geometry specified in the configure + event must be obeyed by the client. + + + The surface is fullscreen. The window geometry specified in the configure + event must be obeyed by the client. + + + 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. + + + 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. + + + + + + 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. + + + + + + + + + + + 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. + + + + + + + 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. + + + + + + + + + + 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. + + + + + + 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. + + + + + + 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. + + + + + + + + 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. + + + + + + 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... + + + + + + + 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. + + + + + 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. + + + + + + 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. + + + + + -- 2.7.4