summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcel Hollerbach <mail@marcel-hollerbach.de>2018-12-09 10:56:48 +0100
committerMarcel Hollerbach <mail@marcel-hollerbach.de>2018-12-09 10:56:48 +0100
commite2c4b855ee213f53c2bf0b7b9565f5da3f98dfba (patch)
tree016286eb189a47fefb8fde9b543eeb268a1423c6
initial everything
-rw-r--r--AUTHORS1
-rw-r--r--COPYING25
-rw-r--r--ChangeLog0
-rw-r--r--NEWS0
-rw-r--r--README5
-rw-r--r--meson.build31
-rw-r--r--module.desktop7
-rw-r--r--src/e_mod_main.c313
-rw-r--r--src/keylogger.c98
-rw-r--r--src/meson.build16
10 files changed, 496 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..115ac3c
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1 @@
Marcel Hollerbach <marcel-hollerbach@t-online.de>
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..f60652c
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,25 @@
1Copyright notice for Enlightenment:
2
3Copyright (C) 2017 Marcel Hollerbach and various contributors (see AUTHORS)
4
5All rights reserved.
6
7Redistribution and use in source and binary forms, with or without
8modification, are permitted provided that the following conditions are met:
9
10 1. Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 2. Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in the
14 documentation and/or other materials provided with the distribution.
15
16THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
17INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
18FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
22OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
25EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/ChangeLog
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/NEWS
diff --git a/README b/README
new file mode 100644
index 0000000..95f930b
--- /dev/null
+++ b/README
@@ -0,0 +1,5 @@
1A Module to display your current keystrokes on the screen.
2
3Helpfull for presentating a live demo infront of a audience
4
5The visuals are displayed on the current active zone on your screen.
diff --git a/meson.build b/meson.build
new file mode 100644
index 0000000..60e9e7a
--- /dev/null
+++ b/meson.build
@@ -0,0 +1,31 @@
1project('presentator', 'c')
2
3e = dependency('enlightenment')
4
5dir_module_e = e.get_pkgconfig_variable('modules')
6release = e.get_pkgconfig_variable('release')
7host_os = host_machine.system()
8cc = meson.get_compiler('c')
9
10if host_os == 'linux'
11 if cc.has_header_symbol('features.h', '__UCLIBC__')
12 host_os = 'linux-uclibc'
13 elif cc.has_header_symbol('features.h', '__dietlibc__')
14 host_os = 'linux-dietlibc'
15 else
16 host_os = 'linux-gnu'
17 endif
18endif
19module_arch = '@0@-@1@-@2@'.format(host_os, host_machine.cpu_family(), release)
20
21install_mod_dir = join_paths(dir_module_e, meson.project_name())
22install_dir = join_paths(install_mod_dir, module_arch)
23
24config_h = configuration_data()
25config_h.set_quoted('MODULE_DIR', install_dir)
26
27subdir('src')
28
29configure_file(output: 'config.h', install: false, configuration: config_h)
30
31install_data('module.desktop', install_dir : install_mod_dir)
diff --git a/module.desktop b/module.desktop
new file mode 100644
index 0000000..754ab28
--- /dev/null
+++ b/module.desktop
@@ -0,0 +1,7 @@
1[Desktop Entry]
2Encoding=UTF-8
3Type=Link
4Name=Presentator
5Comment=Used to show what kind of keys are pressed and released
6Icon=preferences-desktop-wallpaper
7X-Enlightenment-ModuleType=settings
diff --git a/src/e_mod_main.c b/src/e_mod_main.c
new file mode 100644
index 0000000..036f08a
--- /dev/null
+++ b/src/e_mod_main.c
@@ -0,0 +1,313 @@
1#include "e.h"
2#include "../config.h"
3/* module setup */
4E_API E_Module_Api e_modapi =
5{
6 E_MODULE_API_VERSION,
7 "Presentator helper tool"
8};
9
10static Evas_Object *notify, *lb;
11static Eina_Hash *string_hash;
12static Ecore_Event_Handler *event_data, *event_error;
13static Ecore_Exe *keylogger;
14
15typedef struct {
16 unsigned int ref;
17 char *name;
18 Ecore_Timer *timer;
19} Entry;
20
21typedef struct {
22 float scale;
23} Presentator_Config;
24
25static E_Config_DD *dd_config;
26static Presentator_Config *config;
27
28static void _del_key(const char *name);
29
30static Eina_Bool
31_timed_del(void *data)
32{
33 Entry *e = data;
34 _del_key(e->name);
35 e->timer = NULL;
36 return 0;
37}
38
39static void
40_update_place(void)
41{
42 E_Zone *zone = e_zone_current_get();
43 Eina_Rectangle geom, notify_geom;
44 Evas_Coord minw = -1, minh = -1;
45 Edje_Object *edje;
46 Eina_Strbuf *buf;
47 Entry *e;
48
49 Eina_Iterator* iter = eina_hash_iterator_data_new(string_hash);
50
51 buf = eina_strbuf_new();
52
53 EINA_ITERATOR_FOREACH(iter, e)
54 {
55 if (!!eina_strbuf_length_get(buf))
56 eina_strbuf_append(buf, " + ");
57 eina_strbuf_append(buf, e->name);
58 }
59
60 eina_iterator_free(iter);
61 iter = NULL;
62
63 if (!eina_strbuf_length_get(buf))
64 evas_object_hide(notify);
65 else
66 evas_object_show(notify);
67
68 elm_object_text_set(lb, eina_strbuf_string_get(buf));
69
70 eina_strbuf_free(buf);
71
72 edje = elm_layout_edje_get(notify);
73 edje_object_size_min_calc(edje, &minw, &minh);
74
75 e_zone_useful_geometry_get(zone, &geom.x, &geom.y, &geom.w, &geom.h);
76 notify_geom.x = geom.x + (geom.w - minw) /2;
77 notify_geom.y = geom.y + geom.h - minh;
78 notify_geom.w = minw;
79 notify_geom.h = minh;
80
81 evas_object_geometry_set(notify, EINA_RECTANGLE_ARGS(&notify_geom));
82}
83
84static void
85_flush_config(void)
86{
87 e_config_domain_save("presentator", dd_config, config);
88 elm_object_scale_set(lb, config->scale);
89 _update_place();
90}
91
92static void
93_del_key(const char *name)
94{
95 Entry *e = eina_hash_find(string_hash, name);
96
97 //something fishy is going on sometimes, then we dont even have a entry
98 if (!e)
99 {
100 /* there was a release without a hit, usally happens when we are starting up */
101 return;
102 }
103
104 //deref the entry
105 e->ref --;
106
107 if (e->ref == 1)
108 {
109 if (e->timer) ecore_timer_del(e->timer);
110 e->timer = ecore_timer_add(0.5, _timed_del, e);
111 }
112
113 //free if null
114 if (e->ref == 0)
115 {
116
117 //if ref is 0 nuke out that entry
118 eina_hash_del_by_key(string_hash, name);
119 //we need to update the notify visuals depending on the state of the box
120 _update_place();
121 }
122}
123
124static void
125_add_key(const char *name)
126{
127 Entry *e = eina_hash_find(string_hash, name);
128
129 //first if we have already displayed that string do not display it again
130 if (e)
131 {
132 e->ref ++;
133 E_FREE_FUNC(e->timer, ecore_timer_del);
134 return;
135 }
136
137 //save as entry somewhere
138 e = calloc(1, sizeof(Entry));
139 e->ref = 2; //one for beeing used and one for the minimum time
140 e->name = strdup(name);
141 eina_hash_add(string_hash, name, e);
142
143 //update the place here
144 _update_place();
145}
146
147static void
148_entry_free(Entry *e)
149{
150 free(e->name);
151 free(e);
152}
153
154static Eina_Bool
155_msg_from_child_handler(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
156{
157 Ecore_Exe_Event_Data *dataFromProcess = (Ecore_Exe_Event_Data *)event;
158
159 for (int i = 0; dataFromProcess->lines[i].line; ++i)
160 {
161 char first = dataFromProcess->lines[i].line[0];
162 char *key = &dataFromProcess->lines[i].line[1];
163 if (first == ',')
164 {
165 _del_key(key);
166 }
167 else if (first == '.')
168 {
169 _add_key(key);
170 }
171 else
172 {
173 ERR("Unknown message from the keylogger");
174 }
175 }
176 return ECORE_CALLBACK_PASS_ON;
177}
178
179static Eina_Bool
180_msg_from_child_handler_error(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
181{
182 ERR("The keylogger passed out");
183
184 return ECORE_CALLBACK_PASS_ON;
185}
186
187
188static void
189_start_logging(void)
190{
191 string_hash = eina_hash_string_small_new((Eina_Free_Cb)_entry_free);
192
193 notify = elm_layout_add(e_comp->elm);
194 elm_layout_theme_set(notify, "notify", "bottom", "default");
195 evas_object_layer_set(notify, E_LAYER_POPUP);
196 evas_object_pass_events_set(notify, EINA_TRUE);
197
198 lb = elm_label_add(notify);
199 elm_object_content_set(notify, lb);
200 evas_object_show(lb);
201
202
203 _flush_config();
204 /* FIXME this is only on x11 need a different solution for wl */
205 {
206 keylogger = ecore_exe_pipe_run(MODULE_DIR"/keylogger", ECORE_EXE_PIPE_WRITE |
207 ECORE_EXE_PIPE_READ_LINE_BUFFERED |
208 ECORE_EXE_PIPE_READ, notify);
209 EINA_SAFETY_ON_NULL_RETURN(keylogger);
210 event_data = ecore_event_handler_add(ECORE_EXE_EVENT_DATA, _msg_from_child_handler, NULL);
211 event_error = ecore_event_handler_add(ECORE_EXE_EVENT_ERROR, _msg_from_child_handler_error, NULL);
212 }
213}
214
215static void
216_stop_logging(void)
217{
218 E_FREE_FUNC(keylogger, ecore_exe_kill);
219 E_FREE_FUNC(notify, evas_object_del);
220 E_FREE_FUNC(event_data, ecore_event_handler_del);
221 E_FREE_FUNC(event_error, ecore_event_handler_del);
222}
223
224static E_Action *start_stop;
225static Eina_Bool currently_running = EINA_FALSE;
226
227static void
228_toggle_run_mode(E_Object *e, const char *name)
229{
230 if (currently_running)
231 {
232 _stop_logging();
233 currently_running = EINA_FALSE;
234 }
235 else
236 {
237 _start_logging();
238 currently_running = EINA_TRUE;
239 }
240}
241
242static void
243_decrease_fontsize(E_Object *e, const char *name)
244{
245 float scale = elm_object_scale_get(lb);
246
247 config->scale = scale - 0.1;
248 _flush_config();
249}
250
251static void
252_increase_fontsize(E_Object *e, const char *name)
253{
254 float scale = elm_object_scale_get(lb);
255
256 config->scale = scale + 0.1;
257 _flush_config();
258}
259
260static void
261_config_init(void)
262{
263 E_Config_DD *result = E_CONFIG_DD_NEW("presentator", Presentator_Config);
264
265 E_CONFIG_VAL(result, Presentator_Config, scale, FLOAT);
266
267 dd_config = result;
268}
269
270E_API void *
271e_modapi_init(E_Module *m)
272{
273 ecore_init();
274 _config_init();
275
276 config = e_config_domain_load("presentator", dd_config);
277
278 if (!config)
279 config = calloc(1, sizeof(Presentator_Config));
280
281#define ACTION_ADD(_action, _cb, _title, _value, _params, _example, _editable) \
282 { \
283 const char *_name = _value; \
284 if ((_action = e_action_add(_name))) { \
285 _action->func.go = _cb; \
286 e_action_predef_name_set("Presentator", _title, _name, \
287 _params, _example, _editable); \
288 } \
289 }
290 ACTION_ADD(start_stop, _toggle_run_mode, "Start/Stop", "Toggle key highlight mode", NULL, NULL, EINA_FALSE);
291 ACTION_ADD(start_stop, _increase_fontsize, "Increase Fontsize", "Increase Fontsize", NULL, NULL, EINA_FALSE);
292 ACTION_ADD(start_stop, _decrease_fontsize, "Decrease Fontsize", "Decrease Fontsize", NULL, NULL, EINA_FALSE);
293#undef ACTION_ADD
294
295 return m;
296}
297
298E_API int
299e_modapi_shutdown(E_Module *m EINA_UNUSED)
300{
301 E_CONFIG_DD_FREE(dd_config);
302 free(config);
303 config = NULL;
304
305 ecore_shutdown();
306 return 1;
307}
308
309E_API int
310e_modapi_save(E_Module *m EINA_UNUSED)
311{
312 return 1;
313}
diff --git a/src/keylogger.c b/src/keylogger.c
new file mode 100644
index 0000000..525c657
--- /dev/null
+++ b/src/keylogger.c
@@ -0,0 +1,98 @@
1#include <stdio.h>
2#include <stdlib.h>
3#include <X11/Xlib.h>
4#include <X11/Xutil.h>
5#include <X11/extensions/XInput.h>
6
7#define INVALID_EVENT_TYPE -1
8
9static int key_press_type = INVALID_EVENT_TYPE;
10static int key_release_type = INVALID_EVENT_TYPE;
11
12static void
13_press(const char *symbol)
14{
15 fprintf(stdout, ".%s\n", symbol);
16 fflush(stdout);
17}
18
19static void
20_release(const char *symbol)
21{
22 fprintf(stdout, ",%s\n", symbol);
23 fflush(stdout);
24}
25
26int main(void) {
27 int number = 0;
28 int run = 1;
29 int default_screen;
30
31 Display * default_display;
32 Window root_window;
33 XEvent e;
34 XDeviceKeyEvent * key;
35 XEventClass event_list[20];
36
37 default_display = XOpenDisplay(getenv("DISPLAY"));
38 default_screen = DefaultScreen(default_display);
39 root_window = RootWindow(default_display, default_screen);
40
41 int num = 0;
42
43 XDeviceInfo *infos = XListInputDevices(default_display, &num);
44
45 //find all devices that are doing input
46 for (int i = 0; i < num; ++i) {
47 XDevice * device;
48
49 if (infos[i].id == IsXKeyboard) continue;
50 if (infos[i].id == IsXPointer) continue;
51 if (infos[i].id == IsXExtensionKeyboard) continue;
52 if (infos[i].id == IsXExtensionPointer) continue;
53
54
55 switch(infos[i].inputclassinfo->class) {
56 case KeyClass:
57 device = XOpenDevice(default_display, infos[i].id);
58
59 if(!device){
60 fprintf(stderr, "unable to open device\n");
61 return 0;
62 }
63
64 DeviceKeyPress(device, key_press_type, event_list[number]); number++;
65 DeviceKeyRelease(device, key_release_type, event_list[number]); number++;
66
67 break;
68 }
69 }
70
71 /* important */
72 if(XSelectExtensionEvent(default_display, root_window, event_list, number)) {
73 fprintf(stderr, "error selecting extended events\n");
74 return 0;
75 }
76
77 while(run) {
78 XNextEvent(default_display, &e);
79 key = (XDeviceKeyEvent *) &e;
80 const char *symbol;
81
82 symbol = XKeysymToString(XKeycodeToKeysym(default_display, key->keycode, 0));
83
84 if (key->type == key_press_type)
85 {
86 _press(symbol);
87 }
88 else if (key->type == key_release_type)
89 {
90 _release(symbol);
91 }
92 }
93
94 /* Close the connection to the X server */
95 XCloseDisplay(default_display);
96
97 return 0;
98}
diff --git a/src/meson.build b/src/meson.build
new file mode 100644
index 0000000..757896c
--- /dev/null
+++ b/src/meson.build
@@ -0,0 +1,16 @@
1e = dependency('enlightenment')
2x11 = dependency('x11')
3xi = dependency('xi')
4
5module = shared_module('module',
6 'e_mod_main.c',
7 dependencies : e,
8 install_dir: install_dir,
9 install: true,
10 name_prefix: '',
11 link_args: '-Wl,--unresolved-symbols=ignore-in-object-files')
12
13keylogger = executable('keylogger', 'keylogger.c',
14 dependencies : [x11,xi],
15 install_dir: install_dir,
16 install: true)