1 /*
2 This file is part of darktable,
3 Copyright (C) 2013-2020 darktable developers.
4
5 darktable is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 darktable is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with darktable. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "common/dbus.h"
20 #include "common/darktable.h"
21 #include "control/conf.h"
22 #include "control/control.h"
23 #include "common/file_location.h"
24
25 #ifdef USE_LUA
26 #include "lua/call.h"
27 #endif
28
29 /* Introspection data for the service we are exporting */
30 static const gchar introspection_xml[] = "<node>"
31 " <interface name='org.darktable.service.Remote'>"
32 " <method name='Quit' />"
33 " <method name='Open'>"
34 " <arg type='s' name='FileName' direction='in'/>"
35 " <arg type='i' name='id' direction='out' />"
36 " </method>"
37 #ifdef USE_LUA
38 " <method name='Lua'>"
39 " <arg type='s' name='Command' direction='in'/>"
40 " <arg type='s' name='Result' direction='out' />"
41 " </method>"
42 #endif
43 " <property type='s' name='DataDir' access='read'/>"
44 " <property type='s' name='ConfigDir' access='read'/>"
45 " <property type='b' name='LuaEnabled' access='read'/>"
46 " </interface>"
47 "</node>";
48
49
50 #ifdef USE_LUA
dbus_lua_call_finished(lua_State * L,int result,void * data)51 static void dbus_lua_call_finished(lua_State* L,int result,void* data)
52 {
53 GDBusMethodInvocation *invocation = (GDBusMethodInvocation*)data;
54 if(result == LUA_OK)
55 {
56 if(lua_isnil(L, -1))
57 {
58 g_dbus_method_invocation_return_value(invocation, g_variant_new("(s)", ""));
59 }
60 else
61 {
62 const char *checkres = luaL_checkstring(L, -1);
63 g_dbus_method_invocation_return_value(invocation, g_variant_new("(s)", checkres));
64 }
65 }
66 else
67 {
68 const char *msg = luaL_checkstring(L, -1);
69 g_dbus_method_invocation_return_dbus_error(invocation, "org.darktable.Error.LuaError", msg);
70 dt_lua_check_print_error(L,result);
71 }
72 }
73 #endif
74
_handle_method_call(GDBusConnection * connection,const gchar * sender,const gchar * object_path,const gchar * interface_name,const gchar * method_name,GVariant * parameters,GDBusMethodInvocation * invocation,gpointer user_data)75 static void _handle_method_call(GDBusConnection *connection, const gchar *sender, const gchar *object_path,
76 const gchar *interface_name, const gchar *method_name, GVariant *parameters,
77 GDBusMethodInvocation *invocation, gpointer user_data)
78 {
79 if(!g_strcmp0(method_name, "Quit"))
80 {
81 g_dbus_method_invocation_return_value(invocation, NULL);
82 dt_control_quit();
83 }
84 else if(!g_strcmp0(method_name, "Open"))
85 {
86 const gchar *filename;
87 g_variant_get(parameters, "(&s)", &filename);
88 int32_t id = dt_load_from_string(filename, TRUE, NULL);
89 g_dbus_method_invocation_return_value(invocation, g_variant_new("(i)", id));
90 }
91 #ifdef USE_LUA
92 else if(!g_strcmp0(method_name, "Lua"))
93 {
94 const gchar *command;
95 g_variant_get(parameters, "(&s)", &command);
96 dt_lua_async_call_string(command, 1,dbus_lua_call_finished,invocation);
97 // we don't finish the invocation, the async task will do this for us
98 }
99 #endif
100 }
101
102 // TODO: expose the conf? partly? completely?
103
_handle_get_property(GDBusConnection * connection,const gchar * sender,const gchar * object_path,const gchar * interface_name,const gchar * property_name,GError ** error,gpointer user_data)104 static GVariant *_handle_get_property(GDBusConnection *connection, const gchar *sender,
105 const gchar *object_path, const gchar *interface_name,
106 const gchar *property_name, GError **error, gpointer user_data)
107 {
108 GVariant *ret;
109
110 ret = NULL;
111 if(!g_strcmp0(property_name, "DataDir"))
112 {
113 gchar datadir[PATH_MAX] = { 0 };
114 dt_loc_get_datadir(datadir, sizeof(datadir));
115 ret = g_variant_new_string(datadir);
116 }
117 else if(!g_strcmp0(property_name, "ConfigDir"))
118 {
119 gchar configdir[PATH_MAX] = { 0 };
120 dt_loc_get_user_config_dir(configdir, sizeof(configdir));
121 ret = g_variant_new_string(configdir);
122 }
123 else if(!g_strcmp0(property_name, "LuaEnabled"))
124 {
125 #ifdef USE_LUA
126 ret = g_variant_new_boolean(TRUE);
127 #else
128 ret = g_variant_new_boolean(FALSE);
129 #endif
130 }
131 return ret;
132 }
133
134 // static gboolean
135 // _handle_set_property(GDBusConnection *connection,
136 // const gchar *sender,
137 // const gchar *object_path,
138 // const gchar *interface_name,
139 // const gchar *property_name,
140 // GVariant *value,
141 // GError **error,
142 // gpointer user_data)
143 // {
144 // return *error == NULL;
145 // }
146
147 static const GDBusInterfaceVTable interface_vtable = { _handle_method_call, _handle_get_property,
148 // _handle_set_property
149 NULL };
150
_on_bus_acquired(GDBusConnection * connection,const gchar * name,gpointer user_data)151 static void _on_bus_acquired(GDBusConnection *connection, const gchar *name, gpointer user_data)
152 {
153 dt_dbus_t *dbus = (dt_dbus_t *)user_data;
154
155 dbus->registration_id
156 = g_dbus_connection_register_object(connection, "/darktable", dbus->introspection_data->interfaces[0],
157 &interface_vtable, dbus, /* user_data */
158 NULL, /* user_data_free_func */
159 NULL); /* GError** */
160
161 if(dbus->registration_id == 0)
162 dbus->connected
163 = 0; // technically we are connected, but we are not exporting anything. or something like that
164 }
165
_on_name_acquired(GDBusConnection * connection,const gchar * name,gpointer user_data)166 static void _on_name_acquired(GDBusConnection *connection, const gchar *name, gpointer user_data)
167 {
168 dt_dbus_t *dbus = (dt_dbus_t *)user_data;
169 dbus->connected = 1;
170 }
171
_on_name_lost(GDBusConnection * connection,const gchar * name,gpointer user_data)172 static void _on_name_lost(GDBusConnection *connection, const gchar *name, gpointer user_data)
173 {
174 dt_dbus_t *dbus = (dt_dbus_t *)user_data;
175 dbus->connected = 0;
176 }
177
dt_dbus_init()178 struct dt_dbus_t *dt_dbus_init()
179 {
180 dt_dbus_t *dbus = (dt_dbus_t *)g_malloc0(sizeof(dt_dbus_t));
181 if(!dbus) return NULL;
182
183 dbus->introspection_data = g_dbus_node_info_new_for_xml(introspection_xml, NULL);
184
185 if(dbus->introspection_data == NULL) return dbus;
186
187 dbus->owner_id = g_bus_own_name(G_BUS_TYPE_SESSION,
188 "org.darktable.service", // FIXME
189 G_BUS_NAME_OWNER_FLAGS_NONE, _on_bus_acquired, _on_name_acquired,
190 _on_name_lost, dbus, NULL);
191
192 dbus->dbus_connection = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
193 g_object_set(G_OBJECT(dbus->dbus_connection), "exit-on-close", FALSE, (gchar *)0);
194
195 return dbus;
196 }
197
dt_dbus_destroy(const dt_dbus_t * dbus)198 void dt_dbus_destroy(const dt_dbus_t *dbus)
199 {
200 g_bus_unown_name(dbus->owner_id);
201 g_dbus_node_info_unref(dbus->introspection_data);
202 g_object_unref(G_OBJECT(dbus->dbus_connection));
203
204 g_free((dt_dbus_t *)dbus);
205 }
206
dt_dbus_connected(const dt_dbus_t * dbus)207 gboolean dt_dbus_connected(const dt_dbus_t *dbus)
208 {
209 return dbus->connected;
210 }
211
212 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
213 // vim: shiftwidth=2 expandtab tabstop=2 cindent
214 // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
215