diff options
-rw-r--r-- | .gitreview | 5 | ||||
-rw-r--r-- | COPYING | 22 | ||||
-rw-r--r-- | README.md | 97 | ||||
-rw-r--r-- | config/receiver_pipeline_example_general.cfg | 1 | ||||
-rw-r--r-- | config/receiver_pipeline_example_intel.cfg | 1 | ||||
-rw-r--r-- | config/receiver_pipeline_example_rcar.cfg | 1 | ||||
-rw-r--r-- | include/bitmap.h | 35 | ||||
-rw-r--r-- | include/os-compatibility.h | 58 | ||||
-rw-r--r-- | include/wth-receiver-buffer.h | 51 | ||||
-rw-r--r-- | include/wth-receiver-comm.h | 305 | ||||
-rw-r--r-- | include/wth-receiver-seat.h | 263 | ||||
-rw-r--r-- | include/wth-receiver-surface.h | 44 | ||||
-rw-r--r-- | meson.build | 149 | ||||
-rw-r--r-- | meson_options.txt | 7 | ||||
-rw-r--r-- | src/bitmap.c | 107 | ||||
-rw-r--r-- | src/os-compatibility.c | 204 | ||||
-rw-r--r-- | src/wth-receiver-buffer.c | 95 | ||||
-rw-r--r-- | src/wth-receiver-comm.c | 453 | ||||
-rw-r--r-- | src/wth-receiver-gst-egl.c | 1012 | ||||
-rw-r--r-- | src/wth-receiver-gst-shm.c | 847 | ||||
-rw-r--r-- | src/wth-receiver-main.c | 306 | ||||
-rw-r--r-- | src/wth-receiver-seat.c | 311 | ||||
-rw-r--r-- | src/wth-receiver-surface.c | 291 |
23 files changed, 4665 insertions, 0 deletions
diff --git a/.gitreview b/.gitreview new file mode 100644 index 0000000..c14ce8b --- /dev/null +++ b/.gitreview @@ -0,0 +1,5 @@ +[gerrit] +host=gerrit.automotivelinux.org +port=29418 +project=apps/waltham-receiver +defaultbranch=master @@ -0,0 +1,22 @@ +Copyright © 2020 Collabora, Ltd. + +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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..76abc66 --- /dev/null +++ b/README.md @@ -0,0 +1,97 @@ +# Waltham-receiver + +waltham-receiver component is a receiver side implementation for using Waltham +protocol to obtain and process remote output received from remoting-pluging +instantianted/created output by the compositor. + +This component is designed to be used for evaluating the functionalities of +waltham-transmitter plugin. + +This component also acts as weston client application to display/handle various +requests from actual weston client at transmitter side. + +### Architecture + +```` + + ECU 1 ECU 2 + +-----------------------------------------------------+ +----------------------------------------------+ + | +-----------------+ | | | + | | IVI-Application | | | +-----------+-----------+ | + | +-----------------+ | | | Gstreamer | | | + | ^ | Buffer -----------------------> (Decode) | | | + | wayland | +----------------------/ | +-----------+ | | + | v | | (Ethernet) | | Waltham-receiver | | + | +----+---------------------+ | | ----------------------------> | | + | | | Transmitter Plugin |<-----------------------------/ | +-----------------------+ | + | | | | | | Waltham-Protocol | ^ | + | | |---------------------| | | | wayland | | + | | | (remoting-plugin) |------------+ | | v | + | | | | | | +---------------------+ | + | | +-+-------------------+ | | | | | + | | | | | | compositor | | + | | compositor | | | | | | + | +------+-------------------+ | | +----------------+----+ | + | | | | | | + | v | | v | + | +------------+ | | +----------+ | + | | Display | | | | Display | | + | | | | | | | | + | +------------+ | | +----------+ | + +-----------------------------------------------------+ +----------------------------------------------+ + +```` + +### Build Steps (these only apply if building locally) + +1. Prerequisite before building + + weston, wayland, waltham and gstreamer should be built and available. + +2. In waltham-receiver directory, create build directory + + $ meson -Dprefix=$PREFIX_PATH build/ + +3. Run ninja + + $ ninja -C build/ + +4. waltham-receiver binary should be availaible in build directory + +### Connection Establishment + +1. Connect two board over ethernet. + +2. Assign IP to both the boards and check if the simple ping works. + + For example:if transmitter IP: 192.168.2.51 and Waltham-Receiver IP: + 192.168.2.52 then + + $ping 192.168.2.52 (you can also ping vice versa) + +3. Make sure that IP address specified in the weston.ini under + [transmitter-output] matches the Waltham-Receiver IP. + +4. Make sure that IP address on the transmitter side match the Waltham-Receiver + IP. + +### Basic test steps with AGL + +0. Under AGL platform you should already have the waltham-receiver installed. + +1. Start the compositor with the transmitter plugin at the transmitter side, + use agl-shell-app-id=app_id of the run the application and put it on + transmitter screen. Setup a shared port between transmitter and receiver. + Setup the receiver IP address. The transmitter-plugin uses the same section + syntax as the remoting plugin. + +2. Start the compositor at the receiver side + +3. Start the receiver using -p <shared_port> -i <app_id>. If not app_id is + specified the app_id passed by the transmitter will be used. Use -i <app_id> + if you'd like to have the ability to activate and switch the surface if you + intended to start multiple application and still be able to send input and + display the received streamed buffers from the transmitter side. + +4. Start the application on the transmitter side and watch it appear on the + receiver side. diff --git a/config/receiver_pipeline_example_general.cfg b/config/receiver_pipeline_example_general.cfg new file mode 100644 index 0000000..be4d562 --- /dev/null +++ b/config/receiver_pipeline_example_general.cfg @@ -0,0 +1 @@ + udpsrc port=YOUR_RECIEVER_PORT caps="application/x-rtp, media=(string)video, clock-rate=(int)90000, encoding-name=(string)JPEG,payload=(int)26" ! rtpjpegdepay ! jpegdec ! waylandsink name=sink sync=true diff --git a/config/receiver_pipeline_example_intel.cfg b/config/receiver_pipeline_example_intel.cfg new file mode 100644 index 0000000..3872f28 --- /dev/null +++ b/config/receiver_pipeline_example_intel.cfg @@ -0,0 +1 @@ + udpsrc port=YOUR_RECIEVER_PORT caps="application/x-rtp,media=(string)video,clock-rate=(int)90000,encoding-name=(string)H264,payload=(int)96" ! rtpjitterbuffer latency=0 ! rtph264depay ! h264parse config-interval=1 disable-passthrough=true ! mfxdecode ! waylandsink name=sink sync=true diff --git a/config/receiver_pipeline_example_rcar.cfg b/config/receiver_pipeline_example_rcar.cfg new file mode 100644 index 0000000..15fd4ef --- /dev/null +++ b/config/receiver_pipeline_example_rcar.cfg @@ -0,0 +1 @@ + udpsrc port=YOUR_RECIEVER_PORT caps="application/x-rtp,media=(string)video,clock-rate=(int)90000,encoding-name=(string)H264,payload=(int)96" ! rtpjitterbuffer latency=0 ! rtph264depay ! h264parse config-interval=1 disable-passthrough=true ! omxh264dec no-reorder=true ! waylandsink name=sink diff --git a/include/bitmap.h b/include/bitmap.h new file mode 100644 index 0000000..3cef52d --- /dev/null +++ b/include/bitmap.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2013 DENSO CORPORATION + * + * 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 IVICONTROLLER_BITMAP_H_ +#define IVICONTROLLER_BITMAP_H_ + +#include <stdint.h> + +int save_as_bitmap(const char *filename, + const char *buffer, + int32_t image_size, + int32_t width, + int32_t height, + int16_t bpp + ); + +#endif /* IVICONTROLLER_BITMAP_H_*/ diff --git a/include/os-compatibility.h b/include/os-compatibility.h new file mode 100644 index 0000000..690f229 --- /dev/null +++ b/include/os-compatibility.h @@ -0,0 +1,58 @@ +/* + * Copyright © 2012 Collabora, Ltd. + * + * 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. + */ + +#ifndef OS_COMPATIBILITY_H +#define OS_COMPATIBILITY_H + +#include <sys/types.h> + +#ifdef HAVE_EXECINFO_H +#include <execinfo.h> +#else +static inline int +backtrace(void **buffer, int size) +{ + return 0; +} +#endif + +int +os_fd_set_cloexec(int fd); + +int +os_socketpair_cloexec(int domain, int type, int protocol, int *sv); + +int +os_epoll_create_cloexec(void); + +int +os_create_anonymous_file(off_t size); + +#ifndef HAVE_STRCHRNUL +char * +strchrnul(const char *s, int c); +#endif + +#endif /* OS_COMPATIBILITY_H */ diff --git a/include/wth-receiver-buffer.h b/include/wth-receiver-buffer.h new file mode 100644 index 0000000..7e01b8c --- /dev/null +++ b/include/wth-receiver-buffer.h @@ -0,0 +1,51 @@ +/* + * Copyright © 2019 Advanced Driver Information Technology GmbH + * Copyright © 2020 Collabora, Ltd. + * + * 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. + */ +#ifndef WTH_SERVER_WALTHAM_BUFFER_H_ +#define WTH_SERVER_WALTHAM_BUFFER_H_ + +/* wthp_blob_factory protocol object */ +struct blob_factory { + struct wthp_blob_factory *obj; + struct client *client; + struct wl_list link; /* struct client::blob_factory_list */ +}; + +/* wthp_buffer protocol object */ +struct buffer { + struct wthp_buffer *obj; + uint32_t data_sz; + void *data; + int32_t width; + int32_t height; + int32_t stride; + uint32_t format; + struct wl_list link; /* struct client::buffer_list */ +}; + +void +client_bind_blob_factory(struct client *c, struct wthp_blob_factory *obj); + +#endif diff --git a/include/wth-receiver-comm.h b/include/wth-receiver-comm.h new file mode 100644 index 0000000..4499053 --- /dev/null +++ b/include/wth-receiver-comm.h @@ -0,0 +1,305 @@ +/* + * Copyright © 2019 Advanced Driver Information Technology 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. + */ + +/******************************************************************************* +** ** +** TARGET : linux ** +** ** +** PROJECT : waltham-receiver ** +** ** +** PURPOSE : Header file declare macros, extern functions, data types etc, ** +** required to interface with waltham IPC library ** +** ** +*******************************************************************************/ + +#ifndef WTH_SERVER_WALTHAM_COMM_H_ +#define WTH_SERVER_WALTHAM_COMM_H_ + +#include <stdio.h> +#include <stdbool.h> +#include <sys/epoll.h> +#include <errno.h> +#include <netinet/in.h> +#include <string.h> +#include <assert.h> +#include <getopt.h> +#include <unistd.h> + +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <EGL/egl.h> +#include <EGL/eglext.h> + +#include "xdg-shell-client-protocol.h" + +#include <wayland-egl.h> +#include <wayland-client.h> + +#include <waltham-server.h> +#include <waltham-connection.h> + +#define DEBUG 1 + +struct receiver; +struct client; +struct window; + +/***** macros *******/ +#define MAX_EPOLL_WATCHES 2 + +#ifndef container_of +#define container_of(ptr, type, member) ({ \ + const __typeof__( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) +#endif + + +#define wl_list_last_until_empty(pos, head, member) \ + while (!wl_list_empty(head) && \ + (pos = wl_container_of((head)->prev, pos, member), 1)) + +#ifndef ARRAY_LENGTH +#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0]) +#endif + +#ifndef pr_fmt +# define pr_fmt(fmt) fmt +#endif + +#define wth_error(fmt, ...) \ + ({ fprintf(stderr, pr_fmt(fmt), ## __VA_ARGS__); fflush(stderr); }) + + +/****** incline functions *****/ +static inline void * +zalloc(size_t size) +{ + return calloc(1, size); +} + + +/***** Data types *****/ +/* wthp_region protocol object */ +struct region { + struct wthp_region *obj; + /* pixman_region32_t region; */ + struct wl_list link; /* struct client::region_list */ +}; + +/* wthp_compositor protocol object */ +struct compositor { + struct wthp_compositor *obj; + struct client *client; + struct wl_list link; /* struct client::compositor_list */ +}; + +/* wthp_surface protocol object */ +struct surface { + struct wthp_surface *obj; + uint32_t ivi_id; + char *ivi_app_id; + struct ivisurface *ivisurf; + struct wthp_callback *cb; + struct window *shm_window; + struct wl_list link; /* struct client::surface_list */ +}; +/* wthp_ivi_surface protocol object */ +struct ivisurface { + struct wthp_ivi_surface *obj; + struct wthp_callback *cb; + struct wl_list link; /* struct client::surface_list */ + struct surface *surf; +}; + +/* wthp_ivi_application protocol object */ +struct application { + struct wthp_ivi_application *obj; + struct client *client; + struct wl_list link; /* struct client::surface_list */ +}; + +struct application_id { + struct wthp_ivi_app_id *obj; + struct client *client; + struct wl_list link; /* struct client::surface_list */ +}; + +/* wthp_registry protocol object */ +struct registry { + struct wthp_registry *obj; + struct client *client; + struct wl_list link; /* struct client::registry_list */ +}; + +/* epoll structure */ +struct watch { + struct receiver *receiver; + int fd; + void (*cb)(struct watch *w, uint32_t events); +}; + +struct client { + struct wl_list link; /* struct receiver::client_list */ + struct receiver *receiver; + + struct wth_connection *connection; + struct watch conn_watch; + + /* client object lists for clean-up on disconnection */ + struct wl_list registry_list; /* struct registry::link */ + struct wl_list compositor_list; /* struct compositor::link */ + struct wl_list region_list; /* struct region::link */ + struct wl_list surface_list; /* struct surface::link */ + struct wl_list buffer_list; /* struct buffer::link */ + struct wl_list seat_list; /* struct seat::link */ + struct wl_list pointer_list; /* struct pointer::link */ + struct wl_list touch_list; /* struct touch::link */ +}; + +/* receiver structure */ +struct receiver { + int listen_fd; + struct watch listen_watch; + + bool running; + int epoll_fd; + + struct wl_list client_list; /* struct client::link */ +}; + +struct shm_buffer { + struct wl_buffer *buffer; + void *shm_data; + int busy; +}; + +struct display { + struct wl_display *display; + struct wl_registry *registry; + struct wl_compositor *compositor; + struct wl_shm *shm; + bool has_xrgb; + + struct xdg_wm_base *wm_base; + struct wl_seat *seat; + struct wl_pointer *wl_pointer; + struct wl_keyboard *wl_keyboard; + struct wl_touch *wl_touch; + struct window *window; + struct { + EGLDisplay dpy; + EGLContext ctx; + EGLConfig conf; + + PFNEGLCREATEIMAGEKHRPROC create_image; + PFNEGLDESTROYIMAGEKHRPROC destroy_image; + PFNGLEGLIMAGETARGETTEXTURE2DOESPROC image_texture_2d; + } egl; + struct { + GLuint vertex_shader; + GLuint fragment_shader; + GLuint program_object; + GLuint texture; + } gl; +}; + +struct window { + struct display *display; + struct { + GLuint rotation_uniform; + GLuint pos; + GLuint col; + } gl; + int width, height; + int x, y; + struct wl_surface *surface; + struct ivi_surface *ivi_surface; + + struct shm_buffer buffers[2]; + struct shm_buffer *prev_buffer; + + struct wl_callback *callback; + uint32_t window_frames; + uint32_t window_benchmark_time; + int wait; + struct surface *receiver_surf; + int frame_sync; + + struct xdg_surface *xdg_surface; + struct xdg_toplevel *xdg_toplevel; + const char *app_id; + bool wait_for_configure; + int maximized, fullscreen, opaque; + + struct wl_egl_window *native; + EGLSurface egl_surface; + EGLImageKHR egl_img; + struct seat *receiver_seat; + struct pointer *receiver_pointer; + bool ready; + uint32_t id_ivisurf; +}; + +/** +* receiver_accept_client +* +* Accepts new waltham client connection and instantiates client structure +* +* @param names struct receiver *srv +* @param value socket connection info and client data +* @return none +*/ +void receiver_accept_client(struct receiver *srv); + +/** +* receiver_flush_clients +* +* write all the pending requests from the clients to socket +* +* @param names struct receiver *srv +* @param value socket connection info and client data +* @return none +*/ +void receiver_flush_clients(struct receiver *srv); + +/** +* client_destroy +* +* Destroy client connection +* +* @param names struct client *c +* @param value client data +* @return none +*/ +void client_destroy(struct client *c); + +void +client_post_out_of_memory(struct client *c); + +int +wth_receiver_weston_main(struct window *window, const char *app_id, int port); + + +#endif diff --git a/include/wth-receiver-seat.h b/include/wth-receiver-seat.h new file mode 100644 index 0000000..ed01109 --- /dev/null +++ b/include/wth-receiver-seat.h @@ -0,0 +1,263 @@ +/* + * Copyright © 2019 Advanced Driver Information Technology GmbH + * Copyright © 2020 Collabora, Ltd. + * + * 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. + */ + +#ifndef WTH_SERVER_WALTHAM_SEAT_H_ +#define WTH_SERVER_WALTHAM_SEAT_H_ + +struct pointer; +struct touch; +struct window; +struct receiver; +struct client; + +struct wthp_seat; + +enum wthp_seat_capability { + /** + * the seat has pointer devices + */ + WTHP_SEAT_CAPABILITY_POINTER = 1, + /** + * the seat has one or more keyboards + */ + WTHP_SEAT_CAPABILITY_KEYBOARD = 2, + /** + * the seat has touch devices + */ + WTHP_SEAT_CAPABILITY_TOUCH = 4, +}; + + +/* wthp_seat protocol object */ +struct seat { + struct wthp_seat *obj; + struct client *client; + struct pointer *pointer; + struct keyboard *keyboard; + struct touch *touch; + struct wl_list link; /* struct client::seat_list */ +}; + +/* wthp_pointer protocol object */ +struct pointer { + struct wthp_pointer *obj; + struct seat *seat; + struct wl_list link; /* struct client::pointer_list */ +}; + +/* wthp_keyboard protocol object */ +struct keyboard { + struct wthp_keyboard *obj; + struct seat *seat; + struct wl_list link; /* struct client::keyboard_list */ +}; + +/* wthp_touch protocol object */ +struct touch { + struct wthp_touch *obj; + struct seat *seat; + struct wl_list link; /* struct client::touch_list */ +}; + + +/** +* waltham_pointer_enter +* +* Send pointer enter event received from weston to waltham client +* +* @param names struct window *window +* uint32_t serial +* wl_fixed_t sx +* wl_fixed_t sy +* @param value window - window information +* serial - serial number of the enter event +* sx - surface-local x coordinate +* sy - surface-local y coordinate +* @return none +*/ +void waltham_pointer_enter(struct window *window, uint32_t serial, + wl_fixed_t sx, wl_fixed_t sy); + +/** +* waltham_pointer_leave +* +* Send pointer leave event received from weston to waltham client +* +* @param names struct window *window +* uint32_t serial +* @param value window - window information +* serial - serial number of the leave event +* @return none +*/ +void waltham_pointer_leave(struct window *window, uint32_t serial); + +/** +* waltham_pointer_motion +* +* Send pointer motion event received from weston to waltham client +* +* @param names struct window *window +* uint32_t time +* wl_fixed_t sx +* wl_fixed_t sy +* @param value window - window information +* time - timestamp with millisecond granularity +* sx - surface-local x coordinate +* sy - surface-local y coordinate +* @return none +*/ +void waltham_pointer_motion(struct window *window, uint32_t time, + wl_fixed_t sx, wl_fixed_t sy); + +/** +* waltham_pointer_button +* +* Send pointer button event received from weston to waltham client +* +* @param names struct window *window +* uint32_t serial +* uint32_t time +* uint32_t button +* uint32_t state +* @param value window - window information +* serial - serial number of the button event +* time - timestamp with millisecond granularity +* button - button that produced the event +* state - physical state of the button +* @return none +*/ +void waltham_pointer_button(struct window *window, uint32_t serial, + uint32_t time, uint32_t button, + uint32_t state); + +/** +* waltham_pointer_axis +* +* Send pointer axis event received from weston to waltham client +* +* @param names struct window *window +* uint32_t time +* uint32_t axis +* wl_fixed_t value +* @param value window - window information +* time - timestamp with millisecond granularity +* axis - axis type +* value - length of vector in surface-local coordinate space +* @return none +*/ +void waltham_pointer_axis(struct window *window, uint32_t time, + uint32_t axis, wl_fixed_t value); + +/** +* waltham_touch_down +* +* Send touch down event received from weston to waltham client +* +* @param names struct window *window +* uint32_t serial +* uint32_t time +* int32_t id +* wl_fixed_t x_w +* wl_fixed_t y_w +* @param value window - window information +* serial - serial number of the touch down event +* time - timestamp with millisecond granularity +* id - the unique ID of this touch point +* x_w - surface-local x coordinate +* y_w - surface-local y coordinate +* @return none +*/ +void waltham_touch_down(struct window *window, uint32_t serial, + uint32_t time, int32_t id, + wl_fixed_t x_w, wl_fixed_t y_w); + +/** +* waltham_touch_up +* +* Send touch up event received from weston to waltham client +* +* @param names struct window *window +* uint32_t serial +* uint32_t time +* int32_t id +* @param value window - window information +* serial - serial number of the touch up event +* time - timestamp with millisecond granularity +* id - the unique ID of this touch point +* @return none +*/ +void waltham_touch_up(struct window *window, uint32_t serial, + uint32_t time, int32_t id); + +/** +* waltham_touch_motion +* +* Send touch motion event received from weston to waltham client +* +* @param names struct window *window +* uint32_t time +* int32_t id +* wl_fixed_t x_w +* wl_fixed_t y_w +* @param value window - window information +* time - timestamp with millisecond granularity +* id - the unique ID of this touch point +* x_w - surface-local x coordinate +* y_w - surface-local y coordinate +* @return none +*/ +void waltham_touch_motion(struct window *window, uint32_t time, + int32_t id, wl_fixed_t x_w, wl_fixed_t y_w); + +/** +* waltham_touch_frame +* +* Send touch frame event received from weston to waltham client +* +* @param names struct window *window +* @param value window - window information +* @return none +*/ +void waltham_touch_frame(struct window *window); + +/** +* waltham_touch_cancel +* +* Send touch cancel event received from weston to waltham client +* +* @param names struct window *window +* @param value window - window information +* @return none +*/ +void waltham_touch_cancel(struct window *window); + +void +client_bind_seat(struct client *c, struct wthp_seat *obj); + +void +seat_send_updated_caps(struct seat *seat); + +#endif diff --git a/include/wth-receiver-surface.h b/include/wth-receiver-surface.h new file mode 100644 index 0000000..cbcf208 --- /dev/null +++ b/include/wth-receiver-surface.h @@ -0,0 +1,44 @@ +/* + * Copyright © 2019 Advanced Driver Information Technology GmbH + * Copyright © 2020 Collabora, Ltd. + * + * 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. + */ +#ifndef WTH_SERVER_WALTHAM_SURFACE_H_ +#define WTH_SERVER_WALTHAM_SURFACE_H_ + +struct client; +struct compositor; + +void +region_destroy(struct region *region); + +void +compositor_destroy(struct compositor *comp); + +void +surface_destroy(struct surface *surface); + +void +client_bind_compositor(struct client *c, struct wthp_compositor *obj); + +#endif diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..4443a85 --- /dev/null +++ b/meson.build @@ -0,0 +1,149 @@ +project('waltham-receiver', + 'c', + version: '0.0.1', + default_options: [ + 'warning_level=3', + 'c_std=gnu99', + ], + meson_version: '>= 0.50', + license: 'MIT/Expat', +) + +pkgconfig = import('pkgconfig') +cc = meson.get_compiler('c') + +add_project_arguments( + cc.get_supported_arguments([ + '-Wno-unused-parameter', + '-Wno-pedantic', + '-Wextra', + '-Werror' + ]), + language: 'c' +) + +add_project_arguments([ + '-DPACKAGE_STRING="waltham-receiver @0@"'.format(meson.project_version()), + '-D_GNU_SOURCE', + '-D_ALL_SOURCE', + ], + language: 'c' +) + +optional_libc_funcs = [ 'memfd_create', 'strchrnul' ] +foreach func: optional_libc_funcs + if cc.has_function(func) + add_project_arguments('-DHAVE_@0@=1'.format(func.to_upper()), language: 'c') + endif +endforeach + +env_modmap = '' + +libwayland_dep = dependency('wayland-client') +libwayland_cursor_dep = dependency('wayland-cursor') +libwaltham_dep = dependency('waltham') + +dep_scanner = dependency('wayland-scanner', native: true) +prog_scanner = find_program(dep_scanner.get_pkgconfig_variable('wayland_scanner')) +dep_wp = dependency('wayland-protocols', version: '>= 1.18') +dir_wp_base = dep_wp.get_pkgconfig_variable('pkgdatadir') +xdg_shell_xml = join_paths(dir_wp_base, 'stable', 'xdg-shell', 'xdg-shell.xml') + +protocols = [ + { 'name': 'xdg-shell', 'source': 'wp-stable' }, +] + +foreach proto: protocols + proto_name = proto['name'] + if proto['source'] == 'internal' + base_file = proto_name + xml_path = join_paths('protocol', '@0@.xml'.format(base_file)) + elif proto['source'] == 'wp-stable' + base_file = proto_name + xml_path = join_paths(dir_wp_base, 'stable', proto_name, '@0@.xml'.format(base_file)) + else + base_file = '@0@-unstable-@1@'.format(proto_name, proto['version']) + xml_path = join_paths(dir_wp_base, 'unstable', proto_name, '@0@.xml'.format(base_file)) + endif + + foreach output_type: [ 'client-header', 'server-header', 'private-code' ] + if output_type == 'client-header' + output_file = '@0@-client-protocol.h'.format(base_file) + elif output_type == 'server-header' + output_file = '@0@-server-protocol.h'.format(base_file) + else + output_file = '@0@-protocol.c'.format(base_file) + if dep_scanner.version().version_compare('< 1.14.91') + output_type = 'code' + endif + endif + + var_name = output_file.underscorify() + target = custom_target( + '@0@ @1@'.format(base_file, output_type), + command: [ prog_scanner, output_type, '@INPUT@', '@OUTPUT@' ], + input: xml_path, + output: output_file, + ) + + set_variable(var_name, target) + endforeach +endforeach + + +dep_gstreamer = dependency('gstreamer-1.0') +dep_gstreamer_plugins_base = dependency('gstreamer-plugins-base-1.0') +dep_gstreamer_plugins_bad = dependency('gstreamer-plugins-bad-1.0') +dep_gstreamer_app = dependency('gstreamer-app-1.0') +dep_gstreamer_video = dependency('gstreamer-video-1.0') +dep_gstreamer_alloc = dependency('gstreamer-allocators-1.0') +dep_egl = dependency('egl') +dep_gles = dependency('glesv2') +dep_wayland_egl = dependency('wayland-egl') + +prefix_path = get_option('prefix') +binplugin_dir = join_paths(prefix_path, get_option('bindir')) +common_inc = include_directories('include') + +deps_waltham_receiver = [ + libwayland_dep, libwayland_cursor_dep, + libwaltham_dep, + dep_gstreamer, dep_gstreamer_app, dep_gstreamer_plugins_base, + dep_gstreamer_plugins_bad, + dep_gstreamer_video, dep_gstreamer_alloc, + dep_egl, dep_gles, dep_wayland_egl, + # gstwayland-1.0 is special here because gstreamer-plugins-bad-1.0 does not + # provide it + cc.find_library('pthread'), cc.find_library('gstwayland-1.0') +] + +buf_type = get_option('buffer-type') +buf_type_src = [] + +if buf_type == 'egl' + buf_type_src += 'src/wth-receiver-gst-egl.c' +elif buf_type == 'shm' + buf_type_src += 'src/wth-receiver-gst-shm.c' +endif + +srcs_wth_receiver = [ + 'src/bitmap.c', + 'src/os-compatibility.c', + 'src/wth-receiver-comm.c', + 'src/wth-receiver-buffer.c', + 'src/wth-receiver-surface.c', + 'src/wth-receiver-seat.c', + 'src/wth-receiver-main.c', + buf_type_src, + xdg_shell_client_protocol_h, + xdg_shell_protocol_c, +] + +exe_wth_receiver = executable( + 'waltham-receiver', + srcs_wth_receiver, + include_directories: common_inc, + dependencies: deps_waltham_receiver, + install_rpath: binplugin_dir, + install: true +) diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 0000000..30a6f0f --- /dev/null +++ b/meson_options.txt @@ -0,0 +1,7 @@ +option( + 'buffer-type', + type: 'combo', + choices: [ 'auto', 'egl', 'shm' ], + value: 'shm', + description: 'Default buffer type to use' +) diff --git a/src/bitmap.c b/src/bitmap.c new file mode 100644 index 0000000..2c7fdb5 --- /dev/null +++ b/src/bitmap.c @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2013 DENSO CORPORATION + * + * 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 "bitmap.h" +#include <stdio.h> + +struct __attribute__ ((__packed__)) BITMAPFILEHEADER { + char bfType[2]; + uint32_t bfSize; + uint16_t bfReserved1; + uint16_t bfReserved2; + uint32_t bfOffBits; +}; + +struct __attribute__ ((__packed__)) BITMAPINFOHEADER { + uint32_t biSize; + uint32_t biWidth; + uint32_t biHeight; + uint16_t biPlanes; + uint16_t biBitCount; + uint32_t biCompression; + uint32_t biSizeImage; + uint32_t biXPixPerMeter; + uint32_t biYPixPerMeter; + uint32_t biClrUsed; + uint32_t biClrImporant; +}; + +static void +create_file_header(struct BITMAPFILEHEADER *file_header, int32_t image_size) +{ + file_header->bfType[0] = 'B'; + file_header->bfType[1] = 'M'; + file_header->bfSize = sizeof(struct BITMAPFILEHEADER) + + sizeof(struct BITMAPINFOHEADER) + + image_size; + file_header->bfOffBits = sizeof(struct BITMAPFILEHEADER) + + sizeof(struct BITMAPINFOHEADER); +} + +static void +create_info_header(struct BITMAPINFOHEADER *info_header, int32_t image_size, int32_t width, int32_t height, int16_t bpp) +{ + info_header->biSize = sizeof(struct BITMAPINFOHEADER); + info_header->biWidth = width; + info_header->biHeight = height; + info_header->biPlanes = 1; + info_header->biBitCount = bpp; + info_header->biSizeImage = image_size; +} + +static int +write_bitmap(const char *filename, + const struct BITMAPFILEHEADER *file_header, + const struct BITMAPINFOHEADER *info_header, + const char *buffer) +{ + FILE *fp = fopen(filename, "w"); + if (fp == NULL) { + return -1; + } + + fwrite(file_header, sizeof(struct BITMAPFILEHEADER), 1, fp); + fwrite(info_header, sizeof(struct BITMAPINFOHEADER), 1, fp); + fwrite(buffer, info_header->biSizeImage, 1, fp); + + fclose(fp); + return 0; +} + +int +save_as_bitmap(const char *filename, + const char *buffer, + int32_t image_size, + int32_t width, + int32_t height, + int16_t bpp) +{ + if ((filename == NULL) || (buffer == NULL)) { + return -1; + } + + struct BITMAPFILEHEADER file_header = {}; + struct BITMAPINFOHEADER info_header = {}; + + create_file_header(&file_header, image_size); + create_info_header(&info_header, image_size, width, height, bpp); + return write_bitmap(filename, &file_header, &info_header, buffer); +} diff --git a/src/os-compatibility.c b/src/os-compatibility.c new file mode 100644 index 0000000..d9502e5 --- /dev/null +++ b/src/os-compatibility.c @@ -0,0 +1,204 @@ +/* + * Copyright © 2012 Collabora, Ltd. + * + * 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. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/epoll.h> +#include <string.h> +#include <stdlib.h> + +#include "os-compatibility.h" + +int +os_fd_set_cloexec(int fd) +{ + long flags; + + if (fd == -1) + return -1; + + flags = fcntl(fd, F_GETFD); + if (flags == -1) + return -1; + + if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) + return -1; + + return 0; +} + +static int +set_cloexec_or_close(int fd) +{ + if (os_fd_set_cloexec(fd) != 0) { + close(fd); + return -1; + } + return fd; +} + +int +os_socketpair_cloexec(int domain, int type, int protocol, int *sv) +{ + int ret; + +#ifdef SOCK_CLOEXEC + ret = socketpair(domain, type | SOCK_CLOEXEC, protocol, sv); + if (ret == 0 || errno != EINVAL) + return ret; +#endif + + ret = socketpair(domain, type, protocol, sv); + if (ret < 0) + return ret; + + sv[0] = set_cloexec_or_close(sv[0]); + sv[1] = set_cloexec_or_close(sv[1]); + + if (sv[0] != -1 && sv[1] != -1) + return 0; + + close(sv[0]); + close(sv[1]); + return -1; +} + +int +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 insufficent, errno is set to ENOSPC. + * If posix_fallocate() is not supported, program may receive + * SIGBUS on accessing mmap()'ed file contents instead. + */ +int +os_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; +} + +#ifndef HAVE_STRCHRNUL +char * +strchrnul(const char *s, int c) +{ + while (*s && *s != c) + s++; + return (char *)s; +} +#endif diff --git a/src/wth-receiver-buffer.c b/src/wth-receiver-buffer.c new file mode 100644 index 0000000..accf0cd --- /dev/null +++ b/src/wth-receiver-buffer.c @@ -0,0 +1,95 @@ +/* + * Copyright © 2019 Advanced Driver Information Technology GmbH + * Copyright © 2020 Collabora, Ltd. + * + * 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. + */ + +#include "wth-receiver-comm.h" +#include "wth-receiver-buffer.h" + +static void +buffer_handle_destroy(struct wthp_buffer *wthp_buffer) +{ + struct buffer *buf = wth_object_get_user_data((struct wth_object *)wthp_buffer); + + wthp_buffer_free(wthp_buffer); + wl_list_remove(&buf->link); + free(buf); +} + +static const struct wthp_buffer_interface buffer_implementation = { + buffer_handle_destroy +}; + +/* BEGIN wthp_blob_factory implementation */ + +static void +blob_factory_create_buffer(struct wthp_blob_factory *blob_factory, + struct wthp_buffer *wthp_buffer, uint32_t data_sz, void *data, + int32_t width, int32_t height, int32_t stride, uint32_t format) +{ + struct blob_factory *blob = wth_object_get_user_data((struct wth_object *)blob_factory); + struct buffer *buffer; + + buffer = zalloc(sizeof *buffer); + if (!buffer) { + client_post_out_of_memory(blob->client); + return; + } + + wl_list_insert(&blob->client->buffer_list, &buffer->link); + + buffer->data_sz = data_sz; + buffer->data = data; + buffer->width = width; + buffer->height = height; + buffer->stride = stride; + buffer->format = format; + buffer->obj = wthp_buffer; + + wthp_buffer_set_interface(wthp_buffer, &buffer_implementation, buffer); +} + +static const struct wthp_blob_factory_interface blob_factory_implementation = { + blob_factory_create_buffer +}; + +void +client_bind_blob_factory(struct client *c, struct wthp_blob_factory *obj) +{ + struct blob_factory *blob; + + blob = zalloc(sizeof *blob); + if (!blob) { + client_post_out_of_memory(c); + return; + } + + blob->obj = obj; + blob->client = c; + wl_list_insert(&c->compositor_list, &blob->link); + + wthp_blob_factory_set_interface(obj, + &blob_factory_implementation, blob); + fprintf(stderr, "client %p bound wthp_blob_factory\n", c); +} diff --git a/src/wth-receiver-comm.c b/src/wth-receiver-comm.c new file mode 100644 index 0000000..e6c0266 --- /dev/null +++ b/src/wth-receiver-comm.c @@ -0,0 +1,453 @@ +/* + * Copyright © 2019 Advanced Driver Information Technology GmbH + * Copyright © 2020 Collabora, Ltd. + * + * 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. + */ + +/******************************************************************************* +** ** +** TARGET : linux ** +** ** +** PROJECT : waltham-receiver ** +** ** +** PURPOSE : This file acts as interface to waltham IPC library ** +** ** +** ** +*******************************************************************************/ + +#include "wth-receiver-comm.h" +#include "wth-receiver-surface.h" +#include "wth-receiver-seat.h" +#include "wth-receiver-buffer.h" + +extern uint16_t tcp_port; +extern const char *my_app_id; + +void +client_post_out_of_memory(struct client *c) +{ + struct wth_display *disp; + + disp = wth_connection_get_display(c->connection); + wth_object_post_error((struct wth_object *) disp, 1, "out of memory"); +} + +/* + * waltam ivi surface implementation + */ +static void +wthp_ivi_surface_destroy(struct wthp_ivi_surface * ivi_surface) +{ + struct ivisurface *ivisurf = wth_object_get_user_data((struct wth_object *)ivi_surface); + + if (ivisurf->surf->ivi_app_id) + free(ivisurf->surf->ivi_app_id); + + free(ivisurf); +} + +static const struct wthp_ivi_surface_interface wthp_ivi_surface_implementation = { + wthp_ivi_surface_destroy, +}; + + +/** + * app_id version + */ +static void +wthp_ivi_app_id_surface_create(struct wthp_ivi_app_id *ivi_application, + const char *app_id, + struct wthp_surface * wthp_surface, + struct wthp_ivi_surface *obj) +{ + struct surface *surface = wth_object_get_user_data((struct wth_object *)wthp_surface); + + /* we destroy it wthp_ivi_surface_implementation:: */ + if (my_app_id) { + surface->ivi_app_id = strdup(my_app_id); + surface->shm_window->app_id = surface->ivi_app_id; + } + + struct ivisurface *ivisurf; + + ivisurf = zalloc(sizeof *ivisurf); + if (!ivisurf) { + return; + } + + ivisurf->obj = obj; + ivisurf->surf = surface; + + wthp_ivi_surface_set_interface(obj, + &wthp_ivi_surface_implementation, ivisurf); + + if (my_app_id) + wth_receiver_weston_main(surface->shm_window, my_app_id, tcp_port); + else + wth_receiver_weston_main(surface->shm_window, app_id, tcp_port); + + while (!surface->shm_window->ready) + usleep(1); +} + +static const struct wthp_ivi_app_id_interface wthp_ivi_app_id_implementation = { + wthp_ivi_app_id_surface_create, +}; + +static void +client_bind_wthp_ivi_app_id(struct client *c, struct wthp_ivi_app_id *obj) +{ + struct application_id *app; + + app = zalloc(sizeof *app); + if (!app) { + client_post_out_of_memory(c); + return; + } + + app->obj = obj; + app->client = c; + wl_list_insert(&c->compositor_list, &app->link); + + wthp_ivi_app_id_set_interface(obj, &wthp_ivi_app_id_implementation, app); +} + +/* + * waltham registry implementation + */ +static void +registry_destroy(struct registry *reg) +{ + wthp_registry_free(reg->obj); + wl_list_remove(®->link); + free(reg); +} + +static void +registry_handle_destroy(struct wthp_registry *registry) +{ + struct registry *reg = wth_object_get_user_data((struct wth_object *)registry); + registry_destroy(reg); +} + +static void +registry_handle_bind(struct wthp_registry *registry, + uint32_t name, struct wth_object *id, + const char *interface, uint32_t version) +{ + + struct registry *reg = wth_object_get_user_data((struct wth_object *)registry); + + if (strcmp(interface, "wthp_compositor") == 0) { + client_bind_compositor(reg->client, (struct wthp_compositor *)id); + } else if (strcmp(interface, "wthp_blob_factory") == 0) { + struct client *client = reg->client; + struct seat *seat, *tmp, *get_seat; + + client_bind_blob_factory(reg->client, (struct wthp_blob_factory *)id); + + get_seat = NULL; + wl_list_for_each_safe(seat, tmp, &client->seat_list, link) { + get_seat = seat; + } + + if (get_seat) + seat_send_updated_caps(get_seat); + } else if (strcmp(interface, "wthp_ivi_app_id") == 0) { + client_bind_wthp_ivi_app_id(reg->client, (struct wthp_ivi_app_id *) id); + } else if (strcmp(interface, "wthp_seat") == 0) { + client_bind_seat(reg->client, (struct wthp_seat *)id); + } else { + wth_object_post_error((struct wth_object *)registry, 0, + "%s: unknown name %u", __func__, name); + wth_object_delete(id); + } +} + +static const struct wthp_registry_interface registry_implementation = { + registry_handle_destroy, + registry_handle_bind +}; + +/* + * waltham display implementation + */ + +static void +display_handle_client_version(struct wth_display *wth_display, + uint32_t client_version) +{ + wth_object_post_error((struct wth_object *)wth_display, 0, + "unimplemented: %s", __func__); +} + +static void +display_handle_sync(struct wth_display * wth_display, struct wthp_callback * callback) +{ + wthp_callback_send_done(callback, 0); + wthp_callback_free(callback); +} + +static void +display_handle_get_registry(struct wth_display *wth_display, + struct wthp_registry *registry) +{ + struct client *c = wth_object_get_user_data((struct wth_object *)wth_display); + struct registry *reg; + + reg = zalloc(sizeof *reg); + if (!reg) { + client_post_out_of_memory(c); + return; + } + + reg->obj = registry; + reg->client = c; + wl_list_insert(&c->registry_list, ®->link); + wthp_registry_set_interface(registry, + ®istry_implementation, reg); + + wthp_registry_send_global(registry, 1, "wthp_compositor", 4); + wthp_registry_send_global(registry, 1, "wthp_ivi_app_id", 1); + wthp_registry_send_global(registry, 1, "wthp_seat", 4); + wthp_registry_send_global(registry, 1, "wthp_blob_factory", 4); + +} + +const struct wth_display_interface display_implementation = { + display_handle_client_version, + display_handle_sync, + display_handle_get_registry +}; + +/* + * utility functions + */ +static int +watch_ctl(struct watch *w, int op, uint32_t events) +{ + struct epoll_event ee; + + ee.events = events; + ee.data.ptr = w; + return epoll_ctl(w->receiver->epoll_fd, op, w->fd, &ee); +} + +/** +* client_destroy +* +* Destroy client connection +* +* @param names struct client *c +* @param value client data +* @return none +*/ +void +client_destroy(struct client *c) +{ + struct region *region; + struct compositor *comp; + struct registry *reg; + struct surface *surface; + + /* clean up remaining client resources in case the client + * did not. + */ + wl_list_last_until_empty(region, &c->region_list, link) + region_destroy(region); + + wl_list_last_until_empty(comp, &c->compositor_list, link) + compositor_destroy(comp); + + wl_list_last_until_empty(reg, &c->registry_list, link) + registry_destroy(reg); + + wl_list_last_until_empty(surface, &c->surface_list, link) + surface_destroy(surface); + + wl_list_remove(&c->link); + watch_ctl(&c->conn_watch, EPOLL_CTL_DEL, 0); + wth_connection_destroy(c->connection); + free(c); +} + +/* + * functions to handle waltham client connections + */ +static void +connection_handle_data(struct watch *w, uint32_t events) +{ + + struct client *c = container_of(w, struct client, conn_watch); + int ret; + + if (events & EPOLLERR) { + wth_error("Client %p errored out.\n", c); + client_destroy(c); + return; + } + + if (events & EPOLLHUP) { + wth_error("Client %p hung up.\n", c); + client_destroy(c); + return; + } + + if (events & EPOLLOUT) { + ret = wth_connection_flush(c->connection); + if (ret == 0) { + watch_ctl(&c->conn_watch, EPOLL_CTL_MOD, EPOLLIN); + } else if (ret < 0 && errno != EAGAIN) { + wth_error("Client %p flush error.\n", c); + client_destroy(c); + return; + } + } + + if (events & EPOLLIN) { + ret = wth_connection_read(c->connection); + if (ret < 0) { + wth_error("Client %p read error.\n", c); + client_destroy(c); + return; + } + + ret = wth_connection_dispatch(c->connection); + if (ret < 0 && errno != EPROTO) { + wth_error("Client %p dispatch error.\n", c); + client_destroy(c); + return; + } + } +} + +/** + * client_create + * + * Create new client connection + * + * @param srv receiver structure + * @param wth_connection Waltham connection handle + * @return Pointer to client structure + */ +static struct client * +client_create(struct receiver *srv, struct wth_connection *conn) +{ + + struct client *c; + struct wth_display *disp; + + c = zalloc(sizeof *c); + if (!c) + return NULL; + + c->receiver = srv; + c->connection = conn; + + c->conn_watch.receiver = srv; + c->conn_watch.fd = wth_connection_get_fd(conn); + c->conn_watch.cb = connection_handle_data; + if (watch_ctl(&c->conn_watch, EPOLL_CTL_ADD, EPOLLIN) < 0) { + free(c); + return NULL; + } + + + wl_list_insert(&srv->client_list, &c->link); + + wl_list_init(&c->registry_list); + wl_list_init(&c->compositor_list); + wl_list_init(&c->seat_list); + wl_list_init(&c->pointer_list); + wl_list_init(&c->touch_list); + wl_list_init(&c->region_list); + wl_list_init(&c->surface_list); + wl_list_init(&c->buffer_list); + + disp = wth_connection_get_display(c->connection); + wth_display_set_interface(disp, &display_implementation, c); + + return c; +} + + +/** +* receiver_flush_clients +* +* write all the pending requests from the clients to socket +* +* @param names struct receiver *srv +* @param value socket connection info and client data +* @return none +*/ +void +receiver_flush_clients(struct receiver *srv) +{ + struct client *c, *tmp; + int ret; + + wl_list_for_each_safe(c, tmp, &srv->client_list, link) { + /* Flush out buffered requests. If the Waltham socket is + * full, poll it for writable too. + */ + ret = wth_connection_flush(c->connection); + if (ret < 0 && errno == EAGAIN) { + watch_ctl(&c->conn_watch, EPOLL_CTL_MOD, EPOLLIN | EPOLLOUT); + } else if (ret < 0) { + perror("Connection flush failed"); + client_destroy(c); + return; + } + } +} + +/** +* receiver_accept_client +* +* Accepts new waltham client connection and instantiates client structure +* +* @param names struct receiver *srv +* @param value socket connection info and client data +* @return none +*/ +void +receiver_accept_client(struct receiver *srv) +{ + struct client *client; + struct wth_connection *conn; + struct sockaddr_in addr; + socklen_t len; + + len = sizeof(addr); + conn = wth_accept(srv->listen_fd, (struct sockaddr *)&addr, &len); + if (!conn) { + wth_error("Failed to accept a connection.\n"); + return; + } + + client = client_create(srv, conn); + if (!client) { + wth_error("Failed client_create().\n"); + return; + } +} diff --git a/src/wth-receiver-gst-egl.c b/src/wth-receiver-gst-egl.c new file mode 100644 index 0000000..0704e6b --- /dev/null +++ b/src/wth-receiver-gst-egl.c @@ -0,0 +1,1012 @@ +/* + * Copyright © 2019 Advanced Driver Information Technology GmbH + * Copyright © 2020 Collabora, Ltd. + * + * 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. + */ + +/******************************************************************************* + ** ** + ** TARGET : linux ** + ** ** + ** PROJECT : waltham-receiver ** + ** ** + ** PURPOSE : This file is acts as interface to weston compositor at receiver ** + ** side ** + ** ** + *******************************************************************************/ + +#define GST_USE_UNSTABLE_API + +#include <sys/mman.h> +#include <signal.h> +#include <sys/time.h> +#include <gst/gst.h> +#include <gst/video/gstvideometa.h> +#include <gst/allocators/gstdmabuf.h> +#include <gst/app/gstappsink.h> +#include <pthread.h> +#include <gst/wayland/wayland.h> +#include <gst/video/videooverlay.h> + +#include <GL/gl.h> +#include <GLES/gl.h> +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <EGL/egl.h> +#include <EGL/eglext.h> + +#include "xdg-shell-client-protocol.h" + +#include "wth-receiver-seat.h" +#include "wth-receiver-comm.h" +#include "os-compatibility.h" +#include "bitmap.h" + +#define WINDOW_WIDTH_SIZE 800 +#define WINDOW_HEIGHT_SIZE 600 + +#define PIPELINE_SIZE 4096 + +static int running = 1; + +typedef struct _GstAppContext { + GMainLoop *loop; + GstBus *bus; + + GstElement *pipeline; + GstElement *sink; + + GstWaylandVideo *wl_video; + GstVideoOverlay *overlay; + + struct display *display; + struct window *window; + GstVideoInfo info; + +} GstAppContext; + +static const gchar *vertex_shader_str = +"attribute vec4 a_position; \n" +"attribute vec2 a_texCoord; \n" +"varying vec2 v_texCoord; \n" +"void main() \n" +"{ \n" +" gl_Position = a_position; \n" +" v_texCoord = a_texCoord; \n" +"} \n"; + +static const gchar *fragment_shader_str = +"#ifdef GL_ES \n" +"precision mediump float; \n" +"#endif \n" +"varying vec2 v_texCoord; \n" +"uniform sampler2D tex; \n" +"void main() \n" +"{ \n" +"vec2 uv; \n" +"uv = v_texCoord.xy; \n" +"vec4 c = texture2D(tex, uv); \n" +"gl_FragColor = c; \n" +"} \n"; + +/* + * pointer callbcak functions + */ +static void +pointer_handle_enter(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, struct wl_surface *wl_surface, + wl_fixed_t sx, wl_fixed_t sy) +{ + struct display *display = data; + struct window *window = display->window; + + waltham_pointer_enter(window, serial, sx, sy); +} + +static void +pointer_handle_leave(void *data, struct wl_pointer *pointer, + uint32_t serial, struct wl_surface *surface) +{ + struct display *display = data; + struct window *window = display->window; + + waltham_pointer_leave(window, serial); + +} + +static void +pointer_handle_motion(void *data, struct wl_pointer *pointer, + uint32_t time, wl_fixed_t sx, wl_fixed_t sy) +{ + struct display *display = data; + struct window *window = display->window; + + waltham_pointer_motion(window, time, sx, sy); +} + +static void +pointer_handle_button(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, uint32_t time, uint32_t button, + uint32_t state) +{ + struct display *display = data; + struct window *window = display->window; + + waltham_pointer_button(window, serial, time, button, state); +} + +static void +pointer_handle_axis(void *data, struct wl_pointer *wl_pointer, + uint32_t time, uint32_t axis, wl_fixed_t value) +{ + struct display *display = data; + struct window *window = display->window; + + waltham_pointer_axis(window, time, axis, value); +} + +static void +pointer_handle_frame(void *data, struct wl_pointer *pointer) +{ + (void) data; + (void) pointer; +} + +static void +pointer_handle_axis_source(void *data, struct wl_pointer *pointer, + uint32_t source) +{ + (void) data; + (void) pointer; + (void) source; +} + +static void +pointer_handle_axis_stop(void *data, struct wl_pointer *pointer, + uint32_t time, uint32_t axis) +{ + (void) data; + (void) pointer; + (void) time; + (void) axis; +} + +static void +pointer_handle_axis_discrete(void *data, struct wl_pointer *pointer, + uint32_t axis, int32_t discrete) +{ + (void) data; + (void) pointer; + (void) axis; + (void) discrete; +} + +static const struct wl_pointer_listener pointer_listener = { + pointer_handle_enter, + pointer_handle_leave, + pointer_handle_motion, + pointer_handle_button, + pointer_handle_axis, + pointer_handle_frame, + pointer_handle_axis_source, + pointer_handle_axis_stop, + pointer_handle_axis_discrete +}; + +/* + * touch callbcak functions + */ + +static void +touch_handle_down(void *data, struct wl_touch *touch, uint32_t serial, + uint32_t time, struct wl_surface *surface, int32_t id, + wl_fixed_t x_w, wl_fixed_t y_w) +{ + struct display *display = data; + struct window *window = display->window; + + waltham_touch_down(window, serial, time, id, x_w, y_w); +} + +static void +touch_handle_up(void *data, struct wl_touch *touch, uint32_t serial, + uint32_t time, int32_t id) +{ + struct display *display = data; + struct window *window = display->window; + + waltham_touch_up(window, serial, time, id); +} + +static void +touch_handle_motion(void *data, struct wl_touch *touch, uint32_t time, + int32_t id, wl_fixed_t x_w, wl_fixed_t y_w) +{ + struct display *display = data; + struct window *window = display->window; + + waltham_touch_motion(window, time, id, x_w, y_w); +} + +static void +touch_handle_frame(void *data, struct wl_touch *touch) +{ + struct display *display = data; + struct window *window = display->window; + + waltham_touch_frame(window); +} + +static void +touch_handle_cancel(void *data, struct wl_touch *touch) +{ + struct display *display = data; + struct window *window = display->window; + + waltham_touch_cancel(window); +} + +static void +touch_handle_shape(void *data, struct wl_touch *touch, + int32_t id, wl_fixed_t maj, wl_fixed_t min) +{ + (void) data; + (void) touch; + + (void) id; + (void) maj; + (void) min; +} + +static void +touch_handle_orientation(void *data, struct wl_touch *touch, int32_t id, wl_fixed_t orientation) +{ + (void) data; + (void) touch; + + (void) id; + (void) orientation; +} + +static const struct wl_touch_listener touch_listener = { + touch_handle_down, + touch_handle_up, + touch_handle_motion, + touch_handle_frame, + touch_handle_cancel, + touch_handle_shape, + touch_handle_orientation, +}; + +static void +xdg_wm_base_ping(void *data, struct xdg_wm_base *shell, uint32_t serial) +{ + xdg_wm_base_pong(shell, serial); +} + +static const struct xdg_wm_base_listener xdg_wm_base_listener = { + xdg_wm_base_ping, +}; + + +/* + * seat callback + */ +static void +seat_capabilities(void *data, struct wl_seat *wl_seat, enum wl_seat_capability caps) +{ + struct display *display = data; + + if ((caps & WL_SEAT_CAPABILITY_POINTER) && !display->wl_pointer) { + display->wl_pointer = wl_seat_get_pointer(wl_seat); + wl_pointer_set_user_data(display->wl_pointer, display); + wl_pointer_add_listener(display->wl_pointer, &pointer_listener, display); + wl_display_roundtrip(display->display); + } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && display->wl_pointer) { + wl_pointer_destroy(display->wl_pointer); + display->wl_pointer = NULL; + } + + if ((caps & WL_SEAT_CAPABILITY_TOUCH) && !display->wl_touch) { + display->wl_touch = wl_seat_get_touch(wl_seat); + wl_touch_set_user_data(display->wl_touch, display); + wl_touch_add_listener(display->wl_touch, &touch_listener, display); + wl_display_roundtrip(display->display); + } else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && display->wl_touch) { + wl_touch_destroy(display->wl_touch); + display->wl_touch = NULL; + } +} + +static const struct wl_seat_listener seat_listener = { + seat_capabilities, + NULL +}; + +static void +add_seat(struct display *display, uint32_t id, uint32_t version) +{ + display->wl_pointer = NULL; + display->wl_touch = NULL; + display->wl_keyboard = NULL; + display->seat = wl_registry_bind(display->registry, id, + &wl_seat_interface, 1); + wl_seat_add_listener(display->seat, &seat_listener, display); +} + +/* + * registry callback + */ +static void +registry_handle_global(void *data, struct wl_registry *registry, + uint32_t id, const char *interface, uint32_t version) +{ + struct display *d = data; + + if (strcmp(interface, "wl_compositor") == 0) { + d->compositor = wl_registry_bind(registry, + id, &wl_compositor_interface, 1); + } else if (strcmp(interface, "wl_seat") == 0) { + add_seat(d, id, version); + } else if (strcmp(interface, "xdg_wm_base") == 0) { + d->wm_base = wl_registry_bind(registry, id, + &xdg_wm_base_interface, 1); + xdg_wm_base_add_listener(d->wm_base, &xdg_wm_base_listener, d); + } +} + +static void +registry_handle_global_remove(void *data, struct wl_registry *registry, + uint32_t name) +{ + /* stub */ +} + +static const struct wl_registry_listener registry_listener = { + registry_handle_global, + registry_handle_global_remove +}; + + +static struct display * +create_display(void) +{ + struct display *display; + + display = malloc(sizeof *display); + if (display == NULL) { + wth_error("out of memory\n"); + exit(1); + } + display->display = wl_display_connect(NULL); + assert(display->display); + + display->has_xrgb = false; + display->registry = wl_display_get_registry(display->display); + wl_registry_add_listener(display->registry, + ®istry_listener, display); + + wl_display_roundtrip(display->display); + + return display; +} + +static void +destroy_display(struct display *display) +{ + if (display->compositor) + wl_compositor_destroy(display->compositor); + + wl_registry_destroy(display->registry); + wl_display_flush(display->display); + wl_display_disconnect(display->display); + free(display); +} + +static bool +wth_check_egl_extension(const char *extensions, const char *extension) +{ + size_t extlen = strlen(extension); + const char *end = extensions + strlen(extensions); + + while (extensions < end) { + size_t n = 0; + + /* Skip whitespaces, if any */ + if (*extensions == ' ') { + extensions++; + continue; + } + + n = strcspn(extensions, " "); + + /* Compare strings */ + if (n == extlen && strncmp(extension, extensions, n) == 0) + return true; /* Found */ + + extensions += n; + } + + /* Not found */ + return false; +} + +static inline void * +wth_get_egl_proc_address(const char *address) +{ + const char *extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); + + if (extensions && + (wth_check_egl_extension(extensions, "EGL_EXT_platform_wayland") || + wth_check_egl_extension(extensions, "EGL_KHR_platform_wayland"))) { + return (void *) eglGetProcAddress(address); + } + + return NULL; +} + +static inline EGLDisplay +wth_get_egl_display(EGLenum platform, void *native_display, + const EGLint *attrib_list) +{ + static PFNEGLGETPLATFORMDISPLAYEXTPROC get_platform_display = NULL; + + if (!get_platform_display) { + get_platform_display = (PFNEGLGETPLATFORMDISPLAYEXTPROC) + wth_get_egl_proc_address("eglGetPlatformDisplayEXT"); + } + + if (get_platform_display) + return get_platform_display(platform, native_display, attrib_list); + + /* FIXME: no fall-through using eglGetDisplay() */ + return NULL; +} + +static void +init_egl(struct display *display) +{ + EGLint config_attribs[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE + }; + + static const EGLint context_attribs[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + EGLint major, minor, count; + EGLBoolean ret; + + display->egl.dpy = wth_get_egl_display(EGL_PLATFORM_WAYLAND_KHR, + display->display, NULL); + assert(display->egl.dpy); + + ret = eglInitialize(display->egl.dpy, &major, &minor); + assert(ret == EGL_TRUE); + ret = eglBindAPI(EGL_OPENGL_ES_API); + assert(ret == EGL_TRUE); + + ret = eglChooseConfig(display->egl.dpy, config_attribs, + &display->egl.conf, 1, &count); + assert(ret && count >= 1); + + display->egl.ctx = eglCreateContext(display->egl.dpy, + display->egl.conf, + EGL_NO_CONTEXT, context_attribs); + assert(display->egl.ctx); + + eglSwapInterval(display->egl.dpy, 1); + + display->egl.create_image = + (void *) eglGetProcAddress("eglCreateImageKHR"); + assert(display->egl.create_image); + + display->egl.image_texture_2d = + (void *) eglGetProcAddress("glEGLImageTargetTexture2DOES"); + assert(display->egl.image_texture_2d); + + display->egl.destroy_image = + (void *) eglGetProcAddress("eglDestroyImageKHR"); + assert(display->egl.destroy_image); + +} + +GLuint load_shader(GLenum type, const char *shaderSrc) +{ + + GLuint shader; + GLint compiled; + + /* Create the shader object */ + shader = glCreateShader(type); + if (shader == 0) + { + printf("\n Failed to create shader \n"); + return 0; + } + /* Load the shader source */ + glShaderSource(shader, 1, &shaderSrc, NULL); + /* Compile the shader */ + glCompileShader(shader); + /* Check the compile status */ + glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); + if (!compiled) + { + GLint infoLen = 0; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); + if (infoLen > 1) + { + char* infoLog = (char*)malloc (sizeof(char) * infoLen ); + glGetShaderInfoLog(shader, infoLen, NULL, infoLog); + fprintf(stderr, "Error compiling shader:%s\n",infoLog); + free(infoLog); + } + glDeleteShader(shader); + return 0; + } + return shader; +} + +void init_gl(struct display *display) +{ + + GLint linked; + + /* load vertext/fragment shader */ + display->gl.vertex_shader = load_shader(GL_VERTEX_SHADER, vertex_shader_str); + display->gl.fragment_shader = load_shader(GL_FRAGMENT_SHADER, fragment_shader_str); + + /* Create the program object */ + display->gl.program_object = glCreateProgram(); + if (display->gl.program_object == 0) + { + fprintf(stderr, "error program object\n"); + return; + } + + glAttachShader(display->gl.program_object, display->gl.vertex_shader); + glAttachShader(display->gl.program_object, display->gl.fragment_shader); + /* Bind vPosition to attribute 0 */ + glBindAttribLocation(display->gl.program_object, 0, "a_position"); + /* Link the program */ + glLinkProgram(display->gl.program_object); + /* Check the link status */ + glGetProgramiv(display->gl.program_object, GL_LINK_STATUS, &linked); + if (!linked) + { + GLint infoLen = 0; + glGetProgramiv(display->gl.program_object, GL_INFO_LOG_LENGTH, &infoLen); + if (infoLen > 1) + { + char* infoLog = (char*)malloc(sizeof(char) * infoLen); + glGetProgramInfoLog(display->gl.program_object, infoLen, NULL, infoLog); + fprintf(stderr, "Error linking program:%s\n", infoLog); + free(infoLog); + } + glDeleteProgram(display->gl.program_object); + } + + glGenTextures(1, &display->gl.texture); + + return; +} + +static void +handle_xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, + int32_t width, int32_t height, struct wl_array *states) +{ + struct window *window = data; + uint32_t *p; + + window->fullscreen = 0; + window->maximized = 0; + + wl_array_for_each(p, states) { + uint32_t state = *p; + switch (state) { + case XDG_TOPLEVEL_STATE_FULLSCREEN: + window->fullscreen = 1; + break; + case XDG_TOPLEVEL_STATE_MAXIMIZED: + window->maximized = 1; + break; + } + } + + fprintf(stdout, "Got handle_xdg_toplevel_configure() " + "width %d, height %d, full %d, max %d\n", width, height, + window->fullscreen, window->maximized); + + if (width > 0 && height > 0) { + if (!window->fullscreen && !window->maximized) { + window->width = width; + window->height = height; + } + window->width = width; + window->height = height; + } else if (!window->fullscreen && !window->maximized) { + if (width == 0) + window->width = WINDOW_WIDTH_SIZE; + else + window->width = width; + + if (height == 0) + window->height = WINDOW_HEIGHT_SIZE; + else + window->height = height; + } + + fprintf(stdout, "settting width %d, height %d\n", window->width, + window->height); + + if (window->native) { + fprintf(stdout, "wayland-egl to resize to %dx%d\n", window->width, window->height); + wl_egl_window_resize(window->native, window->width, + window->height, 0, 0); + } +} + + +static void +handle_xdg_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel) +{ + running = 0; +} + +static const struct xdg_toplevel_listener xdg_toplevel_listener = { + handle_xdg_toplevel_configure, + handle_xdg_toplevel_close, +}; + +static inline EGLSurface +wth_create_egl_surface(EGLDisplay dpy, EGLConfig config, + void *native_window, const EGLint *attrib_list) +{ + static PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC + create_platform_window = NULL; + + if (!create_platform_window) { + create_platform_window = (PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC) + wth_get_egl_proc_address("eglCreatePlatformWindowSurfaceEXT"); + } + + if (create_platform_window) + return create_platform_window(dpy, config, + native_window, attrib_list); + + return eglCreateWindowSurface(dpy, config, + (EGLNativeWindowType) native_window, + attrib_list); +} + +static inline EGLBoolean +wth_destroy_egl_surface(EGLDisplay display, EGLSurface surface) +{ + return eglDestroySurface(display, surface); +} + +void +redraw(struct window *window) +{ + struct wl_region *region; + struct display *display = window->display; + + if (window->opaque || window->fullscreen) { + region = wl_compositor_create_region(window->display->compositor); + wl_region_add(region, 0, 0, + window->width, + window->height); + wl_surface_set_opaque_region(window->surface, region); + wl_region_destroy(region); + } else { + wl_surface_set_opaque_region(window->surface, NULL); + } + + fprintf(stdout, "Doing a redraw\n"); + eglSwapBuffers(display->egl.dpy, window->egl_surface); +} + +static void +handle_xdg_surface_configure(void *data, struct xdg_surface *surface, uint32_t serial) +{ + struct window *window = data; + + fprintf(stderr, "Sending configure ack\n"); + xdg_surface_ack_configure(surface, serial); + window->wait_for_configure = false; +} + +static const struct xdg_surface_listener xdg_surface_listener = { + handle_xdg_surface_configure, +}; + + +static void +create_surface(struct window *window) +{ + + struct display *display = window->display; + int ret; + + window->surface = wl_compositor_create_surface(display->compositor); + assert(window->surface); + + window->native = wl_egl_window_create(window->surface, + window->width, window->height); + assert(window->native); + + window->egl_surface = wth_create_egl_surface(display->egl.dpy, + display->egl.conf, + window->native, NULL); + + wl_display_roundtrip(display->display); + + if (display->wm_base) { + window->xdg_surface = + xdg_wm_base_get_xdg_surface(display->wm_base, window->surface); + assert(window->xdg_surface); + + xdg_surface_add_listener(window->xdg_surface, + &xdg_surface_listener, window); + window->xdg_toplevel = xdg_surface_get_toplevel(window->xdg_surface); + assert(window->xdg_toplevel); + + xdg_toplevel_add_listener(window->xdg_toplevel, + &xdg_toplevel_listener, window); + + if (window->app_id) + xdg_toplevel_set_app_id(window->xdg_toplevel, window->app_id); + + wl_surface_commit(window->surface); + window->wait_for_configure = true; + } + + + ret = eglMakeCurrent(display->egl.dpy, window->egl_surface, + window->egl_surface, display->egl.ctx); + assert(ret == EGL_TRUE); + + if (!window->frame_sync) + eglSwapInterval(display->egl.dpy, 0); + + glClearColor(0.0, 0.0, 0.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); +} + +static void +create_window(struct window *window, struct display *display, int width, int height, const char *app_id) +{ + window->callback = NULL; + window->display = display; + window->width = width; + window->height = height; + window->window_frames = 0; + window->window_benchmark_time = 0; + window->app_id = app_id; + window->frame_sync = 1; + + create_surface(window); + + return; +} + +static void +destroy_window(struct window *window) +{ + + + if (window->callback) + wl_callback_destroy(window->callback); + + if (window->xdg_toplevel) + xdg_toplevel_destroy(window->xdg_toplevel); + + if (window->xdg_surface) + xdg_surface_destroy(window->xdg_surface); + + + wl_surface_destroy(window->surface); + free(window); + + +} + +static void +signal_int(int signum) +{ + running = 0; +} + + +static void +error_cb(GstBus *bus, GstMessage *msg, gpointer user_data) +{ + GstAppContext *d = user_data; + + gchar *debug = NULL; + GError *err = NULL; + + gst_message_parse_error(msg, &err, &debug); + + g_print("Error: %s\n", err->message); + g_error_free(err); + + if (debug) { + g_print("Debug details: %s\n", debug); + g_free(debug); + } + + gst_element_set_state(d->pipeline, GST_STATE_NULL); +} + +static GstBusSyncReply +bus_sync_handler(GstBus *bus, GstMessage *message, gpointer user_data) +{ + GstAppContext *d = user_data; + + fprintf(stdout, "entering bus_sync_handler() setting it\n"); + + if (gst_is_wayland_display_handle_need_context_message(message)) { + GstContext *context; + struct wl_display *display_handle = d->display->display; + + context = gst_wayland_display_handle_context_new(display_handle); + d->wl_video = GST_WAYLAND_VIDEO(GST_MESSAGE_SRC(message)); + gst_element_set_context(GST_ELEMENT(GST_MESSAGE_SRC(message)), context); + + fprintf(stdout, "bus_sync_handler(): creating context and setting it\n"); + + goto drop; + } else if (gst_is_video_overlay_prepare_window_handle_message(message)) { + struct wl_surface *window_handle = d->window->surface; + + /* GST_MESSAGE_SRC(message) will be the overlay object that we + * have to use. This may be waylandsink, but it may also be + * playbin. In the latter case, we must make sure to use + * playbin instead of waylandsink, because playbin resets the + * window handle and render_rectangle after restarting playback + * and the actual window size is lost */ + d->overlay = GST_VIDEO_OVERLAY(GST_MESSAGE_SRC(message)); + + g_print("setting window handle and size (%d x %d) w %d, h %d\n", + d->window->x, d->window->y, + d->window->width, d->window->height); + + gst_video_overlay_set_window_handle(d->overlay, (guintptr) window_handle); + gst_video_overlay_set_render_rectangle(d->overlay, + d->window->x, d->window->y, + d->window->width, d->window->height); + + goto drop; + } + + return GST_BUS_PASS; + +drop: + gst_message_unref(message); + return GST_BUS_DROP; +} + + +/** + * wth_receiver_weston_main + * + * This is the main function which will handle connection to the compositor at + * receiver side + * + * @param names void *data + * @param value struct window data + * @return 0 on success, -1 on error + */ +int +wth_receiver_weston_main(struct window *window, const char *app_id, int port) +{ + struct sigaction sigint; + GstAppContext gstctx; + int ret = 0; + GError *gerror = NULL; + char pipeline[PIPELINE_SIZE]; + + memset(&gstctx, 0, sizeof(gstctx)); + + sigint.sa_handler = signal_int; + sigemptyset(&sigint.sa_mask); + sigint.sa_flags = SA_RESETHAND; + sigaction(SIGINT, &sigint, NULL); + + /* Initialization for window creation */ + gstctx.display = create_display(); + init_egl(gstctx.display); + + /* ToDo: fix the hardcoded value of width, height */ + create_window(window, gstctx.display, WINDOW_WIDTH_SIZE, + WINDOW_HEIGHT_SIZE, app_id); + init_gl(gstctx.display); + + gstctx.window = window; + gstctx.display->window = window; + + fprintf(stderr, "display %p\n", gstctx.display); + fprintf(stderr, "display->window %p\n", gstctx.display->window); + fprintf(stderr, "window %p\n", window); + + int gargc = 2; + char **gargv = (char**) malloc(2 * sizeof(char*)); + + gargv[0] = strdup("waltham-receiver"); + gargv[1] = strdup("--gst-debug-level=2"); + + /* create gstreamer pipeline */ + gst_init(&gargc, &gargv); + + const char *pipe = "rtpbin name=rtpbin udpsrc " + "caps=\"application/x-rtp,media=(string)video,clock-rate=(int)90000,encoding-name=JPEG,payload=26\" " + "port=%d ! rtpbin.recv_rtp_sink_0 rtpbin. ! " + "rtpjpegdepay ! jpegdec ! waylandsink"; + + memset(pipeline, 0x00, sizeof(pipeline)); + snprintf(pipeline, sizeof(pipeline), pipe, port); + + fprintf(stdout, "pipeline %s\n", pipeline); + + /* parse the pipeline */ + gstctx.pipeline = gst_parse_launch(pipeline, &gerror); + if (!gstctx.pipeline) { + fprintf(stderr, "Could not create gstreamer pipeline.\n"); + destroy_display(gstctx.display); + return -1; + } + + gstctx.bus = gst_element_get_bus(gstctx.pipeline); + gst_bus_add_signal_watch(gstctx.bus); + + g_signal_connect(gstctx.bus, "message::error", G_CALLBACK(error_cb), &gstctx); + gst_bus_set_sync_handler(gstctx.bus, bus_sync_handler, &gstctx, NULL); + gst_object_unref(gstctx.bus); + + gst_element_set_state(gstctx.pipeline, GST_STATE_PLAYING); + + while (running && ret != -1) { + if (window->wait_for_configure) { + ret = wl_display_dispatch(gstctx.display->display); + } else { + ret = wl_display_dispatch_pending(gstctx.display->display); + redraw(window); + } + } + + gst_element_set_state(gstctx.pipeline, GST_STATE_NULL); + + destroy_window(window); + destroy_display(gstctx.display); + gst_object_unref(gstctx.pipeline); + + return 0; +} diff --git a/src/wth-receiver-gst-shm.c b/src/wth-receiver-gst-shm.c new file mode 100644 index 0000000..54950ef --- /dev/null +++ b/src/wth-receiver-gst-shm.c @@ -0,0 +1,847 @@ +/* + * Copyright © 2019 Advanced Driver Information Technology GmbH + * Copyright © 2020 Collabora, Ltd. + * + * 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. + */ + +/******************************************************************************* + ** ** + ** TARGET : linux ** + ** ** + ** PROJECT : waltham-receiver ** + ** ** + ** PURPOSE : This file is acts as interface to weston compositor at receiver ** + ** side ** + ** ** + *******************************************************************************/ + +#define GST_USE_UNSTABLE_API + +#include <sys/mman.h> +#include <signal.h> +#include <sys/time.h> + +#include <gst/gst.h> +#include <gst/video/gstvideometa.h> +#include <gst/allocators/gstdmabuf.h> +#include <gst/app/gstappsink.h> + +#include <pthread.h> +#include <gst/wayland/wayland.h> +#include <gst/video/videooverlay.h> + +#include <sys/mman.h> +#include <unistd.h> +#include <fcntl.h> + +#include "xdg-shell-client-protocol.h" + +#include "wth-receiver-comm.h" +#include "wth-receiver-seat.h" +#include "os-compatibility.h" +#include "bitmap.h" + +#define WINDOW_WIDTH_SIZE 1920 +#define WINDOW_HEIGHT_SIZE 760 + +#define PIPELINE_SIZE 4096 + +static int running = 1; + +typedef struct _GstAppContext { + GMainLoop *loop; + GstBus *bus; + + GstElement *pipeline; + GstElement *sink; + + GstWaylandVideo *wl_video; + GstVideoOverlay *overlay; + + struct display *display; + struct window *window; + GstVideoInfo info; + +} GstAppContext; + +/* + * pointer callbcak functions + */ +static void +pointer_handle_enter(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, struct wl_surface *wl_surface, + wl_fixed_t sx, wl_fixed_t sy) +{ + struct display *display = data; + struct window *window = display->window; + + waltham_pointer_enter(window, serial, sx, sy); +} + +static void +pointer_handle_leave(void *data, struct wl_pointer *pointer, + uint32_t serial, struct wl_surface *surface) +{ + struct display *display = data; + struct window *window = display->window; + + waltham_pointer_leave(window, serial); +} + +static void +pointer_handle_motion(void *data, struct wl_pointer *pointer, + uint32_t time, wl_fixed_t sx, wl_fixed_t sy) +{ + struct display *display = data; + struct window *window = display->window; + + waltham_pointer_motion(window, time, sx, sy); +} + +static void +pointer_handle_button(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, uint32_t time, uint32_t button, + uint32_t state) +{ + struct display *display = data; + struct window *window = display->window; + + waltham_pointer_button(window, serial, time, button, state); +} + +static void +pointer_handle_axis(void *data, struct wl_pointer *wl_pointer, + uint32_t time, uint32_t axis, wl_fixed_t value) +{ + struct display *display = data; + struct window *window = display->window; + + waltham_pointer_axis(window, time, axis, value); +} + +static void +pointer_handle_frame(void *data, struct wl_pointer *pointer) +{ + (void) data; + (void) pointer; +} + +static void +pointer_handle_axis_source(void *data, struct wl_pointer *pointer, + uint32_t source) +{ + (void) data; + (void) pointer; + (void) source; +} + +static void +pointer_handle_axis_stop(void *data, struct wl_pointer *pointer, + uint32_t time, uint32_t axis) +{ + (void) data; + (void) pointer; + (void) time; + (void) axis; +} + +static void +pointer_handle_axis_discrete(void *data, struct wl_pointer *pointer, + uint32_t axis, int32_t discrete) +{ + (void) data; + (void) pointer; + (void) axis; + (void) discrete; +} + +static const struct wl_pointer_listener pointer_listener = { + pointer_handle_enter, + pointer_handle_leave, + pointer_handle_motion, + pointer_handle_button, + pointer_handle_axis, + pointer_handle_frame, + pointer_handle_axis_source, + pointer_handle_axis_stop, + pointer_handle_axis_discrete +}; + +/* + * touch callbcak functions + */ +static void +touch_handle_down(void *data, struct wl_touch *touch, uint32_t serial, + uint32_t time, struct wl_surface *surface, int32_t id, + wl_fixed_t x_w, wl_fixed_t y_w) +{ + struct display *display = data; + struct window *window = display->window; + + waltham_touch_down(window, serial, time, id, x_w, y_w); +} + +static void +touch_handle_up(void *data, struct wl_touch *touch, uint32_t serial, + uint32_t time, int32_t id) +{ + struct display *display = data; + struct window *window = display->window; + + waltham_touch_up(window, serial, time, id); +} + +static void +touch_handle_motion(void *data, struct wl_touch *touch, uint32_t time, + int32_t id, wl_fixed_t x_w, wl_fixed_t y_w) +{ + struct display *display = data; + struct window *window = display->window; + + waltham_touch_motion(window, time, id, x_w, y_w); +} + +static void +touch_handle_frame(void *data, struct wl_touch *touch) +{ + struct display *display = data; + struct window *window = display->window; + + waltham_touch_frame(window); +} + +static void +touch_handle_cancel(void *data, struct wl_touch *touch) +{ + struct display *display = data; + struct window *window = display->window; + + waltham_touch_cancel(window); +} + +static void +touch_handle_shape(void *data, struct wl_touch *touch, + int32_t id, wl_fixed_t maj, wl_fixed_t min) +{ + (void) data; + (void) touch; + + (void) id; + (void) maj; + (void) min; +} + +static void +touch_handle_orientation(void *data, struct wl_touch *touch, int32_t id, wl_fixed_t orientation) +{ + (void) data; + (void) touch; + + (void) id; + (void) orientation; +} + +static const struct wl_touch_listener touch_listener = { + touch_handle_down, + touch_handle_up, + touch_handle_motion, + touch_handle_frame, + touch_handle_cancel, + touch_handle_shape, + touch_handle_orientation, +}; + +static void +xdg_wm_base_ping(void *data, struct xdg_wm_base *shell, uint32_t serial) +{ + xdg_wm_base_pong(shell, serial); +} + +static const struct xdg_wm_base_listener xdg_wm_base_listener = { + xdg_wm_base_ping, +}; + + +/* + * seat callback + */ +static void +seat_capabilities(void *data, struct wl_seat *wl_seat, enum wl_seat_capability caps) +{ + struct display *display = data; + + if ((caps & WL_SEAT_CAPABILITY_POINTER) && !display->wl_pointer) { + display->wl_pointer = wl_seat_get_pointer(wl_seat); + wl_pointer_set_user_data(display->wl_pointer, display); + wl_pointer_add_listener(display->wl_pointer, &pointer_listener, display); + wl_display_roundtrip(display->display); + } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && display->wl_pointer) { + wl_pointer_destroy(display->wl_pointer); + display->wl_pointer = NULL; + } + + if ((caps & WL_SEAT_CAPABILITY_TOUCH) && !display->wl_touch) { + display->wl_touch = wl_seat_get_touch(wl_seat); + wl_touch_set_user_data(display->wl_touch, display); + wl_touch_add_listener(display->wl_touch, &touch_listener, display); + wl_display_roundtrip(display->display); + } else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && display->wl_touch) { + wl_touch_destroy(display->wl_touch); + display->wl_touch = NULL; + } +} + +static const struct wl_seat_listener seat_listener = { + seat_capabilities, + NULL +}; + +static void +add_seat(struct display *display, uint32_t id, uint32_t version) +{ + display->wl_pointer = NULL; + display->wl_touch = NULL; + display->wl_keyboard = NULL; + display->seat = wl_registry_bind(display->registry, id, + &wl_seat_interface, 1); + wl_seat_add_listener(display->seat, &seat_listener, display); +} + +static void +redraw(void *data, struct wl_callback *callback, uint32_t time); + +static void +paint_pixels(void *image, int padding, int width, int height, uint32_t time) +{ + memset(image, 0x00, width * height * 4); +} + +static void +buffer_release(void *data, struct wl_buffer *buffer) +{ + struct shm_buffer *mybuf = data; + mybuf->busy = 0; +} + +static const struct wl_buffer_listener buffer_listener = { + buffer_release +}; + +static int +create_shm_buffer(struct display *display, struct shm_buffer *buffer, + int width, int height, uint32_t format) +{ + struct wl_shm_pool *pool; + int fd, size, stride; + void *data; + + stride = width * 4; + size = stride * height; + + fd = os_create_anonymous_file(size); + if (fd < 0) { + fprintf(stderr, "creating a buffer file for %d B failed: %s\n", + size, strerror(errno)); + return -1; + } + + data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (data == MAP_FAILED) { + fprintf(stderr, "mmap failed: %s\n", strerror(errno)); + close(fd); + return -1; + } + + pool = wl_shm_create_pool(display->shm, fd, size); + buffer->buffer = wl_shm_pool_create_buffer(pool, 0, width, + height, stride, format); + wl_buffer_add_listener(buffer->buffer, &buffer_listener, buffer); + wl_shm_pool_destroy(pool); + close(fd); + + buffer->shm_data = data; + return 0; +} + +static struct shm_buffer * +get_next_buffer(struct window *window) +{ + struct shm_buffer *buffer; + int ret = 0; + + if (!window->buffers[0].busy) + buffer = &window->buffers[0]; + else if (!window->buffers[1].busy) + buffer = &window->buffers[1]; + else + return NULL; + + if (!buffer->buffer) { + fprintf(stdout, "get_next_buffer() buffer is not set, setting with " + "width %d, height %d\n", window->width, window->height); + ret = create_shm_buffer(window->display, buffer, window->width, + window->height, WL_SHM_FORMAT_XRGB8888); + + if (ret < 0) + return NULL; + + /* paint the padding */ + memset(buffer->shm_data, 0x00, window->width * window->height * 4); + } + + return buffer; +} + + +static const struct wl_callback_listener frame_listener = { + redraw +}; + +static void +redraw(void *data, struct wl_callback *callback, uint32_t time) +{ + struct window *window = data; + struct shm_buffer *buffer; + + buffer = get_next_buffer(window); + if (!buffer) { + fprintf(stderr, + !callback ? "Failed to create the first buffer.\n" : + "Both buffers busy at redraw(). Server bug?\n"); + abort(); + } + + // do the actual painting + paint_pixels(buffer->shm_data, 0x0, window->width, window->height, time); + + wl_surface_attach(window->surface, buffer->buffer, 0, 0); + wl_surface_damage(window->surface, 0, 0, window->width, window->height); + + if (callback) + wl_callback_destroy(callback); + + window->callback = wl_surface_frame(window->surface); + wl_callback_add_listener(window->callback, &frame_listener, window); + wl_surface_commit(window->surface); + + buffer->busy = 1; +} + +static void +shm_format(void *data, struct wl_shm *wl_shm, uint32_t format) +{ + struct display *d = data; + + if (format == WL_SHM_FORMAT_XRGB8888) + d->has_xrgb = true; +} + +static const struct wl_shm_listener shm_listener = { + shm_format +}; + +static void +handle_xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, + int32_t width, int32_t height, struct wl_array *states) +{ + struct window *window = data; + uint32_t *p; + + window->fullscreen = 0; + window->maximized = 0; + + wl_array_for_each(p, states) { + uint32_t state = *p; + switch (state) { + case XDG_TOPLEVEL_STATE_FULLSCREEN: + window->fullscreen = 1; + break; + case XDG_TOPLEVEL_STATE_MAXIMIZED: + window->maximized = 1; + break; + } + } + + fprintf(stdout, "Got handle_xdg_toplevel_configure() " + "width %d, height %d, full %d, max %d\n", width, height, + window->fullscreen, window->maximized); + + if (width > 0 && height > 0) { + if (!window->fullscreen && !window->maximized) { + window->width = width; + window->height = height; + } + window->width = width; + window->height = height; + } else if (!window->fullscreen && !window->maximized) { + if (width == 0) + window->width = WINDOW_WIDTH_SIZE; + else + window->width = width; + + if (height == 0) + window->height = WINDOW_HEIGHT_SIZE; + else + window->height = height; + } + + fprintf(stdout, "settting width %d, height %d\n", window->width, + window->height); +} + + +static void +handle_xdg_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel) +{ + running = 0; +} + +static const struct xdg_toplevel_listener xdg_toplevel_listener = { + handle_xdg_toplevel_configure, + handle_xdg_toplevel_close, +}; + +static void +handle_xdg_surface_configure(void *data, struct xdg_surface *surface, uint32_t serial) +{ + struct window *window = data; + + fprintf(stderr, "Sending configure ack\n"); + + xdg_surface_ack_configure(surface, serial); + + if (window->wait_for_configure) { + redraw(window, NULL, 0); + window->wait_for_configure = false; + } +} + +static const struct xdg_surface_listener xdg_surface_listener = { + handle_xdg_surface_configure, +}; + + +static void +create_surface(struct window *window) +{ + struct display *display = window->display; + + window->surface = wl_compositor_create_surface(display->compositor); + assert(window->surface); + + if (display->wm_base) { + window->xdg_surface = + xdg_wm_base_get_xdg_surface(display->wm_base, window->surface); + assert(window->xdg_surface); + + xdg_surface_add_listener(window->xdg_surface, + &xdg_surface_listener, window); + window->xdg_toplevel = xdg_surface_get_toplevel(window->xdg_surface); + assert(window->xdg_toplevel); + + xdg_toplevel_add_listener(window->xdg_toplevel, + &xdg_toplevel_listener, window); + + if (window->app_id) + xdg_toplevel_set_app_id(window->xdg_toplevel, window->app_id); + + wl_surface_commit(window->surface); + window->wait_for_configure = true; + } +} + +static void +create_window(struct window *window, struct display *display, int width, int height, const char *app_id) +{ + window->callback = NULL; + + window->display = display; + window->width = width; + window->height = height; + window->window_frames = 0; + window->window_benchmark_time = 0; + window->app_id = app_id; + window->frame_sync = 1; + + create_surface(window); + + return; +} + +static void +destroy_window(struct window *window) +{ + if (window->callback) + wl_callback_destroy(window->callback); + + if (window->xdg_toplevel) + xdg_toplevel_destroy(window->xdg_toplevel); + + if (window->xdg_surface) + xdg_surface_destroy(window->xdg_surface); + + + wl_surface_destroy(window->surface); + free(window); +} + +static void +signal_int(int signum) +{ + running = 0; +} + +/* + * registry callback + */ +static void +registry_handle_global(void *data, struct wl_registry *registry, + uint32_t id, const char *interface, uint32_t version) +{ + struct display *d = data; + + if (strcmp(interface, "wl_compositor") == 0) { + d->compositor = + wl_registry_bind(registry, + id, &wl_compositor_interface, 1); + } else if (strcmp(interface, "wl_seat") == 0) { + add_seat(d, id, version); + } else if (strcmp(interface, "xdg_wm_base") == 0) { + d->wm_base = wl_registry_bind(registry, id, + &xdg_wm_base_interface, 1); + xdg_wm_base_add_listener(d->wm_base, &xdg_wm_base_listener, d); + + } else if (strcmp(interface, "wl_shm") == 0) { + d->shm = wl_registry_bind(registry, id, &wl_shm_interface, 1); + wl_shm_add_listener(d->shm, &shm_listener, d); + } +} + +static void +registry_handle_global_remove(void *data, struct wl_registry *registry, + uint32_t name) +{ + /* stub */ +} + +static const struct wl_registry_listener registry_listener = { + registry_handle_global, + registry_handle_global_remove +}; + + +static struct display * +create_display(void) +{ + struct display *display; + + display = malloc(sizeof *display); + if (display == NULL) { + wth_error("out of memory\n"); + exit(1); + } + display->display = wl_display_connect(NULL); + assert(display->display); + + display->has_xrgb = false; + display->registry = wl_display_get_registry(display->display); + wl_registry_add_listener(display->registry, + ®istry_listener, display); + + wl_display_roundtrip(display->display); + + return display; +} + +static void +destroy_display(struct display *display) +{ + if (display->compositor) + wl_compositor_destroy(display->compositor); + + wl_registry_destroy(display->registry); + wl_display_flush(display->display); + wl_display_disconnect(display->display); + free(display); +} + +static void +error_cb(GstBus *bus, GstMessage *msg, gpointer user_data) +{ + GstAppContext *d = user_data; + + gchar *debug = NULL; + GError *err = NULL; + + gst_message_parse_error(msg, &err, &debug); + + g_print("Error: %s\n", err->message); + g_error_free(err); + + if (debug) { + g_print("Debug details: %s\n", debug); + g_free(debug); + } + + gst_element_set_state(d->pipeline, GST_STATE_NULL); +} + +static GstBusSyncReply +bus_sync_handler(GstBus *bus, GstMessage *message, gpointer user_data) +{ + GstAppContext *d = user_data; + + if (gst_is_wayland_display_handle_need_context_message(message)) { + GstContext *context; + struct wl_display *display_handle = d->display->display; + + context = gst_wayland_display_handle_context_new(display_handle); + d->wl_video = GST_WAYLAND_VIDEO(GST_MESSAGE_SRC(message)); + gst_element_set_context(GST_ELEMENT(GST_MESSAGE_SRC(message)), context); + + goto drop; + } else if (gst_is_video_overlay_prepare_window_handle_message(message)) { + struct wl_surface *window_handle = d->window->surface; + + /* GST_MESSAGE_SRC(message) will be the overlay object that we + * have to use. This may be waylandsink, but it may also be + * playbin. In the latter case, we must make sure to use + * playbin instead of waylandsink, because playbin resets the + * window handle and render_rectangle after restarting playback + * and the actual window size is lost */ + d->overlay = GST_VIDEO_OVERLAY(GST_MESSAGE_SRC(message)); + + g_print("setting window handle and size (%d x %d) w %d, h %d\n", + d->window->x, d->window->y, + d->window->width, d->window->height); + + gst_video_overlay_set_window_handle(d->overlay, (guintptr) window_handle); + gst_video_overlay_set_render_rectangle(d->overlay, + d->window->x, d->window->y, + d->window->width, d->window->height); + + goto drop; + } + + return GST_BUS_PASS; + +drop: + gst_message_unref(message); + return GST_BUS_DROP; +} + + +/** + * wth_receiver_weston_main + * + * This is the main function which will handle connection to the compositor at + * receiver side + * + * @param names void *data + * @param value struct window data + * @return 0 on success, -1 on error + */ +int +wth_receiver_weston_main(struct window *window, const char *app_id, int port) +{ + struct sigaction sigint; + GstAppContext gstctx; + int ret = 0; + GError *gerror = NULL; + char pipeline[PIPELINE_SIZE]; + + memset(&gstctx, 0, sizeof(gstctx)); + + sigint.sa_handler = signal_int; + sigemptyset(&sigint.sa_mask); + sigint.sa_flags = SA_RESETHAND; + sigaction(SIGINT, &sigint, NULL); + + /* Initialization for window creation */ + gstctx.display = create_display(); + + /* ToDo: fix the hardcoded value of width, height */ + create_window(window, gstctx.display, WINDOW_WIDTH_SIZE, + WINDOW_HEIGHT_SIZE, app_id); + + gstctx.window = window; + gstctx.display->window = window; + + fprintf(stderr, "display %p\n", gstctx.display); + fprintf(stderr, "display->window %p\n", gstctx.display->window); + fprintf(stderr, "window %p\n", window); + + /* Initialise damage to full surface, so the padding gets painted */ + wl_surface_damage(window->surface, 0, 0, + window->width, window->height); + + if (!window->wait_for_configure) + redraw(window, NULL, 0); + + int gargc = 2; + char **gargv = (char**) malloc(2 * sizeof(char*)); + + gargv[0] = strdup("waltham-receiver"); + gargv[1] = strdup("--gst-debug-level=2"); + + /* create gstreamer pipeline */ + gst_init(&gargc, &gargv); + + const char *pipe = "rtpbin name=rtpbin udpsrc " + "caps=\"application/x-rtp,media=(string)video,clock-rate=(int)90000,encoding-name=JPEG,payload=26\" " + "port=%d ! rtpbin.recv_rtp_sink_0 rtpbin. ! " + "rtpjpegdepay ! jpegdec ! waylandsink"; + + memset(pipeline, 0x00, sizeof(pipeline)); + snprintf(pipeline, sizeof(pipeline), pipe, port); + + fprintf(stdout, "pipeline %s\n", pipeline); + + /* parse the pipeline */ + gstctx.pipeline = gst_parse_launch(pipeline, &gerror); + if (!gstctx.pipeline) + fprintf(stderr, "Could not create gstreamer pipeline.\n"); + + gstctx.bus = gst_element_get_bus(gstctx.pipeline); + gst_bus_add_signal_watch(gstctx.bus); + + fprintf(stdout, "registered bus signal\n"); + + g_signal_connect(gstctx.bus, "message::error", G_CALLBACK(error_cb), &gstctx); + gst_bus_set_sync_handler(gstctx.bus, bus_sync_handler, &gstctx, NULL); + gst_object_unref(gstctx.bus); + + gst_element_set_state(gstctx.pipeline, GST_STATE_PLAYING); + + while (running && ret != -1) + ret = wl_display_dispatch(gstctx.display->display); + + gst_element_set_state(gstctx.pipeline, GST_STATE_NULL); + + destroy_window(window); + destroy_display(gstctx.display); + gst_object_unref(gstctx.pipeline); + free(gargv); + + return 0; +} diff --git a/src/wth-receiver-main.c b/src/wth-receiver-main.c new file mode 100644 index 0000000..8af8eda --- /dev/null +++ b/src/wth-receiver-main.c @@ -0,0 +1,306 @@ +/* + * Copyright © 2019 Advanced Driver Information Technology 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. + */ + +/******************************************************************************* +** ** +** TARGET : linux ** +** ** +** PROJECT : waltham-receiver ** +** ** +** PURPOSE : This file handles connection with remote-client ** +** ** +*******************************************************************************/ + +#include <signal.h> +#include "wth-receiver-comm.h" + +#define MAX_EPOLL_WATCHES 2 +#define DEFAULT_TCP_PORT 34400 + +uint16_t tcp_port = 0; +const char *my_app_id = NULL; +static bool *signal_int_handler_run_flag; + +/** Print out the application help + */ +static void +usage(void) +{ + printf("Usage: waltham receiver [options]\n"); + printf("Options:\n"); + printf(" -p --port number TCP port number\n"); + printf(" -i --app_id Specify an app_id\n"); + printf(" -h --help Usage\n"); +} + +static struct option long_options[] = { + {"port", required_argument, 0, 'p'}, + {"app_id", required_argument, NULL, 'i'}, + {"help", no_argument, 0, 'h'}, + {0, 0, 0, 0} +}; + +/** + * parse_args + * + * Parses the application arguments + * The arguments are parsed and saved in static structure for future use. + * + * @param argc The amount of arguments + * @param argv The table of arguments + * + * @return 0 on success, -1 otherwise + */ +static int +parse_args(int argc, char *argv[]) +{ + int c = -1; + int long_index = 0; + + while ((c = getopt_long(argc, argv, "i:p:vh", + long_options, + &long_index)) != -1) { + switch (c) { + case 'i': + my_app_id = optarg; + break; + case 'p': + tcp_port = (uint16_t) atoi(optarg); + break; + case 'v': + printf("No verbose logs for release mode"); + break; + case 'h': + usage(); + return -1; + default: + wth_error("Try %s -h for more information.\n", argv[0]); + return -1; + } + } + + if (tcp_port == 0) { + tcp_port = DEFAULT_TCP_PORT; + } + + + return 0; +} + +static int +watch_ctl(struct watch *w, int op, uint32_t events) +{ + struct epoll_event ee; + + ee.events = events; + ee.data.ptr = w; + return epoll_ctl(w->receiver->epoll_fd, op, w->fd, &ee); +} + +/** +* listen_socket_handle_data +* +* Handles all incoming events on socket +* +* @param names struct watch *w ,uint32_t events +* @param value pointer to watch connection it holds receiver information, Incoming events information +* @return none +*/ +static void +listen_socket_handle_data(struct watch *w, uint32_t events) +{ + struct receiver *srv = container_of(w, struct receiver, listen_watch); + + if (events & EPOLLERR) { + wth_error("Listening socket errored out.\n"); + srv->running = false; + return; + } + + if (events & EPOLLHUP) { + wth_error("Listening socket hung up.\n"); + srv->running = false; + return; + } + + if (events & EPOLLIN) { + receiver_accept_client(srv); + } +} + +/** +* receiver_mainloop +* +* This is the main loop, which will flush all pending clients requests and +* listen to input events from socket +* +* @param names void *data +* @param value pointer to receiver struct - +* struct holds the client connection information +* @return none +*/ +static void +receiver_mainloop(struct receiver *srv) +{ + struct epoll_event ee[MAX_EPOLL_WATCHES]; + struct watch *w; + int count; + int i; + + srv->running = true; + + while (srv->running) { + /* Run any idle tasks at this point. */ + + receiver_flush_clients(srv); + + /* Wait for events or signals */ + count = epoll_wait(srv->epoll_fd, + ee, ARRAY_LENGTH(ee), -1); + if (count < 0 && errno != EINTR) { + perror("Error with epoll_wait"); + break; + } + + /* Handle all fds, both the listening socket + * (see listen_socket_handle_data()) and clients + * (see connection_handle_data()). + */ + for (i = 0; i < count; i++) { + w = ee[i].data.ptr; + w->cb(w, ee[i].events); + } + } +} + +static int +receiver_listen(uint16_t tcp_port) +{ + int fd; + int reuse = 1; + struct sockaddr_in addr; + + fd = socket(AF_INET, SOCK_STREAM, 0); + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(tcp_port); + addr.sin_addr.s_addr = htonl(INADDR_ANY); + + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof reuse); + + if (bind(fd, (struct sockaddr *)&addr, sizeof addr) < 0) { + wth_error("Failed to bind to port %d", tcp_port); + close(fd); + return -1; + } + + if (listen(fd, 1024) < 0) { + wth_error("Failed to listen to port %d", tcp_port); + close (fd); + return -1; + } + + return fd; +} + + +static void +signal_int_handler(int signum) +{ + if (!*signal_int_handler_run_flag) + abort(); + + *signal_int_handler_run_flag = false; +} + +static void +set_sigint_handler(bool *running) +{ + struct sigaction sigint; + + signal_int_handler_run_flag = running; + sigint.sa_handler = signal_int_handler; + sigemptyset(&sigint.sa_mask); + sigint.sa_flags = SA_RESETHAND; + sigaction(SIGINT, &sigint, NULL); +} + +/** + * main + * + * waltham receiver main function, it accepts tcp port number as argument. + * Establishes connection on the port and listen to port for incoming connection + * request from waltham clients + * + * @param names argv - argument list and argc -argument count + * @param value tcp port number as argument + * @return 0 on success, -1 on error + */ +int main(int argc, char *argv[]) +{ + struct receiver srv = { 0 }; + struct client *c; + + /* Get command line arguments */ + if (parse_args(argc, argv) != 0) { + return -1; + } + + set_sigint_handler(&srv.running); + + wl_list_init(&srv.client_list); + + srv.epoll_fd = epoll_create1(EPOLL_CLOEXEC); + if (srv.epoll_fd == -1) { + perror("Error on epoll_create1"); + exit(1); + } + + srv.listen_fd = receiver_listen(tcp_port); + if (srv.listen_fd < 0) { + perror("Error setting up listening socket"); + exit(1); + } + + srv.listen_watch.receiver = &srv; + srv.listen_watch.cb = listen_socket_handle_data; + srv.listen_watch.fd = srv.listen_fd; + if (watch_ctl(&srv.listen_watch, EPOLL_CTL_ADD, EPOLLIN) < 0) { + perror("Error setting up listen polling"); + exit(1); + } + + receiver_mainloop(&srv); + + /* destroy all things */ + wl_list_last_until_empty(c, &srv.client_list, link) + client_destroy(c); + + close(srv.listen_fd); + close(srv.epoll_fd); + + return 0; +} diff --git a/src/wth-receiver-seat.c b/src/wth-receiver-seat.c new file mode 100644 index 0000000..3457748 --- /dev/null +++ b/src/wth-receiver-seat.c @@ -0,0 +1,311 @@ +/* + * Copyright © 2019 Advanced Driver Information Technology GmbH + * Copyright © 2020 Collabora, Ltd. + * + * 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. + */ + +#include "wth-receiver-comm.h" +#include "wth-receiver-seat.h" + +/* + * APIs to send touch events to waltham client + */ +void +waltham_touch_down(struct window *window, uint32_t serial, + uint32_t time, int32_t id, + wl_fixed_t x_w, wl_fixed_t y_w) +{ + struct surface *surface = window->receiver_surf; + struct seat *seat = window->receiver_seat; + struct touch *touch = seat->touch; + + if (touch->obj) { + fprintf(stdout, "waltham_touch_down() sending touch_down\n"); + wthp_touch_send_down(touch->obj, serial, time, surface->obj, id, x_w, y_w); + } + return; +} + +void +waltham_touch_up(struct window *window, uint32_t serial, + uint32_t time, int32_t id) +{ + struct seat *seat = window->receiver_seat; + struct touch *touch = seat->touch; + + if (touch->obj) { + fprintf(stdout, "waltham_touch_motion() sending touch_up\n"); + wthp_touch_send_up(touch->obj, serial, time, id); + } + return; +} + +void +waltham_touch_motion(struct window *window, uint32_t time, + int32_t id, wl_fixed_t x_w, wl_fixed_t y_w) +{ + struct seat *seat = window->receiver_seat; + struct touch *touch = seat->touch; + + if (touch->obj) { + fprintf(stdout, "waltham_touch_motion() sending touch_motion\n"); + wthp_touch_send_motion(touch->obj, time, id, x_w, y_w); + } +} + +void +waltham_touch_frame(struct window *window) +{ + struct seat *seat = window->receiver_seat; + struct touch *touch = seat->touch; + + if (touch->obj) { + fprintf(stdout, "waltham_touch_frame() sending frame\n"); + wthp_touch_send_frame(touch->obj); + } +} + +void +waltham_touch_cancel(struct window *window) +{ + struct seat *seat = window->receiver_seat; + struct touch *touch = seat->touch; + + if (touch->obj) { + fprintf(stdout, "waltham_touch_cancel() sending cancel\n"); + wthp_touch_send_cancel(touch->obj); + } +} + +/* + * APIs to send pointer events to waltham client + */ + +void +waltham_pointer_enter(struct window *window, uint32_t serial, + wl_fixed_t sx, wl_fixed_t sy) +{ + struct surface *surface = window->receiver_surf; + struct seat *seat = window->receiver_seat; + struct pointer *pointer = seat->pointer; + + if (pointer->obj) { + fprintf(stdout, "waltham_pointer_enter() sending enter\n"); + wthp_pointer_send_enter(pointer->obj, serial, surface->obj, sx, sy); + } +} + +void +waltham_pointer_leave(struct window *window, uint32_t serial) +{ + struct surface *surface = window->receiver_surf; + struct seat *seat = window->receiver_seat; + struct pointer *pointer = seat->pointer; + + if (pointer->obj) { + fprintf(stdout, "waltham_pointer_leave() sending leave\n"); + wthp_pointer_send_leave(pointer->obj, serial, surface->obj); + } +} + +void +waltham_pointer_motion(struct window *window, uint32_t time, + wl_fixed_t sx, wl_fixed_t sy) +{ + struct seat *seat = window->receiver_seat; + struct pointer *pointer = seat->pointer; + + if (pointer->obj) { + fprintf(stdout, "waltham_pointer_motion() sending motion\n"); + wthp_pointer_send_motion(pointer->obj, time, sx, sy); + } +} + +void +waltham_pointer_button(struct window *window, uint32_t serial, + uint32_t time, uint32_t button, + uint32_t state) +{ + struct seat *seat = window->receiver_seat; + struct pointer *pointer = seat->pointer; + + if (pointer->obj) { + fprintf(stdout, "waltham_pointer_button() sending button\n"); + wthp_pointer_send_button(pointer->obj, serial, time, button, state); + } +} + +void +waltham_pointer_axis(struct window *window, uint32_t time, + uint32_t axis, wl_fixed_t value) +{ + struct seat *seat = window->receiver_seat; + struct pointer *pointer = seat->pointer; + + if (pointer->obj) { + fprintf(stdout, "waltham_pointer_axis() sending pointer_axis\n"); + wthp_pointer_send_axis(pointer->obj, time, axis, value); + } +} + +/* + * waltham seat implementation + */ + +/* + * waltham touch implementation + */ +static void +touch_release(struct wthp_touch *wthp_touch) +{ + struct touch *touch = wth_object_get_user_data((struct wth_object *)wthp_touch); + fprintf(stdout, "touch_release %p touch %p\n", wthp_touch, touch); +} + +static const struct wthp_touch_interface touch_implementation = { + touch_release, +}; + +/* + * waltham pointer implementation + */ +static void +pointer_set_cursor(struct wthp_pointer *wthp_pointer, uint32_t serial, struct wthp_surface *surface, + int32_t hotspot_x, int32_t hotspot_y) +{ +} + +static void +pointer_release(struct wthp_pointer *wthp_pointer) +{ +} + +static const struct wthp_pointer_interface pointer_implementation = { + pointer_set_cursor, + pointer_release +}; + +static void +seat_get_pointer(struct wthp_seat *wthp_seat, struct wthp_pointer *wthp_pointer) +{ + + fprintf(stdout, "wthp_seat %p get_pointer(%p)\n", wthp_seat, wthp_pointer); + + struct seat *seat = wth_object_get_user_data((struct wth_object *)wthp_seat); + struct pointer *pointer; + + pointer = zalloc(sizeof *pointer); + if (!pointer) { + client_post_out_of_memory(seat->client); + return; + } + + pointer->obj = wthp_pointer; + pointer->seat = seat; + seat->pointer = pointer; + wl_list_insert(&seat->client->pointer_list, &pointer->link); + + wthp_pointer_set_interface(wthp_pointer, &pointer_implementation, pointer); + +} + +static void +seat_get_touch(struct wthp_seat *wthp_seat, struct wthp_touch *wthp_touch) +{ + struct seat *seat = wth_object_get_user_data((struct wth_object *)wthp_seat); + struct touch *touch; + + fprintf(stdout, "wthp_seat %p get_touch(%p)\n", wthp_seat, wthp_touch); + + touch = zalloc(sizeof(*touch)); + if (!touch) { + client_post_out_of_memory(seat->client); + return; + } + + touch->obj = wthp_touch; + touch->seat = seat; + + seat->touch = touch; + + fprintf(stdout, "seat_get_touch() with obj %p\n", touch->obj); + + wl_list_insert(&seat->client->touch_list, &touch->link); + wthp_touch_set_interface(wthp_touch, &touch_implementation, touch); +} + +static void +seat_get_keyboard(struct wthp_seat *wthp_seat, struct wthp_keyboard *kid) +{ + (void) wthp_seat; + (void) kid; +} + +static void +seat_release(struct wthp_seat *wthp_seat) +{ + +} + +static const struct wthp_seat_interface seat_implementation = { + seat_get_pointer, + seat_get_keyboard, + seat_get_touch, + seat_release, +}; + +void +seat_send_updated_caps(struct seat *seat) +{ + enum wthp_seat_capability caps = 0; + + caps |= WTHP_SEAT_CAPABILITY_POINTER; + fprintf(stdout, "WTHP_SEAT_CAPABILITY_POINTER %d\n", caps); + + caps |= WTHP_SEAT_CAPABILITY_TOUCH; + fprintf(stdout, "WTHP_SEAT_CAPABILITY_TOUCH %d\n", caps); + + wthp_seat_send_capabilities(seat->obj, caps); +} + +void +client_bind_seat(struct client *c, struct wthp_seat *obj) +{ + struct seat *seat; + + seat = zalloc(sizeof *seat); + if (!seat) { + client_post_out_of_memory(c); + return; + } + + seat->obj = obj; + seat->client = c; + wl_list_insert(&c->seat_list, &seat->link); + + fprintf(stdout, "wthp_seat object=%p and seat=%p\n",obj,seat); + wthp_seat_set_interface(obj, &seat_implementation, seat); + + fprintf(stdout, "client %p bound wthp_seat\n", c); + seat_send_updated_caps(seat); +} diff --git a/src/wth-receiver-surface.c b/src/wth-receiver-surface.c new file mode 100644 index 0000000..ec89677 --- /dev/null +++ b/src/wth-receiver-surface.c @@ -0,0 +1,291 @@ +/* + * Copyright © 2019 Advanced Driver Information Technology GmbH + * Copyright © 2020 Collabora, Ltd. + * + * 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. + */ +#include "wth-receiver-comm.h" +#include "wth-receiver-buffer.h" +#include "wth-receiver-seat.h" +#include "wth-receiver-surface.h" + +void +wth_receiver_weston_shm_attach(struct window *window, uint32_t data_sz, void * data, + int32_t width, int32_t height, int32_t stride, uint32_t format) +{ + /* stub */ +} + +void +wth_receiver_weston_shm_damage(struct window *window) +{ + /* stub */ +} + +void +wth_receiver_weston_shm_commit(struct window *window) +{ + /* stub */ +} + +/* + * waltham surface implementation + */ +void +surface_destroy(struct surface *surface) +{ + wthp_surface_free(surface->obj); + wl_list_remove(&surface->link); + free(surface); +} + +static void +surface_handle_destroy(struct wthp_surface *wthp_surface) +{ + struct surface *surface = wth_object_get_user_data((struct wth_object *)wthp_surface); + + assert(wthp_surface == surface->obj); + surface_destroy(surface); +} + +static void +surface_handle_attach(struct wthp_surface *wthp_surface, + struct wthp_buffer *wthp_buff, int32_t x, int32_t y) +{ + struct surface *surf = wth_object_get_user_data((struct wth_object *)wthp_surface); + struct buffer *buf = container_of(&wthp_buff, struct buffer, obj); + + if (surf->ivi_id != 0) { + wth_receiver_weston_shm_attach(surf->shm_window, + buf->data_sz, + buf->data, + buf->width, + buf->height, + buf->stride, + buf->format); + + wthp_buffer_send_complete(wthp_buff, 0); + } +} + +static void +surface_handle_damage(struct wthp_surface *wthp_surface, + int32_t x, int32_t y, int32_t width, int32_t height) +{ + struct surface *surf = wth_object_get_user_data((struct wth_object *)wthp_surface); + + if (surf->ivi_id != 0) { + wth_receiver_weston_shm_damage(surf->shm_window); + } +} + +static void +surface_handle_frame(struct wthp_surface *wthp_surface, + struct wthp_callback *callback) +{ + struct surface *surf = wth_object_get_user_data((struct wth_object *)wthp_surface); + surf->cb = callback; +} + +static void +surface_handle_set_opaque_region(struct wthp_surface *wthp_surface, + struct wthp_region *region) +{ + +} + +static void +surface_handle_set_input_region(struct wthp_surface *wthp_surface, + struct wthp_region *region) +{ + +} + +static void +surface_handle_commit(struct wthp_surface *wthp_surface) +{ + struct surface *surf = wth_object_get_user_data((struct wth_object *)wthp_surface); + + if (surf->ivi_id != 0) { + wth_receiver_weston_shm_commit(surf->shm_window); + } +} + +static void +surface_handle_set_buffer_transform(struct wthp_surface *wthp_surface, + int32_t transform) +{ + +} + +static void +surface_handle_set_buffer_scale(struct wthp_surface *wthp_surface, + int32_t scale) +{ + +} + +static void +surface_handle_damage_buffer(struct wthp_surface *wthp_surface, + int32_t x, int32_t y, int32_t width, int32_t height) +{ + +} + +static const struct wthp_surface_interface surface_implementation = { + surface_handle_destroy, + surface_handle_attach, + surface_handle_damage, + surface_handle_frame, + surface_handle_set_opaque_region, + surface_handle_set_input_region, + surface_handle_commit, + surface_handle_set_buffer_transform, + surface_handle_set_buffer_scale, + surface_handle_damage_buffer +}; + +static void +compositor_handle_create_surface(struct wthp_compositor *compositor, + struct wthp_surface *id) +{ + + struct compositor *comp = wth_object_get_user_data((struct wth_object *)compositor); + struct client *client = comp->client; + struct surface *surface; + struct seat *seat, *tmp; + + surface = zalloc(sizeof *surface); + if (!surface) { + client_post_out_of_memory(comp->client); + return; + } + + surface->obj = id; + wl_list_insert(&comp->client->surface_list, &surface->link); + + wthp_surface_set_interface(id, &surface_implementation, surface); + + surface->shm_window = calloc(1, sizeof *surface->shm_window); + if (!surface->shm_window) + return; + + surface->shm_window->receiver_surf = surface; + surface->shm_window->ready = false; + surface->ivi_id = 0; + + wl_list_for_each_safe(seat, tmp, &client->seat_list, link) { + surface->shm_window->receiver_seat = seat; + } +} + +/* + * waltham region implementation + */ +void +region_destroy(struct region *region) +{ + wthp_region_free(region->obj); + wl_list_remove(®ion->link); + free(region); +} + +static void +region_handle_destroy(struct wthp_region *wthp_region) +{ + struct region *region = wth_object_get_user_data((struct wth_object *)wthp_region); + assert(wthp_region == region->obj); + region_destroy(region); +} + +static void +region_handle_add(struct wthp_region *wthp_region, + int32_t x, int32_t y, int32_t width, int32_t height) +{ +} + +static void +region_handle_subtract(struct wthp_region *wthp_region, + int32_t x, int32_t y, + int32_t width, int32_t height) +{ +} + +static const struct wthp_region_interface region_implementation = { + region_handle_destroy, + region_handle_add, + region_handle_subtract +}; + +/* + * waltham compositor implementation + */ +void +compositor_destroy(struct compositor *comp) +{ + wthp_compositor_free(comp->obj); + wl_list_remove(&comp->link); + free(comp); +} + +static void +compositor_handle_create_region(struct wthp_compositor *compositor, + struct wthp_region *id) +{ + struct compositor *comp = wth_object_get_user_data((struct wth_object *)compositor); + struct region *region; + + region = zalloc(sizeof *region); + if (!region) { + client_post_out_of_memory(comp->client); + return; + } + + region->obj = id; + wl_list_insert(&comp->client->region_list, ®ion->link); + + wthp_region_set_interface(id, ®ion_implementation, region); +} + +static const struct wthp_compositor_interface compositor_implementation = { + compositor_handle_create_surface, + compositor_handle_create_region +}; + +void +client_bind_compositor(struct client *c, struct wthp_compositor *obj) +{ + + struct compositor *comp; + + comp = zalloc(sizeof *comp); + if (!comp) { + client_post_out_of_memory(c); + return; + } + + comp->obj = obj; + comp->client = c; + wl_list_insert(&c->compositor_list, &comp->link); + + wthp_compositor_set_interface(obj, &compositor_implementation, comp); +} |