From 41be266fb0bc323d9af2874c71e174be6fa82d8a Mon Sep 17 00:00:00 2001 From: taeyoung Date: Mon, 23 Nov 2015 13:44:26 +0900 Subject: [PATCH] poweroff: add poweroff popup - When power off button is longpressed, the popup can be expected to launch Change-Id: I727e5f538fbd7dd95ab27b425a5f82bbd3502b04 Signed-off-by: taeyoung --- CMakeLists.txt | 23 +- packaging/org.tizen.poweroff-syspopup.manifest | 8 + packaging/system-servant.spec | 60 ++++- src/common/core.c | 249 +++++++++++++++++++ src/common/popup-common-internal.h | 65 +++++ src/common/popup-common.c | 332 +++++++++++++++++++++++++ src/common/popup-common.h | 98 ++++++++ src/common/popup-ui-dpms-none.c | 31 +++ src/common/popup-ui-dpms-wayland.c | 31 +++ src/common/popup-ui-dpms-x.c | 71 ++++++ src/common/popup-ui-normal.c | 105 ++++++++ src/common/popup-ui.c | 169 +++++++++++++ src/common/popup-ui.h | 34 +++ src/launcher/CMakeLists.txt | 3 +- src/launcher/system-servant.efl | 28 --- src/poweroff/CMakeLists.txt | 64 +++++ src/poweroff/org.tizen.poweroff-syspopup.xml | 9 + src/poweroff/poweroff.c | 135 ++++++++++ 18 files changed, 1481 insertions(+), 34 deletions(-) create mode 100644 packaging/org.tizen.poweroff-syspopup.manifest create mode 100644 src/common/core.c create mode 100644 src/common/popup-common-internal.h create mode 100755 src/common/popup-common.c create mode 100644 src/common/popup-common.h create mode 100755 src/common/popup-ui-dpms-none.c create mode 100755 src/common/popup-ui-dpms-wayland.c create mode 100755 src/common/popup-ui-dpms-x.c create mode 100755 src/common/popup-ui-normal.c create mode 100755 src/common/popup-ui.c create mode 100644 src/common/popup-ui.h delete mode 100644 src/launcher/system-servant.efl create mode 100755 src/poweroff/CMakeLists.txt create mode 100755 src/poweroff/org.tizen.poweroff-syspopup.xml create mode 100755 src/poweroff/poweroff.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 2d9a876..660920d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,6 @@ CMAKE_MINIMUM_REQUIRED(VERSION 2.6) PROJECT(${PKGNAME} C) SET(PREFIX ${CMAKE_INSTALL_PREFIX}) -SET(SMACK_DIR "${TZ_SYS_SMACK}/accesses.d") SET(LICENSE_DIR "${TZ_SYS_SHARE}/license") SET(DBUS_SERVICE_DIR "${TZ_SYS_SHARE}/dbus-1/system-services") SET(LOCALE_DIR "${TZ_SYS_SHARE}/locale") @@ -13,5 +12,27 @@ ADD_DEFINITIONS("-DLOCALE_DIR=\"${LOCALE_DIR}\"") ADD_DEFINITIONS("-DLANG_DOMAIN=\"${LANG_DOMAIN}\"") ADD_DEFINITIONS("-DPROFILE=\"${PROFILE}\"") +SET(COMMON_DIR "${CMAKE_SOURCE_DIR}/src/common") +SET(COMMON_SRCS + ${COMMON_DIR}/core.c + ${COMMON_DIR}/popup-common.c + ${COMMON_DIR}/popup-ui.c + ${COMMON_DIR}/popup-ui-normal.c +) + +# DPMS +IF("${DPMS}" STREQUAL "x") +SET(COMMON_SRCS ${COMMON_SRCS} + ${COMMON_DIR}/popup-ui-dpms-x.c) +ELSEIF("${DPMS}" STREQUAL "wayland") +SET(COMMON_SRCS ${COMMON_SRCS} + ${COMMON_DIR}/popup-ui-dpms-wayland.c) +ELSE() +SET(COMMON_SRCS ${COMMON_SRCS} + ${COMMON_DIR}/popup-ui-dpms-none.c) +ENDIF() + + ADD_SUBDIRECTORY(src/launcher) ADD_SUBDIRECTORY(src/po) +ADD_SUBDIRECTORY(src/poweroff) diff --git a/packaging/org.tizen.poweroff-syspopup.manifest b/packaging/org.tizen.poweroff-syspopup.manifest new file mode 100644 index 0000000..5834fd5 --- /dev/null +++ b/packaging/org.tizen.poweroff-syspopup.manifest @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/packaging/system-servant.spec b/packaging/system-servant.spec index 0fb9987..2c64f9b 100755 --- a/packaging/system-servant.spec +++ b/packaging/system-servant.spec @@ -1,11 +1,22 @@ +%bcond_with x +%bcond_with wayland +%bcond_with emulator + %define PROFILE common +#Main applications +%define poweroff_popup on + %if "%{?tizen_profile_name}" == "mobile" %define PROFILE mobile +#Main applicaitons +%define poweroff_popup on %endif %if "%{?tizen_profile_name}" == "wearable" %define PROFILE wearable +#Main applicaitons +%define poweroff_popup on %endif %if "%{?tizen_profile_name}" == "tv" @@ -20,16 +31,25 @@ Group: System/Utilities License: Apache-2.0 Source0: %{name}-%{version}.tar.gz Source1: %{name}.manifest +Source1001: org.tizen.poweroff-syspopup.manifest BuildRequires: pkgconfig(elementary) BuildRequires: pkgconfig(appcore-efl) BuildRequires: pkgconfig(syspopup) BuildRequires: pkgconfig(syspopup-caller) BuildRequires: pkgconfig(notification) BuildRequires: pkgconfig(vconf) +BuildRequires: pkgconfig(glib-2.0) BuildRequires: pkgconfig(dlog) BuildRequires: pkgconfig(dbus-1) BuildRequires: pkgconfig(edbus) +BuildRequires: pkgconfig(deviced) +BuildRequires: pkgconfig(feedback) +BuildRequires: pkgconfig(efl-extension) BuildRequires: pkgconfig(libtzplatform-config) +%if %{with x} +BuildRequires: pkgconfig(ecore-x) +BuildRequires: pkgconfig(utilX) +%endif BuildRequires: cmake BuildRequires: gettext-devel @@ -37,20 +57,45 @@ BuildRequires: gettext-devel System applications such as app-launcher and service file for dbus activation +%if %{?poweroff_popup} == on +%package -n org.tizen.poweroff-syspopup +Summary: poweroff-popup application +Group: main +Requires: %{name} = %{version}-%{release} + +%description -n org.tizen.poweroff-syspopup +poweroff-popup application. +%endif + %prep %setup -q %build cp %{SOURCE1} . -cmake . \ +%if %{poweroff_popup} == on +cp %{SOURCE1001} . +%endif + +%define DPMS none +%if %{with x} +%define DPMS x +%endif +%if %{with wayland} +%define DPMS wayland +%endif + +%cmake . \ -DCMAKE_INSTALL_PREFIX=%{_prefix} \ -DPKGNAME=%{name} \ -DPROFILE=%{PROFILE} \ + -DDPMS=%{DPMS} \ -DTZ_SYS_RO_APP=%{TZ_SYS_RO_APP} \ -DTZ_SYS_RO_PACKAGES=%{TZ_SYS_RO_PACKAGES} \ -DTZ_SYS_SMACK=%{TZ_SYS_SMACK} \ - -DTZ_SYS_SHARE=%{TZ_SYS_SHARE} + -DTZ_SYS_SHARE=%{TZ_SYS_SHARE} \ + -DTZ_SYS_RO_APP=%{TZ_SYS_RO_APP} \ + -DPOWEROFF_POPUP=%{poweroff_popup} \ make %{?jobs:-j%jobs} @@ -64,7 +109,6 @@ rm -rf %{buildroot} %{_bindir}/sysapp-launcher %{_datadir}/license/sysapp-launcher %{_datadir}/dbus-1/system-services/org.tizen.system.popup.service -%{_sysconfdir}/smack/accesses.d/system-servant.efl #po files to support multi-languages %lang(ar) %{_datadir}/locale/ar/LC_MESSAGES/system-servant.mo @@ -127,3 +171,13 @@ rm -rf %{buildroot} %lang(zh_CN) %{_datadir}/locale/zh_CN/LC_MESSAGES/system-servant.mo %lang(zh_HK) %{_datadir}/locale/zh_HK/LC_MESSAGES/system-servant.mo %lang(zh_TW) %{_datadir}/locale/zh_TW/LC_MESSAGES/system-servant.mo + + +%if %{poweroff_popup} == on +%files -n org.tizen.poweroff-syspopup +%manifest org.tizen.poweroff-syspopup.manifest +%license LICENSE +%defattr(-,root,root,-) +%{TZ_SYS_RO_APP}/org.tizen.poweroff-syspopup/bin/poweroff-popup +%{TZ_SYS_SHARE}/packages/org.tizen.poweroff-syspopup.xml +%endif diff --git a/src/common/core.c b/src/common/core.c new file mode 100644 index 0000000..7009139 --- /dev/null +++ b/src/common/core.c @@ -0,0 +1,249 @@ +/* + * system-popup + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "popup-common.h" + +#define SYSPOPUP_CONTENT "_SYSPOPUP_CONTENT_" + +static syspopup_handler handler; +static GList *popup_list = NULL; + +GList *get_popup_list(void) +{ + return popup_list; +} + +void register_popup(const struct popup_ops *ops) +{ + struct object_ops *obj; + if (!ops) { + _E("Invalid parameter"); + return; + } + + obj = (struct object_ops *)calloc(1, sizeof(struct object_ops)); + if (!obj) { + _E("calloc() fialed"); + return; + } + + obj->ops = ops; + + popup_list = g_list_append(popup_list, obj); +} + +static void free_obj(gpointer data) +{ + struct object_ops *obj = data; + FREE(obj); +} + +void unregister_all_popup(void) +{ + if (popup_list) + g_list_free_full(popup_list, free_obj); +} + +void terminate_if_no_popup(void) +{ + GList *l; + struct object_ops *obj; + + for (l = popup_list ; l ; l = g_list_next(l)) { + obj = (struct object_ops *)(l->data); + if (obj->popup) { + _I("popup exists(%s)", obj->ops->name); + return; + } + } + popup_terminate(); +} + +static int load_popup_by_type(bundle *b) +{ + char *type; + GList *l; + struct object_ops *obj; + int ret; + + if (!b) + return -EINVAL; + + type = (char *)bundle_get_val(b, SYSPOPUP_CONTENT); + if (!type) { + _E("FAIL: bundle_get_val()"); + return -ENOMEM; + } + + for (l = popup_list ; l ; l = g_list_next(l)) { + obj = (struct object_ops *)(l->data); + if (!obj || !(obj->ops) || !(obj->ops->name) || !(obj->ops->show)) + continue; + if (strncmp (type, obj->ops->name, strlen(type) + 1)) + continue; + + if (obj->ops->skip && obj->ops->skip(b, obj->ops)) { + terminate_if_no_popup(); + return 0; + } + + if (obj->ops->change) + obj->ops->change(b, obj->ops); + + if (obj->ops->pre) + obj->ops->pre(b, obj->ops); + + ret = obj->ops->show(b, obj->ops); + + if (obj->ops->post) + obj->ops->post(b, obj->ops); + + return ret; + } + return -EINVAL; +} + +static int release_all_handlers(void *data) +{ + GList *l; + struct object_ops *obj; + static bool already = false; + + if (already) + return 0; + already = true; + + for (l = popup_list ; l ; l = g_list_next(l)) { + obj = (struct object_ops *)(l->data); + if (obj && obj->popup) { + if (obj->ops->terminate) + obj->ops->terminate(obj->ops); + release_evas_object(&(obj->popup)); + } + } + + unset_dbus_connection(); + remove_window(); + unregister_all_popup(); + return 0; +} + +static int terminate_by_syspopup(bundle *b, void *data) +{ + return release_all_handlers(data); +} + +static int app_create(void *data) +{ + int ret; + + handler.def_term_fn = terminate_by_syspopup; + handler.def_timeout_fn = NULL; + + /* create window */ + ret = create_window(PACKAGE); + if (ret < 0) + return ret; + + if (appcore_set_i18n(LANG_DOMAIN, LOCALE_DIR) != 0) + _E("FAIL: appcore_set_i18n()"); + + ret = set_dbus_connection(); + if (ret < 0) + _E("Failed to set dbus connection (%d)", ret); + + return 0; +} + +static int app_terminate(void *data) +{ + return release_all_handlers(data); +} + +static int app_pause(void *data) +{ + GList *l; + struct object_ops *obj; + + for (l = popup_list ; l ; l = g_list_next(l)) { + obj = (struct object_ops *)(l->data); + if (obj && obj->ops) { + if (obj->ops->term_pause == NULL + || obj->ops->term_pause(obj->ops)) { + unload_simple_popup(obj->ops); + } + } + } + terminate_if_no_popup(); + return 0; +} + +static int app_resume(void *data) +{ + return 0; +} + +static int app_reset(bundle *b, void *data) +{ + int ret; + Evas_Object *win; + + if (!b) { + ret = -EINVAL; + goto out; + } + + if (syspopup_has_popup(b)) { + syspopup_reset(b); + } else { + win = get_window(); + if (!win) + return -ENOMEM; + + ret = syspopup_create(b, &handler, win, NULL); + if (ret < 0) { + _E("FAIL: syspopup_create(): %d", ret); + goto out; + } + + /* change window priority to normal */ + reset_window_priority(WIN_PRIORITY_HIGH); + } + + ret = load_popup_by_type(b); + if (ret < 0) + goto out; + + return 0; + +out: + popup_terminate(); + return ret; +} + +int main(int argc, char *argv[]) +{ + struct appcore_ops ops = { + .create = app_create, + .terminate = app_terminate, + .pause = app_pause, + .resume = app_resume, + .reset = app_reset, + }; + + return appcore_efl_main(PACKAGE, &argc, &argv, &ops); +} diff --git a/src/common/popup-common-internal.h b/src/common/popup-common-internal.h new file mode 100644 index 0000000..bfec2ce --- /dev/null +++ b/src/common/popup-common-internal.h @@ -0,0 +1,65 @@ +/* + * system-popup + * + * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +#ifndef __COMMON_INTERNAL_H__ +#define __COMMON_INTERNAL_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum win_priority { + WIN_PRIORITY_LOW, + WIN_PRIORITY_NORMAL, + WIN_PRIORITY_HIGH, +}; + +struct popup_ops; + +struct object_ops { + const struct popup_ops *ops; + Evas_Object *popup; + bundle *b; +}; + +/* Common */ +GList *get_popup_list(void); +void popup_terminate(void); +void release_evas_object(Evas_Object **obj); +int get_object_by_ops(const struct popup_ops *ops, struct object_ops **obj); + +/* Window */ +int create_window(const char *name); +void remove_window(void); +Evas_Object *get_window(void); +int window_priority(int priority); + +#endif /* __COMMON_INTERNAL_H__ */ diff --git a/src/common/popup-common.c b/src/common/popup-common.c new file mode 100755 index 0000000..f3cfdae --- /dev/null +++ b/src/common/popup-common.c @@ -0,0 +1,332 @@ +/* + * system-popup + * + * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "popup-common.h" +#include + +#define RETRY_MAX 10 +#define DBUS_REPLY_TIMEOUT (-1) +#define BUF_MAX 64 + +static E_DBus_Connection *edbus_conn = NULL; + +/* Terminate popup */ +static Eina_Bool exit_idler_cb(void *data) +{ + elm_exit(); + return ECORE_CALLBACK_CANCEL; +} + +void popup_terminate(void) +{ + if (ecore_idler_add(exit_idler_cb, NULL)) + return; + + exit_idler_cb(NULL); +} + +/* Feedback */ +void play_feedback(int type, int pattern) +{ + int ret; + + ret = feedback_initialize(); + if (ret != FEEDBACK_ERROR_NONE) { + _E("Cannot initialize feedback"); + return; + } + + switch (type) { + case FEEDBACK_TYPE_SOUND: + case FEEDBACK_TYPE_VIBRATION: + ret = feedback_play_type(type, pattern); + break; + case FEEDBACK_TYPE_NONE: + ret = feedback_play(pattern); + break; + default: + _E("Play type is unknown"); + ret = 0; + } + if (ret != FEEDBACK_ERROR_NONE) + _E("Cannot play feedback: %d", pattern); + + ret = feedback_deinitialize(); + if (ret != FEEDBACK_ERROR_NONE) + _E("Cannot deinitialize feedback"); +} + +static void *thread_feedback(void* data) +{ + long type = (long)data; + if (type < 0) + type = FEEDBACK_PATTERN_LOWBATT; /* Warning */ + play_feedback(FEEDBACK_TYPE_NONE, type); + return NULL; +} + +static void *thread_display(void* data) +{ + if (display_change_state(LCD_NORMAL) < 0) + _E("FAIL: display_change_state()"); + return NULL; +} + +static void start_thread(void *(*operation)(void *), void *data) +{ + pthread_t th; + int ret; + + ret = pthread_create(&th, NULL, operation, data); + if (ret < 0) { + _E("Failed to create pthread(%d)", ret); + return; + } + pthread_detach(th); + return; +} + +void change_display_state(void) +{ + start_thread(thread_display, NULL); +} + +void notify_feedback(long pattern) +{ + start_thread(thread_feedback, (void *)pattern); +} + +/* dbus */ +int set_dbus_connection(void) +{ + int retry; + + if (edbus_conn) + return 0; + + retry = 0; + while (e_dbus_init() == 0) { + if (retry++ >= RETRY_MAX) + return -ENOMEM; + } + + edbus_conn = e_dbus_bus_get(DBUS_BUS_SYSTEM); + if (!edbus_conn) { + _E("Failed to get dbus bus"); + e_dbus_shutdown(); + return -ENOMEM; + } + + return 0; +} + +E_DBus_Connection *get_dbus_connection(void) +{ + return edbus_conn; +} + +void unset_dbus_connection(void) +{ + if (edbus_conn) { + e_dbus_connection_close(edbus_conn); + e_dbus_shutdown(); + edbus_conn = NULL; + } +} + +static int append_variant(DBusMessageIter *iter, const char *sig, char *param[]) +{ + char *ch; + int i; + int iValue; + + if (!sig || !param) + return 0; + + for (ch = (char*)sig, i = 0; *ch != '\0'; ++i, ++ch) { + switch (*ch) { + case 'i': + iValue = atoi(param[i]); + dbus_message_iter_append_basic(iter, DBUS_TYPE_INT32, &iValue); + break; + case 's': + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, ¶m[i]); + break; + default: + return -EINVAL; + } + } + return 0; +} + +int broadcast_dbus_signal(const char *path, const char *interface, + const char *name, const char *sig, char *param[]) +{ + E_DBus_Connection *conn = NULL; + DBusPendingCall *pc; + DBusMessageIter iter; + DBusMessage *msg; + int ret; + + if (!path || !interface || !name) + return -EINVAL; + + conn = get_dbus_connection(); + if (!conn) { + _E("Failed to get dbus connection"); + return -ENOMEM; + } + + msg = dbus_message_new_signal(path, interface, name); + if (!msg) { + _E("FAIL: dbus_message_new_signal()"); + return -ENOMEM; + } + + dbus_message_iter_init_append(msg, &iter); + ret = append_variant(&iter, sig, param); + if (ret < 0) { + _E("append_variant error(%d)", ret); + goto out; + } + + pc = e_dbus_message_send(conn, msg, NULL, -1, NULL); + if (!pc) { + _E("FAIL: e_dbus_message_send()"); + ret = -ECONNREFUSED; + goto out; + } + + ret = 0; + +out: + dbus_message_unref(msg); + return ret; +} + +int popup_dbus_method_sync(const char *dest, const char *path, + const char *interface, const char *method, + const char *sig, char *param[]) +{ + DBusConnection *conn = NULL; + DBusMessage *msg = NULL; + DBusMessageIter iter; + DBusMessage *reply = NULL; + DBusError err; + int ret, result; + + conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL); + if (!conn) { + _E("dbus_bus_get error"); + return -EPERM; + } + + msg = dbus_message_new_method_call(dest, path, interface, method); + if (!msg) { + _E("dbus_message_new_method_call(%s:%s-%s)", + path, interface, method); + ret = -EBADMSG; + goto out; + } + + dbus_message_iter_init_append(msg, &iter); + ret = append_variant(&iter, sig, param); + if (ret < 0) { + _E("append_variant error(%d)", ret); + goto out; + } + + dbus_error_init(&err); + + reply = dbus_connection_send_with_reply_and_block(conn, + msg, DBUS_REPLY_TIMEOUT, &err); + if (!reply) { + _E("dbus_connection_send error(%s:%s)", err.name, err.message); + dbus_error_free(&err); + ret = -ECOMM; + goto out; + } + + ret = dbus_message_get_args(reply, &err, + DBUS_TYPE_INT32, &result, DBUS_TYPE_INVALID); + if (!ret) { + _E("no message : [%s:%s]", err.name, err.message); + dbus_error_free(&err); + ret = -ENOMSG; + goto out; + } + + ret = result; + +out: + if (msg) + dbus_message_unref(msg); + if (reply) + dbus_message_unref(reply); + if (conn) + dbus_connection_unref(conn); + return ret; +} + +void unregister_dbus_signal_handler(E_DBus_Signal_Handler *handler) +{ + E_DBus_Connection *conn; + + if (!handler) + return; + + conn = get_dbus_connection(); + if (!conn) { + _E("Failed to get dbus connection"); + return; + } + + e_dbus_signal_handler_del(conn, handler); +} + +int register_dbus_signal_handler( + E_DBus_Signal_Handler **handler, + const char *path, + const char *iface, + const char *name, + void (*signal_cb)(void *data, DBusMessage *msg), + void *data) +{ + E_DBus_Connection *conn; + E_DBus_Signal_Handler *h; + + if (!handler || !path || !iface || !name || !signal_cb) + return -EINVAL; + + conn = get_dbus_connection(); + if (!conn) { + _E("Failed to get dbus connection"); + return -ENOMEM; + } + + h = e_dbus_signal_handler_add(conn, NULL, + path, iface, name, signal_cb, data); + if (!h) + return -ECONNREFUSED; + + *handler = h; + + return 0; +} + diff --git a/src/common/popup-common.h b/src/common/popup-common.h new file mode 100644 index 0000000..069f954 --- /dev/null +++ b/src/common/popup-common.h @@ -0,0 +1,98 @@ +/* + * system-popup + * + * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +#ifndef __POPUP_COMMON_H__ +#define __POPUP_COMMON_H__ + +#include "popup-common-internal.h" +#include "macro.h" + +struct popup_ops { + /* Name */ + char *name; + + /* Contents */ + char *title; + char *content; + int (*get_content)(const struct popup_ops *ops, + char *content, unsigned int len); + /* Left button */ + char *left_text; + void (*left)(const struct popup_ops *ops); + /* Right button */ + char *right_text; + void (*right)(const struct popup_ops *ops); + + /* Show popup */ + bool (*skip)(bundle *b, const struct popup_ops *ops); + int (*change) (bundle *b, const struct popup_ops *ops); + int (*pre) (bundle *b, const struct popup_ops *ops); + int (*show) (bundle *b, const struct popup_ops *ops); + int (*post) (bundle *b, const struct popup_ops *ops); + void (*terminate)(const struct popup_ops *ops); + + /* Term scenarios */ + bool (*term_pause)(const struct popup_ops *ops); + bool (*term_home)(const struct popup_ops *ops); +}; + +/* Common */ +void terminate_if_no_popup(void); + +/* Popup */ +void register_popup(const struct popup_ops *ops); +void update_popup(const struct popup_ops *old_ops, const struct popup_ops *new_ops); +void unload_simple_popup(const struct popup_ops *ops); +int load_simple_popup(bundle *b, const struct popup_ops *ops); +bool get_check_state(const struct popup_ops *ops); + +/* dbus */ +int set_dbus_connection(void); +void unset_dbus_connection(void); +E_DBus_Connection *get_dbus_connection(void); +int broadcast_dbus_signal( + const char *path, + const char *interface, + const char *name, + const char *sig, + char *param[]); +int popup_dbus_method_sync( + const char *dest, + const char *path, + const char *interface, + const char *method, + const char *sig, + char *param[]); +int register_dbus_signal_handler( + E_DBus_Signal_Handler **handler, + const char *path, + const char *iface, + const char *name, + void (*signal_cb)(void *data, DBusMessage *msg), + void *data); +void unregister_dbus_signal_handler(E_DBus_Signal_Handler *handler); + +/* feedback */ +void play_feedback(int type, int pattern); +void notify_feedback(long pattern); /* using thread */ + +void change_display_state(void); /* using thread */ + +#endif /* __POPUP_COMMON_H__ */ diff --git a/src/common/popup-ui-dpms-none.c b/src/common/popup-ui-dpms-none.c new file mode 100755 index 0000000..3ba1074 --- /dev/null +++ b/src/common/popup-ui-dpms-none.c @@ -0,0 +1,31 @@ +/* + * system-popup + * + * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "popup-ui.h" + +void resize_window(void) +{ + /* Do nothing */ +} + +int window_priority(int priority) +{ + /* Do nothing */ + return 0; +} diff --git a/src/common/popup-ui-dpms-wayland.c b/src/common/popup-ui-dpms-wayland.c new file mode 100755 index 0000000..29f284e --- /dev/null +++ b/src/common/popup-ui-dpms-wayland.c @@ -0,0 +1,31 @@ +/* + * system-popup + * + * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "popup-ui.h" + +void resize_window(void) +{ + /* TODO implement */ +} + +int window_priority(int priority) +{ + /* TODO implement */ + return 0; +} diff --git a/src/common/popup-ui-dpms-x.c b/src/common/popup-ui-dpms-x.c new file mode 100755 index 0000000..db578c1 --- /dev/null +++ b/src/common/popup-ui-dpms-x.c @@ -0,0 +1,71 @@ +/* + * system-popup + * + * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include "popup-ui.h" + +void resize_window(void) +{ + int w, h, len; + Evas_Object *win; + + win = get_window(); + if (win) { + ecore_x_window_size_get( + ecore_x_window_root_first_get(), &w, &h); + len = max(w,h); + evas_object_resize(win, len, len); + } +} + +int window_priority(int priority) +{ + Ecore_X_Window xwin; + Display *dpy; + Evas_Object *win; + + win = get_window(); + if (!win) + return -ENOMEM; + + switch (priority) { + case WIN_PRIORITY_LOW: + priority = UTILX_NOTIFICATION_LEVEL_LOW; + break; + case WIN_PRIORITY_NORMAL: + priority = UTILX_NOTIFICATION_LEVEL_NORMAL; + break; + case WIN_PRIORITY_HIGH: + priority = UTILX_NOTIFICATION_LEVEL_HIGH; + break; + default: + return -EINVAL; + } + if (priority < WIN_PRIORITY_LOW || priority > WIN_PRIORITY_HIGH) + return -EINVAL; + + xwin = elm_win_xwindow_get(win); + dpy = ecore_x_display_get(); + + ecore_x_netwm_window_type_set(xwin, ECORE_X_WINDOW_TYPE_NOTIFICATION); + utilx_set_system_notification_level(dpy, xwin, priority); + + return 0; +} diff --git a/src/common/popup-ui-normal.c b/src/common/popup-ui-normal.c new file mode 100755 index 0000000..83c0ad8 --- /dev/null +++ b/src/common/popup-ui-normal.c @@ -0,0 +1,105 @@ +/* + * system-popup + * + * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "popup-ui.h" + +#define BUF_MAX 512 + +int load_normal_popup(const struct popup_ops *ops) +{ + Evas_Object *lbtn; + Evas_Object *rbtn; + Evas_Object *popup; + Evas_Object *win; + char *text; + char content[BUF_MAX]; + struct object_ops *obj; + int ret; + + if (!ops) + return -EINVAL; + + ret = get_object_by_ops(ops, &obj); + if (ret < 0) { + _E("Failed to get object (%d)", ret); + return -EINVAL; + } + + win = get_window(); + if (!win) + return -ENOMEM; + + evas_object_show(win); + + popup = elm_popup_add(win); + if (!popup) + return -ENOMEM; + + elm_popup_align_set(popup, ELM_NOTIFY_ALIGN_FILL, 1.0); + evas_object_size_hint_weight_set(popup, + EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + + if (ops->title) + elm_object_part_text_set(popup, "title,text", _(ops->title)); + + if (ops->content) + snprintf(content, sizeof(content), "%s", _(ops->content)); + else if (ops->get_content) { + ret = ops->get_content(ops, content, sizeof(content)); + if (ret < 0) { + _E("Failed to get popup content"); + return ret; + } + } else + return -ENOENT; + + text = elm_entry_utf8_to_markup(content); + if (!text) + return -ENOMEM; + elm_object_text_set(popup, text); + free(text); + + if (ops->left_text) { + /* Left button */ + lbtn = elm_button_add(popup); + if (lbtn) { + elm_object_text_set(lbtn, _(ops->left_text)); + elm_object_style_set(lbtn, "bottom"); + elm_object_part_content_set(popup, "button1", lbtn); + evas_object_smart_callback_add(lbtn, "clicked", left_clicked, ops); + } + } + + if (ops->right_text) { + /* Right button */ + rbtn = elm_button_add(popup); + if (rbtn) { + elm_object_text_set(rbtn, _(ops->right_text)); + elm_object_style_set(rbtn, "bottom"); + elm_object_part_content_set(popup, "button2", rbtn); + evas_object_smart_callback_add(rbtn, "clicked", right_clicked, ops); + } + } + + evas_object_show(popup); + + obj->popup = popup; + + return 0; +} diff --git a/src/common/popup-ui.c b/src/common/popup-ui.c new file mode 100755 index 0000000..5407f39 --- /dev/null +++ b/src/common/popup-ui.c @@ -0,0 +1,169 @@ +/* + * system-popup + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "popup-ui.h" + +static Evas_Object *window = NULL; + +/* Common */ +void release_evas_object(Evas_Object **obj) +{ + if (!obj || !(*obj)) + return; + if (*obj == window) + return; + evas_object_del(*obj); + *obj = NULL; +} + +/* Windows */ +Evas_Object *get_window(void) +{ + return window; +} + +void remove_window(void) +{ + if (window) { + evas_object_del(window); + window = NULL; + } +} + +static void win_del(void *data, Evas_Object * obj, void *event) +{ + popup_terminate(); +} + +int create_window(const char *name) +{ + Evas_Object *eo; + + if (!name) + return -EINVAL; + + eo = elm_win_add(NULL, name, ELM_WIN_BASIC); + if (!eo) { + _E("FAIL: elm_win_add()"); + return -ENOMEM;; + } + + elm_win_title_set(eo, name); + elm_win_borderless_set(eo, EINA_TRUE); + elm_win_alpha_set(eo, EINA_TRUE); + elm_win_raise(eo); + evas_object_smart_callback_add(eo, "delete,request", win_del, NULL); + resize_window(); + + window = eo; + + return 0; +} + +int reset_window_priority(int priority) +{ + return window_priority(priority); +} + +/* Popups */ +static void default_button_action(const struct popup_ops *ops) +{ + if (ops) + unload_simple_popup(ops); + terminate_if_no_popup(); +} + +void left_clicked(void *data, Evas_Object * obj, void *event_info) +{ + const struct popup_ops *ops = data; + + if (ops && ops->left) { + ops->left(ops); + return; + } + + default_button_action(ops); +} + +void right_clicked(void *data, Evas_Object * obj, void *event_info) +{ + const struct popup_ops *ops = data; + + if (ops && ops->right) { + ops->right(ops); + return; + } + + default_button_action(ops); +} + +int get_object_by_ops(const struct popup_ops *ops, struct object_ops **obj) +{ + GList *popup_list, *l; + struct object_ops *o; + + if (!ops || !obj) + return -EINVAL; + + popup_list = get_popup_list(); + if (!popup_list) + return -ENOMEM; + + for (l = popup_list ; l ; l = g_list_next(l)) { + o = (struct object_ops *)(l->data); + if (!o || (o->ops != ops)) + continue; + *obj = o; + return 0; + } + + return -ENOENT; +} + +void unload_simple_popup(const struct popup_ops *ops) +{ + struct object_ops *obj; + int ret; + + ret = get_object_by_ops(ops, &obj); + if (ret < 0) + return; + + release_evas_object(&(obj->popup)); +} + +int load_simple_popup(bundle *b, const struct popup_ops *ops) +{ + struct object_ops *obj; + int ret; + + if (!ops) + return -EINVAL; + + unload_simple_popup(ops); + + ret = get_object_by_ops(ops, &obj); + if (ret < 0) { + _E("Failed to get object (%d)", ret); + return -ENOENT; + } + obj->b = bundle_dup(b); + + return load_normal_popup(ops); +} diff --git a/src/common/popup-ui.h b/src/common/popup-ui.h new file mode 100644 index 0000000..c503335 --- /dev/null +++ b/src/common/popup-ui.h @@ -0,0 +1,34 @@ +/* + * system-popup + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +#ifndef __POPUP_UI_H__ +#define __POPUP_UI_H__ + +#include "popup-common.h" + +void left_clicked(void *data, Evas_Object * obj, void *event_info); +void right_clicked(void *data, Evas_Object * obj, void *event_info); + +int load_normal_popup(const struct popup_ops *ops); + +void resize_window(void); +int reset_window_priority(int priority); + +#endif /* __POPU_UI_H__ */ diff --git a/src/launcher/CMakeLists.txt b/src/launcher/CMakeLists.txt index aa74097..1a0fe0d 100755 --- a/src/launcher/CMakeLists.txt +++ b/src/launcher/CMakeLists.txt @@ -7,7 +7,7 @@ SET(SRCS ${LAUNCHER_DIR}/launcher.c ${LAUNCHER_DIR}/popup.c ) -INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src/common) +INCLUDE_DIRECTORIES(${COMMON_DIR}) SET(PKG_MODULES edbus @@ -36,5 +36,4 @@ TARGET_LINK_LIBRARIES(${APPNAME} ${launcher_pkgs_LDFLAGS} "-ldl") INSTALL(TARGETS ${APPNAME} DESTINATION bin) INSTALL(FILES ${LAUNCHER_DIR}/org.tizen.system.popup.service DESTINATION ${DBUS_SERVICE_DIR}) -INSTALL(FILES ${LAUNCHER_DIR}/${PKGNAME}.efl DESTINATION ${SMACK_DIR}) INSTALL(FILES ${CMAKE_SOURCE_DIR}/LICENSE DESTINATION ${LICENSE_DIR} RENAME ${APPNAME}) diff --git a/src/launcher/system-servant.efl b/src/launcher/system-servant.efl deleted file mode 100644 index e487f95..0000000 --- a/src/launcher/system-servant.efl +++ /dev/null @@ -1,28 +0,0 @@ -system-apps aul::db r-x--- ------ -system-apps system::homedir rwxat- ------ -system-apps isf r-x--- ------ -system-apps dbus rwx--- ------ -system-apps xorg rw---- ------ -system-apps device::app_logging -w---- ------ -system-apps device::sys_logging -w---- ------ -system-apps tts-server rwx--- ------ -system-apps deviced::haptic rwx--- ------ -system-apps e17::notification rw---- ------ -org.tizen.indicator system-apps rwx--- ------ -org.tizen.quickpanel system-apps rwx--- ------ -dbus system-apps rw---- ------ -system-apps system::media rwxat- ------ -system-apps system::share rwxat- ------ -system-apps pulseaudio rwxat- ------ -e17 system-apps -w---- ------ -system-apps sys-assert::core rwxat- ------ -stest-service system-apps r-x--- ------ -system-apps stest-service -w---- ------ -system-apps privacy-manager::db r----l ------ -system-apps pkgmgr::db r----l ------ -system-apps ecore::lock rwxat- ------ -system-apps syspopup::db rw---- ------ -system-apps aul::launch r-x--- ------ -system-apps tizen::vconf::setting::admin rw---- ------ -system-apps tizen::vconf::public::admin rw---- ------ -system-apps tizen::vconf::public::r rw---- ------ diff --git a/src/poweroff/CMakeLists.txt b/src/poweroff/CMakeLists.txt new file mode 100755 index 0000000..e811d80 --- /dev/null +++ b/src/poweroff/CMakeLists.txt @@ -0,0 +1,64 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) + +IF("${POWEROFF_POPUP}" STREQUAL "off") + RETURN() +ENDIF("${POWEROFF_POPUP}" STREQUAL "off") + +SET(POWEROFF_SRCS ${COMMON_SRCS} poweroff.c) + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}) +INCLUDE_DIRECTORIES(${COMMON_DIR}) + +SET(VENDOR "tizen") +SET(PACKAGE "poweroff-syspopup") +SET(EXECNAME "poweroff-popup") +SET(PKGNAME "org.${VENDOR}.${PACKAGE}") +SET(POWEROFF_PREFIX "${TZ_SYS_RO_APP}/${PKGNAME}") +SET(BINDIR "${POWEROFF_PREFIX}/bin") +SET(RESDIR "${POWEROFF_PREFIX}/res") +SET(MANIFESTDIR "${TZ_SYS_SHARE}/packages") + +SET(PKG_MODULES + appcore-efl + elementary + dlog + deviced + evas + ecore + edbus + glib-2.0 + vconf + syspopup + syspopup-caller + feedback + efl-extension +) + +IF("${DPMS}" STREQUAL "x") +SET(PKG_MODULES ${PKG_MODULES} + ecore-x + utilX +) +ENDIF("${DPMS}" STREQUAL "x") + +INCLUDE(FindPkgConfig) +pkg_check_modules(poweroff_pkgs REQUIRED ${PKG_MODULES}) + +FOREACH(flag ${poweroff_pkgs_CFLAGS}) + SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") +ENDFOREACH(flag) + +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -O2 -g -Wall -fpie") +SET(CMAKE_C_FLAGS_DEBUG "-O0 -g -fpie") +SET(CMAKE_C_FLAGS_RELEASE "-O2 -fpie") + +ADD_DEFINITIONS("-DPREFIX=\"${CMAKE_INSTALL_PREFIX}\"") +ADD_DEFINITIONS("-DPACKAGE=\"${PACKAGE}\"") + +SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed -pie") + +ADD_EXECUTABLE(${EXECNAME} ${POWEROFF_SRCS}) +TARGET_LINK_LIBRARIES(${EXECNAME} ${poweroff_pkgs_LDFLAGS}) + +INSTALL(TARGETS ${EXECNAME} DESTINATION ${TZ_SYS_RO_APP}/${PKGNAME}/bin) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/src/poweroff/${PKGNAME}.xml DESTINATION ${MANIFESTDIR}) diff --git a/src/poweroff/org.tizen.poweroff-syspopup.xml b/src/poweroff/org.tizen.poweroff-syspopup.xml new file mode 100755 index 0000000..6cdba27 --- /dev/null +++ b/src/poweroff/org.tizen.poweroff-syspopup.xml @@ -0,0 +1,9 @@ + + + + Taeyoung Kim + System popup application (power off system popup) + + + + diff --git a/src/poweroff/poweroff.c b/src/poweroff/poweroff.c new file mode 100755 index 0000000..6f821c2 --- /dev/null +++ b/src/poweroff/poweroff.c @@ -0,0 +1,135 @@ +/* + * system-popup + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +#include "popup-common.h" + +#define SYSTEMD_STOP_POWER_OFF 4 + +static void remove_popup(const struct popup_ops *ops) +{ + static bool terminating = false; + + if (terminating) + return; + + terminating = true; + + unload_simple_popup(ops); + popup_terminate(); +} + +static void pm_state_changed(keynode_t *key, void *data) +{ + const struct popup_ops *ops = data; + + if (!key) + return; + + if (vconf_keynode_get_int(key) != VCONFKEY_PM_STATE_LCDOFF) + return; + + remove_popup(ops); +} + +static void event_back_key_up(void *data, Evas_Object *obj, void *event_info) +{ + const struct popup_ops *ops = data; + remove_popup(ops); +} + +static void register_handlers(const struct popup_ops *ops) +{ + Evas_Object *win; + + if (vconf_notify_key_changed( + VCONFKEY_PM_STATE, + pm_state_changed, + (void *)ops) != 0) + _E("Failed to register vconf"); + + win = get_window(); + if (win) + eext_object_event_callback_add(win, EEXT_CALLBACK_BACK, event_back_key_up, (void*)ops); +} + +static void unregister_handlers(const struct popup_ops *ops) +{ + Evas_Object *win; + + vconf_ignore_key_changed(VCONFKEY_PM_STATE, pm_state_changed); + + win = get_window(); + if (win) + eext_object_event_callback_del(win, EEXT_CALLBACK_BACK, event_back_key_up); +} + +static int poweroff_launch(bundle *b, const struct popup_ops *ops) +{ + register_handlers(ops); + return 0; +} + +static void poweroff_terminate(const struct popup_ops *ops) +{ + unregister_handlers(ops); +} + +static void poweroff_clicked(const struct popup_ops *ops) +{ + Evas_Object *rect, *win; + Evas_Coord w, h, size; + static int bPowerOff = 0; + + if (bPowerOff == 1) + return; + bPowerOff = 1; + + unload_simple_popup(ops); + + win = get_window(); + if (!win) + popup_terminate(); + + rect = evas_object_rectangle_add(evas_object_evas_get(win)); + evas_object_geometry_get(win, NULL, NULL, &w, &h); + size = max(w, h); + evas_object_resize(rect, size, size); + evas_object_color_set(rect, 0, 0, 0, 255); + evas_object_show(rect); + + if (vconf_set_int(VCONFKEY_SYSMAN_POWER_OFF_STATUS, SYSTEMD_STOP_POWER_OFF) != 0) + _E("Failed to request poweroff to deviced"); +} + +static const struct popup_ops poweroff_ops = { + .name = "poweroff", + .show = load_simple_popup, + .title = "IDS_ST_BODY_POWER_OFF", + .content = "IDS_TPLATFORM_BODY_POWER_OFF_THE_DEVICE_Q", + .left_text = "IDS_COM_SK_CANCEL", + .right_text = "IDS_HS_BUTTON_POWER_OFF_ABB2", + .right = poweroff_clicked, + .pre = poweroff_launch, + .terminate = poweroff_terminate, +}; + +static __attribute__ ((constructor)) void poweroff_register_popup(void) +{ + register_popup(&poweroff_ops); +} -- 2.7.4