From: Sung-Jin Park Date: Mon, 5 Aug 2019 06:25:18 +0000 (+0900) Subject: Initial version of headless-server X-Git-Tag: accepted/tizen/5.5/unified/20191031.020308^0 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;ds=sidebyside;h=c6f61e9b1d53e55bc7475d8af1c178cca26543ed;p=platform%2Fcore%2Fuifw%2Fheadless-server.git Initial version of headless-server - Ref revision : b83ac3bcee706dd9043136f603596bbad9d16f46 Signed-off-by: Sung-Jin Park --- diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..21dd7b5 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,3 @@ +Sangjin Lee lsj119@samsung.com +Sungjin Park sj76.park@samsung.com +Jeonghyun Kang jhyuni.kang@samsung.com diff --git a/CODING_STYLE b/CODING_STYLE new file mode 100644 index 0000000..57cb29b --- /dev/null +++ b/CODING_STYLE @@ -0,0 +1,73 @@ +== Coding style == + +You should follow the style of the file you're editing. In general, we +try to follow the rules below. + +- indent with tabs, and a tab is always 4 characters wide +- opening braces are on the same line as the if statement; +- no braces in an if-body with just one statement; +- if one of the branches of an if-else codition has braces, than the + other branch should also have braces; +- there is always an empty line between variable declarations and the + code; + +static int +my_function(void) +{ + int a = 0; + + if (a) + b(); + else + c(); + + if (a) { + b(); + c(); + } else { + d(); + } +} + +- lines should be less than 80 characters wide; +- when breaking lines with functions calls, the parameters are aligned + with the opening parenthesis; +- when assigning a variable with the result of a function call, if the + line would be longer we break it around the equal '=' sign if it makes + sense; + + long_variable_name = + function_with_a_really_long_name(parameter1, parameter2, + parameter3, parameter4); + + x = function_with_a_really_long_name(parameter1, parameter2, + parameter3, parameter4); + +== astyle options == +#command line +#astyle -A8 -t4 -p -z2 -H -k3 -W3 -xC80 -xL -n -r "./*.c" +#or +#astyle -A8t8pz2Hk3W3xC80xLnrf "*.c" + +#default style : linux +--style=linux +--indent=tab=8 +--indent=force-tab-x=8 + +#Padding Options +--pad-oper +--pad-header +--align-pointer=name +--align-reference=name + +#Formatting Options +--max-code-length=80 +--break-after-logical + +#Other Options +--suffix=none +--recursive +--lineend=linux + +== VI options == +TODO: diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..6af77c9 --- /dev/null +++ b/COPYING @@ -0,0 +1,31 @@ +Copyright © 2008-2012 Kristian Høgsberg +Copyright © 2010-2012 Intel Corporation +Copyright © 2011 Benjamin Franzke +Copyright © 2012 Collabora, Ltd. +Copyright © 2015 S-Core Corporation +Copyright © 2015-2017 Samsung Electronics co., Ltd. All Rights Reserved. + +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. + +--- + +The above is the version of the MIT "Expat" License used by X.org: + + http://cgit.freedesktop.org/xorg/xserver/tree/COPYING diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..c390ceb --- /dev/null +++ b/Makefile.am @@ -0,0 +1,4 @@ +SUBDIRS = src + +#pkgconfig_DATA = + diff --git a/README.md b/README.md new file mode 100644 index 0000000..1eea085 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# headless-server +Pepper-based display server for headless (e.g. Tizen Speaker Profile) diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..916169a --- /dev/null +++ b/autogen.sh @@ -0,0 +1,9 @@ +#! /bin/sh + +test -n "$srcdir" || srcdir=`dirname "$0"` +test -n "$srcdir" || srcdir=. +( + cd "$srcdir" && + autoreconf --force -v --install +) || exit +test -n "$NOCONFIGURE" || "$srcdir/configure" "$@" diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..d8f71d8 --- /dev/null +++ b/configure.ac @@ -0,0 +1,44 @@ +m4_define([hsvr_major], 0) +m4_define([hsvr_minor], 0) +m4_define([hsvr_micro], 0) + +m4_define([hsvr_version], [hsvr_major.hsvr_minor.hsvr_micro]) + +AC_PREREQ([2.64]) +AC_INIT([pepper], [pepper_version], [tkq.kim@samsung.com]) + +AC_SUBST([HSVR_VERSION_MAJOR], [hsvr_major_version]) +AC_SUBST([HSVR_VERSION_MINOR], [hsvr_minor_version]) +AC_SUBST([HSVR_VERSION_MICRO], [hsvr_micro_version]) +AC_SUBST([HSVR_VERSION], [hsvr_version]) + +AC_CONFIG_HEADERS([config.h]) +AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz subdir-objects]) +AM_SILENT_RULES([yes]) + +AC_PROG_CC + +LT_PREREQ([2.2]) +LT_INIT([disable-static]) + +if test "x$GCC" = "xyes"; then +GCC_CFLAGS="-Wall -Wextra -Wno-unused-parameter \ + -Wno-missing-field-initializers -g -fvisibility=hidden \ + -Wstrict-prototypes -Wmissing-prototypes -Wsign-compare" +fi +AC_SUBST(GCC_CFLAGS) + +# headless server +HEADLESS_SERVER_REQUIRES="pepper pepper-inotify pepper-keyrouter pepper-devicemgr pepper-xkb pepper-evdev xkbcommon capi-system-peripheral-io xdg-shell-unstable-v6-server tizen-extension-server wayland-tbm-server" +PKG_CHECK_MODULES(HEADLESS_SERVER, $[HEADLESS_SERVER_REQUIRES]) + +AC_SUBST(HEADLESS_SERVER_CFLAGS) +AC_SUBST(HEADLESS_SERVER_LIBS) + +# Output files +AC_CONFIG_FILES([ +Makefile +src/Makefile +]) + +AC_OUTPUT diff --git a/data/scripts/winfo b/data/scripts/winfo new file mode 100644 index 0000000..e320655 --- /dev/null +++ b/data/scripts/winfo @@ -0,0 +1,57 @@ +#!/bin/sh + +if [ "$XDG_RUNTIME_DIR" = "" ]; then + export XDG_RUNTIME_DIR=/run +fi + +WINFO_RUN_DIR="$XDG_RUNTIME_DIR/pepper" + +if [ ! -d "$WINFO_RUN_DIR" ]; then + echo "Error: no ${WINFO_RUN_DIR} directory exist." + exit 1 +fi + +cd $WINFO_RUN_DIR + +function usage() +{ + echo "Usage> # winfo {command}" + echo "" + echo " Supported commands:" + echo "" + echo " protocol_trace_on (turn on wayland protocol trace)" + echo " protocol_trace_off (turn off wayland protocol trace)" + echo " stdout (redirect STDOUT to a file : /run/pepper/stdout.txt)" + echo " stderr (redirect STDERR to a file : /run/pepper/stderr.txt)" + echo " keygrab_status" + echo " keymap" + echo " topvwins" + echo " connected_clients (display connected clients info : pid, uid, gid, socket fd)" + echo " reslist (display resources info of the connected clients" + echo " help (display this help message)" + echo "" + echo " To execute commands, just create/remove/update a file with the commands above." + echo " Please refer to the following examples." + echo "" + echo " # winfo protocol_trace_on : enable event trace" + echo " # winfo protocol_trace_off : disable event trace" + echo " # winfo stdout : redirect STDOUT" + echo " # winfo stderr : redirect STDERR" + echo " # winfo keygrab_status : display keygrab status" + echo " # winfo keymap : display keymap" + echo " # winfo topvwins : display top/visible window stack" + echo " # winfo connected_clients : display connected clients information" + echo " # winfo reslist : display each resources information of connected clients" + echo " # winfo help : display this help message" + echo "" +} + +if [ "$1" = "" ]; then + usage + exit 1 +fi + +CMD="$1" + +rm -f ${CMD} ; touch ${CMD} +echo "winfo ${CMD}" diff --git a/data/units/display-manager-ready.path b/data/units/display-manager-ready.path new file mode 100644 index 0000000..24357bd --- /dev/null +++ b/data/units/display-manager-ready.path @@ -0,0 +1,6 @@ +[Unit] +Description=Path activation for display manager ready service +After=tmp.mount display-manager.service + +[Path] +PathExists=/run/wayland-0 diff --git a/data/units/display-manager-ready.service b/data/units/display-manager-ready.service new file mode 100644 index 0000000..00ac10b --- /dev/null +++ b/data/units/display-manager-ready.service @@ -0,0 +1,13 @@ +[Unit] +Description=Headless Display Manager Ready Service +After=tmp.mount display-manager.service + +[Service] +Type=oneshot +EnvironmentFile=/etc/sysconfig/display-manager.env +SmackProcessLabel=System +ExecStart=/usr/bin/sh -c "while [ ! -e /run/wayland-0 ] ; do /usr/bin/sleep .1 ; done ;/bin/chown -f root:display /run/wayland-0;/bin/chmod 775 /run/wayland-0" +ExecStartPost=/usr/bin/sh -c "/usr/bin/touch /run/.wm_ready" + +[Install] +WantedBy=graphical.target diff --git a/data/units/display-manager.env b/data/units/display-manager.env new file mode 100644 index 0000000..dc45551 --- /dev/null +++ b/data/units/display-manager.env @@ -0,0 +1,3 @@ +TBM_DISPLAY_SERVER=1 +WAYLAND_DISPLAY="wayland-0" +XDG_RUNTIME_DIR=/run diff --git a/data/units/display-manager.service b/data/units/display-manager.service new file mode 100644 index 0000000..5b4663d --- /dev/null +++ b/data/units/display-manager.service @@ -0,0 +1,12 @@ +[Unit] +Description=Headless Display Manager + +[Service] +Type=simple +EnvironmentFile=/etc/sysconfig/display-manager.env +SmackProcessLabel=System +ExecStartPre=/usr/bin/bash -c "/usr/bin/mkdir -p ${XDG_RUNTIME_DIR}/pepper/" +ExecStart=/usr/bin/headless_server + +[Install] +WantedBy=graphical.target diff --git a/data/units/display-user.service b/data/units/display-user.service new file mode 100644 index 0000000..392df83 --- /dev/null +++ b/data/units/display-user.service @@ -0,0 +1,7 @@ +[Unit] +Description=Creating a link file for user to access display manager socket +DefaultDependencies=no + +[Service] +Type=oneshot +ExecStart=/usr/bin/sh -c "while [ ! -e /run/wayland-0 ] ; do /usr/bin/sleep .1 ; done ;/usr/bin/ln -sf /run/wayland-0 /run/user/%U/" diff --git a/data/units/display_env.sh b/data/units/display_env.sh new file mode 100644 index 0000000..6b25629 --- /dev/null +++ b/data/units/display_env.sh @@ -0,0 +1,8 @@ +if [ "$USER" == "root" ]; then + export XDG_RUNTIME_DIR=/run +else + export XDG_RUNTIME_DIR=/run/user/$UID +fi +if [ "$WAYALND_DISPLAY" = "" ]; then + export WAYLAND_DISPLAY=wayland-0 +fi diff --git a/packaging/headless-server.manifest b/packaging/headless-server.manifest new file mode 100644 index 0000000..017d22d --- /dev/null +++ b/packaging/headless-server.manifest @@ -0,0 +1,5 @@ + + + + + diff --git a/packaging/headless-server.spec b/packaging/headless-server.spec new file mode 100644 index 0000000..27894f1 --- /dev/null +++ b/packaging/headless-server.spec @@ -0,0 +1,107 @@ +Name: headless-server +Version: 0.0.1 +Release: 0 +Summary: Display server for headless profile +License: MIT +Group: Graphics & UI Framework/Wayland Window System + +Source: %{name}-%{version}.tar.xz +source1001: %name.manifest + +BuildRequires: autoconf > 2.64 +BuildRequires: automake >= 1.11 +BuildRequires: libtool >= 2.2 +BuildRequires: pkgconfig(pepper) +BuildRequires: pkgconfig(pepper-inotify) +BuildRequires: pkgconfig(pepper-keyrouter) +BuildRequires: pkgconfig(pepper-xkb) +BuildRequires: pkgconfig(pepper-devicemgr) +BuildRequires: pkgconfig(pepper-evdev) +BuildRequires: pkgconfig(xkbcommon) +BuildRequires: pkgconfig(wayland-tbm-server) +BuildRequires: pkgconfig(tizen-extension-server) +BuildRequires: pkgconfig(capi-system-peripheral-io) +BuildRequires: pkgconfig(xdg-shell-unstable-v6-server) +BuildRequires: pkgconfig(tizen-extension-server) + +Requires: pepper pepper-keyrouter pepper-devicemgr pepper-evdev +Requires: pepper-xkb xkeyboard-config xkb-tizen-data +Requires: libtbm +Requires: capi-system-peripheral-io +Conflicts: pepper-doctor + +%description +Headless server is a display server for headless profile. + +%package devel +Summary: Development module for headless-server package +Requires: %{name} = %{version}-%{release} + +%description devel +This package includes developer files common to all packages. + +%prep +%setup -q +cp %{SOURCE1001} . + +%build +%autogen + +make %{?_smp_mflags} + +%install +%make_install + +%define display_user display +%define display_group display + +# install system session services +%__mkdir_p %{buildroot}%{_unitdir} +install -m 644 data/units/display-manager.service %{buildroot}%{_unitdir} +install -m 550 data/scripts/* %{buildroot}%{_bindir} +install -m 644 data/units/display-manager-ready.path %{buildroot}%{_unitdir} +install -m 644 data/units/display-manager-ready.service %{buildroot}%{_unitdir} + +# install user session service +%__mkdir_p %{buildroot}%{_unitdir_user} +install -m 644 data/units/display-user.service %{buildroot}%{_unitdir_user} + +# install env file and scripts for service +%__mkdir_p %{buildroot}%{_sysconfdir}/sysconfig +install -m 0644 data/units/display-manager.env %{buildroot}%{_sysconfdir}/sysconfig +%__mkdir_p %{buildroot}%{_sysconfdir}/profile.d +install -m 0644 data/units/display_env.sh %{buildroot}%{_sysconfdir}/profile.d + +%post -n %{name} -p /sbin/ldconfig +%postun -n %{name} -p /sbin/ldconfig + +%pre +# create groups 'display' +getent group %{display_group} >/dev/null || %{_sbindir}/groupadd -r -o %{display_group} +# create user 'display' +getent passwd %{display_user} >/dev/null || %{_sbindir}/useradd -r -g %{display_group} -d /run/display -s /bin/false -c "Display" %{display_user} + +# create links within systemd's target(s) +%__mkdir_p %{_unitdir}/graphical.target.wants/ +%__mkdir_p %{_unitdir_user}/basic.target.wants/ +ln -sf ../display-manager.service %{_unitdir}/graphical.target.wants/ +ln -sf ../display-manager-ready.service %{_unitdir}/graphical.target.wants/ +ln -sf ../display-user.service %{_unitdir_user}/basic.target.wants/ + +%files +%manifest %{name}.manifest +%defattr(-,root,root,-) +%license COPYING +%{_bindir}/headless* +%{_bindir}/winfo +%{_unitdir}/display-manager-ready.path +%{_unitdir}/display-manager-ready.service +%{_unitdir}/display-manager.service +%{_unitdir_user}/display-user.service +%config %{_sysconfdir}/sysconfig/display-manager.env +%config %{_sysconfdir}/profile.d/display_env.sh + +%files devel +%manifest %{name}.manifest +%defattr(-,root,root,-) + diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..1cdb324 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,14 @@ +bin_PROGRAMS = + +bin_PROGRAMS += headless_server + +headless_server_CFLAGS = $(HEADLESS_SERVER_CFLAGS) +headless_server_LDADD = $(HEADLESS_SERVER_LIBS) + +headless_server_SOURCES = headless_server.c \ + debug/debug.c \ + input/input.c \ + output/output_led.c \ + output/HL_UI_LED_APA102.c \ + output/boot_anim.c \ + shell/shell.c diff --git a/src/debug/debug.c b/src/debug/debug.c new file mode 100644 index 0000000..a6761b7 --- /dev/null +++ b/src/debug/debug.c @@ -0,0 +1,491 @@ +/* +* Copyright © 2019 Samsung Electronics co., Ltd. All Rights Reserved. +* +* 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define MAX_CMDS 256 + +#define STDOUT_REDIR "stdout" +#define STDERR_REDIR "stderr" +#define PROTOCOL_TRACE_ON "protocol_trace_on" +#define PROTOCOL_TRACE_OFF "protocol_trace_off" +#define KEYGRAB_STATUS "keygrab_status" +#define TOPVWINS "topvwins" +#define CONNECTED_CLIENTS "connected_clients" +#define CLIENT_RESOURCES "reslist" +#define KEYMAP "keymap" +#define HELP_MSG "help" + +typedef struct +{ + pepper_compositor_t *compositor; + pepper_inotify_t *inotify; + + pepper_view_t *top_mapped; + pepper_view_t *focus; +} headless_debug_t; + +typedef void (*headless_debug_action_cb_t)(headless_debug_t *hd, void *data); + +typedef struct +{ + const char *cmds; + headless_debug_action_cb_t cb; + headless_debug_action_cb_t disable_cb; +} headless_debug_action_t; + +const static int KEY_DEBUG = 0xdeaddeb0; +extern void wl_debug_server_enable(int enable); + +static void +_headless_debug_usage() +{ + fprintf(stdout, "Supported commands:\n\n"); + fprintf(stdout, "\t %s\n", PROTOCOL_TRACE_ON); + fprintf(stdout, "\t %s\n", PROTOCOL_TRACE_OFF); + fprintf(stdout, "\t %s\n", STDOUT_REDIR); + fprintf(stdout, "\t %s\n", STDERR_REDIR); + fprintf(stdout, "\t %s\n", KEYGRAB_STATUS); + fprintf(stdout, "\t %s\n", TOPVWINS); + fprintf(stdout, "\t %s\n", CONNECTED_CLIENTS); + fprintf(stdout, "\t %s\n", CLIENT_RESOURCES); + fprintf(stdout, "\t %s\n", KEYMAP); + fprintf(stdout, "\t %s\n", HELP_MSG); + + fprintf(stdout, "\nTo execute commands, just create/remove/update a file with the commands above.\n"); + fprintf(stdout, "Please refer to the following examples.\n\n"); + fprintf(stdout, "\t # winfo protocol_trace_on\t : enable event trace\n"); + fprintf(stdout, "\t # winfo event_trace_off\t : disable event trace\n"); + fprintf(stdout, "\t # winfo stdout\t\t\t : redirect STDOUT\n"); + fprintf(stdout, "\t # winfo stderr\t\t\t : redirect STDERR\n"); + fprintf(stdout, "\t # winfo keygrab_status\t\t : display keygrab status\n"); + fprintf(stdout, "\t # winfo topvwins\t\t : display top/visible window stack\n"); + fprintf(stdout, "\t # winfo connected_clients\t : display connected clients information\n"); + fprintf(stdout, "\t # winfo reslist\t\t : display each resources information of connected clients\n"); + fprintf(stdout, "\t # winfo keymap\t\t : display current xkb keymap\n"); + fprintf(stdout, "\t # winfo help\t\t\t : display this help message\n"); +} + +static void +_headless_debug_protocol_trace_on(headless_debug_t *hdebug, void *data) +{ + (void) hdebug; + (void) data; + wl_debug_server_enable(1); +} + +static void +_headless_debug_protocol_trace_off(headless_debug_t *hdebug, void *data) +{ + (void) hdebug; + (void) data; + wl_debug_server_enable(0); +} + +static void +_headless_debug_dummy(headless_debug_t *hdebug, void *data) +{ + (void) hdebug; + (void) data; + _headless_debug_usage(); +} + +static enum wl_iterator_result +_client_get_resource_itr(struct wl_resource *resource, void *data) +{ + int *n_resources = (int *)data; + + PEPPER_TRACE("\t\t [resource][%d] class=%s, id=%u\n", ++(*n_resources), wl_resource_get_class(resource), wl_resource_get_id(resource)); + + return WL_ITERATOR_CONTINUE; +} + +static void +_headless_debug_connected_clients(headless_debug_t *hdebug, void *data) +{ + pid_t pid; + uid_t uid; + gid_t gid; + + int client_fd = -1; + uint32_t n_clients = 0; + int n_resources = 0; + + struct wl_list *clist = NULL; + struct wl_client *client_itr = NULL; + pepper_bool_t need_reslist = PEPPER_FALSE; + + const char *cmds = (const char *)data; + + /* check if reslist feature is required */ + if (cmds && !strncmp(cmds, CLIENT_RESOURCES, MAX_CMDS)) { + need_reslist = PEPPER_TRUE; + } + + /* get client list which bound wl_compositor global */ + clist = wl_display_get_client_list(pepper_compositor_get_display(hdebug->compositor)); + PEPPER_CHECK(clist, return, "Failed to get client list from compositor->display.\n"); + + PEPPER_TRACE("========= [Connected clients information] =========\n"); + wl_client_for_each(client_itr, clist) { + n_clients++; + client_fd = wl_client_get_fd(client_itr); + wl_client_get_credentials(client_itr, &pid, &uid, &gid); + PEPPER_TRACE("\t client[%d]: pid=%d, user=%d, group=%d, socket_fd=%d", n_clients, pid, uid, gid, client_fd); + + if (PEPPER_FALSE == need_reslist) { + PEPPER_TRACE("\n"); + continue; + } + + PEPPER_TRACE("\n"); + wl_client_for_each_resource(client_itr, _client_get_resource_itr, &n_resources); + PEPPER_TRACE("\t\t number of resources = %d\n", n_resources); + + n_resources = 0; + } + + if (!n_clients) + PEPPER_TRACE("============ [No connected clients] ===========\n\n"); +} + +static void +_headless_debug_redir_stdout(headless_debug_t *hdebug, void *data) +{ + (void) hdebug; + (void) data; + + int fd = -1; + int ret = 0; + + fd = open("/run/pepper/stdout.txt", O_CREAT | O_WRONLY | O_APPEND, S_IWUSR | S_IWGRP); + + if (fd < 0) { + PEPPER_TRACE("Failed to open stdout.txt (errno=%s)\n", strerror(errno)); + return; + } + + ret = dup2(fd, 1); + close(fd); + PEPPER_CHECK(ret >= 0, return, "Failed to redirect STDOUT.\n"); + + PEPPER_TRACE("STDOUT has been redirected to stdout.txt.\n"); +} + +static void +_headless_debug_redir_stderr(headless_debug_t *hdebug, void *data) +{ + (void) hdebug; + (void) data; + + int fd = -1; + int ret = 0; + + fd = open("/run/pepper/stderr.txt", O_CREAT | O_WRONLY | O_APPEND, S_IWUSR | S_IWGRP); + + if (fd < 0) { + PEPPER_TRACE("Failed to open stderr.txt (errno=%s)\n", strerror(errno)); + return; + } + + ret = dup2(fd, 2); + close(fd); + PEPPER_CHECK(ret >= 0, return, "Failed to redirect STDERR.\n"); + + PEPPER_TRACE("STDERR has been redirected to stderr.txt.\n"); + +} + +static void +_headless_debug_topvwins(headless_debug_t *hdebug, void *data) +{ + (void) data; + + int cnt = 0; + int w, h; + double x, y; + pid_t pid; + + pepper_list_t *l; + const pepper_list_t *list; + pepper_view_t *view; + pepper_surface_t *surface; + pepper_view_t *top_visible = NULL; + + PEPPER_CHECK(hdebug, return, "[%s] Invalid headless debug !\n", __FUNCTION__); + + PEPPER_TRACE("No. WinID RscID PID w h x y Mapped Visible Top Top_Visible Focus\n"); + PEPPER_TRACE("==========================================================================================\n"); + + list = pepper_compositor_get_view_list(hdebug->compositor); + + pepper_list_for_each_list(l, list) { + view = (pepper_view_t *)l->item; + PEPPER_CHECK(view, continue, "[%s] Invalid object view:%p\n", __FUNCTION__, view); + + surface = pepper_view_get_surface(view); + PEPPER_CHECK(surface, continue, "[%s] Invalid object surface:%p\n", __FUNCTION__, surface); + + cnt++; + pepper_view_get_position(view, &x, &y); + pepper_view_get_size(view, &w, &h); + wl_client_get_credentials(wl_resource_get_client(pepper_surface_get_resource(surface)), &pid, NULL, NULL); + if (!top_visible && pepper_surface_get_buffer(surface)) + top_visible = view; + + pepper_log("DEBUG", PEPPER_LOG_LEVEL_DEBUG, "%3d 0x%08x 0x%08x %5d %4d %4d %4.0f %4.0f %s %s %s %s %s\n", + cnt, surface, pepper_surface_get_resource(surface), pid, w, h, x, y, + pepper_view_is_mapped(view) ? "O" : "X", + pepper_view_is_visible(view) ? "O" : "X", + (hdebug->top_mapped == view) ? "O" : "X", + (top_visible == view) ? "O" : "X", + (hdebug->focus == view) ? "O" : "X"); + } + + PEPPER_TRACE("==========================================================================================\n"); +} + +static void +_headless_debug_keygrab_status(headless_debug_t *hdebug, void *data) +{ + pepper_keyrouter_t *keyrouter; + + keyrouter = headless_input_get_keyrouter(hdebug->compositor); + pepper_keyrouter_debug_keygrab_status_print(keyrouter); +} + +static void +_headless_debug_keymap(headless_debug_t *hdebug, void *data) +{ + pepper_xkb_t *xkb; + + int i; + int min_keycode, max_keycode, num_mods, num_groups; + struct xkb_context *context = NULL; + struct xkb_keymap *keymap = NULL; + struct xkb_state *state = NULL; + xkb_keysym_t sym = XKB_KEY_NoSymbol; + char keyname[256] = {0, }; + + xkb = headless_input_get_xkb(hdebug->compositor); + PEPPER_CHECK(xkb, return, "xkb is not set\n"); + + context = pepper_xkb_get_context(xkb); + PEPPER_CHECK(context, return, "Current pepper_xkb has no context.\n"); + keymap = pepper_xkb_get_keymap(xkb); + PEPPER_CHECK(keymap, return, "Current pepper_xkb has no keymap.\n"); + state = pepper_xkb_get_state(xkb); + PEPPER_CHECK(state, return, "Current pepper_xkb has no state.\n"); + + min_keycode = xkb_keymap_min_keycode(keymap); + max_keycode = xkb_keymap_max_keycode(keymap); + num_groups = xkb_map_num_groups(keymap); + num_mods = xkb_keymap_num_mods(keymap); + + printf("\n"); + printf(" min keycode: %d\n", min_keycode); + printf(" max keycode: %d\n", max_keycode); + printf(" num_groups : %d\n", num_groups); + printf(" num_mods : %d\n", num_mods); + for (i = 0; i < num_mods; i++) { + printf(" [%2d] mod: %s\n", i, xkb_keymap_mod_get_name(keymap, i)); + } + + printf("\n\n\tkeycode\t\tkeyname\t\t keysym\t repeat\n"); + printf(" ----------------------------------------------------------------------\n"); + + for (i = min_keycode; i < (max_keycode + 1); i++) { + sym = xkb_state_key_get_one_sym(state, i); + + memset(keyname, 0, sizeof(keyname)); + xkb_keysym_get_name(sym, keyname, sizeof(keyname)); + + if (!strncmp(keyname, "NoSymbol", sizeof("NoSymbol")) && sym == 0x0) + continue; + + printf("\t%4d%-5s%-25s%-20x%-5d\n", i, "", keyname, sym, xkb_keymap_key_repeats(keymap, i)); + } +} + +static const headless_debug_action_t debug_actions[] = +{ + { STDOUT_REDIR, _headless_debug_redir_stdout, NULL }, + { STDERR_REDIR, _headless_debug_redir_stderr, NULL }, + { PROTOCOL_TRACE_ON, _headless_debug_protocol_trace_on, _headless_debug_protocol_trace_off }, + { PROTOCOL_TRACE_OFF, _headless_debug_protocol_trace_off, NULL }, + { KEYGRAB_STATUS, _headless_debug_keygrab_status, NULL }, + { TOPVWINS, _headless_debug_topvwins, NULL }, + { CONNECTED_CLIENTS, _headless_debug_connected_clients, NULL }, + { CLIENT_RESOURCES, _headless_debug_connected_clients, NULL }, + { KEYMAP, _headless_debug_keymap, NULL }, + { HELP_MSG, _headless_debug_dummy, NULL }, +}; + +static void +_headless_debug_enable_action(headless_debug_t *hdebug, char *cmds) +{ + int n_actions = sizeof(debug_actions)/sizeof(debug_actions[0]); + + for(int n=0 ; n < n_actions ; n++) { + if (!strncmp(cmds, debug_actions[n].cmds, MAX_CMDS)) { + PEPPER_TRACE("[%s : %s]\n", __FUNCTION__, debug_actions[n].cmds); + debug_actions[n].cb(hdebug, (void *)debug_actions[n].cmds); + + break; + } + } +} + +static void +_headless_debug_disable_action(headless_debug_t *hdebug, char *cmds) +{ + int n_actions = sizeof(debug_actions)/sizeof(debug_actions[0]); + + for(int n=0 ; n < n_actions ; n++) { + if (!strncmp(cmds, debug_actions[n].cmds, MAX_CMDS)) { + if (debug_actions[n].disable_cb) { + PEPPER_TRACE("[%s : %s]\n", __FUNCTION__, debug_actions[n].cmds); + debug_actions[n].disable_cb(hdebug, (void *)debug_actions[n].cmds); + } + + break; + } + } +} + +static void +_trace_cb_handle_inotify_event(uint32_t type, pepper_inotify_event_t *ev, void *data) +{ + headless_debug_t *hdebug = data; + char *file_name = pepper_inotify_event_name_get(ev); + + PEPPER_CHECK(hdebug, return, "Invalid headless debug instance\n"); + + switch (type) + { + case PEPPER_INOTIFY_EVENT_TYPE_CREATE: + _headless_debug_enable_action(hdebug, file_name); + break; + case PEPPER_INOTIFY_EVENT_TYPE_REMOVE: + _headless_debug_disable_action(hdebug, file_name); + break; + case PEPPER_INOTIFY_EVENT_TYPE_MODIFY: + break; + default: + PEPPER_TRACE("[%s] Unhandled event type (%d)\n", __FUNCTION__, type); + break; + } +} + +PEPPER_API void +headless_debug_set_focus_view(pepper_compositor_t *compositor, pepper_view_t *focus_view) +{ + headless_debug_t *hdebug = NULL; + + hdebug = (headless_debug_t *)pepper_object_get_user_data((pepper_object_t *) compositor, &KEY_DEBUG); + PEPPER_CHECK(hdebug, return, "Invalid headless debug.\n"); + + if (hdebug->focus != focus_view) { + PEPPER_TRACE("[DEBUG] Focus view has been changed to 0x%x (from 0x%x)\n", focus_view, hdebug->focus); + hdebug->focus = focus_view; + } +} + +PEPPER_API void +headless_debug_set_top_view(pepper_compositor_t *compositor, pepper_view_t *top_view) +{ + headless_debug_t *hdebug = NULL; + + hdebug = (headless_debug_t *)pepper_object_get_user_data((pepper_object_t *) compositor, &KEY_DEBUG); + PEPPER_CHECK(hdebug, return, "Invalid headless debug.\n"); + + if (hdebug->top_mapped != top_view) { + PEPPER_TRACE("[DEBUG] Top view has been changed to 0x%x (from 0x%x)\n", top_view, hdebug->top_mapped); + hdebug->top_mapped = top_view; + } +} + +PEPPER_API void +headless_debug_deinit(pepper_compositor_t * compositor) +{ + headless_debug_t *hdebug = NULL; + + hdebug = (headless_debug_t *)pepper_object_get_user_data((pepper_object_t *) compositor, &KEY_DEBUG); + PEPPER_CHECK(hdebug, return, "Failed to get headless debug instance\n"); + + /* remove the directory watching already */ + if (hdebug->inotify) + pepper_inotify_del(hdebug->inotify, "/run/pepper"); + + /* remove inotify */ + pepper_inotify_destroy(hdebug->inotify); + hdebug->inotify = NULL; + + pepper_object_set_user_data((pepper_object_t *)hdebug->compositor, &KEY_DEBUG, NULL, NULL); + free(hdebug); +} + +pepper_bool_t +headless_debug_init(pepper_compositor_t *compositor) +{ + int n_actions; + headless_debug_t *hdebug = NULL; + pepper_inotify_t *inotify = NULL; + pepper_bool_t res = PEPPER_FALSE; + + hdebug = (headless_debug_t*)calloc(1, sizeof(headless_debug_t)); + PEPPER_CHECK(hdebug, goto error, "Failed to alloc for headless debug\n"); + hdebug->compositor = compositor; + + /* create inotify to watch file(s) for event trace */ + inotify = pepper_inotify_create(hdebug->compositor, _trace_cb_handle_inotify_event, hdebug); + PEPPER_CHECK(inotify, goto error, "Failed to create inotify\n"); + + /* add a directory for watching */ + res = pepper_inotify_add(inotify, "/run/pepper"); + PEPPER_CHECK(res, goto error, "Failed on pepper_inotify_add()\n"); + + hdebug->inotify = inotify; + n_actions = sizeof(debug_actions)/sizeof(debug_actions[0]); + + PEPPER_TRACE("[%s] Done (%d actions have been defined.)\n", __FUNCTION__, n_actions); + + pepper_object_set_user_data((pepper_object_t *)compositor, &KEY_DEBUG, hdebug, NULL); + return PEPPER_TRUE; + +error: + headless_debug_deinit(compositor); + + return PEPPER_FALSE; +} diff --git a/src/headless_server.c b/src/headless_server.c new file mode 100644 index 0000000..dd82f9a --- /dev/null +++ b/src/headless_server.c @@ -0,0 +1,116 @@ +/* + * Copyright © 2019 Samsung Electronics co., Ltd. All Rights Reserved. + * + * 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 +#include +#include + +#include +#include + +static int +handle_sigint(int signal_number, void *data) +{ + struct wl_display *display = (struct wl_display *)data; + wl_display_terminate(display); + + return 0; +} + +static pepper_bool_t +init_signal(pepper_compositor_t *compositor) +{ + struct wl_display *display; + struct wl_event_loop *loop; + struct wl_event_source *sigint; + + display = pepper_compositor_get_display(compositor); + loop = wl_display_get_event_loop(display); + sigint = wl_event_loop_add_signal(loop, SIGINT, handle_sigint, display); + if (!sigint) + return PEPPER_FALSE; + + return PEPPER_TRUE; +} + +int main(int argc, char *argv[]) +{ + const char *socket_name = NULL; + pepper_compositor_t *compositor = NULL; + pepper_bool_t ret; + + /* set STDOUT/STDERR bufferless */ + setvbuf(stdout, NULL, _IONBF, 0); + setvbuf(stderr, NULL, _IONBF, 0); + + if (getenv("PEPPER_DLOG_ENABLE")) { + PEPPER_TRACE("pepper log will be written to dlog !\n"); + pepper_log_dlog_enable(1); + } + + socket_name = getenv("WAYLAND_DISPLAY"); + + if (!socket_name) + socket_name = "wayland-0"; + + if (!getenv("XDG_RUNTIME_DIR")) + setenv("XDG_RUNTIME_DIR", "/run", 1); + + /* create pepper compositir */ + compositor = pepper_compositor_create(socket_name); + PEPPER_CHECK(compositor, return EXIT_FAILURE, "Failed to create compositor !"); + + /* Init event trace */ + ret = headless_debug_init(compositor); + PEPPER_CHECK(ret, goto end, "headless_debug_init() failed\n"); + + /* Init input for headless */ + ret = headless_input_init(compositor); + PEPPER_CHECK(ret, goto end, "headless_input_init() failed\n"); + + /* Init Output */ + ret = headless_output_init(compositor); + PEPPER_CHECK(ret, goto end, "headless_output_init() failed.\n"); + + /* Init Shell */ + ret = headless_shell_init(compositor); + PEPPER_CHECK(ret, goto end, "headless_shell_init() failed.\n"); + + /* Init Signal for SIGINT */ + init_signal(compositor); + + /* run event loop */ + wl_display_run(pepper_compositor_get_display(compositor)); + +end: + /* Deinit Process */ + headless_shell_deinit(compositor); + headless_input_deinit(compositor); + headless_output_deinit(compositor); + headless_debug_deinit(compositor); + pepper_compositor_destroy(compositor); + + return EXIT_SUCCESS; +} diff --git a/src/headless_server.h b/src/headless_server.h new file mode 100644 index 0000000..d176e6e --- /dev/null +++ b/src/headless_server.h @@ -0,0 +1,60 @@ +/* +* Copyright © 2019 Samsung Electronics co., Ltd. All Rights Reserved. +* +* 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 HEADLESS_SERVER_H +#define HEADLESS_SERVER_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* APIs for headless_output */ +PEPPER_API pepper_bool_t headless_output_init(pepper_compositor_t *compositor); +PEPPER_API void headless_output_deinit(pepper_compositor_t *compositor); + +/* APIs for headless_shell */ +PEPPER_API pepper_bool_t headless_shell_init(pepper_compositor_t *compositor); +PEPPER_API void headless_shell_deinit(pepper_compositor_t *compositor); + +/* APIs for headless_input */ +PEPPER_API pepper_bool_t headless_input_init(pepper_compositor_t *compositor); +PEPPER_API void headless_input_deinit(pepper_compositor_t *compositor); +PEPPER_API void headless_input_set_focus_view(pepper_compositor_t *compositor, pepper_view_t *view); +PEPPER_API void headless_input_set_top_view(pepper_compositor_t *compositor, pepper_view_t *view); +PEPPER_API void *headless_input_get_keyrouter(pepper_compositor_t *compositor); +PEPPER_API void *headless_input_get_xkb(pepper_compositor_t *compositor); + +/* APIs for headless_debug */ +PEPPER_API pepper_bool_t headless_debug_init(pepper_compositor_t *compositor); +PEPPER_API void headless_debug_deinit(pepper_compositor_t *compositor); +PEPPER_API void headless_debug_set_focus_view(pepper_compositor_t *compositor, pepper_view_t *view); +PEPPER_API void headless_debug_set_top_view(pepper_compositor_t *compositor, pepper_view_t *view); + +#ifdef __cplusplus +} +#endif + +#endif /* HEADLESS_SERVER_H */ + diff --git a/src/input/input.c b/src/input/input.c new file mode 100644 index 0000000..d831e3e --- /dev/null +++ b/src/input/input.c @@ -0,0 +1,442 @@ +/* +* Copyright © 2019 Samsung Electronics co., Ltd. All Rights Reserved. +* +* 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 +#include +#include +#include +#include +#include + +typedef struct +{ + pepper_compositor_t *compositor; + pepper_seat_t *seat; + pepper_evdev_t *evdev; + pepper_keyboard_t *keyboard; + pepper_input_device_t *default_device; + pepper_inotify_t *inotify; + + pepper_view_t *focus_view; + pepper_view_t *top_view; + + pepper_keyrouter_t *keyrouter; + pepper_devicemgr_t *devicemgr; + pepper_xkb_t *xkb; + + pepper_event_listener_t *listener_seat_keyboard_key; + pepper_event_listener_t *listener_seat_keyboard_add; + pepper_event_listener_t *listener_seat_add; + pepper_event_listener_t *listener_input_device_add; + + uint32_t ndevices; +} headless_input_t; + +const static int KEY_INPUT = 0xdeadbeaf; + +static void headless_input_init_event_listeners(headless_input_t *hi); +static void headless_input_deinit_event_listeners(headless_input_t *hi); + +/* seat keyboard add event handler */ +static void +_cb_handle_seat_keyboard_add(pepper_event_listener_t *listener, pepper_object_t *object, uint32_t id, void *info, void *data) +{ + pepper_event_listener_t *h = NULL; + pepper_keyboard_t *keyboard = (pepper_keyboard_t *)info; + headless_input_t *hi = (headless_input_t *)data; + + PEPPER_TRACE("[%s] keyboard added\n", __FUNCTION__); + + /* FIXME: without a keymap, ecore wl2 based client must work properly. */ + //pepper_keyboard_set_keymap_info(keyboard, WL_KEYBOARD_KEYMAP_FORMAT_NO_KEYMAP, -1, 0); + pepper_xkb_keyboard_set_keymap(hi->xkb, keyboard, NULL); + + pepper_keyrouter_set_keyboard(hi->keyrouter, keyboard); + h = pepper_object_add_event_listener((pepper_object_t *)keyboard, PEPPER_EVENT_KEYBOARD_KEY, + 0, pepper_keyrouter_event_handler, hi->keyrouter); + PEPPER_CHECK(h, goto end, "Failed to add keyboard key listener.\n"); + hi->listener_seat_keyboard_key = h; + hi->keyboard = keyboard; + + return; + +end: + headless_input_deinit_event_listeners(hi); +} + +/* compositor input device add event handler */ +static void +_cb_handle_input_device_add(pepper_event_listener_t *listener, pepper_object_t *object, uint32_t id, void *info, void *data) +{ + pepper_input_device_t *device = (pepper_input_device_t *)info; + headless_input_t *hi = (headless_input_t *)data; + + /* temporary : only add keyboard device to a seat */ + if (!(WL_SEAT_CAPABILITY_KEYBOARD & pepper_input_device_get_caps(device))) + return; + + PEPPER_TRACE("[%s] input device added.\n", __FUNCTION__); + + if (hi->seat) + pepper_seat_add_input_device(hi->seat, device); +} + +/* seat add event handler */ +static void +_cb_handle_seat_add(pepper_event_listener_t *listener, pepper_object_t *object, uint32_t id, void *info, void *data) +{ + pepper_event_listener_t *h = NULL; + pepper_seat_t *seat = (pepper_seat_t *)info; + headless_input_t *hi = (headless_input_t *)data; + + PEPPER_TRACE("[%s] seat added. name:%s\n", __FUNCTION__, pepper_seat_get_name(seat)); + + h = pepper_object_add_event_listener((pepper_object_t *)seat, PEPPER_EVENT_SEAT_KEYBOARD_ADD, + 0, _cb_handle_seat_keyboard_add, hi); + PEPPER_CHECK(h, goto end, "Failed to add seat keyboard add listener.\n"); + hi->listener_seat_keyboard_add = h; + + return; + +end: + headless_input_deinit_event_listeners(hi); +} + +static void +_cb_handle_inotify_event(uint32_t type, pepper_inotify_event_t *ev, void *data) +{ + headless_input_t *hi = data; + + PEPPER_CHECK(hi, return, "Invalid headless input\n"); + + switch (type) + { + case PEPPER_INOTIFY_EVENT_TYPE_CREATE: + pepper_evdev_device_path_add(hi->evdev, pepper_inotify_event_name_get(ev)); + break; + case PEPPER_INOTIFY_EVENT_TYPE_REMOVE: + pepper_evdev_device_path_remove(hi->evdev, pepper_inotify_event_name_get(ev)); + break; + case PEPPER_INOTIFY_EVENT_TYPE_MODIFY: + pepper_evdev_device_path_remove(hi->evdev, pepper_inotify_event_name_get(ev)); + pepper_evdev_device_path_add(hi->evdev, pepper_inotify_event_name_get(ev)); + break; + default: + break; + } +} + +PEPPER_API void * +headless_input_get_keyrouter(pepper_compositor_t *compositor) +{ + headless_input_t *hi; + hi = pepper_object_get_user_data((pepper_object_t *)compositor, &KEY_INPUT); + PEPPER_CHECK(hi, return NULL, "input system is not initialized\n"); + + return hi->keyrouter; +} + +PEPPER_API void * +headless_input_get_xkb(pepper_compositor_t *compositor) +{ + headless_input_t *hi; + hi = pepper_object_get_user_data((pepper_object_t *)compositor, &KEY_INPUT); + PEPPER_CHECK(hi, return NULL, "input system is not initialized\n"); + + return hi->xkb; +} + +void +headless_input_set_focus_view(pepper_compositor_t *compositor, pepper_view_t *focus_view) +{ + headless_input_t *hi; + + hi = (headless_input_t *)pepper_object_get_user_data((pepper_object_t *) compositor, &KEY_INPUT); + PEPPER_CHECK(hi, return, "Invalid headless input.\n"); + + if (hi->focus_view != focus_view) + { + pepper_keyboard_send_leave(hi->keyboard, hi->focus_view); + pepper_keyboard_set_focus(hi->keyboard, focus_view); + pepper_keyboard_send_enter(hi->keyboard, focus_view); + + hi->focus_view = focus_view; + } + + if (hi->keyrouter) + pepper_keyrouter_set_focus_view(hi->keyrouter, focus_view); +} + +void +headless_input_set_top_view(void *compositor, pepper_view_t *top_view) +{ + headless_input_t *hi; + + hi = (headless_input_t *)pepper_object_get_user_data((pepper_object_t *) compositor, &KEY_INPUT); + PEPPER_CHECK(hi, return, "Invalid headless input.\n"); + + if (hi->top_view == top_view) return; + + hi->top_view = top_view; + + if (hi->keyrouter) + pepper_keyrouter_set_top_view(hi->keyrouter, top_view); +} + +static void +headless_input_init_event_listeners(headless_input_t *hi) +{ + pepper_event_listener_t *h = NULL; + pepper_object_t *compositor = (pepper_object_t *)hi->compositor; + + /* register event listeners */ + h = pepper_object_add_event_listener((pepper_object_t *)compositor, + PEPPER_EVENT_COMPOSITOR_SEAT_ADD, 0, _cb_handle_seat_add, hi); + PEPPER_CHECK(h, goto end, "Failed to add seat add listener.\n"); + hi->listener_seat_add = h; + + h = pepper_object_add_event_listener((pepper_object_t *)compositor, + PEPPER_EVENT_COMPOSITOR_INPUT_DEVICE_ADD, 0, _cb_handle_input_device_add, hi); + PEPPER_CHECK(h, goto end, "Failed to add input device add listener.\n"); + hi->listener_input_device_add = h; + + return; + +end: + PEPPER_ERROR("[%s] Failed to init listeners", __FUNCTION__); + headless_input_deinit_event_listeners(hi); +} + +static void +headless_input_deinit_event_listeners(headless_input_t *hi) +{ + pepper_event_listener_remove(hi->listener_seat_keyboard_key); + pepper_event_listener_remove(hi->listener_seat_keyboard_add); + pepper_event_listener_remove(hi->listener_seat_add); + pepper_event_listener_remove(hi->listener_input_device_add); + + PEPPER_TRACE("[%s] event listeners have been removed.\n", __FUNCTION__); +} + +static void +headless_input_deinit_input(headless_input_t *hi) +{ + if (hi->inotify) + { + pepper_inotify_destroy(hi->inotify); + hi->inotify = NULL; + } + + if (hi->default_device) + { + pepper_input_device_destroy(hi->default_device); + hi->default_device = NULL; + } + + pepper_evdev_destroy(hi->evdev); + + if (hi->seat) + pepper_seat_destroy(hi->seat); + + hi->seat = NULL; + hi->evdev = NULL; + hi->ndevices = 0; +} + +static pepper_bool_t +headless_input_create_input_device(headless_input_t *hi, uint32_t caps) +{ + pepper_input_device_t *input_device = NULL; + + /* create a default pepper input device */ + input_device = pepper_input_device_create(hi->compositor, caps, NULL, hi); + PEPPER_CHECK(input_device, return PEPPER_FALSE, "Failed to create a keyboard device !\n"); + + hi->default_device = input_device; + return PEPPER_TRUE; +} + +static pepper_bool_t +headless_input_init_input(headless_input_t *hi) +{ + uint32_t caps = 0; + uint32_t probed = 0; + pepper_bool_t res = PEPPER_FALSE; + pepper_evdev_t *evdev = NULL; + pepper_inotify_t *inotify = NULL; + + /* create pepper evdev */ + evdev = pepper_evdev_create(hi->compositor); + PEPPER_CHECK(evdev, goto end, "Failed to create evdev !\n"); + + hi->evdev = evdev; + + /* probe evdev keyboard device(s) */ + caps |= WL_SEAT_CAPABILITY_KEYBOARD; + probed = pepper_evdev_device_probe(evdev, caps); + + if (!probed) + { + PEPPER_TRACE("No evdev device has been probed. A default key device will be created.\n"); + + res = headless_input_create_input_device(hi, caps); + PEPPER_CHECK(res, goto end, "Failed to create any input device(s) !\n"); + + probed++; + } + + hi->ndevices = probed; + + PEPPER_TRACE("%d evdev device(s) has been found.\n", probed); + + inotify = pepper_inotify_create(hi->compositor, _cb_handle_inotify_event, hi); + PEPPER_CHECK(inotify, goto end, "Failed to create inotify\n"); + + pepper_inotify_add(inotify, "/dev/input/"); + + hi->inotify = inotify; + + return PEPPER_TRUE; + +end: + pepper_evdev_destroy(evdev); + + return PEPPER_FALSE; +} + +static void +headless_input_init_modules(headless_input_t *hi) +{ + const char *seat_name = NULL; + pepper_seat_t *seat = NULL; + + pepper_keyrouter_t *keyrouter = NULL; + pepper_devicemgr_t *devicemgr = NULL; + pepper_xkb_t *xkb = NULL; + + PEPPER_TRACE("[%s] ... begin\n", __FUNCTION__); + + seat_name = getenv("XDG_SEAT"); + + if (!seat_name) + seat_name = "seat0"; + + /* create a default seat (seat0) */ + seat = pepper_compositor_add_seat(hi->compositor, seat_name); + PEPPER_CHECK(seat, goto end, "Failed to add seat (%s)!\n", seat_name); + + hi->seat = seat; + + /* create pepper xkb */ + xkb = pepper_xkb_create(); + PEPPER_CHECK(xkb, goto end, "Failed to create pepper_xkb !\n"); + + hi->xkb = xkb; + + /* create pepper keyrouter */ + keyrouter = pepper_keyrouter_create(hi->compositor); + PEPPER_CHECK(keyrouter, goto end, "Failed to create keyrouter !\n"); + + hi->keyrouter = keyrouter; + + /* create pepper devicemgr */ + devicemgr = pepper_devicemgr_create(hi->compositor, hi->seat); + PEPPER_CHECK(devicemgr, goto end, "Failed to create devicemgr !\n"); + pepper_devicemgr_xkb_enable(devicemgr); + + hi->devicemgr = devicemgr; + + PEPPER_TRACE("[%s] ... done\n", __FUNCTION__); + + return; +end: + if (hi->xkb) + pepper_xkb_destroy(hi->xkb); + if (hi->keyrouter) + pepper_keyrouter_destroy(hi->keyrouter); + if (hi->devicemgr) + pepper_devicemgr_destroy(hi->devicemgr); + if (hi->seat) + pepper_seat_destroy(hi->seat); + + hi->xkb = NULL; + hi->keyrouter = NULL; + hi->devicemgr = NULL; + hi->seat = NULL; +} + +static void +headless_input_deinit_modules(headless_input_t *hi) +{ + if (hi->xkb) + pepper_xkb_destroy(hi->xkb); + if (hi->keyrouter) + pepper_keyrouter_destroy(hi->keyrouter); + if (hi->devicemgr) + pepper_devicemgr_destroy(hi->devicemgr); + + hi->xkb = NULL; + hi->keyrouter = NULL; + hi->devicemgr = NULL; +} + +PEPPER_API void +headless_input_deinit(pepper_compositor_t * compositor) +{ + headless_input_t *hi = NULL; + + hi = (headless_input_t *)pepper_object_get_user_data((pepper_object_t *) compositor, &KEY_INPUT); + PEPPER_CHECK(hi, return, "Failed to get headless input instance.\n"); + + headless_input_deinit_event_listeners(hi); + headless_input_deinit_modules(hi); + headless_input_deinit_input(hi); + + pepper_object_set_user_data((pepper_object_t *)hi->compositor, &KEY_INPUT, NULL, NULL); + free(hi); +} + +pepper_bool_t +headless_input_init(pepper_compositor_t *compositor) +{ + headless_input_t *hi = NULL; + pepper_bool_t init = PEPPER_FALSE; + + hi = (headless_input_t*)calloc(1, sizeof(headless_input_t)); + PEPPER_CHECK(hi, goto error, "Failed to alloc for input\n"); + hi->compositor = compositor; + + headless_input_init_event_listeners(hi); + headless_input_init_modules(hi); + init = headless_input_init_input(hi); + PEPPER_CHECK(init, goto error, "headless_input_init_input() failed\n"); + + pepper_object_set_user_data((pepper_object_t *)compositor, &KEY_INPUT, hi, NULL); + + return PEPPER_TRUE; + +error: + headless_input_deinit(compositor); + + return PEPPER_FALSE; +} diff --git a/src/output/HL_UI_LED.h b/src/output/HL_UI_LED.h new file mode 100644 index 0000000..ea9889f --- /dev/null +++ b/src/output/HL_UI_LED.h @@ -0,0 +1,144 @@ +/* + * Copyright © 2019 Samsung Electronics co., Ltd. All Rights Reserved. + * + * 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 __HL_UI_LED_H__ +#define __HL_UI_LED_H__ +#include +#include +#include +#include +#include + +#define B_OFF_SET 1 +#define G_OFF_SET 2 +#define R_OFF_SET 3 + +#define BITRATE 8000000 + +typedef struct{ + uint32_t number; + peripheral_spi_h hnd_spi; + uint8_t *pixels; + uint8_t brightness; +}HL_UI_LED; + +/** + * @brief: Initialise a set of apa102 LEDs + * + * @param[in] led_num: Number of leds (0-255) + * + * @returns: pointer of handler\ Success + * NULL\ Error + */ +HL_UI_LED *HL_UI_LED_Init(uint32_t led_num); + +/** + * @brief: Change the global brightness and fresh + * + * @param[in] handle: handler of HL_UI_LED + * @param[in] brightness: New brightness value + */ +void HL_UI_LED_Change_Brightness(HL_UI_LED *handle, uint8_t brightness); + +/** + * @brief: Get the brightness + * + * @param[in] handle: handler of HL_UI_LED + * @return current brightness value (0-31) + */ +int HL_UI_LED_Get_Brightness(HL_UI_LED *handle); + +/** + * @brief: Set color for a specific pixel by giving R, G and B value separately + * + * @param[in] handle: handler of HL_UI_LED + * @param[in] index: Index of the target led (0-255) + * @param[in] red: Intensity of red colour (0-255) + * @param[in] green: Intensity of green colour (0-255) + * @param[in] blue: Intensity of blue colour (0-255) + */ +void HL_UI_LED_Set_Pixel_RGB(HL_UI_LED *handle, uint32_t index, uint8_t red, uint8_t green, uint8_t blue); + +/** + * @brief: Get colour form a specific pixel for R, G and B separately + * + * @param[in] handle: handler of HL_UI_LED + * @param[in] index: Index of the target led (0-255) + * @param[out] red: Intensity of red colour (0-255) + * @param[out] green: Intensity of green colour (0-255) + * @param[out] blue: Intensity of blue colour (0-255) + */ +void HL_UI_LED_Get_Pixel_RGB(HL_UI_LED *handle, uint32_t index, uint8_t *red, uint8_t *green, uint8_t *blue); + +/** + * @brief: Set color for a specific pixel by using 4byte date + * + * @param[in] handle: handler of HL_UI_LED + * @param[in] index: Index of the target led (0-255) + * @param[in] red: Intensity of red colour (0-255) + * @param[in] green: Intensity of green colour (0-255) + * @param[in] blue: Intensity of blue colour (0-255) + * + * @example: HL_UI_LED_Get_Pixel_RGB(1, 0xFF0000) sets the 1st LED to red colour + */ +void HL_UI_LED_Set_Pixel_4byte(HL_UI_LED *handle, uint32_t index, uint32_t colour); + +/** + * @brief: Get colour form a specific pixel + * + * @param[in] handle: handler of HL_UI_LED + * @param[in] index: Index of the target led (0-255) + * + * @returns: 32 bits colour data + */ +uint32_t HL_UI_LED_Get_Pixel_4byte(HL_UI_LED *handle, uint32_t index); + +/** + * @brief: Clear all the pixels + * + * @param[in] handle: handler of HL_UI_LED + */ +void HL_UI_LED_Clear_All(HL_UI_LED *handle); + +/** + * @brief: Refresh display (After modifing pixel colour) + */ +int HL_UI_LED_Refresh(HL_UI_LED *handle); + +/** + * @brief: Show display (After modifing pixel colour) + * + * @param[in] handle: handler of HL_UI_LED + */ +int HL_UI_LED_Show(HL_UI_LED *handle); + + +/** + * @brief: Close SPI file, release memory + * + * @param[in] handle: handler of HL_UI_LED + */ +void HL_UI_LED_Close(HL_UI_LED *handle); +#endif diff --git a/src/output/HL_UI_LED_APA102.c b/src/output/HL_UI_LED_APA102.c new file mode 100644 index 0000000..63b8c81 --- /dev/null +++ b/src/output/HL_UI_LED_APA102.c @@ -0,0 +1,234 @@ +/* + * Copyright © 2019 Samsung Electronics co., Ltd. All Rights Reserved. + * + * 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 "HL_UI_LED.h" + +#define SUCCESS_FLAG 760302 +#define RETRY_TIMES 3 + +#define SPI_BUS 0 +#define SPI_DEV 1 + +HL_UI_LED * +HL_UI_LED_Init(uint32_t led_num) +{ + HL_UI_LED *handle; + int count = 0; + int ret; + + handle = (HL_UI_LED*)malloc(sizeof(HL_UI_LED)); + if(handle == NULL) + { + return NULL; + } + handle->number = led_num; + handle->brightness = 0xFF; + handle->pixels = (uint8_t *)malloc(handle->number * 4); + if(handle->pixels == NULL) + { + free(handle); + return NULL; + } + + while(count < RETRY_TIMES) + { + if(peripheral_spi_open(SPI_BUS, SPI_DEV, &(handle->hnd_spi)) == 0) + { + printf("spi open success!\n"); + count = SUCCESS_FLAG; + if((ret = peripheral_spi_set_frequency(handle->hnd_spi, BITRATE)) != 0) + { + printf("Frequency Failed : 0x%x\n", ret); + } + if((ret = peripheral_spi_set_bits_per_word(handle->hnd_spi, 8)) != 0) + { + printf("BIT_WORD Failed : 0x%x\n", ret); + } + if((ret = peripheral_spi_set_bit_order(handle->hnd_spi,PERIPHERAL_SPI_BIT_ORDER_MSB)) != 0) + { + printf("BIT_ORDER Failed : 0x%x\n", ret); + } + if((ret = peripheral_spi_set_mode(handle->hnd_spi,PERIPHERAL_SPI_MODE_1)) != 0) + { + printf("SPI Mode Failed : 0x%x\n", ret); + } + break; + } + else + { + count++; + continue; + } + } + if(count == SUCCESS_FLAG) + { + HL_UI_LED_Clear_All(handle); + return handle; + } + else + { + free(handle->pixels); + free(handle); + return NULL; + } +} + +void +HL_UI_LED_Change_Brightness(HL_UI_LED *handle, uint8_t brightness) +{ + if (brightness > 31) + handle->brightness = 0xFF; + else + handle->brightness = 0xE0 | (0x1F & brightness); + HL_UI_LED_Refresh(handle); +} + +int +HL_UI_LED_Get_Brightness(HL_UI_LED *handle) +{ + return handle->brightness & 0x1F; +} + +void +HL_UI_LED_Set_Pixel_RGB(HL_UI_LED *handle, uint32_t index, uint8_t red, uint8_t green, uint8_t blue) +{ + if (index < handle->number) { + uint8_t *ptr = &(handle->pixels[index * 4]); + ptr[R_OFF_SET] = red; + ptr[G_OFF_SET] = green; + ptr[B_OFF_SET] = blue; + } +} + +void +HL_UI_LED_Get_Pixel_RGB(HL_UI_LED *handle, uint32_t index, uint8_t *red, uint8_t *green, uint8_t *blue) +{ + if (index < handle->number) { + uint8_t *ptr = &(handle->pixels[index * 4]); + *red = ptr[R_OFF_SET]; + *green = ptr[G_OFF_SET]; + *blue = ptr[B_OFF_SET]; + } +} + +void +HL_UI_LED_Set_Pixel_4byte(HL_UI_LED *handle, uint32_t index, uint32_t colour) +{ + uint8_t r, g, b; + uint8_t *ptr = (uint8_t *)&colour; + r = ptr[R_OFF_SET]; + g = ptr[G_OFF_SET]; + b = ptr[B_OFF_SET]; + HL_UI_LED_Set_Pixel_RGB(handle, index, r, g, b); +} + +uint32_t +HL_UI_LED_Get_Pixel_4byte(HL_UI_LED *handle, uint32_t index) +{ + uint8_t r=0, g=0, b=0; + uint32_t colour = 0; + uint8_t *ptr = (uint8_t *)&colour; + HL_UI_LED_Get_Pixel_RGB(handle, index, &r, &g, &b); + ptr[R_OFF_SET] = r; + ptr[G_OFF_SET] = g; + ptr[B_OFF_SET] = b; + return colour; +} + +void +HL_UI_LED_Clear_All(HL_UI_LED *handle) +{ + uint8_t *ptr; + uint32_t i; + for(ptr = handle->pixels, i=0; inumber; i++, ptr += 4) { + ptr[1] = 0x00; + ptr[2] = 0x00; + ptr[3] = 0x00; + } + HL_UI_LED_Refresh(handle); +} + +int +HL_UI_LED_Refresh(HL_UI_LED *handle) +{ + int ret; + uint32_t i; + uint32_t buf_len = 4 + 4 * handle->number + (handle->number + 15) / 16 + 1; + uint8_t *ptr, *qtr; + uint8_t *tx = (uint8_t *)malloc(buf_len); + + if( tx == NULL ) + { + return -1; + } + // start frame + for (i = 0; i < 4; i++) + *(tx + i) = 0x00; + + // LED data + qtr = tx + 4; + + for(ptr = handle->pixels, i=0; inumber; i++, ptr += 4, qtr += 4) { + qtr[0] = handle->brightness; + qtr[1] = ptr[1]; + qtr[2] = ptr[2]; + qtr[3] = ptr[3]; + } + + // end frame + for (i = handle->number * 4 + 4; i < buf_len; i++) + { + *(tx + i) = 0x00; + } + + ret = peripheral_spi_write(handle->hnd_spi, tx, buf_len); + free(tx); + if (ret != 0) + { + fprintf(stdout, "[Error] can't send spi message\n"); + return -2; + } + + return 0; +} + +int +HL_UI_LED_Show(HL_UI_LED *handle) +{ + return HL_UI_LED_Refresh(handle); +} + +void +HL_UI_LED_Close(HL_UI_LED *handle) +{ + HL_UI_LED_Clear_All(handle); + peripheral_spi_close(handle->hnd_spi); + + if (handle->pixels) { + free(handle->pixels); + } + + free(handle); +} diff --git a/src/output/boot_anim.c b/src/output/boot_anim.c new file mode 100644 index 0000000..83ba500 --- /dev/null +++ b/src/output/boot_anim.c @@ -0,0 +1,141 @@ +/* +* Copyright © 2019 Samsung Electronics co., Ltd. All Rights Reserved. +* +* 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 +#include +#include +#include + +#include +#include +#include +#include "HL_UI_LED.h" +#include "output_internal.h" + +typedef struct { + HL_UI_LED *led; + + struct wl_event_source *source; + uint32_t serial; + int index; + + pepper_event_listener_t *surface_add_listener; +} boot_ani_t; + +#define ANI_INTERVAL 40 //20ms + +static int +boot_ani_timer_cb(void *data) +{ + boot_ani_t *ani = (boot_ani_t *)data; + uint8_t r,g,b; + uint32_t color; + + if (ani->index == 0) { + r = (uint8_t)(rand()%0xFF); + g = (uint8_t)(rand()%0xFF); + b = (uint8_t)(rand()%0xFF); + HL_UI_LED_Set_Pixel_RGB(ani->led, ani->index, r, g, b); + } else { + color = HL_UI_LED_Get_Pixel_4byte(ani->led, ani->index - 1); + HL_UI_LED_Set_Pixel_4byte(ani->led, ani->index, color); + } + + HL_UI_LED_Refresh(ani->led); + + ani->serial++; + ani->index = (ani->serial)%12; + + wl_event_source_timer_update(ani->source, ANI_INTERVAL); + + return 1; +} + +static void +boot_ani_surface_add_cb(pepper_event_listener_t *listener, + pepper_object_t *object, + uint32_t id, void *info, void *data) +{ + led_output_t *output = (led_output_t *)data; + + boot_ani_stop(output); +} + +void boot_ani_start(led_output_t *output) +{ + struct wl_event_loop *loop; + boot_ani_t *ani; + int ret; + + PEPPER_TRACE("[OUTPUT] start boot-animation\n"); + + loop = wl_display_get_event_loop(pepper_compositor_get_display(output->compositor)); + PEPPER_CHECK(loop, return, "failed to wl_display_get_event_loop()\n"); + + ani = (boot_ani_t *)calloc(sizeof(boot_ani_t), 1); + PEPPER_CHECK(ani, return, "failed to alloc\n"); + + ani->source = wl_event_loop_add_timer(loop, boot_ani_timer_cb, ani); + PEPPER_CHECK(ani, goto err, "failed to wl_event_loop_add_timer()\n"); + + ret = wl_event_source_timer_update(ani->source, ANI_INTERVAL); + PEPPER_CHECK(!ret, goto err, "failed to wl_event_source_timer_update\n"); + + ani->surface_add_listener = pepper_object_add_event_listener((pepper_object_t *)output->compositor, + PEPPER_EVENT_COMPOSITOR_SURFACE_ADD, + 0, boot_ani_surface_add_cb, output); + + ani->led = output->ui_led; + output->boot_ani = ani; + return; +err: + if (ani) { + if (ani->source) + wl_event_source_remove(ani->source); + + free(ani); + } + return; +} + +void boot_ani_stop(led_output_t *output) +{ + boot_ani_t *ani; + + if (!output->boot_ani) return; + + ani = (boot_ani_t *)output->boot_ani; + + HL_UI_LED_Clear_All(ani->led); + wl_event_source_remove(ani->source); + + if(ani->surface_add_listener) { + pepper_event_listener_remove(ani->surface_add_listener); + ani->surface_add_listener = NULL; + } + + free(ani); + + output->boot_ani = NULL; + return; +} diff --git a/src/output/output_internal.h b/src/output/output_internal.h new file mode 100644 index 0000000..2bfa286 --- /dev/null +++ b/src/output/output_internal.h @@ -0,0 +1,51 @@ +/* +* Copyright © 2019 Samsung Electronics co., Ltd. All Rights Reserved. +* +* 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 +#include +#include +#include + +#include + +#define NUM_LED 12 + +typedef struct { + pepper_compositor_t *compositor; + pepper_output_t *output; + pepper_plane_t *plane; + + int num_led; + HL_UI_LED *ui_led; + + struct wayland_tbm_server *tbm_server; + struct wl_event_source *frame_done; + + pepper_view_t *top_view; + + //For booting animation + void *boot_ani; +}led_output_t; + +PEPPER_API void boot_ani_start(led_output_t *output); +PEPPER_API void boot_ani_stop(led_output_t *output); diff --git a/src/output/output_led.c b/src/output/output_led.c new file mode 100644 index 0000000..8410495 --- /dev/null +++ b/src/output/output_led.c @@ -0,0 +1,374 @@ +/* +* Copyright © 2019 Samsung Electronics co., Ltd. All Rights Reserved. +* +* 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 +#include +#include +#include + +#include +#include +#include +#include "HL_UI_LED.h" +#include "output_internal.h" + +static const int KEY_OUTPUT; +static void led_output_add_frame_done(led_output_t *output); +static void led_output_update(led_output_t *output); + +static void +led_output_destroy(void *data) +{ + led_output_t *output = (led_output_t *)data; + PEPPER_TRACE("Output Destroy %p base %p\n", output, output->output); + + if (output->ui_led) { + HL_UI_LED_Close(output->ui_led); + output->ui_led = NULL; + } + + if (output->tbm_server) { + wayland_tbm_server_deinit(output->tbm_server); + output->tbm_server = NULL; + } +} + +static int32_t +led_output_get_subpixel_order(void *o) +{ + return 0; +} + +static const char * +led_output_get_maker_name(void *o) +{ + return "PePPer LED"; +} + +static const char * +led_output_get_model_name(void *o) +{ + return "PePPer LED"; +} + +static int +led_output_get_mode_count(void *o) +{ + return 1; +} + +static void +led_output_get_mode(void *o, int index, pepper_output_mode_t *mode) +{ + led_output_t *output = (led_output_t *)o; + + PEPPER_TRACE("[OUTPUT]\n"); + + if (index != 0) + return; + + mode->flags = WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED; + mode->w = output->num_led; + mode->h = output->num_led; + mode->refresh = 60000; +} + +static pepper_bool_t +led_output_set_mode(void *o, const pepper_output_mode_t *mode) +{ + return PEPPER_FALSE; +} + +static void +led_output_assign_planes(void *o, const pepper_list_t *view_list) +{ + led_output_t *output = (led_output_t *)o; + pepper_list_t *l; + pepper_view_t *view, *top_view = NULL; + + PEPPER_TRACE("[OUTPUT] Assign plane\n"); + pepper_list_for_each_list(l, view_list) { + view = (pepper_view_t*)l->item; + + if (pepper_view_is_mapped(view) && pepper_view_is_visible(view)) { + top_view = view; + break; + } + } + + if (output->top_view != top_view) + PEPPER_TRACE("\tTop-View is changed(%p -> %p)\n", output->top_view, top_view); + + output->top_view = top_view; +} + +static void +led_output_start_repaint_loop(void *o) +{ + led_output_t *output = (led_output_t *)o; + struct timespec ts; + + PEPPER_TRACE("[OUTPUT] Start reapint loop\n"); + pepper_compositor_get_time(output->compositor, &ts); + pepper_output_finish_frame(output->output, &ts); +} + +static void +led_output_repaint(void *o, const pepper_list_t *plane_list) +{ + pepper_list_t *l; + pepper_plane_t *plane; + led_output_t *output = (led_output_t *)o; + + PEPPER_TRACE("[OUTPUT] Repaint\n"); + + pepper_list_for_each_list(l, plane_list) { + plane = (pepper_plane_t *)l->item; + pepper_plane_clear_damage_region(plane); + } + + led_output_update(output); + led_output_add_frame_done(output); +} + +static void +led_output_attach_surface(void *o, pepper_surface_t *surface, int *w, int *h) +{ + *w = 10; + *h = 10; + PEPPER_TRACE("[OUTPUT] attach surface:%p\n", surface); +} + +static void +led_output_flush_surface_damage(void *o, pepper_surface_t *surface, pepper_bool_t *keep_buffer) +{ + *keep_buffer = PEPPER_TRUE; + PEPPER_TRACE("[OUTPUT] flush_surface_damage surface:%p\n", surface); +} + +struct pepper_output_backend led_output_backend = { + led_output_destroy, + + led_output_get_subpixel_order, + led_output_get_maker_name, + led_output_get_model_name, + + led_output_get_mode_count, + led_output_get_mode, + led_output_set_mode, + + led_output_assign_planes, + led_output_start_repaint_loop, + led_output_repaint, + led_output_attach_surface, + led_output_flush_surface_damage, +}; + +static void +led_output_update_led(led_output_t *output, unsigned char *data) +{ + int i; + uint8_t *ptr = (uint8_t *)data; + + if (data == NULL) { + PEPPER_TRACE("[OUTPUT] update LED to empty\n"); + HL_UI_LED_Clear_All(output->ui_led); + return; + } + + for(i=0; inum_led; i++) { + HL_UI_LED_Set_Pixel_RGB(output->ui_led, i, ptr[R_OFF_SET], ptr[G_OFF_SET], ptr[B_OFF_SET]); + ptr += 4; + } + + HL_UI_LED_Refresh(output->ui_led); +} + +static void +led_output_update(led_output_t *output) +{ + pepper_buffer_t *buf; + pepper_surface_t *surface; + struct wl_resource *buf_res; + tbm_surface_h tbm_surface; + tbm_surface_info_s info; + int ret; + + if (!output->top_view) { + if (!output->ui_led) + PEPPER_TRACE("[UPDATE LED] Empty Display\n"); + else + led_output_update_led(output, NULL); + + return; + } + + surface = pepper_view_get_surface(output->top_view); + PEPPER_CHECK(surface, return, "fail to get a surafce from a view(%p)\n", output->top_view); + + buf = pepper_surface_get_buffer(surface); + PEPPER_CHECK(buf, return, "fail to get a pepper_buffer from a surface(%p)\n", surface); + + buf_res = pepper_buffer_get_resource(buf); + tbm_surface = wayland_tbm_server_get_surface(NULL, buf_res); + PEPPER_CHECK(tbm_surface, return, "fail to get a tbm_surface from a pepper_buffer(%p)\n", buf); + + ret = tbm_surface_map(tbm_surface, TBM_SURF_OPTION_READ, &info); + PEPPER_CHECK(ret == TBM_SURFACE_ERROR_NONE, return, "fail to map the tbm_surface\n"); + + if (!output->ui_led) + PEPPER_TRACE("[UPDATE LED] %s\n", (char*)info.planes[0].ptr); + else + led_output_update_led(output, info.planes[0].ptr); + + tbm_surface_unmap(tbm_surface); +} + +static void +led_output_cb_frame_done(void *data) +{ + led_output_t *output = (led_output_t *)data; + + PEPPER_TRACE("[OUTPUT] frame_done %p\n", output); + output->frame_done = NULL; + + pepper_output_finish_frame(output->output, NULL); +} + +static void +led_output_add_frame_done(led_output_t *output) +{ + struct wl_event_loop *loop; + + PEPPER_TRACE("[OUTPUT] Add idle for frame(output:%p, frame_done:%p)\n", output, output->frame_done); + + if (!output || output->frame_done) { + PEPPER_TRACE("[OUTPUT] skip add frame_done\n"); + return; + } + + loop = wl_display_get_event_loop(pepper_compositor_get_display(output->compositor)); + PEPPER_CHECK(loop, return, "[OUTPUT] fail to get event loop\n"); + + output->frame_done = wl_event_loop_add_idle(loop, led_output_cb_frame_done, output); + PEPPER_CHECK(output->frame_done, return, "[OUTPUT] fail to add idle\n"); +} + +static void +pepper_output_bind_display(led_output_t *output) +{ + tbm_bufmgr bufmgr = NULL; + + PEPPER_CHECK(getenv("TBM_DISPLAY_SERVER"), return, "[TBM] run the subcompoitor mode\n"); + + bufmgr = wayland_tbm_server_get_bufmgr(output->tbm_server); + PEPPER_CHECK(bufmgr, return, "fail to get tbm_bufmgr\n"); + + if (!tbm_bufmgr_bind_native_display(bufmgr, (void *)pepper_compositor_get_display(output->compositor))) + { + PEPPER_CHECK(0, return, "fail to tbm_bufmgr_bind_native_display\n"); + } + + return; +} + +pepper_bool_t +headless_output_init(pepper_compositor_t *compositor) +{ + led_output_t *output = (led_output_t*)calloc(sizeof(led_output_t), 1); + + PEPPER_TRACE("Output Init\n"); + + if (!output) { + PEPPER_ERROR("Failed to allocate memory in %s\n", __FUNCTION__); + goto error; + } + + output->compositor = compositor; + output->tbm_server = wayland_tbm_server_init(pepper_compositor_get_display(compositor), NULL, -1, 0); + PEPPER_CHECK(output->tbm_server, goto error, "failed to wayland_tbm_server_init.\n"); + + pepper_output_bind_display(output); + + output->num_led = NUM_LED; + output->ui_led = HL_UI_LED_Init(output->num_led); + if (output->ui_led) HL_UI_LED_Change_Brightness(output->ui_led, 0x1); + + if (!output->ui_led) + PEPPER_ERROR("HL_UI_LED_Init() failed.\n"); + else + boot_ani_start(output); + + output->output = pepper_compositor_add_output(compositor, + &led_output_backend, "led_output", + output, WL_OUTPUT_TRANSFORM_NORMAL, 1); + PEPPER_CHECK(output->output, goto error, "pepper_compositor_add_output() failed.\n"); + + output->plane = pepper_output_add_plane(output->output, NULL); + PEPPER_CHECK(output->plane, goto error, "pepper_output_add_plane() failed.\n"); + + pepper_object_set_user_data((pepper_object_t *)compositor, + &KEY_OUTPUT, output, NULL); + PEPPER_TRACE("\t Add Output %p, base %p\n", output, output->output); + PEPPER_TRACE("\t Add Output %p, plane %p\n", output, output->plane); + PEPPER_TRACE("\t Userdata %p\n", pepper_object_get_user_data((pepper_object_t *)compositor,&KEY_OUTPUT)); + return PEPPER_TRUE; + +error: + if (output->ui_led) + HL_UI_LED_Close(output->ui_led); + + if (output->tbm_server) + wayland_tbm_server_deinit(output->tbm_server); + + if (output->output) + pepper_output_destroy(output->output); + + if (output) + free(output); + return PEPPER_FALSE; +} + +void +headless_output_deinit(pepper_compositor_t *compositor) +{ + led_output_t *output; + + + output = pepper_object_get_user_data((pepper_object_t *)compositor, &KEY_OUTPUT); + + if (output) { + pepper_object_set_user_data((pepper_object_t *)compositor, &KEY_OUTPUT, NULL, NULL); + + if (output->boot_ani) { + boot_ani_stop(output); + } + + pepper_output_destroy(output->output); + led_output_destroy(output); + + free(output); + } + + PEPPER_TRACE("Output Deinit ... DONE\n"); +} diff --git a/src/shell/shell.c b/src/shell/shell.c new file mode 100644 index 0000000..75e5ddc --- /dev/null +++ b/src/shell/shell.c @@ -0,0 +1,1331 @@ +/* +* Copyright © 2019 Samsung Electronics co., Ltd. All Rights Reserved. +* +* 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 +#include +#include +#include + +#include +#include +#include +#include + +#include "headless_server.h" + +#define UPDATE_SURFACE_TYPE 0 //update the surface_type(map. unmap) +#define SET_UPDATE(x, type) (x |= ((uint32_t)(1<surface_type == HEADLESS_SURFACE_TOPLEVEL), return, "Invalid surface type.\n"); + PEPPER_CHECK((hs_surface->zxdg_surface == resource), return, "Invalid surface."); + + PEPPER_TRACE("[SHELL] zxdg_toplevel_cb_resource_destroy: view:%p, hs_surface:%p\n", hs_surface->view, hs_surface); + + if (hs_surface->view) { + pepper_view_unmap(hs_surface->view); + headless_shell_add_idle(hs_surface->hs_shell); + } + + hs_surface->surface_type = HEADLESS_SURFACE_NONE; + hs_surface->zxdg_surface = NULL; + SET_UPDATE(hs_surface->updates, UPDATE_SURFACE_TYPE); +} + +static void +zxdg_toplevel_cb_destroy(struct wl_client *client, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static void +zxdg_toplevel_cb_parent_set(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *res_parent) +{ +} + +static void +zxdg_toplevel_cb_title_set(struct wl_client *client, + struct wl_resource *resource, + const char *title) +{ +} + +static void +zxdg_toplevel_cb_app_id_set(struct wl_client *client, + struct wl_resource *resource, + const char *app_id) +{ +} + +static void +zxdg_toplevel_cb_win_menu_show(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *res_seat, + uint32_t serial, + int32_t x, + int32_t y) +{ +} + +static void +zxdg_toplevel_cb_move(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *res_seat, + uint32_t serial) +{ +} + +static void +zxdg_toplevel_cb_resize(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *res_seat, + uint32_t serial, + uint32_t edges) +{ +} + +static void +zxdg_toplevel_cb_max_size_set(struct wl_client *client, + struct wl_resource *resource, + int32_t w, + int32_t h) +{ +} + +static void +zxdg_toplevel_cb_min_size_set(struct wl_client *client, + struct wl_resource *resource, + int32_t w, + int32_t h) +{ +} + +static void +zxdg_toplevel_cb_maximized_set(struct wl_client *client, struct wl_resource *resource) +{ +} + +static void +zxdg_toplevel_cb_maximized_unset(struct wl_client *client, struct wl_resource *resource) +{ +} + +static void +zxdg_toplevel_cb_fullscreen_set(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *res_output) +{ +} + +static void +zxdg_toplevel_cb_fullscreen_unset(struct wl_client *client, struct wl_resource *resource) +{ +} + +static void +zxdg_toplevel_cb_minimized_set(struct wl_client *client, struct wl_resource *resource) +{ +} + +static const struct zxdg_toplevel_v6_interface zxdg_toplevel_interface = +{ + zxdg_toplevel_cb_destroy, + zxdg_toplevel_cb_parent_set, + zxdg_toplevel_cb_title_set, + zxdg_toplevel_cb_app_id_set, + zxdg_toplevel_cb_win_menu_show, + zxdg_toplevel_cb_move, + zxdg_toplevel_cb_resize, + zxdg_toplevel_cb_max_size_set, + zxdg_toplevel_cb_min_size_set, + zxdg_toplevel_cb_maximized_set, + zxdg_toplevel_cb_maximized_unset, + zxdg_toplevel_cb_fullscreen_set, + zxdg_toplevel_cb_fullscreen_unset, + zxdg_toplevel_cb_minimized_set +}; + +static void +zxdg_popup_cb_resource_destroy(struct wl_resource *resource) +{ + headless_shell_surface_t *hs_surface = (headless_shell_surface_t *)wl_resource_get_user_data(resource); + + PEPPER_CHECK(hs_surface, return, "fail to get headless_surface.\n"); + PEPPER_CHECK((hs_surface->surface_type == HEADLESS_SURFACE_POPUP), return, "Invalid surface type.\n"); + PEPPER_CHECK((hs_surface->zxdg_surface == resource), return, "Invalid surface."); + + hs_surface->surface_type = HEADLESS_SURFACE_NONE; + hs_surface->zxdg_surface = NULL; + SET_UPDATE(hs_surface->updates, UPDATE_SURFACE_TYPE); +} + +static void +zxdg_popup_cb_destroy(struct wl_client *client, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static void +zxdg_popup_cb_grab(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *res_seat, + uint32_t serial) +{ +} + +static const struct zxdg_popup_v6_interface zxdg_popup_interface = +{ + zxdg_popup_cb_destroy, + zxdg_popup_cb_grab +}; + +static void +zxdg_surface_cb_resource_destroy(struct wl_resource *resource) +{ + headless_shell_surface_t *hs_surface; + + hs_surface = wl_resource_get_user_data(resource); + PEPPER_CHECK(hs_surface, return, "fail to get hs_surface\n"); + + PEPPER_TRACE("[SHELL] zxdg_surface_cb_resource_destroy: hs_surface:%p view:%p\n", hs_surface, hs_surface->view); + + if (hs_surface->view) { + pepper_view_destroy(hs_surface->view); + hs_surface->view = NULL; + } + + if (hs_surface->cb_commit) { + pepper_event_listener_remove(hs_surface->cb_commit); + hs_surface->cb_commit = NULL; + } + + hs_surface->zxdg_shell_surface = NULL; + hs_surface->skip_focus = PEPPER_FALSE; + hs_surface->visibility = TIZEN_VISIBILITY_VISIBILITY_FULLY_OBSCURED; + + SET_UPDATE(hs_surface->updates, UPDATE_SURFACE_TYPE); + headless_shell_add_idle(hs_surface->hs_shell); +} + +static void +zxdg_surface_cb_destroy(struct wl_client *client, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static void +zxdg_surface_cb_toplevel_get(struct wl_client *client, struct wl_resource *resource, uint32_t id) +{ + headless_shell_surface_t *hs_surface = (headless_shell_surface_t *)wl_resource_get_user_data(resource); + struct wl_resource *new_res; + + PEPPER_CHECK(hs_surface, return, "fail to get headless_surface\n"); + PEPPER_CHECK((hs_surface->zxdg_surface == NULL), return, "alwreay assign zdg_surface:%p role:%d\n", hs_surface->zxdg_surface, hs_surface->surface_type); + + new_res = wl_resource_create(client, &zxdg_toplevel_v6_interface, 1, id); + if (!new_res) { + PEPPER_ERROR("fail to create zxdg_toplevel"); + wl_resource_post_no_memory(resource); + return; + } + + wl_resource_set_implementation(new_res, + &zxdg_toplevel_interface, + hs_surface, + zxdg_toplevel_cb_resource_destroy); + + hs_surface->surface_type = HEADLESS_SURFACE_TOPLEVEL; + hs_surface->zxdg_surface = new_res; + + SET_UPDATE(hs_surface->updates, UPDATE_SURFACE_TYPE); +} + +static void +zxdg_surface_cb_popup_get(struct wl_client *client, + struct wl_resource *resource, + uint32_t id, + struct wl_resource *res_parent, + struct wl_resource *res_pos) +{ + headless_shell_surface_t *hs_surface = (headless_shell_surface_t *)wl_resource_get_user_data(resource); + struct wl_resource *new_res; + + PEPPER_CHECK(hs_surface, return, "fail to get headless_surface\n"); + PEPPER_CHECK((hs_surface->zxdg_surface == NULL), return, "alwreay assign zdg_surface:%p role:%d\n", hs_surface->zxdg_surface, hs_surface->surface_type); + + new_res = wl_resource_create(client, &zxdg_popup_v6_interface, 1, id); + if (!new_res) { + PEPPER_ERROR("fail to create popup"); + wl_resource_post_no_memory(resource); + return; + } + + wl_resource_set_implementation(new_res, + &zxdg_popup_interface, + hs_surface, + zxdg_popup_cb_resource_destroy); + + hs_surface->surface_type = HEADLESS_SURFACE_POPUP; + hs_surface->zxdg_surface = new_res; + + SET_UPDATE(hs_surface->updates, UPDATE_SURFACE_TYPE); +} + +static void +zxdg_surface_cb_win_geometry_set(struct wl_client *client, + struct wl_resource *resource, + int32_t x, + int32_t y, + int32_t w, + int32_t h) +{ +} + +static void +zxdg_surface_cb_configure_ack(struct wl_client *client, struct wl_resource *resource, uint32_t serial) +{ + headless_shell_surface_t *hs_surface; + + hs_surface = wl_resource_get_user_data(resource); + PEPPER_CHECK(hs_surface, return, "fail to get headless_shell_surface\n"); + + hs_surface->last_ack_configure = serial; +} + +static const struct zxdg_surface_v6_interface zxdg_surface_interface = +{ + zxdg_surface_cb_destroy, + zxdg_surface_cb_toplevel_get, + zxdg_surface_cb_popup_get, + zxdg_surface_cb_win_geometry_set, + zxdg_surface_cb_configure_ack +}; + +static void +zxdg_positioner_cb_destroy(struct wl_client *client, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static void +zxdg_positioner_cb_size_set(struct wl_client *client, + struct wl_resource *resource, + int32_t w, int32_t h) +{ +} + +static void +zxdg_positioner_cb_anchor_rect_set(struct wl_client *client, + struct wl_resource *resource, + int32_t x, int32_t y, int32_t w, int32_t h) +{ +} + +static void +zxdg_positioner_cb_anchor_set(struct wl_client *client, + struct wl_resource *resource, + enum zxdg_positioner_v6_anchor anchor) +{ +} + +static void +zxdg_positioner_cb_gravity_set(struct wl_client *client, + struct wl_resource *resource, + enum zxdg_positioner_v6_gravity gravity) +{ +} + +static void +zxdg_positioner_cb_constraint_adjustment_set(struct wl_client *client, + struct wl_resource *resource, + enum zxdg_positioner_v6_constraint_adjustment constraint_adjustment) +{ +} + +static void +zxdg_positioner_cb_offset_set(struct wl_client *client, + struct wl_resource *resource, + int32_t x, int32_t y) +{ +} + +static const struct zxdg_positioner_v6_interface zxdg_positioner_interface = +{ + zxdg_positioner_cb_destroy, + zxdg_positioner_cb_size_set, + zxdg_positioner_cb_anchor_rect_set, + zxdg_positioner_cb_anchor_set, + zxdg_positioner_cb_gravity_set, + zxdg_positioner_cb_constraint_adjustment_set, + zxdg_positioner_cb_offset_set, +}; + +static void +zxdg_shell_cb_destroy(struct wl_client *client, struct wl_resource *resource) +{ + PEPPER_TRACE("Destroy zxdg_shell\n"); + + wl_resource_destroy(resource); +} + +static void +zxdg_shell_cb_positioner_create(struct wl_client *client, struct wl_resource *resource, uint32_t id) +{ + struct wl_resource *new_res; + + PEPPER_TRACE("Create zxdg_positoiner\n"); + + new_res = wl_resource_create(client, &zxdg_positioner_v6_interface, 1, id); + if (!new_res) + { + PEPPER_ERROR("fail to create zxdg_positioner\n"); + wl_resource_post_no_memory(resource); + return; + } + + wl_resource_set_implementation(new_res, &zxdg_positioner_interface, NULL, NULL); +} + +static void +zxdg_shell_cb_surface_get(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *wsurface) +{ + headless_shell_t *hs; + headless_shell_surface_t *hs_surface = NULL; + pepper_surface_t *psurface; + const char *role; + + hs = wl_resource_get_user_data(resource); + if (!hs) { + PEPPER_ERROR("fail to get headless_shell\n"); + wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT, + "failed to get headless shell"); + return; + } + + psurface = wl_resource_get_user_data(wsurface); + PEPPER_CHECK(psurface, goto error, "faile to get pepper_surface\n"); + + hs_surface = (headless_shell_surface_t *)pepper_object_get_user_data((pepper_object_t *)psurface, wsurface); + if (!hs_surface) { + PEPPER_ERROR("fail to get headless_shell_surface_t: %p key:%p\n", psurface, wsurface); + wl_resource_post_no_memory(resource); + return; + } + + hs_surface->zxdg_shell_surface = wl_resource_create(client, &zxdg_surface_v6_interface, 1, id); + if (!hs_surface->zxdg_shell_surface) { + PEPPER_ERROR("fail to create the zxdg_surface\n"); + wl_resource_post_no_memory(resource); + goto error; + } + + wl_resource_set_implementation(hs_surface->zxdg_shell_surface, + &zxdg_surface_interface, + hs_surface, + zxdg_surface_cb_resource_destroy); + + hs_surface->view = pepper_compositor_add_view(hs->compositor); + if (!hs_surface->view) { + PEPPER_ERROR("fail to create the pepper_view\n"); + wl_resource_post_no_memory(resource); + goto error; + } + + if (!pepper_view_set_surface(hs_surface->view, psurface)) { + PEPPER_ERROR("fail to set surface\n"); + wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT, + "Assign set psurface to pview"); + goto error; + } + + hs_surface->cb_commit = pepper_object_add_event_listener((pepper_object_t *)psurface, + PEPPER_EVENT_SURFACE_COMMIT, 0, headless_shell_cb_surface_commit, hs_surface); + + role = pepper_surface_get_role(psurface); + if (!role) { + if (!pepper_surface_set_role(psurface, "xdg_surface")) { + PEPPER_ERROR("fail to set role\n"); + wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT, + "Assign \"xdg_surface\" to wl_surface failed\n"); + goto error; + } + } else { + PEPPER_CHECK(!strcmp(role, "xdg_surface"), goto error, "surface has alweady role %s\n", role); + } + + PEPPER_TRACE("[SHELL] create zxdg_surface:%p, pview:%p, psurface:%p\n", + hs_surface->zxdg_shell_surface, + hs_surface->view, + psurface); + + return; +error: + if (hs_surface) { + if (hs_surface->view) { + pepper_view_destroy(hs_surface->view); + hs_surface->view = NULL; + } + + if (hs_surface->zxdg_shell_surface) { + wl_resource_destroy(hs_surface->zxdg_shell_surface); + hs_surface->zxdg_shell_surface = NULL; + } + } +} + +static void +zxdg_shell_cb_pong(struct wl_client *client, struct wl_resource *resource, uint32_t serial) +{ + +} + +static const struct zxdg_shell_v6_interface zxdg_shell_interface = +{ + zxdg_shell_cb_destroy, + zxdg_shell_cb_positioner_create, + zxdg_shell_cb_surface_get, + zxdg_shell_cb_pong +}; + +static void +zxdg_shell_cb_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) +{ + struct wl_resource *resource; + headless_shell_t *hs = (headless_shell_t *)data; + + PEPPER_TRACE("Bind zxdg_shell\n"); + + /* Create resource for zxdg_shell_v6 */ + resource = wl_resource_create(client, + &zxdg_shell_v6_interface, + version, + id); + PEPPER_CHECK(resource, goto err_shell, "fail to create the zxdg_shell_v6\n"); + + wl_resource_set_implementation(resource, &zxdg_shell_interface, hs, NULL); + + return; +err_shell: + wl_resource_destroy(resource); + wl_client_post_no_memory(client); +} + +static pepper_bool_t +zxdg_init(headless_shell_t *shell) +{ + struct wl_display *display; + + display = pepper_compositor_get_display(shell->compositor); + + shell->zxdg_shell = wl_global_create(display, &zxdg_shell_v6_interface, 1, shell, zxdg_shell_cb_bind); + PEPPER_CHECK(shell->zxdg_shell, return PEPPER_FALSE, "fail to create zxdg_shell\n"); + + return PEPPER_TRUE; +} + +void +zxdg_deinit(headless_shell_t *shell) +{ + if (shell->zxdg_shell) + wl_global_destroy(shell->zxdg_shell); +} + +static void +tizen_visibility_cb_destroy(struct wl_client *client, struct wl_resource *res_tzvis) +{ + wl_resource_destroy(res_tzvis); +} + +static const struct tizen_visibility_interface tizen_visibility = +{ + tizen_visibility_cb_destroy +}; + +static void +tizen_visibility_cb_vis_destroy(struct wl_resource *res_tzvis) +{ + headless_shell_surface_t *hs_surface = (headless_shell_surface_t *)wl_resource_get_user_data(res_tzvis); + + PEPPER_CHECK(hs_surface, return, "[SHELL] cannot get headless_shell_surface_t\n"); + hs_surface->tizen_visibility = NULL; +} + +static void +tizen_position_cb_destroy(struct wl_client *client, struct wl_resource *res_tzpos) +{ + wl_resource_destroy(res_tzpos); +} + +static void +tizen_position_cb_set(struct wl_client *client, struct wl_resource *res_tzpos, int32_t x, int32_t y) +{ +} + +static const struct tizen_position_interface tizen_position = +{ + tizen_position_cb_destroy, + tizen_position_cb_set, +}; + +static void +tizen_position_cb_pos_destroy(struct wl_resource *res_tzpos) +{ +} + +static void +tizen_policy_cb_vis_get(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *surf) +{ + pepper_surface_t *psurface; + headless_shell_surface_t *hs_surface; + struct wl_resource *new_res; + + psurface = wl_resource_get_user_data(surf); + PEPPER_CHECK(psurface, return, "fail to get pepper_surface_t\n"); + + hs_surface = pepper_object_get_user_data((pepper_object_t *)psurface, surf); + PEPPER_CHECK(hs_surface, return, "fail to get headless_shell_surface\n"); + + new_res = wl_resource_create(client, &tizen_visibility_interface, 5, id); + if (!new_res) { + PEPPER_ERROR("fail to create tizen_visibility"); + wl_resource_post_no_memory(resource); + return; + } + + wl_resource_set_implementation(new_res, + &tizen_visibility, + hs_surface, + tizen_visibility_cb_vis_destroy); + + hs_surface->tizen_visibility = new_res; + + if (hs_surface->visibility != TIZEN_VISIBILITY_VISIBILITY_FULLY_OBSCURED) + tizen_visibility_send_notify(hs_surface->tizen_visibility, hs_surface->visibility); +} + +static void +tizen_policy_cb_pos_get(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *surf) +{ + struct wl_resource *new_res; + + new_res = wl_resource_create(client, &tizen_position_interface, 1, id); + if (!new_res) { + PEPPER_ERROR("fail to create tizen_visibility"); + wl_resource_post_no_memory(resource); + return; + } + + wl_resource_set_implementation(new_res, + &tizen_position, + NULL, + tizen_position_cb_pos_destroy); +} + +static void +tizen_policy_cb_activate(struct wl_client *client, struct wl_resource *resource, struct wl_resource *surf) +{ + pepper_surface_t *psurface; + headless_shell_surface_t *hs_surface; + + psurface = wl_resource_get_user_data(surf); + PEPPER_CHECK(psurface, return, "fail to get pepper_surface_t\n"); + + hs_surface = pepper_object_get_user_data((pepper_object_t *)psurface, surf); + PEPPER_CHECK(hs_surface, return, "fail to get headless_shell_surface\n"); + PEPPER_CHECK(hs_surface->view, return, "invalid view from headless_shell_surface\n"); + + pepper_view_stack_top(hs_surface->view, PEPPER_TRUE); + + headless_shell_add_idle(hs_surface->hs_shell); +} + +static void +tizen_policy_cb_activate_below_by_res_id(struct wl_client *client, struct wl_resource *resource, uint32_t res_id, uint32_t below_res_id) +{ +} + +static void +tizen_policy_cb_raise(struct wl_client *client, struct wl_resource *resource, struct wl_resource *surf) +{ +} + +static void +tizen_policy_cb_lower(struct wl_client *client, struct wl_resource *resource, struct wl_resource *surf) +{ +} + +static void +tizen_policy_cb_lower_by_res_id(struct wl_client *client, struct wl_resource *resource, uint32_t res_id) +{ +} + +static void +tizen_policy_cb_focus_skip_set(struct wl_client *client, struct wl_resource *resource, struct wl_resource *surf) +{ + pepper_surface_t *psurface; + headless_shell_surface_t *hs_surface; + + psurface = wl_resource_get_user_data(surf); + PEPPER_CHECK(psurface, return, "fail to get pepper_surface_t\n"); + + hs_surface = pepper_object_get_user_data((pepper_object_t *)psurface, surf); + PEPPER_CHECK(hs_surface, return, "fail to get headless_shell_surface\n"); + + hs_surface->skip_focus = PEPPER_TRUE; +} + +static void +tizen_policy_cb_focus_skip_unset(struct wl_client *client, struct wl_resource *resource, struct wl_resource *surf) +{ + pepper_surface_t *psurface; + headless_shell_surface_t *hs_surface; + + psurface = wl_resource_get_user_data(surf); + PEPPER_CHECK(psurface, return, "fail to get pepper_surface_t\n"); + + hs_surface = pepper_object_get_user_data((pepper_object_t *)psurface, surf); + PEPPER_CHECK(hs_surface, return, "fail to get headless_shell_surface\n"); + + hs_surface->skip_focus = PEPPER_FALSE; +} + +static void +tizen_policy_cb_role_set(struct wl_client *client, struct wl_resource *resource, struct wl_resource *surf, const char *role) +{ +} + +static void +tizen_policy_cb_type_set(struct wl_client *client, struct wl_resource *resource, struct wl_resource *surf, uint32_t type) +{ +} + +static void +tizen_policy_cb_conformant_set(struct wl_client *client, struct wl_resource *resource, struct wl_resource *surf) +{ +} + +static void +tizen_policy_cb_conformant_unset(struct wl_client *client, struct wl_resource *resource, struct wl_resource *surf) +{ +} + +static void +tizen_policy_cb_conformant_get(struct wl_client *client, struct wl_resource *resource, struct wl_resource *surf) +{ + tizen_policy_send_conformant(resource, surf, 0); +} + +static void +tizen_policy_cb_notilv_set(struct wl_client *client, struct wl_resource *resource, struct wl_resource *surf, int32_t lv) +{ +} + +static void +tizen_policy_cb_transient_for_set(struct wl_client *client, struct wl_resource *resource, uint32_t child_id, uint32_t parent_id) +{ +} + +static void +tizen_policy_cb_transient_for_unset(struct wl_client *client, struct wl_resource *resource, uint32_t child_id) +{ + tizen_policy_send_transient_for_done(resource, child_id); +} + +static void +tizen_policy_cb_win_scrmode_set(struct wl_client *client, struct wl_resource *resource, struct wl_resource *surf, uint32_t mode) +{ + tizen_policy_send_window_screen_mode_done + (resource, surf, mode, TIZEN_POLICY_ERROR_STATE_NONE); +} + +static void +tizen_policy_cb_subsurf_place_below_parent(struct wl_client *client, struct wl_resource *resource, struct wl_resource *subsurf) +{ +} + +static void +tizen_policy_cb_subsurf_stand_alone_set(struct wl_client *client, struct wl_resource *resource, struct wl_resource *subsurf) +{ +} + +static void +tizen_policy_cb_subsurface_get(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *surface, uint32_t parent_id) +{ + wl_client_post_implementation_error(client, "Headless server not support tizen_subsurface\n"); +} + +static void +tizen_policy_cb_opaque_state_set(struct wl_client *client, struct wl_resource *resource, struct wl_resource *surface, int32_t state) +{ +} + +static void +tizen_policy_cb_iconify(struct wl_client *client, struct wl_resource *resource, struct wl_resource *surf) +{ +} + +static void +tizen_policy_cb_uniconify(struct wl_client *client, struct wl_resource *resource, struct wl_resource *surf) +{ +} + +static void +tizen_policy_cb_aux_hint_add(struct wl_client *client, struct wl_resource *resource, struct wl_resource *surf, int32_t id, const char *name, const char *value) +{ +} + +static void +tizen_policy_cb_aux_hint_change(struct wl_client *client, struct wl_resource *resource, struct wl_resource *surf, int32_t id, const char *value) +{ +} + +static void +tizen_policy_cb_aux_hint_del(struct wl_client *client, struct wl_resource *resource, struct wl_resource *surf, int32_t id) +{ +} + +static void +tizen_policy_cb_supported_aux_hints_get(struct wl_client *client, struct wl_resource *resource, struct wl_resource *surf) +{ +} + +static void +tizen_policy_cb_background_state_set(struct wl_client *client, struct wl_resource *resource, uint32_t pid) +{ +} + +static void +tizen_policy_cb_background_state_unset(struct wl_client *client, struct wl_resource *resource, uint32_t pid) +{ +} + +static void +tizen_policy_cb_floating_mode_set(struct wl_client *client, struct wl_resource *resource, struct wl_resource *surf) +{ +} + +static void +tizen_policy_cb_floating_mode_unset(struct wl_client *client, struct wl_resource *resource, struct wl_resource *surf) +{ +} + +static void +tizen_policy_cb_stack_mode_set(struct wl_client *client, struct wl_resource *resource, struct wl_resource *surf, uint32_t mode) +{ +} + +static void +tizen_policy_cb_activate_above_by_res_id(struct wl_client *client, struct wl_resource *resource, uint32_t res_id, uint32_t above_res_id) +{ +} + +static void +tizen_policy_cb_subsurf_watcher_get(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *surface) +{ + wl_client_post_implementation_error(client, "Headless server not support tizen_subsurface\n"); +} + +static void +tizen_policy_cb_parent_set(struct wl_client *client, struct wl_resource *resource, struct wl_resource *child, struct wl_resource *parent) +{ +} + +static void +tizen_policy_cb_ack_conformant_region(struct wl_client *client, struct wl_resource *resource, struct wl_resource *surface, uint32_t serial) +{ +} + +static void +tizen_policy_cb_destroy(struct wl_client *client, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static void +tizen_policy_cb_has_video(struct wl_client *client, struct wl_resource *resource, struct wl_resource *surface, uint32_t has) +{ +} + +static const struct tizen_policy_interface tizen_policy_iface = +{ + tizen_policy_cb_vis_get, + tizen_policy_cb_pos_get, + tizen_policy_cb_activate, + tizen_policy_cb_activate_below_by_res_id, + tizen_policy_cb_raise, + tizen_policy_cb_lower, + tizen_policy_cb_lower_by_res_id, + tizen_policy_cb_focus_skip_set, + tizen_policy_cb_focus_skip_unset, + tizen_policy_cb_role_set, + tizen_policy_cb_type_set, + tizen_policy_cb_conformant_set, + tizen_policy_cb_conformant_unset, + tizen_policy_cb_conformant_get, + tizen_policy_cb_notilv_set, + tizen_policy_cb_transient_for_set, + tizen_policy_cb_transient_for_unset, + tizen_policy_cb_win_scrmode_set, + tizen_policy_cb_subsurf_place_below_parent, + tizen_policy_cb_subsurf_stand_alone_set, + tizen_policy_cb_subsurface_get, + tizen_policy_cb_opaque_state_set, + tizen_policy_cb_iconify, + tizen_policy_cb_uniconify, + tizen_policy_cb_aux_hint_add, + tizen_policy_cb_aux_hint_change, + tizen_policy_cb_aux_hint_del, + tizen_policy_cb_supported_aux_hints_get, + tizen_policy_cb_background_state_set, + tizen_policy_cb_background_state_unset, + tizen_policy_cb_floating_mode_set, + tizen_policy_cb_floating_mode_unset, + tizen_policy_cb_stack_mode_set, + tizen_policy_cb_activate_above_by_res_id, + tizen_policy_cb_subsurf_watcher_get, + tizen_policy_cb_parent_set, + tizen_policy_cb_ack_conformant_region, + tizen_policy_cb_destroy, + tizen_policy_cb_has_video, +}; + +static void +tizen_policy_cb_unbind(struct wl_resource *resource) +{ + headless_shell_t *shell = (headless_shell_t *)wl_resource_get_user_data(resource); + + shell->tizen_policy = NULL; +} + +static void +tizen_policy_cb_bind(struct wl_client *client, void *data, uint32_t ver, uint32_t id) +{ + headless_shell_t *shell = (headless_shell_t *)data; + struct wl_resource *resource; + + PEPPER_CHECK(shell, goto err, "data is NULL\n"); + + resource = wl_resource_create(client, + &tizen_policy_interface, + ver, + id); + PEPPER_CHECK(resource, goto err, "fail to create tizen_policy\n"); + + wl_resource_set_implementation(resource, + &tizen_policy_iface, + shell, + tizen_policy_cb_unbind); + return; + +err: + wl_client_post_no_memory(client); +} + +static pepper_bool_t +tizen_policy_init(headless_shell_t *shell) +{ + struct wl_display *display; + + display = pepper_compositor_get_display(shell->compositor); + + shell->tizen_policy = wl_global_create(display, &tizen_policy_interface, 7, shell, tizen_policy_cb_bind); + PEPPER_CHECK(shell->tizen_policy, return PEPPER_FALSE, "faile to create tizen_policy\n"); + + return PEPPER_TRUE; +} + +void +tizen_policy_deinit(headless_shell_t *shell) +{ + if (shell->tizen_policy) + wl_global_destroy(shell->tizen_policy); +} + +static void +headless_shell_send_visiblity(pepper_view_t *view, uint8_t visibility) +{ + pepper_surface_t *surface; + headless_shell_surface_t *hs_surface; + + if (view == NULL) return; + + surface = pepper_view_get_surface(view); + PEPPER_CHECK(surface, return, "[SHELL] Invalid object surface:%p\n", surface); + + hs_surface = pepper_object_get_user_data((pepper_object_t *)surface, pepper_surface_get_resource(surface)); + PEPPER_CHECK(hs_surface, return, "[SHELL] Invalid object headless_surface:%p\n", hs_surface); + + if (hs_surface->visibility == visibility) { + PEPPER_TRACE("[SHELL] Same Visibility hs_surface:%p, visibility:%d\n", hs_surface, visibility); + return; + } + + if (hs_surface->tizen_visibility) + tizen_visibility_send_notify(hs_surface->tizen_visibility, visibility); + + hs_surface->visibility = visibility; + PEPPER_TRACE("[SHELL] Set Visibility hs_surface:%p, visibility:%d\n", hs_surface, visibility); +} + +static void +headless_shell_cb_idle(void *data) +{ + headless_shell_t *hs_shell = (headless_shell_t *)data; + const pepper_list_t *list; + pepper_list_t *l; + pepper_view_t *view; + pepper_surface_t *surface; + headless_shell_surface_t *hs_surface; + + pepper_view_t *focus = NULL, *top = NULL, *top_visible = NULL; + + PEPPER_TRACE("[SHELL] Enter Idle\n"); + list = pepper_compositor_get_view_list(hs_shell->compositor); + + pepper_list_for_each_list(l, list) { + view = (pepper_view_t *)l->item; + PEPPER_CHECK(view, continue, "[SHELL] idle_cb, Invalid object view:%p\n", view); + + surface = pepper_view_get_surface(view); + PEPPER_CHECK(surface, continue, "[SHELL] idle_cb, Invalid object surface:%p\n", surface); + + hs_surface = pepper_object_get_user_data((pepper_object_t *)surface, pepper_surface_get_resource(surface)); + PEPPER_CHECK(hs_surface, continue, "[SHELL] idle_cb, Invalid object headless_surface:%p\n", hs_surface); + + if (!pepper_view_is_mapped(view)) { + headless_shell_send_visiblity(view, TIZEN_VISIBILITY_VISIBILITY_FULLY_OBSCURED); + continue; + } + + if (!top) + top = view; + + if (!focus && !hs_surface->skip_focus) + focus = view; + + if (!top_visible && pepper_surface_get_buffer(surface)) + top_visible = view; + + if (top && focus && top_visible) + break; + } + + if (top != hs_shell->top_mapped) { + const pepper_list_t *l; + pepper_list_t *ll; + + PEPPER_TRACE("[SHELL] IDLE : top-view change: %p to %p\n", hs_shell->top_mapped , top); + hs_shell->top_mapped = top; + headless_input_set_top_view(hs_shell->compositor, hs_shell->top_mapped); + headless_debug_set_top_view(hs_shell->compositor, hs_shell->top_mapped); + + /*Force update the output*/ + l = pepper_compositor_get_output_list(hs_shell->compositor); + pepper_list_for_each_list(ll, l) { + pepper_output_add_damage_region((pepper_output_t *)ll->item, NULL); + } + } + + if (focus != hs_shell->focus) { + PEPPER_TRACE("[SHELL] IDLE : focus-view change: %p to %p\n", hs_shell->focus , focus); + hs_shell->focus = focus; + headless_input_set_focus_view(hs_shell->compositor, hs_shell->focus); + headless_debug_set_focus_view(hs_shell->compositor, hs_shell->focus); + } + + if (top_visible != hs_shell->top_visible) { + PEPPER_TRACE("[SHELL] IDLE : visible-view change: %p to %p\n", hs_shell->top_visible, top_visible); + headless_shell_send_visiblity(hs_shell->top_visible, TIZEN_VISIBILITY_VISIBILITY_FULLY_OBSCURED); + headless_shell_send_visiblity(top_visible, TIZEN_VISIBILITY_VISIBILITY_UNOBSCURED); + hs_shell->top_visible = top_visible; + } + + hs_shell->cb_idle = NULL; +} + +static void +headless_shell_cb_surface_commit(pepper_event_listener_t *listener, + pepper_object_t *object, + uint32_t id, void *info, void *data) +{ + headless_shell_surface_t * hs_surface = (headless_shell_surface_t *)data; + + PEPPER_CHECK(((pepper_object_t *)hs_surface->surface == object), return, "Invalid object\n"); + + /*TODO + 1. Check the changes(buffer, map status...) + */ + if (IS_UPDATE(hs_surface->updates, UPDATE_SURFACE_TYPE)) { + if (hs_surface->surface_type != HEADLESS_SURFACE_NONE) + pepper_view_map(hs_surface->view); + else + pepper_view_unmap(hs_surface->view); + + PEPPER_TRACE("Surface type change. view:%p, type:%d, res:%p\n", hs_surface->view, hs_surface->surface_type, hs_surface->zxdg_surface); + } + + hs_surface->updates = 0; + + headless_shell_add_idle(hs_surface->hs_shell); +} + +static void +headless_shell_cb_surface_free(void *data) +{ + headless_shell_surface_t *surface = (headless_shell_surface_t *)data; + + PEPPER_TRACE("[SHELL] hs_surface free surface:%p, view:%p, zxdg_shell_surface:%p, zxdg_surface:%p\n", + surface->surface, surface->view, + surface->zxdg_shell_surface, surface->zxdg_surface); + + free(surface); +} + +static void +headless_shell_cb_surface_add(pepper_event_listener_t *listener, + pepper_object_t *object, + uint32_t id, void *info, void *data) +{ + headless_shell_surface_t *hs_surface; + pepper_surface_t *surface = (pepper_surface_t *)info; + + hs_surface = (headless_shell_surface_t*)calloc(sizeof(headless_shell_surface_t), 1); + PEPPER_CHECK(hs_surface, return, "fail to alloc for headless_shell_surface\n"); + + hs_surface->hs_shell = (headless_shell_t *)data; + hs_surface->surface = (pepper_surface_t *)surface; + hs_surface->visibility = TIZEN_VISIBILITY_VISIBILITY_FULLY_OBSCURED; + + pepper_object_set_user_data((pepper_object_t *)surface, + pepper_surface_get_resource(surface), + hs_surface, + headless_shell_cb_surface_free); + PEPPER_TRACE("[SHELL] surface_Add: pepper_surface:%, headless_shell:%p to p\n", surface, hs_surface); +} + +static void +headless_shell_cb_surface_remove(pepper_event_listener_t *listener, + pepper_object_t *object, + uint32_t id, void *info, void *data) +{ + headless_shell_surface_t *hs_surface; + pepper_surface_t *surface = (pepper_surface_t *)info; + + hs_surface = pepper_object_get_user_data((pepper_object_t *)surface, pepper_surface_get_resource(surface)); + PEPPER_TRACE("[SHELL] surface_remove: pepper_surface:%p, headless_shell:%p\n", object, hs_surface); + + if (hs_surface->zxdg_surface) { + wl_resource_set_user_data(hs_surface->zxdg_surface, NULL); + hs_surface->zxdg_surface = NULL; + } + + if (hs_surface->zxdg_shell_surface) { + wl_resource_set_user_data(hs_surface->zxdg_shell_surface, NULL); + hs_surface->zxdg_shell_surface = NULL; + } + + if (hs_surface->view) { + pepper_view_destroy(hs_surface->view); + hs_surface->view = NULL; + } + + SET_UPDATE(hs_surface->updates, UPDATE_SURFACE_TYPE); + headless_shell_add_idle(hs_surface->hs_shell); +} + +static void +headless_shell_cb_view_remove(pepper_event_listener_t *listener, + pepper_object_t *object, + uint32_t id, void *info, void *data) +{ + pepper_view_t *view = (pepper_view_t *)info; + headless_shell_t *shell = (headless_shell_t *)data; + + if (view == shell->top_mapped) + shell->top_mapped = NULL; + + if (view == shell->top_visible) + shell->top_visible = NULL; + + if (view == shell->focus) + shell->focus = NULL; + + headless_shell_add_idle(shell); +} + +static void +headless_shell_add_idle(headless_shell_t *shell) +{ + struct wl_event_loop *loop; + + if (!shell || shell->cb_idle) + return; + + loop = wl_display_get_event_loop(pepper_compositor_get_display(shell->compositor)); + PEPPER_CHECK(loop, return, "fail to get event loop\n"); + + shell->cb_idle = wl_event_loop_add_idle(loop, headless_shell_cb_idle, shell); + PEPPER_CHECK(shell->cb_idle, return, "fail to add idle\n"); +} + +static void +headless_shell_init_listeners(headless_shell_t *shell) +{ + shell->surface_add_listener = pepper_object_add_event_listener((pepper_object_t *)shell->compositor, + PEPPER_EVENT_COMPOSITOR_SURFACE_ADD, + 0, headless_shell_cb_surface_add, shell); + + shell->surface_remove_listener = pepper_object_add_event_listener((pepper_object_t *)shell->compositor, + PEPPER_EVENT_COMPOSITOR_SURFACE_REMOVE, + 0, headless_shell_cb_surface_remove, shell); + + shell->view_remove_listener = pepper_object_add_event_listener((pepper_object_t *)shell->compositor, + PEPPER_EVENT_COMPOSITOR_VIEW_REMOVE, + 0, headless_shell_cb_view_remove, shell); +} + +static void +headless_shell_deinit_listeners(headless_shell_t *shell) +{ + pepper_event_listener_remove(shell->surface_add_listener); + pepper_event_listener_remove(shell->surface_remove_listener); + pepper_event_listener_remove(shell->view_remove_listener); +} + +static void +headless_shell_destroy(headless_shell_t *shell) +{ + if (!shell) + return; + + if (shell->cb_idle) + wl_event_source_remove(shell->cb_idle); + + headless_shell_deinit_listeners(shell); + zxdg_deinit(shell); + tizen_policy_deinit(shell); +} + +void +headless_shell_deinit(pepper_compositor_t *compositor) +{ + headless_shell_t *shell; + + PEPPER_CHECK(compositor, return, "compositor is NULL\n"); + + shell = (headless_shell_t *)pepper_object_get_user_data((pepper_object_t *)compositor, &KEY_SHELL); + PEPPER_CHECK(shell, return, "shell is NULL\n"); + + headless_shell_destroy(shell); + + pepper_object_set_user_data((pepper_object_t *)shell->compositor, &KEY_SHELL, NULL, NULL); + free(shell); +} + +pepper_bool_t +headless_shell_init(pepper_compositor_t *compositor) +{ + headless_shell_t *shell; + + shell = (headless_shell_t*)calloc(sizeof(headless_shell_t), 1); + PEPPER_CHECK(shell, goto error, "fail to alloc for shell\n"); + shell->compositor = compositor; + + headless_shell_init_listeners(shell); + PEPPER_CHECK(zxdg_init(shell), goto error, "zxdg_init() failed\n"); + PEPPER_CHECK(tizen_policy_init(shell), goto error, "tizen_policy_init() failed\n"); + + pepper_object_set_user_data((pepper_object_t *)compositor, &KEY_SHELL, shell, NULL); + + return PEPPER_TRUE; + +error: + if (shell) { + headless_shell_destroy(shell); + free(shell); + } + return PEPPER_FALSE; +}