aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorVincent Torri <vincent.torri@gmail.com>2012-12-02 22:35:45 +0000
committerVincent Torri <vincent.torri@gmail.com>2012-12-02 22:35:45 +0000
commit7d6010b12c47a20e492da808e3192c3f87dab619 (patch)
tree26c6fd189e046a76560c0bc740b85f4d767ae399 /src
parentemotion: update gitignore (diff)
downloadefl-7d6010b12c47a20e492da808e3192c3f87dab619.tar.gz
merge: add escape ecore, fix several bugs
SVN revision: 79995
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am37
-rw-r--r--src/Makefile_Ecore.am88
-rw-r--r--src/Makefile_Ecore_Cocoa.am37
-rw-r--r--src/Makefile_Ecore_Con.am70
-rw-r--r--src/Makefile_Ecore_DirectFB.am34
-rw-r--r--src/Makefile_Ecore_Evas.am160
-rw-r--r--src/Makefile_Ecore_FB.am42
-rw-r--r--src/Makefile_Ecore_File.am41
-rw-r--r--src/Makefile_Ecore_Imf.am186
-rw-r--r--src/Makefile_Ecore_Input.am70
-rw-r--r--src/Makefile_Ecore_Ipc.am37
-rw-r--r--src/Makefile_Ecore_Psl1ght.am38
-rw-r--r--src/Makefile_Ecore_SDL.am38
-rw-r--r--src/Makefile_Ecore_Wayland.am40
-rw-r--r--src/Makefile_Ecore_Win32.am51
-rw-r--r--src/Makefile_Ecore_WinCE.am41
-rw-r--r--src/Makefile_Ecore_X.am126
-rw-r--r--src/Makefile_Escape.am25
-rw-r--r--src/Makefile_Evas.am64
-rw-r--r--src/lib/ecore/Ecore.h2954
-rw-r--r--src/lib/ecore/Ecore_Getopt.h419
-rw-r--r--src/lib/ecore/ecore.c878
-rw-r--r--src/lib/ecore/ecore_alloc.c132
-rw-r--r--src/lib/ecore/ecore_anim.c633
-rw-r--r--src/lib/ecore/ecore_app.c98
-rw-r--r--src/lib/ecore/ecore_events.c648
-rw-r--r--src/lib/ecore/ecore_exe.c1913
-rw-r--r--src/lib/ecore/ecore_exe_ps3.c20
-rw-r--r--src/lib/ecore/ecore_exe_win32.c1055
-rw-r--r--src/lib/ecore/ecore_exe_wince.c21
-rw-r--r--src/lib/ecore/ecore_getopt.c1936
-rw-r--r--src/lib/ecore/ecore_glib.c346
-rw-r--r--src/lib/ecore/ecore_idle_enterer.c313
-rw-r--r--src/lib/ecore/ecore_idle_exiter.c264
-rw-r--r--src/lib/ecore/ecore_idler.c247
-rw-r--r--src/lib/ecore/ecore_job.c198
-rw-r--r--src/lib/ecore/ecore_main.c2111
-rw-r--r--src/lib/ecore/ecore_pipe.c748
-rw-r--r--src/lib/ecore/ecore_poll.c490
-rw-r--r--src/lib/ecore/ecore_private.h398
-rw-r--r--src/lib/ecore/ecore_signal.c594
-rw-r--r--src/lib/ecore/ecore_thread.c1509
-rw-r--r--src/lib/ecore/ecore_throttle.c104
-rw-r--r--src/lib/ecore/ecore_time.c184
-rw-r--r--src/lib/ecore/ecore_timer.c1015
-rw-r--r--src/lib/ecore_cocoa/Ecore_Cocoa.h147
-rw-r--r--src/lib/ecore_cocoa/Ecore_Cocoa_Keys.h285
-rw-r--r--src/lib/ecore_cocoa/ecore_cocoa.m283
-rw-r--r--src/lib/ecore_cocoa/ecore_cocoa_private.h11
-rw-r--r--src/lib/ecore_cocoa/ecore_cocoa_window.m163
-rw-r--r--src/lib/ecore_con/Ecore_Con.h1948
-rw-r--r--src/lib/ecore_con/Ecore_Con_Eet.h73
-rw-r--r--src/lib/ecore_con/dns.c7878
-rw-r--r--src/lib/ecore_con/dns.h1076
-rw-r--r--src/lib/ecore_con/ecore_con.c2596
-rw-r--r--src/lib/ecore_con/ecore_con_alloc.c101
-rw-r--r--src/lib/ecore_con/ecore_con_ares.c628
-rw-r--r--src/lib/ecore_con/ecore_con_dns.c344
-rw-r--r--src/lib/ecore_con/ecore_con_eet.c822
-rw-r--r--src/lib/ecore_con/ecore_con_info.c458
-rw-r--r--src/lib/ecore_con/ecore_con_local.c325
-rw-r--r--src/lib/ecore_con/ecore_con_local_win32.c754
-rw-r--r--src/lib/ecore_con/ecore_con_private.h390
-rw-r--r--src/lib/ecore_con/ecore_con_socks.c962
-rw-r--r--src/lib/ecore_con/ecore_con_ssl.c2141
-rw-r--r--src/lib/ecore_con/ecore_con_url.c1683
-rw-r--r--src/lib/ecore_directfb/Ecore_DirectFB.h181
-rw-r--r--src/lib/ecore_directfb/ecore_directfb.c758
-rw-r--r--src/lib/ecore_directfb/ecore_directfb_keys.h184
-rw-r--r--src/lib/ecore_directfb/ecore_directfb_private.h52
-rw-r--r--src/lib/ecore_evas/Ecore_Evas.h2256
-rw-r--r--src/lib/ecore_evas/ecore_evas.c2781
-rw-r--r--src/lib/ecore_evas/ecore_evas_buffer.c836
-rw-r--r--src/lib/ecore_evas/ecore_evas_cocoa.c584
-rw-r--r--src/lib/ecore_evas/ecore_evas_directfb.c606
-rw-r--r--src/lib/ecore_evas/ecore_evas_ews.c1469
-rw-r--r--src/lib/ecore_evas/ecore_evas_extn.c2266
-rw-r--r--src/lib/ecore_evas/ecore_evas_fb.c678
-rw-r--r--src/lib/ecore_evas/ecore_evas_private.h490
-rw-r--r--src/lib/ecore_evas/ecore_evas_psl1ght.c515
-rw-r--r--src/lib/ecore_evas/ecore_evas_sdl.c665
-rw-r--r--src/lib/ecore_evas/ecore_evas_util.c451
-rw-r--r--src/lib/ecore_evas/ecore_evas_wayland_common.c785
-rw-r--r--src/lib/ecore_evas/ecore_evas_wayland_egl.c434
-rw-r--r--src/lib/ecore_evas/ecore_evas_wayland_shm.c656
-rw-r--r--src/lib/ecore_evas/ecore_evas_win32.c1524
-rw-r--r--src/lib/ecore_evas/ecore_evas_wince.c67
-rw-r--r--src/lib/ecore_evas/ecore_evas_x.c3663
-rw-r--r--src/lib/ecore_fb/Ecore_Fb.h100
-rw-r--r--src/lib/ecore_fb/ecore_fb.c129
-rw-r--r--src/lib/ecore_fb/ecore_fb_kbd.c326
-rw-r--r--src/lib/ecore_fb/ecore_fb_keytable.h129
-rw-r--r--src/lib/ecore_fb/ecore_fb_li.c721
-rw-r--r--src/lib/ecore_fb/ecore_fb_private.h94
-rw-r--r--src/lib/ecore_fb/ecore_fb_ps2.c223
-rw-r--r--src/lib/ecore_fb/ecore_fb_ts.c362
-rw-r--r--src/lib/ecore_fb/ecore_fb_vt.c322
-rw-r--r--src/lib/ecore_file/Ecore_File.h190
-rw-r--r--src/lib/ecore_file/ecore_file.c1137
-rw-r--r--src/lib/ecore_file/ecore_file_download.c455
-rw-r--r--src/lib/ecore_file/ecore_file_monitor.c180
-rw-r--r--src/lib/ecore_file/ecore_file_monitor_inotify.c331
-rw-r--r--src/lib/ecore_file/ecore_file_monitor_poll.c340
-rw-r--r--src/lib/ecore_file/ecore_file_monitor_win32.c310
-rw-r--r--src/lib/ecore_file/ecore_file_path.c192
-rw-r--r--src/lib/ecore_file/ecore_file_private.h129
-rw-r--r--src/lib/ecore_imf/Ecore_IMF.h577
-rw-r--r--src/lib/ecore_imf/Ecore_IMF_Evas.h50
-rw-r--r--src/lib/ecore_imf/ecore_imf.c73
-rw-r--r--src/lib/ecore_imf/ecore_imf_context.c1900
-rw-r--r--src/lib/ecore_imf/ecore_imf_evas.c324
-rw-r--r--src/lib/ecore_imf/ecore_imf_module.c212
-rw-r--r--src/lib/ecore_imf/ecore_imf_private.h84
-rw-r--r--src/lib/ecore_input/Ecore_Input.h236
-rw-r--r--src/lib/ecore_input/Ecore_Input_Evas.h64
-rw-r--r--src/lib/ecore_input/ecore_input.c127
-rw-r--r--src/lib/ecore_input/ecore_input_compose.c61
-rw-r--r--src/lib/ecore_input/ecore_input_compose.h9895
-rw-r--r--src/lib/ecore_input/ecore_input_evas.c418
-rw-r--r--src/lib/ecore_input/ecore_input_evas_private.h37
-rw-r--r--src/lib/ecore_input/ecore_input_private.h37
-rw-r--r--src/lib/ecore_ipc/Ecore_Ipc.h328
-rw-r--r--src/lib/ecore_ipc/ecore_ipc.c1599
-rw-r--r--src/lib/ecore_ipc/ecore_ipc_private.h105
-rw-r--r--src/lib/ecore_sdl/Ecore_Sdl.h114
-rw-r--r--src/lib/ecore_sdl/Ecore_Sdl_Keys.h266
-rw-r--r--src/lib/ecore_sdl/ecore_sdl.c334
-rw-r--r--src/lib/ecore_sdl/ecore_sdl_private.h36
-rw-r--r--src/lib/ecore_wayland/Ecore_Wayland.h392
-rw-r--r--src/lib/ecore_wayland/ecore_wl.c497
-rw-r--r--src/lib/ecore_wayland/ecore_wl_dnd.c485
-rw-r--r--src/lib/ecore_wayland/ecore_wl_input.c1208
-rw-r--r--src/lib/ecore_wayland/ecore_wl_output.c87
-rw-r--r--src/lib/ecore_wayland/ecore_wl_private.h103
-rw-r--r--src/lib/ecore_wayland/ecore_wl_window.c707
-rw-r--r--src/lib/ecore_win32/Ecore_Win32.h526
-rw-r--r--src/lib/ecore_win32/ecore_win32.c841
-rw-r--r--src/lib/ecore_win32/ecore_win32_cursor.c305
-rwxr-xr-xsrc/lib/ecore_win32/ecore_win32_dnd.c221
-rw-r--r--src/lib/ecore_win32/ecore_win32_dnd_data_object.cpp209
-rw-r--r--src/lib/ecore_win32/ecore_win32_dnd_data_object.h49
-rw-r--r--src/lib/ecore_win32/ecore_win32_dnd_drop_source.cpp92
-rw-r--r--src/lib/ecore_win32/ecore_win32_dnd_drop_source.h36
-rw-r--r--src/lib/ecore_win32/ecore_win32_dnd_drop_target.cpp232
-rw-r--r--src/lib/ecore_win32/ecore_win32_dnd_drop_target.h47
-rw-r--r--src/lib/ecore_win32/ecore_win32_dnd_enumformatetc.cpp157
-rw-r--r--src/lib/ecore_win32/ecore_win32_dnd_enumformatetc.h50
-rw-r--r--src/lib/ecore_win32/ecore_win32_event.c1307
-rw-r--r--src/lib/ecore_win32/ecore_win32_private.h170
-rw-r--r--src/lib/ecore_win32/ecore_win32_window.c1418
-rw-r--r--src/lib/ecore_wince/Ecore_WinCE.h314
-rw-r--r--src/lib/ecore_wince/ecore_wince.c400
-rw-r--r--src/lib/ecore_wince/ecore_wince_event.c1123
-rw-r--r--src/lib/ecore_wince/ecore_wince_private.h85
-rw-r--r--src/lib/ecore_wince/ecore_wince_window.c827
-rw-r--r--src/lib/ecore_x/Ecore_X.h2407
-rw-r--r--src/lib/ecore_x/Ecore_X_Atoms.h292
-rw-r--r--src/lib/ecore_x/Ecore_X_Cursor.h87
-rw-r--r--src/lib/ecore_x/ecore_x_atoms_decl.h602
-rw-r--r--src/lib/ecore_x/xcb/ecore_xcb.c1583
-rw-r--r--src/lib/ecore_x/xcb/ecore_xcb_atoms.c149
-rw-r--r--src/lib/ecore_x/xcb/ecore_xcb_composite.c290
-rw-r--r--src/lib/ecore_x/xcb/ecore_xcb_cursor.c400
-rw-r--r--src/lib/ecore_x/xcb/ecore_xcb_damage.c155
-rw-r--r--src/lib/ecore_x/xcb/ecore_xcb_dnd.c688
-rw-r--r--src/lib/ecore_x/xcb/ecore_xcb_dpms.c320
-rw-r--r--src/lib/ecore_x/xcb/ecore_xcb_drawable.c123
-rw-r--r--src/lib/ecore_x/xcb/ecore_xcb_e.c1576
-rw-r--r--src/lib/ecore_x/xcb/ecore_xcb_error.c123
-rw-r--r--src/lib/ecore_x/xcb/ecore_xcb_events.c2824
-rw-r--r--src/lib/ecore_x/xcb/ecore_xcb_extensions.c148
-rw-r--r--src/lib/ecore_x/xcb/ecore_xcb_gc.c173
-rw-r--r--src/lib/ecore_x/xcb/ecore_xcb_gesture.c203
-rw-r--r--src/lib/ecore_x/xcb/ecore_xcb_icccm.c1569
-rw-r--r--src/lib/ecore_x/xcb/ecore_xcb_image.c738
-rw-r--r--src/lib/ecore_x/xcb/ecore_xcb_input.c274
-rw-r--r--src/lib/ecore_x/xcb/ecore_xcb_keymap.c491
-rw-r--r--src/lib/ecore_x/xcb/ecore_xcb_mwm.c104
-rw-r--r--src/lib/ecore_x/xcb/ecore_xcb_netwm.c1604
-rw-r--r--src/lib/ecore_x/xcb/ecore_xcb_pixmap.c128
-rw-r--r--src/lib/ecore_x/xcb/ecore_xcb_private.h468
-rw-r--r--src/lib/ecore_x/xcb/ecore_xcb_randr.c3807
-rw-r--r--src/lib/ecore_x/xcb/ecore_xcb_region.c159
-rw-r--r--src/lib/ecore_x/xcb/ecore_xcb_render.c225
-rw-r--r--src/lib/ecore_x/xcb/ecore_xcb_screensaver.c370
-rw-r--r--src/lib/ecore_x/xcb/ecore_xcb_selection.c1026
-rw-r--r--src/lib/ecore_x/xcb/ecore_xcb_shape.c50
-rw-r--r--src/lib/ecore_x/xcb/ecore_xcb_sync.c338
-rw-r--r--src/lib/ecore_x/xcb/ecore_xcb_textlist.c509
-rw-r--r--src/lib/ecore_x/xcb/ecore_xcb_vsync.c375
-rw-r--r--src/lib/ecore_x/xcb/ecore_xcb_window.c2238
-rw-r--r--src/lib/ecore_x/xcb/ecore_xcb_window_prop.c720
-rw-r--r--src/lib/ecore_x/xcb/ecore_xcb_window_shadow.c410
-rw-r--r--src/lib/ecore_x/xcb/ecore_xcb_window_shape.c790
-rw-r--r--src/lib/ecore_x/xcb/ecore_xcb_xdefaults.c116
-rw-r--r--src/lib/ecore_x/xcb/ecore_xcb_xfixes.c744
-rw-r--r--src/lib/ecore_x/xcb/ecore_xcb_xinerama.c139
-rw-r--r--src/lib/ecore_x/xcb/ecore_xcb_xtest.c215
-rw-r--r--src/lib/ecore_x/xlib/ecore_x.c2242
-rw-r--r--src/lib/ecore_x/xlib/ecore_x_atoms.c109
-rw-r--r--src/lib/ecore_x/xlib/ecore_x_composite.c176
-rw-r--r--src/lib/ecore_x/xlib/ecore_x_cursor.c246
-rw-r--r--src/lib/ecore_x/xlib/ecore_x_damage.c71
-rw-r--r--src/lib/ecore_x/xlib/ecore_x_dnd.c706
-rw-r--r--src/lib/ecore_x/xlib/ecore_x_dpms.c247
-rw-r--r--src/lib/ecore_x/xlib/ecore_x_drawable.c118
-rw-r--r--src/lib/ecore_x/xlib/ecore_x_e.c1670
-rw-r--r--src/lib/ecore_x/xlib/ecore_x_error.c126
-rw-r--r--src/lib/ecore_x/xlib/ecore_x_events.c2523
-rw-r--r--src/lib/ecore_x/xlib/ecore_x_fixes.c365
-rw-r--r--src/lib/ecore_x/xlib/ecore_x_gc.c171
-rw-r--r--src/lib/ecore_x/xlib/ecore_x_gesture.c137
-rw-r--r--src/lib/ecore_x/xlib/ecore_x_icccm.c1214
-rw-r--r--src/lib/ecore_x/xlib/ecore_x_image.c626
-rw-r--r--src/lib/ecore_x/xlib/ecore_x_mwm.c106
-rw-r--r--src/lib/ecore_x/xlib/ecore_x_netwm.c2083
-rw-r--r--src/lib/ecore_x/xlib/ecore_x_pixmap.c121
-rw-r--r--src/lib/ecore_x/xlib/ecore_x_private.h379
-rw-r--r--src/lib/ecore_x/xlib/ecore_x_randr.c103
-rw-r--r--src/lib/ecore_x/xlib/ecore_x_randr.h7
-rw-r--r--src/lib/ecore_x/xlib/ecore_x_randr_11.c334
-rw-r--r--src/lib/ecore_x/xlib/ecore_x_randr_12.c2438
-rw-r--r--src/lib/ecore_x/xlib/ecore_x_randr_12_edid.c463
-rw-r--r--src/lib/ecore_x/xlib/ecore_x_randr_13.c68
-rw-r--r--src/lib/ecore_x/xlib/ecore_x_region.c158
-rw-r--r--src/lib/ecore_x/xlib/ecore_x_screensaver.c204
-rw-r--r--src/lib/ecore_x/xlib/ecore_x_selection.c1021
-rw-r--r--src/lib/ecore_x/xlib/ecore_x_sync.c159
-rw-r--r--src/lib/ecore_x/xlib/ecore_x_test.c167
-rw-r--r--src/lib/ecore_x/xlib/ecore_x_vsync.c351
-rw-r--r--src/lib/ecore_x/xlib/ecore_x_window.c1727
-rw-r--r--src/lib/ecore_x/xlib/ecore_x_window_prop.c760
-rw-r--r--src/lib/ecore_x/xlib/ecore_x_window_shape.c658
-rw-r--r--src/lib/ecore_x/xlib/ecore_x_xi2.c336
-rw-r--r--src/lib/ecore_x/xlib/ecore_x_xinerama.c91
-rw-r--r--src/lib/escape/Escape.h52
-rw-r--r--src/lib/escape/escape_libgen.c90
-rw-r--r--src/lib/escape/escape_libgen.h32
-rw-r--r--src/lib/escape/escape_mman.c67
-rw-r--r--src/lib/escape/escape_unistd.c186
-rw-r--r--src/lib/escape/escape_unistd.h107
-rw-r--r--src/lib/escape/sys/mman.h179
-rw-r--r--src/modules/ecore/immodules/ibus/ibus_imcontext.c822
-rw-r--r--src/modules/ecore/immodules/ibus/ibus_imcontext.h36
-rw-r--r--src/modules/ecore/immodules/ibus/ibus_module.c109
-rw-r--r--src/modules/ecore/immodules/scim/scim_imcontext.cpp2900
-rw-r--r--src/modules/ecore/immodules/scim/scim_imcontext.h42
-rw-r--r--src/modules/ecore/immodules/scim/scim_module.cpp104
-rw-r--r--src/modules/ecore/immodules/xim/ecore_imf_xim.c1555
-rw-r--r--src/utils/ecore/makekeys.c326
-rwxr-xr-xsrc/utils/ecore/mkks.sh10
251 files changed, 158641 insertions, 64 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 68b83fc598..e9c5a9bdbe 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -13,10 +13,47 @@ if HAVE_WINDOWS
include Makefile_Evil.am
endif
+if HAVE_PS3
+include Makefile_Escape.am
+endif
+
include Makefile_Eina.am
include Makefile_Eo.am
include Makefile_Eet.am
include Makefile_Evas.am
+include Makefile_Ecore.am
+include Makefile_Ecore_Con.am
+include Makefile_Ecore_Ipc.am
+include Makefile_Ecore_File.am
+include Makefile_Ecore_Input.am
+if HAVE_ECORE_COCOA
+include Makefile_Ecore_Cocoa.am
+endif
+if HAVE_ECORE_DIRECTFB
+include Makefile_Ecore_DirectFB.am
+endif
+if HAVE_ECORE_FB
+include Makefile_Ecore_FB.am
+endif
+if HAVE_PS3
+include Makefile_Ecore_Psl1ght.am
+endif
+if HAVE_ECORE_SDL
+include Makefile_Ecore_SDL.am
+endif
+if HAVE_ECORE_WAYLAND
+include Makefile_Ecore_Wayland.am
+endif
+if HAVE_WIN32
+include Makefile_Ecore_Win32.am
+endif
+if HAVE_WINCE
+include Makefile_Ecore_WinCE.am
+endif
+if HAVE_ECORE_X
+include Makefile_Ecore_X.am
+endif
+include Makefile_Ecore_Imf.am
include Makefile_Embryo.am
.PHONY: benchmark examples
diff --git a/src/Makefile_Ecore.am b/src/Makefile_Ecore.am
new file mode 100644
index 0000000000..eabe603558
--- /dev/null
+++ b/src/Makefile_Ecore.am
@@ -0,0 +1,88 @@
+
+### Library
+
+lib_LTLIBRARIES += lib/ecore/libecore.la
+
+installed_ecoremainheadersdir = $(includedir)/ecore-@VMAJ@
+dist_installed_ecoremainheaders_DATA = \
+lib/ecore/Ecore.h \
+lib/ecore/Ecore_Getopt.h
+
+lib_ecore_libecore_la_SOURCES = \
+lib/ecore/ecore.c \
+lib/ecore/ecore_alloc.c \
+lib/ecore/ecore_anim.c \
+lib/ecore/ecore_app.c \
+lib/ecore/ecore_events.c \
+lib/ecore/ecore_getopt.c \
+lib/ecore/ecore_glib.c \
+lib/ecore/ecore_idle_enterer.c \
+lib/ecore/ecore_idle_exiter.c \
+lib/ecore/ecore_idler.c \
+lib/ecore/ecore_job.c \
+lib/ecore/ecore_main.c \
+lib/ecore/ecore_pipe.c \
+lib/ecore/ecore_poll.c \
+lib/ecore/ecore_time.c \
+lib/ecore/ecore_timer.c \
+lib/ecore/ecore_thread.c \
+lib/ecore/ecore_throttle.c \
+lib/ecore/ecore_private.h
+
+if HAVE_WIN32
+lib_ecore_libecore_la_SOURCES += lib/ecore/ecore_exe_win32.c
+else
+if HAVE_WINCE
+lib_ecore_libecore_la_SOURCES += lib/ecore/ecore_exe_wince.c
+else
+#if ECORE_HAVE_PS3
+#libecore_la_SOURCES += ecore_exe_ps3.c
+#else
+#if ECORE_HAVE_EXOTIC
+#libecore_la_SOURCES +=
+#else
+lib_ecore_libecore_la_SOURCES += lib/ecore/ecore_signal.c lib/ecore/ecore_exe.c
+#endif
+#endif
+endif
+endif
+
+lib_ecore_libecore_la_CPPFLAGS = \
+-I$(top_srcdir)/src/lib/eina \
+-I$(top_srcdir)/src/lib/eo \
+-I$(top_srcdir)/src/lib/ecore \
+-I$(top_builddir)/src/lib/eina \
+-I$(top_builddir)/src/lib/eo \
+-I$(top_builddir)/src/lib/ecore \
+-DEFL_ECORE_BUILD \
+@EFL_CFLAGS@ \
+@EFL_COV_CFLAGS@ \
+@ECORE_CFLAGS@
+
+if HAVE_WINDOWS
+lib_ecore_libecore_la_CPPFLAGS += \
+-I$(top_srcdir)/src/lib/evil \
+-I$(top_builddir)/src/lib/evil
+endif
+
+if HAVE_PS3
+lib_ecore_libecore_la_CPPFLAGS += \
+-I$(top_srcdir)/src/lib/escape \
+-I$(top_builddir)/src/lib/escape
+endif
+
+lib_ecore_libecore_la_LIBADD = \
+lib/eo/libeo.la \
+lib/eina/libeina.la
+
+if HAVE_WINDOWS
+lib_ecore_libecore_la_LIBADD += lib/evil/libevil.la
+endif
+
+if HAVE_PS3
+lib_ecore_libecore_la_LIBADD += lib/escape/libescape.la
+endif
+
+lib_ecore_libecore_la_LIBADD += @ECORE_LIBS@ @EFL_COV_LIBS@ -lm
+
+lib_ecore_libecore_la_LDFLAGS = -no-undefined -version-info @version_info@ @release_info@
diff --git a/src/Makefile_Ecore_Cocoa.am b/src/Makefile_Ecore_Cocoa.am
new file mode 100644
index 0000000000..f7f82918a1
--- /dev/null
+++ b/src/Makefile_Ecore_Cocoa.am
@@ -0,0 +1,37 @@
+
+### Library
+
+lib_LTLIBRARIES += lib/ecore_cocoa/libecore_cocoa.la
+
+installed_ecorecocoamainheadersdir = $(includedir)/ecore-@VMAJ@
+dist_installed_ecorecocoamainheaders_DATA = \
+lib/ecore_cocoa/Ecore_Cocoa.h \
+lib/ecore_cocoa/Ecore_Cocoa_Keys.h
+
+lib_ecore_cocoa_libecore_cocoa_la_SOURCES = \
+lib/ecore_cocoa/ecore_cocoa.m \
+lib/ecore_cocoa/ecore_cocoa_window.m \
+lib/ecore_cocoa/ecore_cocoa_private.h
+
+lib_ecore_cocoa_libecore_cocoa_la_CPPFLAGS = \
+-I$(top_srcdir)/src/lib/eina \
+-I$(top_srcdir)/src/lib/eo \
+-I$(top_srcdir)/src/lib/ecore \
+-I$(top_srcdir)/src/lib/ecore_input \
+-I$(top_srcdir)/src/lib/ecore_cocoa \
+-I$(top_builddir)/src/lib/eina \
+-I$(top_builddir)/src/lib/eo \
+-I$(top_builddir)/src/lib/ecore \
+-I$(top_builddir)/src/lib/ecore_input \
+-I$(top_builddir)/src/lib/ecore_cocoa \
+@EFL_CFLAGS@ \
+@EFL_COV_CFLAGS@
+
+lib_ecore_cocoa_libecore_cocoa_la_LIBADD = \
+lib/ecore_input/libecore_input.la \
+lib/ecore/libecore.la \
+lib/eo/libeo.la \
+lib/eina/libeina.la \
+@ECORE_COCOA_LIBS@
+
+lib_ecore_cocoa_libecore_cocoa_la_LDFLAGS = -no-undefined -version-info @version_info@ @release_info@
diff --git a/src/Makefile_Ecore_Con.am b/src/Makefile_Ecore_Con.am
new file mode 100644
index 0000000000..f2e6ee8537
--- /dev/null
+++ b/src/Makefile_Ecore_Con.am
@@ -0,0 +1,70 @@
+
+### Library
+
+lib_LTLIBRARIES += lib/ecore_con/libecore_con.la
+
+installed_ecoreconmainheadersdir = $(includedir)/ecore-@VMAJ@
+dist_installed_ecoreconmainheaders_DATA = \
+lib/ecore_con/Ecore_Con.h \
+lib/ecore_con/Ecore_Con_Eet.h
+
+lib_ecore_con_libecore_con_la_SOURCES = \
+lib/ecore_con/ecore_con_alloc.c \
+lib/ecore_con/ecore_con.c \
+lib/ecore_con/ecore_con_eet.c \
+lib/ecore_con/ecore_con_socks.c \
+lib/ecore_con/ecore_con_ssl.c \
+lib/ecore_con/ecore_con_url.c \
+lib/ecore_con/ecore_con_private.h
+
+if HAVE_WINDOWS
+lib_ecore_con_libecore_con_la_SOURCES += lib/ecore_con/ecore_con_local_win32.c
+else
+lib_ecore_con_libecore_con_la_SOURCES += lib/ecore_con/ecore_con_local.c
+endif
+
+if HAVE_CARES
+lib_ecore_con_libecore_con_la_SOURCES += lib/ecore_con/ecore_con_ares.c
+else
+if HAVE_IPV6
+lib_ecore_con_libecore_con_la_SOURCES += lib/ecore_con/ecore_con_dns.c lib/ecore_con/dns.c lib/ecore_con/dns.h
+else
+lib_ecore_con_libecore_con_la_SOURCES += lib/ecore_con/ecore_con_info.c
+endif
+endif
+
+lib_ecore_con_libecore_con_la_CPPFLAGS = \
+-I$(top_srcdir)/src/lib/eina \
+-I$(top_srcdir)/src/lib/eo \
+-I$(top_srcdir)/src/lib/eet \
+-I$(top_srcdir)/src/lib/ecore \
+-I$(top_srcdir)/src/lib/ecore_con \
+-I$(top_builddir)/src/lib/eina \
+-I$(top_builddir)/src/lib/eo \
+-I$(top_builddir)/src/lib/eet \
+-I$(top_builddir)/src/lib/ecore \
+-I$(top_builddir)/src/lib/ecore_con \
+-DEFL_ECORE_CON_BUILD \
+@EFL_CFLAGS@ \
+@EFL_COV_CFLAGS@ \
+@ECORE_CON_CFLAGS@
+
+if HAVE_WINDOWS
+lib_ecore_con_libecore_con_la_CPPFLAGS += \
+-I$(top_srcdir)/src/lib/evil \
+-I$(top_builddir)/src/lib/evil
+endif
+
+lib_ecore_con_libecore_con_la_LIBADD = \
+lib/ecore/libecore.la \
+lib/eet/libeet.la \
+lib/eo/libeo.la \
+lib/eina/libeina.la
+
+if HAVE_WINDOWS
+lib_ecore_con_libecore_con_la_LIBADD += lib/evil/libevil.la
+endif
+
+lib_ecore_con_libecore_con_la_LIBADD += @ECORE_CON_LIBS@ @EFL_COV_LIBS@ -lm
+
+lib_ecore_con_libecore_con_la_LDFLAGS = -no-undefined -version-info @version_info@ @release_info@
diff --git a/src/Makefile_Ecore_DirectFB.am b/src/Makefile_Ecore_DirectFB.am
new file mode 100644
index 0000000000..6e2d54c27a
--- /dev/null
+++ b/src/Makefile_Ecore_DirectFB.am
@@ -0,0 +1,34 @@
+
+### Library
+
+lib_LTLIBRARIES += lib/ecore_directfb/libecore_directfb.la
+
+installed_ecoredirectfbmainheadersdir = $(includedir)/ecore-@VMAJ@
+dist_installed_ecoredirectfbmainheaders_DATA = \
+lib/ecore_directfb/Ecore_DirectFB.h
+
+lib_ecore_directfb_libecore_directfb_la_SOURCES = \
+lib/ecore_directfb/ecore_directfb.c \
+lib/ecore_directfb/ecore_directfb_keys.h \
+lib/ecore_directfb/ecore_directfb_private.h
+
+lib_ecore_directfb_libecore_directfb_la_CPPFLAGS = \
+-I$(top_srcdir)/src/lib/eina \
+-I$(top_srcdir)/src/lib/eo \
+-I$(top_srcdir)/src/lib/ecore \
+-I$(top_srcdir)/src/lib/ecore_directfb \
+-I$(top_builddir)/src/lib/eina \
+-I$(top_builddir)/src/lib/eo \
+-I$(top_builddir)/src/lib/ecore \
+-I$(top_builddir)/src/lib/ecore_directfb \
+@EFL_CFLAGS@ \
+@EFL_COV_CFLAGS@ \
+@ECORE_DIRECTFB_CFLAGS@
+
+lib_ecore_directfb_libecore_directfb_la_LIBADD = \
+lib/ecore/libecore.la \
+lib/eo/libeo.la \
+lib/eina/libeina.la \
+@ECORE_DIRECTFB_LIBS@
+
+lib_ecore_directfb_libecore_directfb_la_LDFLAGS = -no-undefined -version-info @version_info@ @release_info@
diff --git a/src/Makefile_Ecore_Evas.am b/src/Makefile_Ecore_Evas.am
new file mode 100644
index 0000000000..595cd25f0f
--- /dev/null
+++ b/src/Makefile_Ecore_Evas.am
@@ -0,0 +1,160 @@
+
+### Library
+
+lib_LTLIBRARIES += lib/ecore_evas/libecore_evas.la
+
+installed_ecoreevasmainheadersdir = $(includedir)/ecore-@VMAJ@
+dist_installed_ecoreevasmainheaders_DATA = \
+lib/ecore_evas/Ecore_Evas.h
+
+lib_ecore_evas_libecore_evas_la_SOURCES = \
+lib/ecore_evas/ecore_evas.c \
+lib/ecore_evas/ecore_evas_util.c \
+lib/ecore_evas/ecore_evas_x.c \
+lib/ecore_evas/ecore_evas_fb.c \
+lib/ecore_evas/ecore_evas_buffer.c \
+lib/ecore_evas/ecore_evas_directfb.c \
+lib/ecore_evas/ecore_evas_win32.c \
+lib/ecore_evas/ecore_evas_sdl.c \
+lib/ecore_evas/ecore_evas_cocoa.c \
+lib/ecore_evas/ecore_evas_wince.c \
+lib/ecore_evas/ecore_evas_ews.c \
+lib/ecore_evas/ecore_evas_psl1ght.c \
+lib/ecore_evas/ecore_evas_wayland_shm.c \
+lib/ecore_evas/ecore_evas_wayland_egl.c \
+lib/ecore_evas/ecore_evas_extn.c \
+lib/ecore_evas/ecore_evas_private.h
+
+if BUILD_ECORE_WAYLAND
+lib_ecore_evas_libecore_evas_la_SOURCES += \
+lib/ecore_evas/ecore_evas_wayland_common.c
+endif
+
+lib_ecore_evas_libecore_evas_la_CPPFLAGS = \
+-I$(top_srcdir)/src/lib/eina \
+-I$(top_srcdir)/src/lib/eo \
+-I$(top_srcdir)/src/lib/ecore \
+-I$(top_srcdir)/src/lib/ecore_ipc \
+-I$(top_srcdir)/src/lib/ecore_input \
+-I$(top_srcdir)/src/lib/ecore_input_evas \
+-I$(top_srcdir)/src/lib/ecore_evas \
+-I$(top_builddir)/src/lib/eina \
+-I$(top_builddir)/src/lib/eo \
+-I$(top_builddir)/src/lib/ecore \
+-I$(top_builddir)/src/lib/ecore_ipc \
+-I$(top_builddir)/src/lib/ecore_input \
+-I$(top_builddir)/src/lib/ecore_input_evas \
+-I$(top_builddir)/src/lib/ecore_evas \
+-DEFL_ECORE_EVAS_BUILD \
+@EFL_CFLAGS@ \
+@EFL_COV_CFLAGS@
+
+if BUILD_ECORE_COCOA
+lib_ecore_evas_libecore_evas_la_CPPFLAGS += \
+-I$(top_srcdir)/src/lib/ecore_cocoa \
+-I$(top_builddir)/src/lib/ecore_cocoa
+endif
+
+if BUILD_ECORE_DIRECTFB
+lib_ecore_evas_libecore_evas_la_CPPFLAGS += \
+-I$(top_srcdir)/src/lib/ecore_directfb \
+-I$(top_builddir)/src/lib/ecore_directfb
+endif
+
+if BUILD_ECORE_FB
+lib_ecore_evas_libecore_evas_la_CPPFLAGS += \
+-I$(top_srcdir)/src/lib/ecore_fb \
+-I$(top_builddir)/src/lib/ecore_fb
+endif
+
+if BUILD_ECORE_PSL1GHT
+lib_ecore_evas_libecore_evas_la_CPPFLAGS += \
+-I$(top_srcdir)/src/lib/ecore_psl1ght \
+-I$(top_builddir)/src/lib/ecore_psl1ght
+endif
+
+if BUILD_ECORE_SDL
+lib_ecore_evas_libecore_evas_la_CPPFLAGS += \
+-I$(top_srcdir)/src/lib/ecore_sdl \
+-I$(top_builddir)/src/lib/ecore_sdl
+endif
+
+if BUILD_ECORE_WAYLAND
+lib_ecore_evas_libecore_evas_la_CPPFLAGS += \
+-I$(top_srcdir)/src/lib/ecore_wayland \
+-I$(top_builddir)/src/lib/ecore_wayland
+endif
+
+if BUILD_ECORE_WIN32
+lib_ecore_evas_libecore_evas_la_CPPFLAGS += \
+-I$(top_srcdir)/src/lib/ecore_win32 \
+-I$(top_builddir)/src/lib/ecore_win32
+endif
+
+if BUILD_ECORE_WINCE
+lib_ecore_evas_libecore_evas_la_CPPFLAGS += \
+-I$(top_srcdir)/src/lib/ecore_wince \
+-I$(top_builddir)/src/lib/ecore_wince
+endif
+
+if BUILD_ECORE_X
+lib_ecore_evas_libecore_evas_la_CPPFLAGS += \
+-I$(top_srcdir)/src/lib/ecore_x \
+-I$(top_builddir)/src/lib/ecore_x
+endif
+
+lib_ecore_evas_libecore_evas_la_LIBADD = \
+lib/ecore_input_evas/libecore_input_evas.la \
+lib/ecore_input/libecore_input.la \
+lib/ecore_ipc/libecore_ipc.la \
+lib/ecore/libecore.la \
+lib/eo/libeo.la \
+lib/eina/libeina.la \
+@ECORE_EVAS_LIBS@
+
+if BUILD_ECORE_COCOA
+lib_ecore_evas_libecore_evas_la_LIBADD = \
+lib/ecore_cocoa/libecore_cocoa.la
+endif
+
+if BUILD_ECORE_DIRECTFB
+lib_ecore_evas_libecore_evas_la_LIBADD = \
+lib/ecore_directfb/libecore_directfb.la
+endif
+
+if BUILD_ECORE_FB
+lib_ecore_evas_libecore_evas_la_LIBADD = \
+lib/ecore_fb/libecore_fb.la
+endif
+
+if BUILD_ECORE_PSL1GHT
+lib_ecore_evas_libecore_evas_la_LIBADD = \
+lib/ecore_psl1ght/libecore_psl1ght.la
+endif
+
+if BUILD_ECORE_SDL
+lib_ecore_evas_libecore_evas_la_LIBADD = \
+lib/ecore_sdl/libecore_sdl.la
+endif
+
+if BUILD_ECORE_WAYLAND
+lib_ecore_evas_libecore_evas_la_LIBADD = \
+lib/ecore_wayland/libecore_wayland.la
+endif
+
+if BUILD_ECORE_WIN32
+lib_ecore_evas_libecore_evas_la_LIBADD = \
+lib/ecore_win32/libecore_win32.la
+endif
+
+if BUILD_ECORE_WINCE
+lib_ecore_evas_libecore_evas_la_LIBADD = \
+lib/ecore_wince/libecore_wince.la
+endif
+
+if BUILD_ECORE_X
+lib_ecore_evas_libecore_evas_la_LIBADD = \
+lib/ecore_x/libecore_x.la
+endif
+
+lib_ecore_evas_libecore_evas_la_LDFLAGS = -no-undefined -version-info @version_info@ @release_info@
diff --git a/src/Makefile_Ecore_FB.am b/src/Makefile_Ecore_FB.am
new file mode 100644
index 0000000000..adcc5ebdc6
--- /dev/null
+++ b/src/Makefile_Ecore_FB.am
@@ -0,0 +1,42 @@
+
+### Library
+
+lib_LTLIBRARIES += lib/ecore_fb/libecore_fb.la
+
+installed_ecorefbmainheadersdir = $(includedir)/ecore-@VMAJ@
+dist_installed_ecorefbmainheaders_DATA = \
+lib/ecore_fb/Ecore_Fb.h
+
+lib_ecore_fb_libecore_fb_la_SOURCES = \
+lib/ecore_fb/ecore_fb.c \
+lib/ecore_fb/ecore_fb_li.c \
+lib/ecore_fb/ecore_fb_ts.c \
+lib/ecore_fb/ecore_fb_vt.c \
+lib/ecore_fb/ecore_fb_keytable.h \
+lib/ecore_fb/ecore_fb_private.h
+# deprecated sources (might not compile):
+# lib/ecore_fb/ecore_fb_kbd.c
+# lib/ecore_fb/ecore_fb_ps2.c
+
+lib_ecore_fb_libecore_fb_la_CPPFLAGS = \
+-I$(top_srcdir)/src/lib/eina \
+-I$(top_srcdir)/src/lib/eo \
+-I$(top_srcdir)/src/lib/ecore \
+-I$(top_srcdir)/src/lib/ecore_input \
+-I$(top_srcdir)/src/lib/ecore_fb \
+-I$(top_builddir)/src/lib/eina \
+-I$(top_builddir)/src/lib/eo \
+-I$(top_builddir)/src/lib/ecore \
+-I$(top_builddir)/src/lib/ecore_input \
+-I$(top_builddir)/src/lib/ecore_fb \
+@EFL_CFLAGS@ \
+@EFL_COV_CFLAGS@ \
+@ECORE_FB_CFLAGS@
+
+lib_ecore_fb_libecore_fb_la_LIBADD = \
+lib/ecore/libecore.la \
+lib/eo/libeo.la \
+lib/eina/libeina.la \
+@ECORE_FB_LIBS@
+
+lib_ecore_fb_libecore_fb_la_LDFLAGS = -no-undefined -version-info @version_info@ @release_info@
diff --git a/src/Makefile_Ecore_File.am b/src/Makefile_Ecore_File.am
new file mode 100644
index 0000000000..2172245342
--- /dev/null
+++ b/src/Makefile_Ecore_File.am
@@ -0,0 +1,41 @@
+
+### Library
+
+lib_LTLIBRARIES += lib/ecore_file/libecore_file.la
+
+installed_ecorefilemainheadersdir = $(includedir)/ecore-@VMAJ@
+dist_installed_ecorefilemainheaders_DATA = \
+lib/ecore_file/Ecore_File.h
+
+lib_ecore_file_libecore_file_la_SOURCES = \
+lib/ecore_file/ecore_file.c \
+lib/ecore_file/ecore_file_download.c \
+lib/ecore_file/ecore_file_monitor.c \
+lib/ecore_file/ecore_file_monitor_inotify.c \
+lib/ecore_file/ecore_file_monitor_poll.c \
+lib/ecore_file/ecore_file_monitor_win32.c \
+lib/ecore_file/ecore_file_path.c \
+lib/ecore_file/ecore_file_private.h
+
+lib_ecore_file_libecore_file_la_CPPFLAGS = \
+-I$(top_srcdir)/src/lib/eina \
+-I$(top_srcdir)/src/lib/eo \
+-I$(top_srcdir)/src/lib/ecore \
+-I$(top_srcdir)/src/lib/ecore_con \
+-I$(top_srcdir)/src/lib/ecore_file \
+-I$(top_builddir)/src/lib/eina \
+-I$(top_builddir)/src/lib/eo \
+-I$(top_builddir)/src/lib/ecore \
+-I$(top_builddir)/src/lib/ecore_con \
+-I$(top_builddir)/src/lib/ecore_file \
+-DEFL_ECORE_FILE_BUILD \
+@EFL_CFLAGS@ \
+@EFL_COV_CFLAGS@
+
+lib_ecore_file_libecore_file_la_LIBADD = \
+lib/ecore_con/libecore_con.la \
+lib/ecore/libecore.la \
+lib/eo/libeo.la \
+lib/eina/libeina.la
+
+lib_ecore_file_libecore_file_la_LDFLAGS = -no-undefined -version-info @version_info@ @release_info@
diff --git a/src/Makefile_Ecore_Imf.am b/src/Makefile_Ecore_Imf.am
new file mode 100644
index 0000000000..820ae2b12a
--- /dev/null
+++ b/src/Makefile_Ecore_Imf.am
@@ -0,0 +1,186 @@
+
+### Library
+
+lib_LTLIBRARIES += \
+lib/ecore_imf/libecore_imf.la \
+lib/ecore_imf/libecore_imf_evas.la
+
+installed_ecoreimfmainheadersdir = $(includedir)/ecore-@VMAJ@
+dist_installed_ecoreimfmainheaders_DATA = \
+lib/ecore_imf/Ecore_IMF.h
+
+lib_ecore_imf_libecore_imf_la_SOURCES = \
+lib/ecore_imf/ecore_imf.c \
+lib/ecore_imf/ecore_imf_context.c \
+lib/ecore_imf/ecore_imf_module.c \
+lib/ecore_imf/ecore_imf_private.h
+
+lib_ecore_imf_libecore_imf_la_CPPFLAGS = \
+-I$(top_srcdir)/src/lib/eina \
+-I$(top_srcdir)/src/lib/eo \
+-I$(top_srcdir)/src/lib/ecore \
+-I$(top_srcdir)/src/lib/ecore_imf \
+-I$(top_builddir)/src/lib/eina \
+-I$(top_builddir)/src/lib/eo \
+-I$(top_builddir)/src/lib/ecore \
+-I$(top_builddir)/src/lib/ecore_imf \
+-DPACKAGE_LIB_DIR=\"$(libdir)\" \
+-DEFL_ECORE_IMF_BUILD \
+@EFL_CFLAGS@ \
+@EFL_COV_CFLAGS@
+
+lib_ecore_imf_libecore_imf_la_LIBADD = \
+lib/ecore/libecore.la \
+lib/eo/libeo.la \
+lib/eina/libeina.la
+
+lib_ecore_imf_libecore_imf_la_LDFLAGS = -no-undefined -version-info @version_info@ @release_info@
+
+installed_ecoreimfevasmainheadersdir = $(includedir)/ecore-@VMAJ@
+dist_installed_ecoreimfevasmainheaders_DATA = \
+lib/ecore_imf/Ecore_IMF_Evas.h
+
+lib_ecore_imf_libecore_imf_evas_la_SOURCES = \
+lib/ecore_imf/ecore_imf_evas.c
+
+lib_ecore_imf_libecore_imf_evas_la_CPPFLAGS = \
+-I$(top_srcdir)/src/lib/eina \
+-I$(top_srcdir)/src/lib/eo \
+-I$(top_srcdir)/src/lib/evas \
+-I$(top_srcdir)/src/lib/ecore \
+-I$(top_srcdir)/src/lib/ecore_imf \
+-I$(top_srcdir)/src/lib/ecore_imf_evas \
+-I$(top_builddir)/src/lib/eina \
+-I$(top_builddir)/src/lib/eo \
+-I$(top_builddir)/src/lib/evas \
+-I$(top_builddir)/src/lib/ecore \
+-I$(top_builddir)/src/lib/ecore_imf \
+-I$(top_builddir)/src/lib/ecore_imf_evas \
+-DEFL_ECORE_IMF_EVAS_BUILD \
+@EFL_CFLAGS@ \
+@EFL_COV_CFLAGS@
+
+lib_ecore_imf_libecore_imf_evas_la_LIBADD = \
+lib/ecore_imf/libecore_imf.la \
+lib/ecore/libecore.la \
+lib/evas/libevas.la \
+lib/eo/libeo.la \
+lib/eina/libeina.la
+
+lib_ecore_imf_libecore_imf_evas_la_LDFLAGS = -no-undefined -version-info @version_info@ @release_info@
+
+
+### Modules
+
+# Ibus
+
+if BUILD_ECORE_IMF_IBUS
+ecoreimfibuspkgdir = $(libdir)/ecore/immodules
+ecoreimfibuspkg_LTLIBRARIES = modules/ecore/immodules/ibus/ibus.la
+modules_ecore_immodules_ibus_ibus_la_SOURCES = \
+modules/ecore/immodules/ibus/ibus_module.c \
+modules/ecore/immodules/ibus/ibus_imcontext.c \
+modules/ecore/immodules/ibus/ibus_imcontext.h
+modules_ecore_immodules_ibus_ibus_la_CPPFLAGS = \
+-I$(top_srcdir)/src/lib/eina \
+-I$(top_srcdir)/src/lib/eo \
+-I$(top_srcdir)/src/lib/ecore \
+-I$(top_srcdir)/src/lib/ecore_input \
+-I$(top_srcdir)/src/lib/ecore_x \
+-I$(top_srcdir)/src/lib/ecore_imf \
+-I$(top_srcdir)/src/lib/ecore_evas \
+-I$(top_builddir)/src/lib/eina \
+-I$(top_builddir)/src/lib/eo \
+-I$(top_builddir)/src/lib/ecore \
+-I$(top_builddir)/src/lib/ecore_input \
+-I$(top_builddir)/src/lib/ecore_x \
+-I$(top_builddir)/src/lib/ecore_imf \
+-I$(top_builddir)/src/lib/ecore_evas \
+-DEFL_ECORE_IMF_EVAS_BUILD \
+@EFL_CFLAGS@ \
+@EFL_COV_CFLAGS@ \
+@IBUS_CFLAGS@
+modules_ecore_immodules_ibus_ibus_la_LIBADD = \
+lib/ecore_imf/libecore_evas.la \
+lib/ecore_imf/libecore_imf.la \
+lib/ecore_x/libecore_x.la \
+lib/ecore/libecore.la \
+lib/eo/libeo.la \
+lib/eina/libeina.la \
+@IBUS_LIBS@
+modules_ecore_immodules_ibus_ibus_la_LDFLAGS = -no-undefined -module -avoid-version
+modules_ecore_immodules_ibus_ibus_la_LIBTOOLFLAGS = --tag=disable-static
+endif
+
+# Scim
+
+if BUILD_ECORE_IMF_SCIM
+ecoreimfscimpkgdir = $(libdir)/ecore/immodules
+ecoreimfscimpkg_LTLIBRARIES = modules/ecore/immodules/scim/scim.la
+modules_ecore_immodules_scim_scim_la_SOURCES = \
+modules/ecore/immodules/scim/scim_module.cpp \
+modules/ecore/immodules/scim/scim_imcontext.cpp \
+modules/ecore/immodules/scim/scim_imcontext.h
+modules_ecore_immodules_scim_scim_la_CPPFLAGS = \
+-I$(top_srcdir)/src/lib/eina \
+-I$(top_srcdir)/src/lib/eo \
+-I$(top_srcdir)/src/lib/ecore \
+-I$(top_srcdir)/src/lib/ecore_input \
+-I$(top_srcdir)/src/lib/ecore_x \
+-I$(top_srcdir)/src/lib/ecore_imf \
+-I$(top_srcdir)/src/lib/ecore_evas \
+-I$(top_builddir)/src/lib/eina \
+-I$(top_builddir)/src/lib/eo \
+-I$(top_builddir)/src/lib/ecore \
+-I$(top_builddir)/src/lib/ecore_input \
+-I$(top_builddir)/src/lib/ecore_x \
+-I$(top_builddir)/src/lib/ecore_imf \
+-I$(top_builddir)/src/lib/ecore_evas \
+-DEFL_ECORE_IMF_EVAS_BUILD \
+@EFL_CFLAGS@ \
+@EFL_COV_CFLAGS@ \
+@SCIM_CFLAGS@
+modules_ecore_immodules_scim_scim_la_LIBADD = \
+lib/ecore_imf/libecore_evas.la \
+lib/ecore_imf/libecore_imf.la \
+lib/ecore_x/libecore_x.la \
+lib/ecore/libecore.la \
+lib/eo/libeo.la \
+lib/eina/libeina.la \
+@SCIM_LIBS@
+modules_ecore_immodules_scim_scim_la_LDFLAGS = -no-undefined -module -avoid-version
+modules_ecore_immodules_scim_scim_la_LIBTOOLFLAGS = --tag=disable-static
+endif
+
+# Xim
+
+if BUILD_ECORE_IMF_XIM
+ecoreimfximpkgdir = $(libdir)/ecore/immodules
+ecoreimfximpkg_LTLIBRARIES = modules/ecore/immodules/xim/xim.la
+modules_ecore_immodules_xim_xim_la_SOURCES = \
+modules/ecore/immodules/xim/ecore_imf_xim.c
+modules_ecore_immodules_xim_xim_la_CPPFLAGS = \
+-I$(top_srcdir)/src/lib/eina \
+-I$(top_srcdir)/src/lib/eo \
+-I$(top_srcdir)/src/lib/ecore \
+-I$(top_srcdir)/src/lib/ecore_input \
+-I$(top_srcdir)/src/lib/ecore_x \
+-I$(top_srcdir)/src/lib/ecore_imf \
+-I$(top_builddir)/src/lib/eina \
+-I$(top_builddir)/src/lib/eo \
+-I$(top_builddir)/src/lib/ecore \
+-I$(top_builddir)/src/lib/ecore_input \
+-I$(top_builddir)/src/lib/ecore_x \
+-I$(top_builddir)/src/lib/ecore_imf \
+-DEFL_ECORE_IMF_EVAS_BUILD \
+@EFL_CFLAGS@ \
+@EFL_COV_CFLAGS@
+modules_ecore_immodules_xim_xim_la_LIBADD = \
+lib/ecore_imf/libecore_imf.la \
+lib/ecore_x/libecore_x.la \
+lib/ecore/libecore.la \
+lib/eo/libeo.la \
+lib/eina/libeina.la
+modules_ecore_immodules_xim_xim_la_LDFLAGS = -no-undefined -module -avoid-version
+modules_ecore_immodules_xim_xim_la_LIBTOOLFLAGS = --tag=disable-static
+endif
diff --git a/src/Makefile_Ecore_Input.am b/src/Makefile_Ecore_Input.am
new file mode 100644
index 0000000000..29aa449f45
--- /dev/null
+++ b/src/Makefile_Ecore_Input.am
@@ -0,0 +1,70 @@
+
+### Library
+
+lib_LTLIBRARIES += \
+lib/ecore_input/libecore_input.la \
+lib/ecore_input/libecore_input_evas.la
+
+installed_ecoreinputmainheadersdir = $(includedir)/ecore-@VMAJ@
+dist_installed_ecoreinputmainheaders_DATA = \
+lib/ecore_input/Ecore_Input.h
+
+lib_ecore_input_libecore_input_la_SOURCES = \
+lib/ecore_input/ecore_input.c \
+lib/ecore_input/ecore_input_compose.c \
+lib/ecore_input/ecore_input_compose.h \
+lib/ecore_input/ecore_input_private.h
+
+lib_ecore_input_libecore_input_la_CPPFLAGS = \
+-I$(top_srcdir)/src/lib/eina \
+-I$(top_srcdir)/src/lib/eo \
+-I$(top_srcdir)/src/lib/ecore \
+-I$(top_srcdir)/src/lib/ecore_input \
+-I$(top_builddir)/src/lib/eina \
+-I$(top_builddir)/src/lib/eo \
+-I$(top_builddir)/src/lib/ecore \
+-I$(top_builddir)/src/lib/ecore_input \
+-DEFL_ECORE_INPUT_BUILD \
+@EFL_CFLAGS@ \
+@EFL_COV_CFLAGS@
+
+lib_ecore_input_libecore_input_la_LIBADD = \
+lib/ecore/libecore.la \
+lib/eo/libeo.la \
+lib/eina/libeina.la
+
+lib_ecore_input_libecore_input_la_LDFLAGS = -no-undefined -version-info @version_info@ @release_info@
+
+installed_ecoreinputevasmainheadersdir = $(includedir)/ecore-@VMAJ@
+dist_installed_ecoreinputevasmainheaders_DATA = \
+lib/ecore_input/Ecore_Input_Evas.h
+
+lib_ecore_input_libecore_input_evas_la_SOURCES = \
+lib/ecore_input/ecore_input_evas.c \
+lib/ecore_input/ecore_input_evas_private.h
+
+lib_ecore_input_libecore_input_evas_la_CPPFLAGS = \
+-I$(top_srcdir)/src/lib/eina \
+-I$(top_srcdir)/src/lib/eo \
+-I$(top_srcdir)/src/lib/evas \
+-I$(top_srcdir)/src/lib/ecore \
+-I$(top_srcdir)/src/lib/ecore_input \
+-I$(top_srcdir)/src/lib/ecore_input_evas \
+-I$(top_builddir)/src/lib/eina \
+-I$(top_builddir)/src/lib/eo \
+-I$(top_builddir)/src/lib/evas \
+-I$(top_builddir)/src/lib/ecore \
+-I$(top_builddir)/src/lib/ecore_input \
+-I$(top_builddir)/src/lib/ecore_input_evas \
+-DEFL_ECORE_INPUT_EVAS_BUILD \
+@EFL_CFLAGS@ \
+@EFL_COV_CFLAGS@
+
+lib_ecore_input_libecore_input_evas_la_LIBADD = \
+lib/ecore_input/libecore_input.la \
+lib/ecore/libecore.la \
+lib/evas/libevas.la \
+lib/eo/libeo.la \
+lib/eina/libeina.la
+
+lib_ecore_input_libecore_input_evas_la_LDFLAGS = -no-undefined -version-info @version_info@ @release_info@
diff --git a/src/Makefile_Ecore_Ipc.am b/src/Makefile_Ecore_Ipc.am
new file mode 100644
index 0000000000..ba09f374b7
--- /dev/null
+++ b/src/Makefile_Ecore_Ipc.am
@@ -0,0 +1,37 @@
+
+### Library
+
+lib_LTLIBRARIES += lib/ecore_ipc/libecore_ipc.la
+
+installed_ecoreipcmainheadersdir = $(includedir)/ecore-@VMAJ@
+dist_installed_ecoreipcmainheaders_DATA = \
+lib/ecore_ipc/Ecore_Ipc.h
+
+lib_ecore_ipc_libecore_ipc_la_SOURCES = \
+lib/ecore_ipc/ecore_ipc.c \
+lib/ecore_ipc/ecore_ipc_private.h
+
+lib_ecore_ipc_libecore_ipc_la_CPPFLAGS = \
+-I$(top_srcdir)/src/lib/eina \
+-I$(top_srcdir)/src/lib/eo \
+-I$(top_srcdir)/src/lib/ecore \
+-I$(top_srcdir)/src/lib/ecore_con \
+-I$(top_srcdir)/src/lib/ecore_ipc \
+-I$(top_builddir)/src/lib/eina \
+-I$(top_builddir)/src/lib/eo \
+-I$(top_builddir)/src/lib/ecore \
+-I$(top_builddir)/src/lib/ecore_con \
+-I$(top_builddir)/src/lib/ecore_ipc \
+-DEFL_ECORE_IPC_BUILD \
+@EFL_CFLAGS@ \
+@EFL_COV_CFLAGS@
+
+lib_ecore_ipc_libecore_ipc_la_LIBADD = \
+lib/ecore_con/libecore_con.la \
+lib/ecore/libecore.la \
+lib/eo/libeo.la \
+lib/eina/libeina.la
+
+lib_ecore_ipc_libecore_ipc_la_LIBADD += @EFL_COV_LIBS@ -lm
+
+lib_ecore_ipc_libecore_ipc_la_LDFLAGS = -no-undefined -version-info @version_info@ @release_info@
diff --git a/src/Makefile_Ecore_Psl1ght.am b/src/Makefile_Ecore_Psl1ght.am
new file mode 100644
index 0000000000..9ac146c63d
--- /dev/null
+++ b/src/Makefile_Ecore_Psl1ght.am
@@ -0,0 +1,38 @@
+
+### Library
+
+lib_LTLIBRARIES += lib/ecore_psl1ght/libecore_psl1ght.la
+
+installed_ecorepsl1ghtmainheadersdir = $(includedir)/ecore-@VMAJ@
+dist_installed_ecorepsl1ghtmainheaders_DATA = \
+lib/ecore_psl1ght/Ecore_Psl1ght.h
+
+lib_ecore_psl1ght_libecore_psl1ght_la_SOURCES = \
+lib/ecore_psl1ght/ecore_psl1ght.c \
+lib/ecore_psl1ght/moveutil.c \
+lib/ecore_psl1ght/spursutil.c \
+lib/ecore_psl1ght/Ecore_Psl1ght_Keys.h \
+lib/ecore_psl1ght/ecore_psl1ght_private.h
+
+lib_ecore_psl1ght_libecore_psl1ght_la_CPPFLAGS = \
+-I$(top_srcdir)/src/lib/eina \
+-I$(top_srcdir)/src/lib/eo \
+-I$(top_srcdir)/src/lib/ecore \
+-I$(top_srcdir)/src/lib/ecore_input \
+-I$(top_srcdir)/src/lib/ecore_psl1ght \
+-I$(top_builddir)/src/lib/eina \
+-I$(top_builddir)/src/lib/eo \
+-I$(top_builddir)/src/lib/ecore \
+-I$(top_builddir)/src/lib/ecore_input \
+-I$(top_builddir)/src/lib/ecore_psl1ght \
+-DEFL_ECORE_PSL1GHT_BUILD \
+@EFL_CFLAGS@ \
+@EFL_COV_CFLAGS@
+
+lib_ecore_psl1ght_libecore_psl1ght_la_LIBADD = \
+lib/ecore_input/libecore_input.la \
+lib/ecore/libecore.la \
+lib/eo/libeo.la \
+lib/eina/libeina.la
+
+lib_ecore_psl1ght_libecore_psl1ght_la_LDFLAGS = -no-undefined -version-info @version_info@ @release_info@
diff --git a/src/Makefile_Ecore_SDL.am b/src/Makefile_Ecore_SDL.am
new file mode 100644
index 0000000000..62e7a10266
--- /dev/null
+++ b/src/Makefile_Ecore_SDL.am
@@ -0,0 +1,38 @@
+
+### Library
+
+lib_LTLIBRARIES += lib/ecore_sdl/libecore_sdl.la
+
+installed_ecoresdlmainheadersdir = $(includedir)/ecore-@VMAJ@
+dist_installed_ecoresdlmainheaders_DATA = \
+lib/ecore_sdl/Ecore_Sdl.h
+
+lib_ecore_sdl_libecore_sdl_la_SOURCES = \
+lib/ecore_sdl/ecore_sdl.c \
+lib/ecore_sdl/Ecore_Sdl_Keys.h \
+lib/ecore_sdl/ecore_sdl_private.h
+
+lib_ecore_sdl_libecore_sdl_la_CPPFLAGS = \
+-I$(top_srcdir)/src/lib/eina \
+-I$(top_srcdir)/src/lib/eo \
+-I$(top_srcdir)/src/lib/ecore \
+-I$(top_srcdir)/src/lib/ecore_input \
+-I$(top_srcdir)/src/lib/ecore_sdl \
+-I$(top_builddir)/src/lib/eina \
+-I$(top_builddir)/src/lib/eo \
+-I$(top_builddir)/src/lib/ecore \
+-I$(top_builddir)/src/lib/ecore_input \
+-I$(top_builddir)/src/lib/ecore_sdl \
+-DEFL_ECORE_SDL_BUILD \
+@EFL_CFLAGS@ \
+@EFL_COV_CFLAGS@ \
+@ECORE_SDL_CFLAGS@
+
+lib_ecore_sdl_libecore_sdl_la_LIBADD = \
+lib/ecore_input/libecore_input.la \
+lib/ecore/libecore.la \
+lib/eo/libeo.la \
+lib/eina/libeina.la \
+@ECORE_SDL_LIBS@
+
+lib_ecore_sdl_libecore_sdl_la_LDFLAGS = -no-undefined -version-info @version_info@ @release_info@
diff --git a/src/Makefile_Ecore_Wayland.am b/src/Makefile_Ecore_Wayland.am
new file mode 100644
index 0000000000..4923a1abf4
--- /dev/null
+++ b/src/Makefile_Ecore_Wayland.am
@@ -0,0 +1,40 @@
+
+### Library
+
+lib_LTLIBRARIES += lib/ecore_wayland/libecore_wayland.la
+
+installed_ecorewaylandmainheadersdir = $(includedir)/ecore-@VMAJ@
+dist_installed_ecorewaylandmainheaders_DATA = \
+lib/ecore_wayland/Ecore_Wayland.h
+
+lib_ecore_wayland_libecore_wayland_la_SOURCES = \
+lib/ecore_wayland/ecore_wayland.c \
+lib/ecore_wayland/ecore_wl_dnd.c \
+lib/ecore_wayland/ecore_wl_input.c \
+lib/ecore_wayland/ecore_wl_output.c \
+lib/ecore_wayland/ecore_wl_window.c \
+lib/ecore_wayland/ecore_wayland_private.h
+
+lib_ecore_wayland_libecore_wayland_la_CPPFLAGS = \
+-I$(top_srcdir)/src/lib/eina \
+-I$(top_srcdir)/src/lib/eo \
+-I$(top_srcdir)/src/lib/ecore \
+-I$(top_srcdir)/src/lib/ecore_input \
+-I$(top_srcdir)/src/lib/ecore_wayland \
+-I$(top_builddir)/src/lib/eina \
+-I$(top_builddir)/src/lib/eo \
+-I$(top_builddir)/src/lib/ecore \
+-I$(top_builddir)/src/lib/ecore_input \
+-I$(top_builddir)/src/lib/ecore_wayland \
+@EFL_CFLAGS@ \
+@EFL_COV_CFLAGS@ \
+@ECORE_WAYLAND_CFLAGS@
+
+lib_ecore_wayland_libecore_wayland_la_LIBADD = \
+lib/ecore/libecore_input.la \
+lib/ecore/libecore.la \
+lib/eo/libeo.la \
+lib/eina/libeina.la \
+@ECORE_WAYLAND_LIBS@
+
+lib_ecore_wayland_libecore_wayland_la_LDFLAGS = -no-undefined -version-info @version_info@ @release_info@
diff --git a/src/Makefile_Ecore_Win32.am b/src/Makefile_Ecore_Win32.am
new file mode 100644
index 0000000000..2491ca5aa7
--- /dev/null
+++ b/src/Makefile_Ecore_Win32.am
@@ -0,0 +1,51 @@
+
+### Library
+
+lib_LTLIBRARIES += lib/ecore_win32/libecore_win32.la
+
+installed_ecorewin32mainheadersdir = $(includedir)/ecore-@VMAJ@
+dist_installed_ecorewin32mainheaders_DATA = \
+lib/ecore_win32/Ecore_Win32.h
+
+lib_ecore_win32_libecore_win32_la_SOURCES = \
+lib/ecore_win32/ecore_win32.c \
+lib/ecore_win32/ecore_win32_cursor.c \
+lib/ecore_win32/ecore_win32_dnd.c \
+lib/ecore_win32/ecore_win32_dnd_enumformatetc.cpp \
+lib/ecore_win32/ecore_win32_dnd_data_object.cpp \
+lib/ecore_win32/ecore_win32_dnd_drop_source.cpp \
+lib/ecore_win32/ecore_win32_dnd_drop_target.cpp \
+lib/ecore_win32/ecore_win32_event.c \
+lib/ecore_win32/ecore_win32_window.c \
+lib/ecore_win32/ecore_win32_private.h \
+lib/ecore_win32/ecore_win32_dnd_enumformatetc.h \
+lib/ecore_win32/ecore_win32_dnd_data_object.h \
+lib/ecore_win32/ecore_win32_dnd_drop_source.h \
+lib/ecore_win32/ecore_win32_dnd_drop_target.h
+
+lib_ecore_win32_libecore_win32_la_CPPFLAGS = \
+-I$(top_srcdir)/src/lib/evil \
+-I$(top_srcdir)/src/lib/eina \
+-I$(top_srcdir)/src/lib/eo \
+-I$(top_srcdir)/src/lib/ecore \
+-I$(top_srcdir)/src/lib/ecore_input \
+-I$(top_srcdir)/src/lib/ecore_win32 \
+-I$(top_builddir)/src/lib/evil \
+-I$(top_builddir)/src/lib/eina \
+-I$(top_builddir)/src/lib/eo \
+-I$(top_builddir)/src/lib/ecore \
+-I$(top_builddir)/src/lib/ecore_input \
+-I$(top_builddir)/src/lib/ecore_win32 \
+-DEFL_ECORE_WIN32_BUILD \
+@EFL_CFLAGS@ \
+@EFL_COV_CFLAGS@
+
+lib_ecore_win32_libecore_win32_la_LIBADD = \
+lib/ecore_input/libecore_input.la \
+lib/ecore/libecore.la \
+lib/eo/libeo.la \
+lib/eina/libeina.la \
+lib/evil/libevil.la \
+@ECORE_WIN32_LIBS@
+
+lib_ecore_win32_libecore_win32_la_LDFLAGS = -no-undefined -version-info @version_info@ @release_info@
diff --git a/src/Makefile_Ecore_WinCE.am b/src/Makefile_Ecore_WinCE.am
new file mode 100644
index 0000000000..29122ab8c0
--- /dev/null
+++ b/src/Makefile_Ecore_WinCE.am
@@ -0,0 +1,41 @@
+
+### Library
+
+lib_LTLIBRARIES += lib/ecore_wince/libecore_wince.la
+
+installed_ecorewincemainheadersdir = $(includedir)/ecore-@VMAJ@
+dist_installed_ecorewincemainheaders_DATA = \
+lib/ecore_wince/Ecore_Wince.h
+
+lib_ecore_wince_libecore_wince_la_SOURCES = \
+lib/ecore_wince/ecore_wince.c \
+lib/ecore_wince/ecore_wince_event.c \
+lib/ecore_wince/ecore_wince_window.c \
+lib/ecore_wince/ecore_wince_private.h
+
+lib_ecore_wince_libecore_wince_la_CPPFLAGS = \
+-I$(top_srcdir)/src/lib/evil \
+-I$(top_srcdir)/src/lib/eina \
+-I$(top_srcdir)/src/lib/eo \
+-I$(top_srcdir)/src/lib/ecore \
+-I$(top_srcdir)/src/lib/ecore_input \
+-I$(top_srcdir)/src/lib/ecore_wince \
+-I$(top_builddir)/src/lib/evil \
+-I$(top_builddir)/src/lib/eina \
+-I$(top_builddir)/src/lib/eo \
+-I$(top_builddir)/src/lib/ecore \
+-I$(top_builddir)/src/lib/ecore_input \
+-I$(top_builddir)/src/lib/ecore_wince \
+-DEFL_ECORE_WINCE_BUILD \
+@EFL_CFLAGS@ \
+@EFL_COV_CFLAGS@
+
+lib_ecore_wince_libecore_wince_la_LIBADD = \
+lib/ecore_input/libecore_input.la \
+lib/ecore/libecore.la \
+lib/eo/libeo.la \
+lib/eina/libeina.la \
+lib/evil/libevil.la \
+@ECORE_WINCE_LIBS@
+
+lib_ecore_wince_libecore_wince_la_LDFLAGS = -no-undefined -version-info @version_info@ @release_info@
diff --git a/src/Makefile_Ecore_X.am b/src/Makefile_Ecore_X.am
new file mode 100644
index 0000000000..cda857bd59
--- /dev/null
+++ b/src/Makefile_Ecore_X.am
@@ -0,0 +1,126 @@
+
+### Library
+
+lib_LTLIBRARIES += lib/ecore_x/libecore_x.la
+
+installed_ecorexmainheadersdir = $(includedir)/ecore-@VMAJ@
+dist_installed_ecorexmainheaders_DATA = \
+lib/ecore_x/Ecore_X.h \
+lib/ecore_x/Ecore_X_Atoms.h \
+lib/ecore_x/Ecore_X_Cursor.h
+
+if HAVE_ECORE_X_XCB
+lib_ecore_x_libecore_x_la_SOURCES = \
+lib/ecore_x/xcb/ecore_xcb.c \
+lib/ecore_x/xcb/ecore_xcb_atoms.c \
+lib/ecore_x/xcb/ecore_xcb_extensions.c \
+lib/ecore_x/xcb/ecore_xcb_shape.c \
+lib/ecore_x/xcb/ecore_xcb_screensaver.c \
+lib/ecore_x/xcb/ecore_xcb_sync.c \
+lib/ecore_x/xcb/ecore_xcb_render.c \
+lib/ecore_x/xcb/ecore_xcb_randr.c \
+lib/ecore_x/xcb/ecore_xcb_xfixes.c \
+lib/ecore_x/xcb/ecore_xcb_composite.c \
+lib/ecore_x/xcb/ecore_xcb_cursor.c \
+lib/ecore_x/xcb/ecore_xcb_damage.c \
+lib/ecore_x/xcb/ecore_xcb_dnd.c \
+lib/ecore_x/xcb/ecore_xcb_dpms.c \
+lib/ecore_x/xcb/ecore_xcb_drawable.c \
+lib/ecore_x/xcb/ecore_xcb_e.c \
+lib/ecore_x/xcb/ecore_xcb_gc.c \
+lib/ecore_x/xcb/ecore_xcb_image.c \
+lib/ecore_x/xcb/ecore_xcb_input.c \
+lib/ecore_x/xcb/ecore_xcb_gesture.c \
+lib/ecore_x/xcb/ecore_xcb_mwm.c \
+lib/ecore_x/xcb/ecore_xcb_pixmap.c \
+lib/ecore_x/xcb/ecore_xcb_region.c \
+lib/ecore_x/xcb/ecore_xcb_selection.c \
+lib/ecore_x/xcb/ecore_xcb_textlist.c \
+lib/ecore_x/xcb/ecore_xcb_events.c \
+lib/ecore_x/xcb/ecore_xcb_keymap.c \
+lib/ecore_x/xcb/ecore_xcb_netwm.c \
+lib/ecore_x/xcb/ecore_xcb_icccm.c \
+lib/ecore_x/xcb/ecore_xcb_window.c \
+lib/ecore_x/xcb/ecore_xcb_window_prop.c \
+lib/ecore_x/xcb/ecore_xcb_window_shape.c \
+lib/ecore_x/xcb/ecore_xcb_window_shadow.c \
+lib/ecore_x/xcb/ecore_xcb_xinerama.c \
+lib/ecore_x/xcb/ecore_xcb_error.c \
+lib/ecore_x/xcb/ecore_xcb_xtest.c \
+lib/ecore_x/xcb/ecore_xcb_vsync.c \
+lib/ecore_x/xcb/ecore_xcb_xdefaults.c \
+lib/ecore_x/xcb/ecore_xcb_private.h
+else
+lib_ecore_x_libecore_x_la_SOURCES = \
+lib/ecore_x/xlib/ecore_x.c \
+lib/ecore_x/xlib/ecore_x_dnd.c \
+lib/ecore_x/xlib/ecore_x_sync.c \
+lib/ecore_x/xlib/ecore_x_randr.c \
+lib/ecore_x/xlib/ecore_x_randr_11.c \
+lib/ecore_x/xlib/ecore_x_randr_12.c \
+lib/ecore_x/xlib/ecore_x_randr_12_edid.c \
+lib/ecore_x/xlib/ecore_x_randr_13.c \
+lib/ecore_x/xlib/ecore_x_fixes.c \
+lib/ecore_x/xlib/ecore_x_damage.c \
+lib/ecore_x/xlib/ecore_x_composite.c \
+lib/ecore_x/xlib/ecore_x_error.c \
+lib/ecore_x/xlib/ecore_x_events.c \
+lib/ecore_x/xlib/ecore_x_icccm.c \
+lib/ecore_x/xlib/ecore_x_netwm.c \
+lib/ecore_x/xlib/ecore_x_mwm.c \
+lib/ecore_x/xlib/ecore_x_e.c \
+lib/ecore_x/xlib/ecore_x_selection.c \
+lib/ecore_x/xlib/ecore_x_window.c \
+lib/ecore_x/xlib/ecore_x_window_prop.c \
+lib/ecore_x/xlib/ecore_x_window_shape.c \
+lib/ecore_x/xlib/ecore_x_pixmap.c \
+lib/ecore_x/xlib/ecore_x_gc.c \
+lib/ecore_x/xlib/ecore_x_xinerama.c \
+lib/ecore_x/xlib/ecore_x_screensaver.c \
+lib/ecore_x/xlib/ecore_x_dpms.c \
+lib/ecore_x/xlib/ecore_x_drawable.c \
+lib/ecore_x/xlib/ecore_x_cursor.c \
+lib/ecore_x/xlib/ecore_x_test.c \
+lib/ecore_x/xlib/ecore_x_atoms.c \
+lib/ecore_x/xlib/ecore_x_region.c \
+lib/ecore_x/xlib/ecore_x_image.c \
+lib/ecore_x/xlib/ecore_x_xi2.c \
+lib/ecore_x/xlib/ecore_x_vsync.c \
+lib/ecore_x/xlib/ecore_x_randr.h \
+lib/ecore_x/xlib/ecore_x_gesture.c \
+lib/ecore_x/xlib/ecore_x_private.h
+endif
+
+lib_ecore_x_libecore_x_la_CPPFLAGS = \
+-I$(top_srcdir)/src/lib/eina \
+-I$(top_srcdir)/src/lib/eo \
+-I$(top_srcdir)/src/lib/ecore \
+-I$(top_srcdir)/src/lib/ecore_input \
+-I$(top_srcdir)/src/lib/ecore_x \
+-I$(top_builddir)/src/lib/eina \
+-I$(top_builddir)/src/lib/eo \
+-I$(top_builddir)/src/lib/ecore \
+-I$(top_builddir)/src/lib/ecore_input \
+-I$(top_builddir)/src/lib/ecore_x \
+-DEFL_ECORE_X_BUILD \
+@EFL_CFLAGS@ \
+@EFL_COV_CFLAGS@ \
+@ECORE_X_CFLAGS@
+
+lib_ecore_x_libecore_x_la_LIBADD = \
+lib/ecore_input/libecore_input.la \
+lib/ecore/libecore.la \
+lib/eo/libeo.la \
+lib/eina/libeina.la \
+@ECORE_X_LIBS@
+
+lib_ecore_x_libecore_x_la_LDFLAGS = -no-undefined -version-info @version_info@ @release_info@
+
+### Utils
+
+noinst_PROGRAMS = utils/ecore/makekeys
+
+utils_ecore_makekeys_SOURCES = utils/ecore/makekeys.c
+
+EXTRA_DIST += utils/ecore/mkks.sh
+
diff --git a/src/Makefile_Escape.am b/src/Makefile_Escape.am
new file mode 100644
index 0000000000..d237d184a2
--- /dev/null
+++ b/src/Makefile_Escape.am
@@ -0,0 +1,25 @@
+
+### Library
+
+lib_LTLIBRARIES += lib/escape/libescape.la
+
+install_escapeheadersdir = $(includedir)/escape-@VMAJ@
+dist_install_escapeheaders_DATA = \
+lib/escape/Escape.h \
+lib/escape/escape_libgen.h \
+lib/escape/escape_unistd.h
+
+escapemmanheadersdir = $(includedir)/escape-@VMAJ@/sys
+dist_escapemmanheaders_DATA = \
+lib/escape/sys/mman.h
+
+lib_escape_libescape_la_SOURCES = \
+lib/escape/escape_libgen.c \
+lib/escape/escape_mman.c \
+lib/escape/escape_unistd.c
+
+lib_escape_libescape_la_CPPFLAGS = \
+-I$(top_srcdir)/src/lib/escape \
+-I$(top_builddir)/src/lib/escape
+lib_escape_libescape_la_LIBADD = @ESCAPE_LIBS@
+lib_escape_libescape_la_LDFLAGS = -no-undefined -version-info @version_info@
diff --git a/src/Makefile_Evas.am b/src/Makefile_Evas.am
index 616c466b4e..62b86c0f14 100644
--- a/src/Makefile_Evas.am
+++ b/src/Makefile_Evas.am
@@ -439,70 +439,6 @@ modules_evas_engines_buffer_module_la_LIBTOOLFLAGS = --tag=disable-static
endif
endif
-if BUILD_ENGINE_DIRECT3D
-dist_installed_evasmainheaders_DATA += modules/evas/engines/direct3d/Evas_Engine_Direct3D.h
-DIRECT3D_SOURCES = \
-modules/evas/engines/direct3d/evas_engine.c \
-modules/evas/engines/direct3d/evas_direct3d_context.cpp \
-modules/evas/engines/direct3d/evas_direct3d_device.cpp \
-modules/evas/engines/direct3d/evas_direct3d_image_cache.cpp \
-modules/evas/engines/direct3d/evas_direct3d_main.cpp \
-modules/evas/engines/direct3d/evas_direct3d_object.cpp \
-modules/evas/engines/direct3d/evas_direct3d_object_font.cpp \
-modules/evas/engines/direct3d/evas_direct3d_object_image.cpp \
-modules/evas/engines/direct3d/evas_direct3d_object_line.cpp \
-modules/evas/engines/direct3d/evas_direct3d_object_rect.cpp \
-modules/evas/engines/direct3d/evas_direct3d_scene.cpp \
-modules/evas/engines/direct3d/evas_direct3d_shader_pack.cpp \
-modules/evas/engines/direct3d/evas_direct3d_vertex_buffer_cache.cpp \
-modules/evas/engines/direct3d/array.h \
-modules/evas/engines/direct3d/evas_direct3d_context.h \
-modules/evas/engines/direct3d/evas_direct3d_device.h \
-modules/evas/engines/direct3d/evas_direct3d_image_cache.h \
-modules/evas/engines/direct3d/evas_direct3d_object.h \
-modules/evas/engines/direct3d/evas_direct3d_object_font.h \
-modules/evas/engines/direct3d/evas_direct3d_object_image.h \
-modules/evas/engines/direct3d/evas_direct3d_object_line.h \
-modules/evas/engines/direct3d/evas_direct3d_object_rect.h \
-modules/evas/engines/direct3d/evas_direct3d_scene.h \
-modules/evas/engines/direct3d/evas_direct3d_shader_pack.h \
-modules/evas/engines/direct3d/evas_direct3d_vertex_buffer_cache.h \
-modules/evas/engines/direct3d/evas_engine.h \
-modules/evas/engines/direct3d/ref.h
-if EVAS_STATIC_BUILD_DIRECT3D
-lib_evas_libevas_la_SOURCES += $(DIRECT3D_SOURCES)
-lib_evas_libevas_la_CPPFLAGS += @evas_engine_direct3d_cflags@
-lib_evas_libevas_la_CXXFLAGS += -fno-rtti -fno-exceptions
-lib_evas_libevas_la_LIBADD += @evas_engine_direct3d_libs@
-else
-enginedirect3dpkgdir = $(libdir)/evas/modules/engines/direct3d/$(MODULE_ARCH)
-enginedirect3dpkg_LTLIBRARIES = modules/evas/engines/direct3d/module.la
-modules_evas_engines_direct3d_module_la_SOURCES = $(DIRECT3D_SOURCES)
-modules_evas_engines_direct3d_module_la_CPPFLAGS = \
--I$(top_srcdir)/src/lib/eina \
--I$(top_builddir)/src/lib/eina \
--I$(top_srcdir)/src/lib/eo \
--I$(top_builddir)/src/lib/eo \
--I$(top_srcdir)/src/lib/evas \
--I$(top_builddir)/src/lib/evas \
--I$(top_srcdir)/src/lib/evas/include \
--I$(top_builddir)/src/lib/evas/include \
--I$(top_srcdir)/src/lib/evas/cserve2 \
--I$(top_builddir)/src/lib/evas/cserve2 \
--DEFL_EVAS_BUILD \
-@evas_engine_direct3d_cflags@ \
-@EFL_CFLAGS@ \
-@EVAS_CFLAGS@
-modules_evas_engines_direct3d_module_la_CXXFLAGS = -fno-rtti -fno-exceptions
-modules_evas_engines_direct3d_module_la_LIBADD = \
-lib/evas/libevas.la \
-@EFL_LIBS@ \
-@evas_engine_direct3d_libs@
-modules_evas_engines_direct3d_module_la_LDFLAGS = -no-undefined -module -avoid-version
-modules_evas_engines_direct3d_module_la_LIBTOOLFLAGS = --tag=disable-static
-endif
-endif
-
if BUILD_ENGINE_DIRECTFB
dist_installed_evasmainheaders_DATA += modules/evas/engines/directfb/Evas_Engine_DirectFB.h
DIRECTFB_SOURCES = \
diff --git a/src/lib/ecore/Ecore.h b/src/lib/ecore/Ecore.h
new file mode 100644
index 0000000000..5a02211526
--- /dev/null
+++ b/src/lib/ecore/Ecore.h
@@ -0,0 +1,2954 @@
+/**
+ @brief Ecore Library Public API Calls
+
+ These routines are used for Ecore Library interaction
+ */
+
+/**
+
+ @mainpage Ecore
+
+ @version 1.7
+ @date 2000-2012
+
+ Please see the @ref authors page for contact details.
+
+ @section intro Introduction
+
+ Ecore is a library of convenience functions. A brief explanation of how to use
+ it can be found in @ref Ecore_Main_Loop_Page.
+
+ The Ecore library provides the following modules:
+ @li @ref Ecore_Main_Loop_Group
+ @li @ref Ecore_File_Group
+ @li @ref Ecore_Con_Group
+ @li @ref Ecore_Evas_Group
+ @li @ref Ecore_FB_Group
+ @li @ref Ecore_IMF_Lib_Group
+ @li @ref Ecore_IMF_Context_Group
+ @li @ref Ecore_IMF_Context_Module_Group
+ @li @ref Ecore_IMF_Evas_Group
+ @li @link Ecore_Ipc.h Ecore_IPC - Inter Process Communication functions. @endlink
+ @li @link Ecore_X.h Ecore_X - X Windows System wrapper. @endlink
+ @li @ref Ecore_Win32_Group
+ @li @ref Ecore_WinCE_Group
+
+ For more info on Ecore usage, there are these @ref Examples.
+
+ @section compiling How to compile using Ecore?
+ pkgconfig (.pc) files are installed for every ecore module.
+ Thus, to compile using any of them, you can use something like the following:
+
+@verbatim
+gcc *.c $(pkg-config ecore ecore-$x ecore-$y [...] --cflags --libs)
+@endverbatim
+
+ @section install How is it installed?
+
+ Suggested configure options for ecore for a Linux desktop X display
+ with OpenGL and Software support, communication (networking) and
+ IPC (inter process communication):
+
+@verbatim
+./configure \
+ --enable-ecore-con \
+ --enable-ecore-ipc \
+ --enable-ecore-file \
+ --enable-ecore-input \
+ --enable-ecore-input-evas \
+ --enable-ecore-x \
+ --enable-ecore-evas \
+ --enable-ecore-evas-software-buffer \
+ --enable-ecore-evas-software-x11 \
+ --enable-ecore-evas-opengl-x11
+make
+sudo make install
+@endverbatim
+
+ */
+
+/**
+ @page authors Authors
+ @author Carsten Haitzler <raster@rasterman.com>
+ @author Tom Gilbert <tom@linuxbrit.co.uk>
+ @author Burra <burra@colorado.edu>
+ @author Chris Ross <chris@darkrock.co.uk>
+ @author Term <term@twistedpath.org>
+ @author Tilman Sauerbeck <tilman@code-monkey.de>
+ @author Ibukun Olumuyiwa <ibukun@computer.org>
+ @author Yuri <da2001@hotmail.ru>
+ @author Nicholas Curran <quasar@bigblue.net.au>
+ @author Howell Tam <pigeon@pigeond.net>
+ @author Nathan Ingersoll <rbdpngn@users.sourceforge.net>
+ @author Andrew Elcock <andy@elcock.org>
+ @author Kim Woelders <kim@woelders.dk>
+ @author Sebastian Dransfeld <sebastid@tango.flipp.net>
+ @author Simon Poole <simon.armlinux@themalago.net>
+ @author Jorge Luis Zapata Muga <jorgeluis.zapata@gmail.com>
+ @author dan sinclair <zero@everburning.com>
+ @author Michael 'Mickey' Lauer <mickey@tm.informatik.uni-frankfurt.de>
+ @author David 'onefang' Seikel <onefang@gmail.com>
+ @author Hisham 'CodeWarrior' Mardam Bey <hisham@hisham.cc>
+ @author Brian 'rephorm' Mattern <rephorm@rephorm.com>
+ @author Tim Horton <hortont424@gmail.com>
+ @author Arnaud de Turckheim 'quarium' <quarium@gmail.com>
+ @author Matt Barclay <mbarclay@gmail.com>
+ @author Peter Wehrfritz <peter.wehrfritz@web.de>
+ @author Albin "Lutin" Tonnerre <albin.tonnerre@gmail.com>
+ @author Vincent Torri <vincent.torri@gmail.com>
+ @author Lars Munch <lars@segv.dk>
+ @author Andre Dieb <andre.dieb@gmail.com>
+ @author Mathieu Taillefumier <mathieu.taillefumier@free.fr>
+ @author Rui Miguel Silva Seabra <rms@1407.org>
+ @author Samsung Electronics
+ @author Samsung SAIT
+ @author Nicolas Aguirre <aguirre.nicolas@gmail.com>
+ @author Brett Nash <nash@nash.id.au>
+ @author Mike Blumenkrantz <michael.blumenkrantz@gmail.com>
+ @author Leif Middelschulte <leif.middelschulte@gmail.com>
+ @author Mike McCormack <mj.mccormack@samsung.com>
+ @author Sangho Park <gouache95@gmail.com>
+ @author Jihoon Kim <jihoon48.kim@samsung.com> <imfine98@gmail.com>
+ @author PnB <Poor.NewBie@gmail.com>
+ @author Daniel Juyung Seo <seojuyung2@gmail.com> <juyung.seo@samsung.com>
+ @author Christopher 'devilhorns' Michael <cpmichael1@comcast.net>
+ @author ChunEon Park <hermet@hermet.pe.kr>
+ @author xlopez@igalia.com
+ @author Rafael Antognolli <antognolli@profusion.mobi>
+ @author Kim Yunhan <spbear@gmail.com>
+ @author Youness Alaoui <kakaroto@kakaroto.homelinux.net>
+ @author Bluezery <ohpowel@gmail.com>
+ @author Doyoun Kang <wayofmine@gmail.com> <doyoun.kang@samsung.com>
+ @author Haifeng Deng <haifeng.deng@samsung.com>
+ @author Jérémy Zurcher <jeremy@asynk.ch>
+ @author Vikram Narayanan <vikram186@gmail.com>
+
+ Please contact <enlightenment-devel@lists.sourceforge.net> to get in
+ contact with the developers and maintainers.
+ */
+
+/**
+ * @page Ecore_Main_Loop_Page The Ecore Main Loop
+ *
+ * @section intro What is Ecore?
+ *
+ * Ecore is a clean and tiny event loop library with many modules to do lots of
+ * convenient things for a programmer, to save time and effort. It's small and
+ * lean, designed to work from embedded systems all the way up to large and
+ * powerful multi-cpu workstations. The main loop has a number of primitives to
+ * be used with its main loop. It serializes all the primitives and allows for
+ * great responsiveness without the need for threads(or any other concurrency).
+ *
+ * @subsection timers Timers
+ *
+ * Timers serve two main purposes: doing something at a specified time and
+ * repeatedly doing something with a set interval.
+ * @see Ecore_Timer_Group
+ *
+ * @subsection pollers Pollers
+ *
+ * Pollers allow for polling to be centralized into a single place therefore
+ * alleviating the need for different parts of the program to wake up at
+ * different times to do polling, thereby making the code simpler and more
+ * efficient.
+ * @see Ecore_Poller_Group
+ *
+ * @subsection idler Idlers
+ *
+ * There are three types of idlers, enterers, idlers(proper) and exiters, they
+ * are called, respectively, when the program is about to enter an idle state,
+ * when the program is idle and when the program is leaving an idle state. Idler
+ * enterers are usually a good place to update the program state. Proper idlers
+ * are the appropriate place to do heavy computational tasks thereby using what
+ * would otherwise be wasted CPU cycles. Exiters are the perfect place to do
+ * anything your program should do just before processing events(also timers,
+ * poolers, file descriptor handlers and animators)
+ * @see Ecore_Idle_Group
+ *
+ * @subsection fd_handler File descriptor handlers
+ *
+ * File descriptor handlers allow you to monitor when there is data available to
+ * read on file descriptors, when writing will not block or if there was an
+ * error. Any valid file descriptor can be used with this API, regardless of if
+ * was gotten with an OS specific API or from ecore.
+ * @see Ecore_FD_Handler_Group
+ *
+ * @subsection animators Animators
+ *
+ * Ecore provides a facility called animators, so named since the intended use
+ * was in animations, that facilitates knowing what percentage of a given
+ * interval has elapsed. This is perfect for performing animations, but is not
+ * limited to that use, it can, for example, also be used to create a progress
+ * bar.
+ * @see Ecore_Animator_Group
+ *
+ * @subsection ev_handlers Event handlers
+ *
+ * Event handlers are, arguably, the most important feature of the ecore main
+ * loop, they are what allows the programmer to easily handle user interaction.
+ * Events however are not only things the user does, events can represent
+ * anything for which a type is created.
+ * @see Ecore_Event_Group
+ *
+ * All of these primitives are discussed in more detail in their respective
+ * pages linked above.
+ *
+ * Here is a diagram of the main loop flow of a simple program:
+ *
+ * @image html prog_flow.png
+ * @image latex prog_flow.eps width=\textwidth
+ *
+ *
+ *
+ * @section work How does Ecore work?
+ *
+ * Ecore is very easy to learn and use. All the function calls are designed to
+ * be easy to remember, explicit in describing what they do, and heavily
+ * name-spaced. Ecore programs can start and be very simple.
+ *
+ * For example:
+ *
+ * @code
+ * #include <Ecore.h>
+ *
+ * int
+ * main(int argc, const char **argv)
+ * {
+ * ecore_init();
+ * ecore_app_args_set(argc, argv);
+ * ecore_main_loop_begin();
+ * ecore_shutdown();
+ * return 0;
+ * }
+ * @endcode
+ *
+ * This program is very simple and doesn't check for errors, but it does start up
+ * and begin a main loop waiting for events or timers to tick off. This program
+ * doesn't set up any, but now we can expand on this simple program a little
+ * more by adding some event handlers and timers.
+ *
+ * @code
+ * #include <Ecore.h>
+ *
+ * Ecore_Timer *timer1 = NULL;
+ * Ecore_Event_Handler *handler1 = NULL;
+ * double start_time = 0.0;
+ *
+ * int
+ * timer_func(void *data)
+ * {
+ * printf("Tick timer. Sec: %3.2f\n", ecore_time_get() - start_time);
+ * return 1;
+ * }
+ *
+ * int
+ * exit_func(void *data, int ev_type, void *ev)
+ * {
+ * Ecore_Event_Signal_Exit *e;
+ *
+ * e = (Ecore_Event_Signal_Exit *)ev;
+ * if (e->interrupt) printf("Exit: interrupt\n");
+ * else if (e->quit) printf("Exit: quit\n");
+ * else if (e->terminate) printf("Exit: terminate\n");
+ * ecore_main_loop_quit();
+ * return 1;
+ * }
+ *
+ * int
+ * main(int argc, const char **argv)
+ * {
+ * ecore_init();
+ * ecore_app_args_set(argc, argv);
+ * start_time = ecore_time_get();
+ * handler1 = ecore_event_handler_add(ECORE_EVENT_SIGNAL_EXIT, exit_func, NULL);
+ * timer1 = ecore_timer_add(0.5, timer_func, NULL);
+ * ecore_main_loop_begin();
+ * ecore_shutdown();
+ * return 0;
+ * }
+ * @endcode
+ *
+ * In the previous example, we initialize our application and get the time at
+ * which our program has started so we can calculate an offset. We set
+ * up a timer to tick off in 0.5 seconds, and since it returns 1, will
+ * keep ticking off every 0.5 seconds until it returns 0, or is deleted
+ * by hand. An event handler is set up to call a function -
+ * exit_func(),
+ * whenever an event of type ECORE_EVENT_SIGNAL_EXIT is received (CTRL-C
+ * on the command line will cause such an event to happen). If this event
+ * occurs it tells you what kind of exit signal was received, and asks
+ * the main loop to quit when it is finished by calling
+ * ecore_main_loop_quit().
+ *
+ * The handles returned by ecore_timer_add() and
+ * ecore_event_handler_add() are
+ * only stored here as an example. If you don't need to address the timer or
+ * event handler again you don't need to store the result, so just call the
+ * function, and don't assign the result to any variable.
+ *
+ * This program looks slightly more complex than needed to do these simple
+ * things, but in principle, programs don't get any more complex. You add more
+ * event handlers, for more events, will have more timers and such, BUT it all
+ * follows the same principles as shown in this example.
+ *
+ */
+
+/*
+ @page Ecore_Config_Page The Enlightened Property Library
+
+ The Enlightened Property Library (Ecore_Config) is an abstraction
+ from the complexities of writing your own configuration. It provides
+ many features using the Enlightenment 17 development libraries.
+
+ To use the library, you:
+ @li Set the default values of your properties.
+ @li Load the configuration from a file. You must set the default values
+ first, so that the library knows the correct type of each argument.
+
+ The following examples show how to use the Enlightened Property Library:
+ @li @link config_basic_example.c config_basic_example.c @endlink
+ @li @link config_listener_example.c config_listener_example.c @endlink
+
+ */
+
+/**
+ @page X_Window_System_Page X Window System
+
+ The Ecore library includes a wrapper for handling the X window system.
+ This page briefly explains what the X window system is and various terms
+ that are used.
+ */
+
+#ifndef _ECORE_H
+#define _ECORE_H
+
+#ifdef _MSC_VER
+# include <Evil.h>
+#endif
+
+#include <Eina.h>
+
+/* This include has been added to support Eo in Ecore */
+#include <Eo.h>
+
+#ifdef EAPI
+# undef EAPI
+#endif
+
+#ifdef _WIN32
+# ifdef EFL_ECORE_BUILD
+# ifdef DLL_EXPORT
+# define EAPI __declspec(dllexport)
+# else
+# define EAPI
+# endif /* ! DLL_EXPORT */
+# else
+# define EAPI __declspec(dllimport)
+# endif /* ! EFL_ECORE_BUILD */
+#else
+# ifdef __GNUC__
+# if __GNUC__ >= 4
+# define EAPI __attribute__ ((visibility("default")))
+# else
+# define EAPI
+# endif
+# else
+# define EAPI
+# endif
+#endif /* ! _WIN32 */
+
+#ifdef _WIN32
+# include <winsock2.h>
+#elif defined (__FreeBSD__) || defined (__OpenBSD__)
+# include <sys/select.h>
+# include <signal.h>
+#elif defined (__ANDROID__)
+# include <sys/select.h>
+#else
+# include <sys/time.h>
+# if !defined (EXOTIC_NO_SIGNAL)
+# include <signal.h>
+# endif
+#endif
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup Ecore_Init_Group Ecore initialization, shutdown functions and reset on fork.
+ *
+ * @{
+ */
+
+EAPI int ecore_init(void);
+EAPI int ecore_shutdown(void);
+/**
+ * @}
+ */
+
+/**
+ * @defgroup Ecore_Main_Loop_Group Ecore main loop
+ *
+ * This group discusses functions that are acting on Ecore's main loop itself or
+ * on events and infrastructure directly linked to it. Most programs only need
+ * to start and end the main loop, the rest of the function discussed here are
+ * meant to be used in special situations, and with great care.
+ *
+ * For details on the usage of ecore's main loop and how it interacts with other
+ * ecore facilities see: @ref Ecore_Main_Loop_Page.
+ *
+ * @{
+ */
+
+#define ECORE_VERSION_MAJOR 1
+#define ECORE_VERSION_MINOR 8
+
+typedef struct _Ecore_Version
+{
+ int major;
+ int minor;
+ int micro;
+ int revision;
+} Ecore_Version;
+
+EAPI extern Ecore_Version *ecore_version;
+
+#define ECORE_CALLBACK_CANCEL EINA_FALSE /**< Return value to remove a callback */
+#define ECORE_CALLBACK_RENEW EINA_TRUE /**< Return value to keep a callback */
+
+#define ECORE_CALLBACK_PASS_ON EINA_TRUE /**< Return value to pass event to next handler */
+#define ECORE_CALLBACK_DONE EINA_FALSE /**< Return value to stop event handling */
+
+/**
+ * @typedef Ecore_Task_Cb Ecore_Task_Cb
+ * A callback run for a task (timer, idler, poller, animator, etc)
+ */
+typedef Eina_Bool (*Ecore_Task_Cb)(void *data);
+
+/**
+ * @typedef Ecore_Select_Function
+ * A function which can be used to replace select() in the main loop
+ */
+typedef int (*Ecore_Select_Function)(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
+
+EAPI void ecore_main_loop_iterate(void);
+
+EAPI void ecore_main_loop_select_func_set(Ecore_Select_Function func);
+EAPI Ecore_Select_Function ecore_main_loop_select_func_get(void);
+
+EAPI Eina_Bool ecore_main_loop_glib_integrate(void);
+EAPI void ecore_main_loop_glib_always_integrate_disable(void);
+
+EAPI void ecore_main_loop_begin(void);
+EAPI void ecore_main_loop_quit(void);
+
+/**
+ * @typedef Ecore_Cb Ecore_Cb
+ * A generic callback called as a hook when a certain point in
+ * execution is reached.
+ */
+typedef void (*Ecore_Cb)(void *data);
+
+/**
+ * @typedef Ecore_Data_Cb Ecore_Data_Cb
+ * A callback which is used to return data to the main function
+ */
+typedef void *(*Ecore_Data_Cb)(void *data);
+
+/**
+ * Add a function to be called by ecore_fork_reset()
+ *
+ * This queues @p func to be called (and passed @p data as its argument) when
+ * ecore_fork_reset() is called. This allows other libraries and subsystems
+ * to also reset their internal state after a fork.
+ *
+ * @since 1.7
+ */
+EAPI Eina_Bool ecore_fork_reset_callback_add(Ecore_Cb func, const void *data);
+
+/**
+ * This removes the callback specified
+ *
+ * This deletes the callback added by ecore_fork_reset_callback_add() using
+ * the function and data pointer to specify which to remove.
+ *
+ * @since 1.7
+ */
+EAPI Eina_Bool ecore_fork_reset_callback_del(Ecore_Cb func, const void *data);
+
+/**
+ * Reset the ecore internal state after a fork
+ *
+ * Ecore maintains internal data that can be affected by the fork() system call
+ * which creates a duplicate of the current process. This also duplicates
+ * file descriptors which is problematic in that these file descriptors still
+ * point to their original sources. This function makes ecore reset internal
+ * state (e.g. pipes used for signalling between threads) so they function
+ * correctly afterwards.
+ *
+ * It is highly suggested that you call this function after any fork()
+ * system call inside the child process if you intend to use ecore features
+ * after this point and not call any exec() family functions. Not doing so
+ * will cause possible misbehaviour.
+ *
+ * @since 1.7
+ */
+EAPI void ecore_fork_reset(void);
+
+/**
+ * @brief Call callback asynchronously in the main loop.
+ * @since 1.1.0
+ *
+ * @param callback The callback to call in the main loop
+ * @param data The data to give to that call back
+ *
+ * For all calls that need to happen in the main loop (most EFL functions do),
+ * this helper function provides the infrastructure needed to do it safely
+ * by avoiding dead lock, race condition and properly wake up the main loop.
+ *
+ * Remember after that function call, you should never touch again the @p data
+ * in the thread, it is owned by the main loop and your callback should take
+ * care of freeing it if necessary.
+ */
+EAPI void ecore_main_loop_thread_safe_call_async(Ecore_Cb callback, void *data);
+
+/**
+ * @brief Call callback synchronously in the main loop.
+ * @since 1.1.0
+ *
+ * @param callback The callback to call in the main loop
+ * @param data The data to give to that call back
+ * @return the value returned by the callback in the main loop
+ *
+ * For all calls that need to happen in the main loop (most EFL functions do),
+ * this helper function provides the infrastructure needed to do it safely
+ * by avoiding dead lock, race condition and properly wake up the main loop.
+ *
+ * Remember this function will block until the callback is executed in the
+ * main loop. It can take time and you have no guaranty about the timeline.
+ */
+EAPI void *ecore_main_loop_thread_safe_call_sync(Ecore_Data_Cb callback, void *data);
+
+/**
+ * @brief This function suspend the main loop in a know state
+ * @since 1.1.0
+ *
+ * @result the number of time ecore_thread_main_loop_begin() has been called
+ * in this thread, if the main loop was suspended correctly. If not, it return @c -1.
+ *
+ * This function suspend the main loop in a know state, this let you
+ * use any EFL call you want after it return. Be carefully, the main loop
+ * is blocked until you call ecore_thread_main_loop_end(). This is
+ * the only sane way to achieve pseudo thread safety.
+ *
+ * Notice that until the main loop is blocked, the thread is blocked
+ * and their is noway around that.
+ *
+ * We still advise you, when possible, to use ecore_main_loop_thread_safe_call_async()
+ * as it will not block the thread nor the main loop.
+ */
+EAPI int ecore_thread_main_loop_begin(void);
+
+/**
+ * @brief Unlock the main loop.
+ * @since 1.1.0
+ *
+ * @result the number of time ecore_thread_main_loop_end() need to be called before
+ * the main loop is unlocked again. @c -1 will be returned if you are trying to unlock
+ * when there wasn't enough call to ecore_thread_main_loop_begin().
+ *
+ * After a call to ecore_thread_main_loop_begin(), you need to absolutely
+ * call ecore_thread_main_loop_end(), or you application will stay frozen.
+ */
+EAPI int ecore_thread_main_loop_end(void);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup Ecore_Event_Group Ecore Event functions
+ *
+ * Ecore events provide two main features that are of use to those using ecore:
+ * creating events and being notified of events. Those two will usually be used
+ * in different contexts, creating events is mainly done by libraries wrapping
+ * some system functionality while being notified of events is mainly a
+ * necessity of applications.
+ *
+ * For a program to be notified of events it's interested in it needs to have a
+ * function to process the event and to register that function as the callback
+ * to the event, that's all:
+ * @code
+ * ecore_event_handler_add(EVENT_TYPE, _my_event_handler, some_data);
+ * ...
+ * static Eina_Bool
+ * _my_event_handler(void *data, int type, void *event)
+ * {
+ * //data is some_data
+ * //event is provided by whoever created the event
+ * //Do really cool stuff with event
+ * }
+ * @endcode
+ *
+ * One very important thing to note here is the @c EVENT_TYPE, to register a
+ * handler for an event you must know its type before hand. Ecore provides
+ * the following events which are emitted in response to POSIX
+ * signals(https://en.wikipedia.org/wiki/Signal_%28computing%29):
+ * @li @b ECORE_EVENT_SIGNAL_USER
+ * @li @b ECORE_EVENT_SIGNAL_HUP
+ * @li @b ECORE_EVENT_SIGNAL_POWER
+ * @li @b ECORE_EVENT_SIGNAL_EXIT
+ *
+ * @warning Don't override these using the @c signal or @c sigaction calls.
+ * These, however, aren't the only signals one can handle. Many
+ * libraries(including ecore modules) have their own signals that can be
+ * listened for and handled, to do that one only needs to know the type of the
+ * event. This information can be found on the documentation of the library
+ * emitting the signal, so, for example, for events related to windowing one
+ * would look in @ref Ecore_Evas_Group.
+ *
+ * Examples of libraries that integrate into ecore's main loop by providing
+ * events are @ref Ecore_Con_Group, @ref Ecore_Evas_Group and @ref
+ * Ecore_Exe_Group, amongst others. This usage can be divided into two parts,
+ * setup and adding events. The setup is very simple, all that needs doing is
+ * getting a type id for the event:
+ * @code
+ * int MY_EV_TYPE = ecore_event_type_new();
+ * @endcode
+ * @note This variable should be declared in the header since it'll be needed by
+ * anyone wishing to register a handler to your event.
+ *
+ * The complexity of adding of an event to the queue depends on whether that
+ * event sends uses @c event, if it doesn't it a one-liner:
+ * @code
+ * ecore_event_add(MY_EV_TYPE, NULL, NULL, NULL);
+ * @endcode
+ * The usage when an @c event is needed is not that much more complex and can be
+ * seen in @ref ecore_event_add.
+ *
+ * Examples that deals with events:
+ * @li @ref ecore_event_example_01_c
+ * @li @ref ecore_event_example_02_c
+ *
+ * @ingroup Ecore_Main_Loop_Group
+ *
+ * @{
+ */
+
+#define ECORE_EVENT_NONE 0
+#define ECORE_EVENT_SIGNAL_USER 1 /**< User signal event */
+#define ECORE_EVENT_SIGNAL_HUP 2 /**< Hup signal event */
+#define ECORE_EVENT_SIGNAL_EXIT 3 /**< Exit signal event */
+#define ECORE_EVENT_SIGNAL_POWER 4 /**< Power signal event */
+#define ECORE_EVENT_SIGNAL_REALTIME 5 /**< Realtime signal event */
+#define ECORE_EVENT_COUNT 6
+
+typedef struct _Ecore_Win32_Handler Ecore_Win32_Handler; /**< A handle for HANDLE handlers on Windows */
+typedef struct _Ecore_Event_Handler Ecore_Event_Handler; /**< A handle for an event handler */
+typedef struct _Ecore_Event_Filter Ecore_Event_Filter; /**< A handle for an event filter */
+typedef struct _Ecore_Event Ecore_Event; /**< A handle for an event */
+typedef struct _Ecore_Event_Signal_User Ecore_Event_Signal_User; /**< User signal event */
+typedef struct _Ecore_Event_Signal_Hup Ecore_Event_Signal_Hup; /**< Hup signal event */
+typedef struct _Ecore_Event_Signal_Exit Ecore_Event_Signal_Exit; /**< Exit signal event */
+typedef struct _Ecore_Event_Signal_Power Ecore_Event_Signal_Power; /**< Power signal event */
+typedef struct _Ecore_Event_Signal_Realtime Ecore_Event_Signal_Realtime; /**< Realtime signal event */
+
+/**
+ * @typedef Ecore_Filter_Cb
+ * A callback used for filtering events from the main loop.
+ */
+typedef Eina_Bool (*Ecore_Filter_Cb)(void *data, void *loop_data, int type, void *event);
+
+/**
+ * @typedef Ecore_End_Cb Ecore_End_Cb
+ * This is the callback which is called at the end of a function,
+ * usually for cleanup purposes.
+ */
+typedef void (*Ecore_End_Cb)(void *user_data, void *func_data);
+
+/**
+ * @typedef Ecore_Event_Handler_Cb Ecore_Event_Handler_Cb
+ * A callback used by the main loop to handle events of a specified
+ * type.
+ */
+typedef Eina_Bool (*Ecore_Event_Handler_Cb)(void *data, int type, void *event);
+
+struct _Ecore_Event_Signal_User /** User signal event */
+{
+ int number; /**< The signal number. Either 1 or 2 */
+ void *ext_data; /**< Extension data - not used */
+
+#if !defined (_WIN32) && !defined (__lv2ppu__) && !defined (EXOTIC_NO_SIGNAL)
+ siginfo_t data; /**< Signal info */
+#endif
+};
+
+struct _Ecore_Event_Signal_Hup /** Hup signal event */
+{
+ void *ext_data; /**< Extension data - not used */
+
+#if !defined (_WIN32) && !defined (__lv2ppu__) && !defined (EXOTIC_NO_SIGNAL)
+ siginfo_t data; /**< Signal info */
+#endif
+};
+
+struct _Ecore_Event_Signal_Exit /** Exit request event */
+{
+ Eina_Bool interrupt : 1; /**< Set if the exit request was an interrupt signal*/
+ Eina_Bool quit : 1; /**< set if the exit request was a quit signal */
+ Eina_Bool terminate : 1; /**< Set if the exit request was a terminate signal */
+ void *ext_data; /**< Extension data - not used */
+
+#if !defined (_WIN32) && !defined (__lv2ppu__) && !defined (EXOTIC_NO_SIGNAL)
+ siginfo_t data; /**< Signal info */
+#endif
+};
+
+struct _Ecore_Event_Signal_Power /** Power event */
+{
+ void *ext_data; /**< Extension data - not used */
+
+#if !defined (_WIN32) && !defined (__lv2ppu__) && !defined (EXOTIC_NO_SIGNAL)
+ siginfo_t data; /**< Signal info */
+#endif
+};
+
+struct _Ecore_Event_Signal_Realtime /** Realtime event */
+{
+ int num; /**< The realtime signal's number */
+
+#if !defined (_WIN32) && !defined (__lv2ppu__) && !defined (EXOTIC_NO_SIGNAL)
+ siginfo_t data; /**< Signal info */
+#endif
+};
+
+/**
+ * @brief Add an event handler.
+ * @param type The type of the event this handler will get called for
+ * @param func The function to call when the event is found in the queue
+ * @param data A data pointer to pass to the called function @p func
+ * @return A new Event handler, or @c NULL on failure.
+ *
+ * Add an event handler to the list of handlers. This will, on success, return
+ * a handle to the event handler object that was created, that can be used
+ * later to remove the handler using ecore_event_handler_del(). The @p type
+ * parameter is the integer of the event type that will trigger this callback
+ * to be called. The callback @p func is called when this event is processed
+ * and will be passed the event type, a pointer to the private event
+ * structure that is specific to that event type, and a data pointer that is
+ * provided in this call as the @p data parameter.
+ *
+ * When the callback @p func is called, it must return 1 or 0. If it returns
+ * 1 (or ECORE_CALLBACK_PASS_ON), It will keep being called as per normal, for
+ * each handler set up for that event type. If it returns 0 (or
+ * ECORE_CALLBACK_DONE), it will cease processing handlers for that particular
+ * event, so all handler set to handle that event type that have not already
+ * been called, will not be.
+ */
+EAPI Ecore_Event_Handler *ecore_event_handler_add(int type, Ecore_Event_Handler_Cb func, const void *data);
+/**
+ * @brief Delete an event handler.
+ * @param event_handler Event handler handle to delete
+ * @return Data passed to handler
+ *
+ * Delete a specified event handler from the handler list. On success this will
+ * delete the event handler and return the pointer passed as @p data when the
+ * handler was added by ecore_event_handler_add(). On failure @c NULL will be
+ * returned. Once a handler is deleted it will no longer be called.
+ */
+EAPI void *ecore_event_handler_del(Ecore_Event_Handler *event_handler);
+/**
+ * @brief Add an event to the event queue.
+ * @param type The event type to add to the end of the event queue
+ * @param ev The data structure passed as @c event to event handlers
+ * @param func_free The function to be called to free @a ev
+ * @param data The data pointer to be passed to the free function
+ * @return A Handle for that event on success, otherwise NULL
+ *
+ * If it succeeds, an event of type @a type will be added to the queue for
+ * processing by event handlers added by ecore_event_handler_add(). The @a ev
+ * parameter will be passed as the @c event parameter of the handler. When the
+ * event is no longer needed, @a func_free will be called and passed @a ev for
+ * cleaning up. If @p func_free is NULL, free() will be called with the private
+ * structure pointer.
+ */
+EAPI Ecore_Event *ecore_event_add(int type, void *ev, Ecore_End_Cb func_free, void *data);
+/**
+ * @brief Delete an event from the queue.
+ * @param event The event handle to delete
+ * @return The data pointer originally set for the event free function
+ *
+ * This deletes the event @p event from the event queue, and returns the
+ * @p data parameter originally set when adding it with ecore_event_add(). This
+ * does not immediately call the free function, and it may be called later on
+ * cleanup, and so if the free function depends on the data pointer to work,
+ * you should defer cleaning of this till the free function is called later.
+ */
+EAPI void *ecore_event_del(Ecore_Event *event);
+/**
+ * @brief Get the data associated with an #Ecore_Event_Handler
+ * @param eh The event handler
+ * @return The data
+ *
+ * This function returns the data previously associated with @p eh by
+ * ecore_event_handler_add().
+ */
+EAPI void *ecore_event_handler_data_get(Ecore_Event_Handler *eh);
+/**
+ * @brief Set the data associated with an #Ecore_Event_Handler
+ * @param eh The event handler
+ * @param data The data to associate
+ * @return The previous data
+ *
+ * This function sets @p data to @p eh and returns the old data pointer
+ * which was previously associated with @p eh by ecore_event_handler_add().
+ */
+EAPI void *ecore_event_handler_data_set(Ecore_Event_Handler *eh, const void *data);
+/**
+ * @brief Allocate a new event type id sensibly and return the new id.
+ * @return A new event type id.
+ *
+ * This function allocates a new event type id and returns it. Once an event
+ * type has been allocated it can never be de-allocated during the life of
+ * the program. There is no guarantee of the contents of this event ID, or how
+ * it is calculated, except that the ID will be unique to the current instance
+ * of the process.
+ */
+EAPI int ecore_event_type_new(void);
+/**
+ * @brief Add a filter the current event queue.
+ *
+ * @param func_start Function to call just before filtering and return data
+ * @param func_filter Function to call on each event
+ * @param func_end Function to call after the queue has been filtered
+ * @param data Data to pass to the filter functions
+ * @return A filter handle on success, @c NULL otherwise.
+ *
+ * Adds a callback to filter events from the event queue. Filters are called on
+ * the queue just before Event handler processing to try and remove redundant
+ * events. Just as processing is about to start @a func_start is called and
+ * passed the @a data pointer, the return value of this functions is passed to
+ * @a func_filter as loop_data. @a func_filter is also passed @a data and the
+ * event type and event structure. If this @a func_filter returns
+ * @c EINA_FALSE, the event is removed from the queue, if it returns
+ * @c EINA_TRUE, the event is kept. When processing is finished @p func_end is
+ * called and is passed the loop_data(returned by @c func_start) and @p data
+ * pointer to clean up.
+ */
+EAPI Ecore_Event_Filter *ecore_event_filter_add(Ecore_Data_Cb func_start, Ecore_Filter_Cb func_filter, Ecore_End_Cb func_end, const void *data);
+/**
+ * @brief Delete an event filter.
+ * @param ef The event filter handle
+ * @return The data set for the filter on success, @c NULL otherwise.
+ *
+ * Delete a filter that has been added by its @p ef handle.
+ */
+EAPI void *ecore_event_filter_del(Ecore_Event_Filter *ef);
+/**
+ * @brief Return the current event type being handled.
+ * @return The current event type being handled if inside a handler callback,
+ * ECORE_EVENT_NONE otherwise
+ *
+ * If the program is currently inside an Ecore event handler callback this
+ * will return the type of the current event being processed.
+ *
+ * This is useful when certain Ecore modules such as Ecore_Evas "swallow"
+ * events and not all the original information is passed on. In special cases
+ * this extra information may be useful or needed and using this call can let
+ * the program know if the event type being handled is one it wants to get more
+ * information about.
+ */
+EAPI int ecore_event_current_type_get(void);
+/**
+ * @brief Return the current event type pointer handled.
+ * @return The current event pointer being handled if inside a handler callback,
+ * @c NULL otherwise.
+ *
+ * If the program is currently inside an Ecore event handler callback this
+ * will return the pointer of the current event being processed.
+ *
+ * This is useful when certain Ecore modules such as Ecore_Evas "swallow"
+ * events and not all the original information is passed on. In special cases
+ * this extra information may be useful or needed and using this call can let
+ * the program access the event data if the type of the event is handled by
+ * the program.
+ */
+EAPI void *ecore_event_current_event_get(void);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup Ecore_Exe_Group Process Spawning Functions
+ *
+ * This module is responsible for managing portable processes using Ecore.
+ * With this module you're able to spawn processes and you also can pause,
+ * quit your spawned processes.
+ * An interaction between your process and those spawned is possible
+ * using pipes or signals.
+ *
+ * Example
+ * @li @ref Ecore_exe_simple_example_c
+ *
+ * @ingroup Ecore_Main_Loop_Group
+ *
+ * @{
+ */
+
+/** Inherit priority from parent process */
+#define ECORE_EXE_PRIORITY_INHERIT 9999
+
+EAPI extern int ECORE_EXE_EVENT_ADD; /**< A child process has been added */
+EAPI extern int ECORE_EXE_EVENT_DEL; /**< A child process has been deleted (it exited, naming consistent with the rest of ecore). */
+EAPI extern int ECORE_EXE_EVENT_DATA; /**< Data from a child process. */
+EAPI extern int ECORE_EXE_EVENT_ERROR; /**< Errors from a child process. */
+
+/**
+ * @enum _Ecore_Exe_Flags
+ * Flags for executing a child with its stdin and/or stdout piped back.
+ */
+enum _Ecore_Exe_Flags /* flags for executing a child with its stdin and/or stdout piped back */
+{
+ ECORE_EXE_NONE = 0, /**< No exe flags at all */
+ ECORE_EXE_PIPE_READ = 1, /**< Exe Pipe Read mask */
+ ECORE_EXE_PIPE_WRITE = 2, /**< Exe Pipe Write mask */
+ ECORE_EXE_PIPE_ERROR = 4, /**< Exe Pipe error mask */
+ ECORE_EXE_PIPE_READ_LINE_BUFFERED = 8, /**< Reads are buffered until a newline and split 1 line per Ecore_Exe_Event_Data_Line */
+ ECORE_EXE_PIPE_ERROR_LINE_BUFFERED = 16, /**< Errors are buffered until a newline and split 1 line per Ecore_Exe_Event_Data_Line */
+ ECORE_EXE_PIPE_AUTO = 32, /**< stdout and stderr are buffered automatically */
+ ECORE_EXE_RESPAWN = 64, /**< FIXME: Exe is restarted if it dies */
+ ECORE_EXE_USE_SH = 128, /**< Use /bin/sh to run the command. */
+ ECORE_EXE_NOT_LEADER = 256, /**< Do not use setsid() to have the executed process be its own session leader */
+ ECORE_EXE_TERM_WITH_PARENT = 512 /**< Makes child receive SIGTERM when parent dies. */
+};
+typedef enum _Ecore_Exe_Flags Ecore_Exe_Flags;
+
+/**
+ * @enum _Ecore_Exe_Win32_Priority
+ * Defines the priority of the proccess.
+ */
+enum _Ecore_Exe_Win32_Priority
+{
+ ECORE_EXE_WIN32_PRIORITY_IDLE, /**< Idle priority, for monitoring the system */
+ ECORE_EXE_WIN32_PRIORITY_BELOW_NORMAL, /**< Below default priority */
+ ECORE_EXE_WIN32_PRIORITY_NORMAL, /**< Default priority */
+ ECORE_EXE_WIN32_PRIORITY_ABOVE_NORMAL, /**< Above default priority */
+ ECORE_EXE_WIN32_PRIORITY_HIGH, /**< High priority, use with care as other threads in the system will not get processor time */
+ ECORE_EXE_WIN32_PRIORITY_REALTIME /**< Realtime priority, should be almost never used as it can interrupt system threads that manage mouse input, keyboard input, and background disk flushing */
+};
+typedef enum _Ecore_Exe_Win32_Priority Ecore_Exe_Win32_Priority;
+
+typedef struct _Ecore_Exe Ecore_Exe; /**< A handle for spawned processes */
+
+/**
+ * @typedef Ecore_Exe_Cb Ecore_Exe_Cb
+ * A callback to run with the associated @ref Ecore_Exe, usually
+ * for cleanup purposes.
+ */
+typedef void (*Ecore_Exe_Cb)(void *data, const Ecore_Exe *exe);
+
+typedef struct _Ecore_Exe_Event_Add Ecore_Exe_Event_Add; /**< Spawned Exe add event */
+typedef struct _Ecore_Exe_Event_Del Ecore_Exe_Event_Del; /**< Spawned Exe exit event */
+typedef struct _Ecore_Exe_Event_Data_Line Ecore_Exe_Event_Data_Line; /**< Lines from a child process */
+typedef struct _Ecore_Exe_Event_Data Ecore_Exe_Event_Data; /**< Data from a child process */
+
+struct _Ecore_Exe_Event_Add /** Process add event */
+{
+ Ecore_Exe *exe; /**< The handle to the added process */
+ void *ext_data; /**< Extension data - not used */
+};
+
+struct _Ecore_Exe_Event_Del /** Process exit event */
+{
+ pid_t pid; /**< The process ID of the process that exited */
+ int exit_code; /**< The exit code of the process */
+ Ecore_Exe *exe; /**< The handle to the exited process, or @c NULL if not found */
+ int exit_signal; /** < The signal that caused the process to exit */
+ Eina_Bool exited : 1; /** < set to 1 if the process exited of its own accord */
+ Eina_Bool signalled : 1; /** < set to 1 id the process exited due to uncaught signal */
+ void *ext_data; /**< Extension data - not used */
+#if !defined (_WIN32) && !defined (__lv2ppu__) && !defined (EXOTIC_NO_SIGNAL)
+ siginfo_t data; /**< Signal info */
+#endif
+};
+
+struct _Ecore_Exe_Event_Data_Line /**< Lines from a child process */
+{
+ char *line; /**< The bytes of a line of buffered data */
+ int size; /**< The size of the line buffer in bytes */
+};
+
+struct _Ecore_Exe_Event_Data /** Data from a child process event */
+{
+ Ecore_Exe *exe; /**< The handle to the process */
+ void *data; /**< the raw binary data from the child process that was received */
+ int size; /**< the size of this data in bytes */
+ Ecore_Exe_Event_Data_Line *lines; /**< an array of line data if line buffered, the last one has it's line member set to @c NULL */
+};
+
+EAPI void ecore_exe_run_priority_set(int pri);
+EAPI int ecore_exe_run_priority_get(void);
+EAPI Ecore_Exe *ecore_exe_run(const char *exe_cmd, const void *data);
+EAPI Ecore_Exe *ecore_exe_pipe_run(const char *exe_cmd, Ecore_Exe_Flags flags, const void *data);
+EAPI void ecore_exe_callback_pre_free_set(Ecore_Exe *exe, Ecore_Exe_Cb func);
+EAPI Eina_Bool ecore_exe_send(Ecore_Exe *exe, const void *data, int size);
+EAPI void ecore_exe_close_stdin(Ecore_Exe *exe);
+EAPI void ecore_exe_auto_limits_set(Ecore_Exe *exe, int start_bytes, int end_bytes, int start_lines, int end_lines);
+EAPI Ecore_Exe_Event_Data *ecore_exe_event_data_get(Ecore_Exe *exe, Ecore_Exe_Flags flags);
+EAPI void ecore_exe_event_data_free(Ecore_Exe_Event_Data *data);
+EAPI void *ecore_exe_free(Ecore_Exe *exe);
+EAPI pid_t ecore_exe_pid_get(const Ecore_Exe *exe);
+EAPI void ecore_exe_tag_set(Ecore_Exe *exe, const char *tag);
+EAPI const char *ecore_exe_tag_get(const Ecore_Exe *exe);
+EAPI const char *ecore_exe_cmd_get(const Ecore_Exe *exe);
+EAPI void *ecore_exe_data_get(const Ecore_Exe *exe);
+EAPI void *ecore_exe_data_set(Ecore_Exe *exe, void *data);
+EAPI Ecore_Exe_Flags ecore_exe_flags_get(const Ecore_Exe *exe);
+EAPI void ecore_exe_pause(Ecore_Exe *exe);
+EAPI void ecore_exe_continue(Ecore_Exe *exe);
+EAPI void ecore_exe_interrupt(Ecore_Exe *exe);
+EAPI void ecore_exe_quit(Ecore_Exe *exe);
+EAPI void ecore_exe_terminate(Ecore_Exe *exe);
+EAPI void ecore_exe_kill(Ecore_Exe *exe);
+EAPI void ecore_exe_signal(Ecore_Exe *exe, int num);
+EAPI void ecore_exe_hup(Ecore_Exe *exe);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup Ecore_FD_Handler_Group File Descriptor Handling Functions
+ *
+ * @brief Functions that deal with file descriptor handlers.
+ *
+ * File descriptor handlers facilitate reading, writing and checking for errors
+ * without blocking the program or doing expensive pooling. This can be used to
+ * monitor a socket, pipe, or other stream for which an FD can be had.
+ *
+ * @warning File descriptor handlers can't be used to monitor for file creation,
+ * modification or deletion, see @ref Ecore_File_Group for this.
+ *
+ * One common FD to be monitored is the standard input(stdin), monitoring it for
+ * reading requires a single call:
+ * @code
+ * static Eina_Bool
+ * _my_cb_func(void *data, Ecore_Fd_Handler *handler)
+ * {
+ * char c;
+ * scanf("%c", &c); //Guaranteed not to block
+ * ... do stuff with c ...
+ * }
+ * ecore_main_fd_handler_add(STDIN_FILENO, ECORE_FD_READ, _my_cb_func, NULL, NULL, NULL);
+ * @endcode
+ *
+ * When using a socket, pipe or other stream it's important to remember that
+ * errors may occur and as such to monitor not only for reading/writing but also
+ * for errors using the @ref ECORE_FD_ERROR flag.
+ *
+ * Example of use of a file descriptor handler:
+ * @li @ref ecore_fd_handler_example_c
+ *
+ * @ingroup Ecore_Main_Loop_Group
+ *
+ * @{
+ */
+
+typedef struct _Ecore_Fd_Handler Ecore_Fd_Handler; /**< A handle for Fd handlers */
+
+/**
+ * @enum _Ecore_Fd_Handler_Flags
+ * What to monitor the file descriptor for: reading, writing or error.
+ */
+enum _Ecore_Fd_Handler_Flags
+{
+ ECORE_FD_READ = 1, /**< Fd Read mask */
+ ECORE_FD_WRITE = 2, /**< Fd Write mask */
+ ECORE_FD_ERROR = 4 /**< Fd Error mask */
+};
+typedef enum _Ecore_Fd_Handler_Flags Ecore_Fd_Handler_Flags;
+
+/**
+ * @typedef Ecore_Fd_Cb Ecore_Fd_Cb
+ * A callback used by an @ref Ecore_Fd_Handler.
+ */
+typedef Eina_Bool (*Ecore_Fd_Cb)(void *data, Ecore_Fd_Handler *fd_handler);
+
+/**
+ * @typedef Ecore_Fd_Prep_Cb Ecore_Fd_Prep_Cb
+ * A callback used by an @ref Ecore_Fd_Handler.
+ */
+typedef void (*Ecore_Fd_Prep_Cb)(void *data, Ecore_Fd_Handler *fd_handler);
+
+/**
+ * @typedef Ecore_Win32_Handle_Cb Ecore_Win32_Handle_Cb
+ * A callback used by an @ref Ecore_Win32_Handler.
+ */
+typedef Eina_Bool (*Ecore_Win32_Handle_Cb)(void *data, Ecore_Win32_Handler *wh);
+
+/**
+ * @brief Adds a callback for activity on the given file descriptor.
+ *
+ * @param fd The file descriptor to watch.
+ * @param flags To monitor it for reading use @c ECORE_FD_READ, for writing @c
+ * ECORE_FD_WRITE, and for error @c ECORE_FD_ERROR. Values by |(ored).
+ * @param func The callback function.
+ * @param data The data to pass to the callback.
+ * @param buf_func The function to call to check if any data has been buffered
+ * and already read from the fd. May be @c NULL.
+ * @param buf_data The data to pass to the @p buf_func function.
+ * @return A fd handler handle on success, @c NULL otherwise.
+ *
+ * @a func will be called during the execution of @ref Ecore_Main_Loop_Page
+ * when the file descriptor is available for reading, writing, or there has been
+ * an error(depending on the given @a flags).
+ *
+ * When @a func returns ECORE_CALLBACK_CANCEL, it indicates that the
+ * handler should be marked for deletion (identical to calling @ref
+ * ecore_main_fd_handler_del).
+ *
+ * @warning @a buf_func is meant for @b internal use only and should be @b
+ * avoided.
+ *
+ * The return value of @a buf_func has a different meaning, when it returns
+ * ECORE_CALLBACK_CANCEL, it indicates that @a func @b shouldn't be called, and
+ * when it returns ECORE_CALLBACK_RENEW it indicates @a func should be called.
+ * The return value of @a buf_func will not cause the FD handler to be deleted.
+ *
+ * @a buf_func is called during event loop handling to check if data that has
+ * been read from the file descriptor is in a buffer and is available to read.
+ * Some systems, notably xlib, handle their own buffering, and would otherwise
+ * not work with select(). These systems should use a @a buf_func. This is a
+ * most annoying hack, only ecore_x uses it, so refer to that for an example.
+ *
+ * @warning This function should @b not be used for monitoring "normal" files, like text files.
+ *
+ */
+EAPI Ecore_Fd_Handler *ecore_main_fd_handler_add(int fd, Ecore_Fd_Handler_Flags flags, Ecore_Fd_Cb func, const void *data, Ecore_Fd_Cb buf_func, const void *buf_data);
+
+/**
+ * @brief Adds a callback for activity on the given file descriptor.
+ *
+ * @param fd The file descriptor to watch.
+ * @param flags To monitor it for reading use @c ECORE_FD_READ, for writing @c
+ * ECORE_FD_WRITE, and for error @c ECORE_FD_ERROR. Values by |(ored).
+ * @param func The callback function.
+ * @param data The data to pass to the callback.
+ * @param buf_func The function to call to check if any data has been buffered
+ * and already read from the fd. May be @c NULL.
+ * @param buf_data The data to pass to the @p buf_func function.
+ * @return A fd handler handle on success, @c NULL otherwise.
+ *
+ * This function is identical to ecore_main_fd_handler_add, except that it supports regular files.
+ * @warning This function should ONLY be called with ECORE_FD_ERROR, otherwise it will call the fd
+ * handler constantly.
+ * @warning Do not use this function unless you know what you are doing.
+ *
+ * @since 1.7
+ */
+EAPI Ecore_Fd_Handler *ecore_main_fd_handler_file_add(int fd, Ecore_Fd_Handler_Flags flags, Ecore_Fd_Cb func, const void *data, Ecore_Fd_Cb buf_func, const void *buf_data);
+
+/**
+ * @brief Set the prepare callback with data for a given #Ecore_Fd_Handler
+ *
+ * @param fd_handler The fd handler
+ * @param func The prep function
+ * @param data The data to pass to the prep function
+ *
+ * This function will be called prior to any fd handler's callback function
+ * (even the other fd handlers), before entering the main loop select function.
+ *
+ * @note Once a prepare callback is set for a fd handler, it cannot be changed.
+ * You need to delete the fd handler and create a new one, to set another
+ * callback.
+ * @note You probably don't need this function. It is only necessary for very
+ * uncommon cases that need special behavior.
+ */
+EAPI void ecore_main_fd_handler_prepare_callback_set(Ecore_Fd_Handler *fd_handler, Ecore_Fd_Prep_Cb func, const void *data);
+/**
+ * @brief Marks an FD handler for deletion.
+ * @param fd_handler The FD handler.
+ * @return The data pointer set using @ref ecore_main_fd_handler_add, for
+ * @a fd_handler on success, @c NULL otherwise.
+ * This function marks an fd handler to be deleted during an iteration of the
+ * main loop. It does NOT close the associated fd!
+ *
+ * @warning If the underlying fd is already closed ecore may complain if the
+ * main loop is using epoll internally, and also in some rare cases this may
+ * cause crashes and instability. Remember to delete your fd handlers before the
+ * fds they listen to are closed.
+ */
+EAPI void *ecore_main_fd_handler_del(Ecore_Fd_Handler *fd_handler);
+/**
+ * @brief Retrieves the file descriptor that the given handler is handling.
+ * @param fd_handler The given FD handler.
+ * @return The file descriptor the handler is watching.
+ */
+EAPI int ecore_main_fd_handler_fd_get(Ecore_Fd_Handler *fd_handler);
+/**
+ * @brief Gets which flags are active on an FD handler.
+ * @param fd_handler The given FD handler.
+ * @param flags The flags, @c ECORE_FD_READ, @c ECORE_FD_WRITE or
+ * @c ECORE_FD_ERROR to query.
+ * @return @c EINA_TRUE if any of the given flags are active, @c EINA_FALSE
+ * otherwise.
+ */
+EAPI Eina_Bool ecore_main_fd_handler_active_get(Ecore_Fd_Handler *fd_handler, Ecore_Fd_Handler_Flags flags);
+/**
+ * @brief Set what active streams the given FD handler should be monitoring.
+ * @param fd_handler The given FD handler.
+ * @param flags The flags to be watching.
+ */
+EAPI void ecore_main_fd_handler_active_set(Ecore_Fd_Handler *fd_handler, Ecore_Fd_Handler_Flags flags);
+
+EAPI Ecore_Win32_Handler *ecore_main_win32_handler_add(void *h, Ecore_Win32_Handle_Cb func, const void *data);
+EAPI void *ecore_main_win32_handler_del(Ecore_Win32_Handler *win32_handler);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup Ecore_Poller_Group Ecore Poll functions
+ *
+ * Ecore poller provides infrastructure for the creation of pollers. Pollers
+ * are, in essence, callbacks that share a single timer per type. Because not
+ * all pollers need to be called at the same frequency the user may specify the
+ * frequency in ticks(each expiration of the shared timer is called a tick, in
+ * ecore poller parlance) for each added poller. Ecore pollers should only be
+ * used when the poller doesn't have specific requirements on the exact times
+ * to poll.
+ *
+ * This architecture means that the main loop is only woken up once to handle
+ * all pollers of that type, this will save power as the CPU has more of a
+ * chance to go into a low power state the longer it is asleep for, so this
+ * should be used in situations where power usage is a concern.
+ *
+ * For now only 1 core poller type is supported: ECORE_POLLER_CORE, the default
+ * interval for ECORE_POLLER_CORE is 0.125(or 1/8th) second.
+ *
+ * The creation of a poller is extremely simple and only requires one line:
+ * @code
+ * ecore_poller_add(ECORE_POLLER_CORE, 1, my_poller_function, NULL);
+ * @endcode
+ * This sample creates a poller to call @c my_poller_function at every tick with
+ * @c NULL as data.
+ *
+ * Example:
+ * @li @ref ecore_poller_example_c
+ *
+ * @ingroup Ecore_Main_Loop_Group
+ *
+ * @{
+ */
+
+/**
+ * @enum _Ecore_Poller_Type
+ * Defines the frequency of ticks for the poller.
+ */
+enum _Ecore_Poller_Type /* Poller types */
+{
+ ECORE_POLLER_CORE = 0 /**< The core poller interval */
+};
+typedef enum _Ecore_Poller_Type Ecore_Poller_Type;
+
+/*
+ * @since 1.8
+ */
+
+typedef Eo Ecore_Poller; /**< A handle for pollers */
+
+#define ECORE_POLLER_CLASS ecore_poller_class_get()
+const Eo_Class *ecore_poller_class_get(void) EINA_CONST;
+extern EAPI Eo_Op ECORE_POLLER_BASE_ID;
+
+enum
+{
+ ECORE_POLLER_SUB_ID_CONSTRUCTOR,
+ ECORE_POLLER_SUB_ID_INTERVAL_SET,
+ ECORE_POLLER_SUB_ID_INTERVAL_GET,
+ ECORE_POLLER_SUB_ID_LAST,
+};
+
+#define ECORE_POLLER_ID(sub_id) (ECORE_POLLER_BASE_ID + sub_id)
+
+/**
+ * @def ecore_poller_constructor
+ * @since 1.8
+ *
+ * Contructor with parameters for Ecore Poller.
+ *
+ * @param[in] type
+ * @param[in] interval
+ * @param[in] func
+ * @param[in] data
+ *
+ */
+#define ecore_poller_constructor(type, interval, func, data) ECORE_POLLER_ID(ECORE_POLLER_SUB_ID_CONSTRUCTOR), EO_TYPECHECK(Ecore_Poller_Type, type), EO_TYPECHECK(int, interval), EO_TYPECHECK(Ecore_Task_Cb, func), EO_TYPECHECK(const void *, data)
+
+/**
+ * @def ecore_poller_interval_set
+ * @since 1.8
+ *
+ * Changes the polling interval rate of poller.
+ *
+ * @param[in] interval
+ * @param[out] ret
+ *
+ * @see ecore_poller_poller_interval_set
+ */
+#define ecore_poller_interval_set(interval, ret) ECORE_POLLER_ID(ECORE_POLLER_SUB_ID_INTERVAL_SET), EO_TYPECHECK(int, interval), EO_TYPECHECK(Eina_Bool *, ret)
+
+/**
+ * @def ecore_poller_interval_get
+ * @since 1.8
+ *
+ * Gets the polling interval rate of poller.
+ *
+ * @param[out] ret
+ *
+ * @see ecore_poller_poller_interval_get
+ */
+#define ecore_poller_interval_get(ret) ECORE_POLLER_ID(ECORE_POLLER_SUB_ID_INTERVAL_GET), EO_TYPECHECK(int *, ret)
+
+
+/**
+ * @brief Sets the time(in seconds) between ticks for the given poller type.
+ * @param type The poller type to adjust.
+ * @param poll_time The time(in seconds) between ticks of the timer.
+ *
+ * This will adjust the time between ticks of the given timer type defined by
+ * @p type to the time period defined by @p poll_time.
+ */
+EAPI void ecore_poller_poll_interval_set(Ecore_Poller_Type type, double poll_time);
+/**
+ * @brief Gets the time(in seconds) between ticks for the given poller type.
+ * @param type The poller type to query.
+ * @return The time in seconds between ticks of the poller timer.
+ *
+ * This will get the time between ticks of the specified poller timer.
+ */
+EAPI double ecore_poller_poll_interval_get(Ecore_Poller_Type type);
+/**
+ * @brief Changes the polling interval rate of @p poller.
+ * @param poller The Ecore_Poller to change the interval of.
+ * @param interval The tick interval to set; must be a power of 2 and <= 32768.
+ * @return Returns true on success, false on failure.
+ *
+ * This allows the changing of a poller's polling interval. It is useful when
+ * you want to alter a poll rate without deleting and re-creating a poller.
+ */
+EAPI Eina_Bool ecore_poller_poller_interval_set(Ecore_Poller *poller, int interval);
+/**
+ * @brief Gets the polling interval rate of @p poller.
+ * @param poller The Ecore_Poller to change the interval of.
+ * @return Returns the interval, in ticks, that @p poller polls at.
+ *
+ * This returns a poller's polling interval, or 0 on error.
+ */
+EAPI int ecore_poller_poller_interval_get(Ecore_Poller *poller);
+/**
+ * @brief Creates a poller to call the given function at a particular tick interval.
+ * @param type The ticker type to attach the poller to. Must be ECORE_POLLER_CORE.
+ * @param interval The poll interval.
+ * @param func The poller function.
+ * @param data Data to pass to @a func when it is called.
+ * @return A poller object on success, @c NULL otherwise.
+ *
+ * This function adds @a func as a poller callback that will be called every @a
+ * interval ticks together with other pollers of type @a type. @a func will be
+ * passed the @p data pointer as a parameter.
+ *
+ * The @p interval must be between 1 and 32768 inclusive, and must be a power of
+ * 2 (i.e. 1, 2, 4, 8, 16, ... 16384, 32768). The exact tick in which @a func
+ * will be called is undefined, as only the interval between calls can be
+ * defined. Ecore will endeavor to keep pollers synchronized and to call as
+ * many in 1 wakeup event as possible. If @a interval is not a power of two, the
+ * closest power of 2 greater than @a interval will be used.
+ *
+ * When the poller @p func is called, it must return a value of either
+ * ECORE_CALLBACK_RENEW(or 1) or ECORE_CALLBACK_CANCEL(or 0). If it
+ * returns 1, it will be called again at the next tick, or if it returns
+ * 0 it will be deleted automatically making any references/handles for it
+ * invalid.
+ */
+EAPI Ecore_Poller *ecore_poller_add(Ecore_Poller_Type type, int interval, Ecore_Task_Cb func, const void *data);
+/**
+ * @brief Delete the specified poller from the timer list.
+ * @param poller The poller to delete.
+ * @return The data pointer set for the timer when @ref ecore_poller_add was
+ * called on success, @c NULL otherwise.
+ *
+ * @note @a poller must be a valid handle. If the poller function has already
+ * returned 0, the handle is no longer valid (and does not need to be deleted).
+ */
+EAPI void *ecore_poller_del(Ecore_Poller *poller);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup Ecore_Animator_Group Ecore Animator functions
+ *
+ * @brief Ecore animators are a helper to simplify creating
+ * animations.
+ *
+ * Creating an animation is as simple as saying for how long it
+ * should be run and having a callback that does the animation,
+ * something like this:
+ * @code
+ * static Eina_Bool
+ * _do_animation(void *data, double pos)
+ * {
+ * evas_object_move(data, 100 * pos, 100 * pos);
+ * ... do some more animating ...
+ * }
+ * ...
+ *ecore_animator_timeline_add(2, _do_animation, my_evas_object);
+ * @endcode
+ * In the sample above we create an animation to move
+ * @c my_evas_object from position (0,0) to (100,100) in 2 seconds.
+ *
+ * If your animation will run for an unspecified amount of time you
+ * can use ecore_animator_add(), which is like using
+ *ecore_timer_add() with the interval being the
+ * @ref ecore_animator_frametime_set "framerate". Note that this has
+ * tangible benefits to creating a timer for each animation in terms
+ * of performance.
+ *
+ * For a more detailed example that show several animation see
+ * @ref tutorial_ecore_animator.
+ *
+ * @ingroup Ecore_Main_Loop_Group
+ *
+ * @{
+ */
+
+/*
+ * @since 1.8
+ */
+typedef Eo Ecore_Animator; /**< A handle for animators */
+#define ECORE_ANIMATOR_CLASS ecore_animator_class_get()
+const Eo_Class *ecore_animator_class_get(void) EINA_CONST;
+
+extern EAPI Eo_Op ECORE_ANIMATOR_BASE_ID;
+
+enum
+{
+ ECORE_ANIMATOR_SUB_ID_CONSTRUCTOR,
+ ECORE_ANIMATOR_SUB_ID_TIMELINE_CONSTRUCTOR,
+ ECORE_ANIMATOR_SUB_ID_LAST
+};
+
+#define ECORE_ANIMATOR_ID(sub_id) (ECORE_ANIMATOR_BASE_ID + sub_id)
+
+/**
+ * @def ecore_animator_constructor
+ * @since 1.8
+ *
+ * Contructor.
+ *
+ * @param[in] func
+ * @param[in] data
+ *
+ */
+#define ecore_animator_constructor(func, data) ECORE_ANIMATOR_ID(ECORE_ANIMATOR_SUB_ID_CONSTRUCTOR), EO_TYPECHECK(Ecore_Task_Cb, func), EO_TYPECHECK(const void *, data)
+
+/**
+ * @def ecore_animator_timeline_constructor
+ * @since 1.8
+ *
+ * Contructor.
+ *
+ * @param[in] runtime
+ * @param[in] func
+ * @param[in] data
+ *
+ */
+#define ecore_animator_timeline_constructor(runtime, func, data) ECORE_ANIMATOR_ID(ECORE_ANIMATOR_SUB_ID_TIMELINE_CONSTRUCTOR), EO_TYPECHECK(double, runtime), EO_TYPECHECK(Ecore_Timeline_Cb, func), EO_TYPECHECK(const void *, data)
+
+/**
+ * @enum _Ecore_Pos_Map
+ * Defines the position mappings for the animation.
+ */
+enum _Ecore_Pos_Map /* Position mappings */
+{
+ ECORE_POS_MAP_LINEAR, /**< Linear 0.0 -> 1.0 */
+ ECORE_POS_MAP_ACCELERATE, /**< Start slow then speed up */
+ ECORE_POS_MAP_DECELERATE, /**< Start fast then slow down */
+ ECORE_POS_MAP_SINUSOIDAL, /**< Start slow, speed up then slow down at end */
+ ECORE_POS_MAP_ACCELERATE_FACTOR, /**< Start slow then speed up, v1 being a power factor, 0.0 being linear, 1.0 being normal accelerate, 2.0 being much more pronounced accelerate (squared), 3.0 being cubed, etc. */
+ ECORE_POS_MAP_DECELERATE_FACTOR, /**< Start fast then slow down, v1 being a power factor, 0.0 being linear, 1.0 being normal decelerate, 2.0 being much more pronounced decelerate (squared), 3.0 being cubed, etc. */
+ ECORE_POS_MAP_SINUSOIDAL_FACTOR, /**< Start slow, speed up then slow down at end, v1 being a power factor, 0.0 being linear, 1.0 being normal sinusoidal, 2.0 being much more pronounced sinusoidal (squared), 3.0 being cubed, etc. */
+ ECORE_POS_MAP_DIVISOR_INTERP, /**< Start at gradient * v1, interpolated via power of v2 curve */
+ ECORE_POS_MAP_BOUNCE, /**< Start at 0.0 then "drop" like a ball bouncing to the ground at 1.0, and bounce v2 times, with decay factor of v1 */
+ ECORE_POS_MAP_SPRING /**< Start at 0.0 then "wobble" like a spring rest position 1.0, and wobble v2 times, with decay factor of v1 */
+};
+typedef enum _Ecore_Pos_Map Ecore_Pos_Map;
+
+/**
+ * @enum _Ecore_Animator_Source
+ * Defines the timing sources for animators.
+ */
+enum _Ecore_Animator_Source /* Timing sources for animators */
+{
+ ECORE_ANIMATOR_SOURCE_TIMER, /**< The default system clock/timer based animator that ticks every "frametime" seconds */
+ ECORE_ANIMATOR_SOURCE_CUSTOM /**< A custom animator trigger that you need to call ecore_animator_trigger() to make it tick */
+};
+typedef enum _Ecore_Animator_Source Ecore_Animator_Source;
+
+/**
+ * @typedef Ecore_Timeline_Cb Ecore_Timeline_Cb
+ * A callback run for a task (animators with runtimes)
+ */
+typedef Eina_Bool (*Ecore_Timeline_Cb)(void *data, double pos);
+
+/**
+ * @brief Add an animator to call @p func at every animation tick during main
+ * loop execution.
+ *
+ * @param func The function to call when it ticks off
+ * @param data The data to pass to the function
+ * @return A handle to the new animator
+ *
+ * This function adds a animator and returns its handle on success and @c NULL
+ * on failure. The function @p func will be called every N seconds where N is
+ * the @p frametime interval set by ecore_animator_frametime_set(). The
+ * function will be passed the @p data pointer as its parameter.
+ *
+ * When the animator @p func is called, it must return a value of either 1 or
+ * 0. If it returns 1 (or ECORE_CALLBACK_RENEW), it will be called again at
+ * the next tick, or if it returns 0 (or ECORE_CALLBACK_CANCEL) it will be
+ * deleted automatically making any references/handles for it invalid.
+ *
+ * @note The default @p frametime value is 1/30th of a second.
+ *
+ * @see ecore_animator_timeline_add()
+ * @see ecore_animator_frametime_set()
+ */
+EAPI Ecore_Animator *ecore_animator_add(Ecore_Task_Cb func, const void *data);
+/**
+ * @brief Add a animator that runs for a limited time
+ *
+ * @param runtime The time to run in seconds
+ * @param func The function to call when it ticks off
+ * @param data The data to pass to the function
+ * @return A handle to the new animator
+ *
+ * This function is just like ecore_animator_add() except the animator only
+ * runs for a limited time specified in seconds by @p runtime. Once the
+ * runtime the animator has elapsed (animator finished) it will automatically
+ * be deleted. The callback function @p func can return ECORE_CALLBACK_RENEW
+ * to keep the animator running or ECORE_CALLBACK_CANCEL ro stop it and have
+ * it be deleted automatically at any time.
+ *
+ * The @p func will ALSO be passed a position parameter that will be in value
+ * from 0.0 to 1.0 to indicate where along the timeline (0.0 start, 1.0 end)
+ * the animator run is at. If the callback wishes not to have a linear
+ * transition it can "map" this value to one of several curves and mappings
+ * via ecore_animator_pos_map().
+ *
+ * @note The default @p frametime value is 1/30th of a second.
+ *
+ * @see ecore_animator_add()
+ * @see ecore_animator_pos_map()
+ * @since 1.1.0
+ */
+EAPI Ecore_Animator *ecore_animator_timeline_add(double runtime, Ecore_Timeline_Cb func, const void *data);
+/**
+ * @brief Delete the specified animator from the animator list.
+ *
+ * @param animator The animator to delete
+ * @return The data pointer set for the animator on add
+ *
+ * Delete the specified @p animator from the set of animators that are
+ * executed during main loop execution. This function returns the data
+ * parameter that was being passed to the callback on success, or @c NULL on
+ * failure. After this call returns the specified animator object @p animator
+ * is invalid and should not be used again. It will not get called again after
+ * deletion.
+ */
+EAPI void *ecore_animator_del(Ecore_Animator *animator);
+/**
+ * @brief Suspend the specified animator.
+ *
+ * @param animator The animator to delete
+ *
+ * The specified @p animator will be temporarily removed from the set of
+ * animators that are executed during main loop.
+ *
+ * @warning Freezing an animator doesn't freeze accounting of how long that
+ * animator has been running. Therefore if the animator was created with
+ *ecore_animator_timeline_add() the @p pos argument given to the callback
+ * will increase as if the animator hadn't been frozen and the animator may
+ * have it's execution halted if @p runtime elapsed.
+ */
+EAPI void ecore_animator_freeze(Ecore_Animator *animator);
+/**
+ * @brief Restore execution of the specified animator.
+ *
+ * @param animator The animator to delete
+ *
+ * The specified @p animator will be put back in the set of animators that are
+ * executed during main loop.
+ */
+EAPI void ecore_animator_thaw(Ecore_Animator *animator);
+/**
+ * @brief Set the animator call interval in seconds.
+ *
+ * @param frametime The time in seconds in between animator ticks.
+ *
+ * This function sets the time interval (in seconds) between animator ticks.
+ * At every tick the callback of every existing animator will be called.
+ *
+ * @warning Too small a value may cause performance issues and too high a
+ * value may cause your animation to seem "jerky".
+ *
+ * @note The default @p frametime value is 1/30th of a second.
+ */
+EAPI void ecore_animator_frametime_set(double frametime);
+/**
+ * @brief Get the animator call interval in seconds.
+ *
+ * @return The time in second in between animator ticks.
+ *
+ * This function retrieves the time in seconds between animator ticks.
+ *
+ * @see ecore_animator_frametime_set()
+ */
+EAPI double ecore_animator_frametime_get(void);
+/**
+ * @brief Maps an input position from 0.0 to 1.0 along a timeline to a
+ * position in a different curve.
+ *
+ * @param pos The input position to map
+ * @param map The mapping to use
+ * @param v1 A parameter use by the mapping (pass 0.0 if not used)
+ * @param v2 A parameter use by the mapping (pass 0.0 if not used)
+ * @return The mapped value
+ *
+ * Takes an input position (0.0 to 1.0) and maps to a new position (normally
+ * between 0.0 and 1.0, but it may go above/below 0.0 or 1.0 to show that it
+ * has "overshot" the mark) using some interpolation (mapping) algorithm.
+ *
+ * This function useful to create non-linear animations. It offers a variety
+ * of possible animation curves to be used:
+ * @li ECORE_POS_MAP_LINEAR - Linear, returns @p pos
+ * @li ECORE_POS_MAP_ACCELERATE - Start slow then speed up
+ * @li ECORE_POS_MAP_DECELERATE - Start fast then slow down
+ * @li ECORE_POS_MAP_SINUSOIDAL - Start slow, speed up then slow down at end
+ * @li ECORE_POS_MAP_ACCELERATE_FACTOR - Start slow then speed up, v1 being a
+ * power factor, 0.0 being linear, 1.0 being ECORE_POS_MAP_ACCELERATE, 2.0
+ * being much more pronounced accelerate (squared), 3.0 being cubed, etc.
+ * @li ECORE_POS_MAP_DECELERATE_FACTOR - Start fast then slow down, v1 being a
+ * power factor, 0.0 being linear, 1.0 being ECORE_POS_MAP_DECELERATE, 2.0
+ * being much more pronounced decelerate (squared), 3.0 being cubed, etc.
+ * @li ECORE_POS_MAP_SINUSOIDAL_FACTOR - Start slow, speed up then slow down
+ * at end, v1 being a power factor, 0.0 being linear, 1.0 being
+ * ECORE_POS_MAP_SINUSOIDAL, 2.0 being much more pronounced sinusoidal
+ * (squared), 3.0 being cubed, etc.
+ * @li ECORE_POS_MAP_DIVISOR_INTERP - Start at gradient * v1, interpolated via
+ * power of v2 curve
+ * @li ECORE_POS_MAP_BOUNCE - Start at 0.0 then "drop" like a ball bouncing to
+ * the ground at 1.0, and bounce v2 times, with decay factor of v1
+ * @li ECORE_POS_MAP_SPRING - Start at 0.0 then "wobble" like a spring rest
+ * position 1.0, and wobble v2 times, with decay factor of v1
+ * @note When not listed v1 and v2 have no effect.
+ *
+ * @image html ecore-pos-map.png
+ * @image latex ecore-pos-map.eps width=\textwidth
+ *
+ * One way to use this would be:
+ * @code
+ * double pos; // input position in a timeline from 0.0 to 1.0
+ * double out; // output position after mapping
+ * int x1, y1, x2, y2; // x1 & y1 are start position, x2 & y2 are end position
+ * int x, y; // x & y are the calculated position
+ *
+ * out = ecore_animator_pos_map(pos, ECORE_POS_MAP_BOUNCE, 1.8, 7);
+ * x = (x1 * out) + (x2 * (1.0 - out));
+ * y = (y1 * out) + (y2 * (1.0 - out));
+ * move_my_object_to(myobject, x, y);
+ * @endcode
+ * This will make an animation that bounces 7 each times diminishing by a
+ * factor of 1.8.
+ *
+ * @see _Ecore_Pos_Map
+ *
+ * @since 1.1.0
+ */
+EAPI double ecore_animator_pos_map(double pos, Ecore_Pos_Map map, double v1, double v2);
+/**
+ * @brief Set the source of animator ticks for the mainloop
+ *
+ * @param source The source of animator ticks to use
+ *
+ * This sets the source of animator ticks. When an animator is active the
+ * mainloop will "tick" over frame by frame calling all animators that are
+ * registered until none are. The mainloop will tick at a given rate based
+ * on the animator source. The default source is the system clock timer
+ * source - ECORE_ANIMATOR_SOURCE_TIMER. This source uses the system clock
+ * to tick over every N seconds (specified by ecore_animator_frametime_set(),
+ * with the default being 1/30th of a second unless set otherwise). You can
+ * set a custom tick source by setting the source to
+ * ECORE_ANIMATOR_SOURCE_CUSTOM and then drive it yourself based on some input
+ * tick source (like another application via ipc, some vertical blanking
+ * interrupt interrupt etc.) using
+ *ecore_animator_custom_source_tick_begin_callback_set() and
+ *ecore_animator_custom_source_tick_end_callback_set() to set the functions
+ * that will be called to start and stop the ticking source, which when it
+ * gets a "tick" should call ecore_animator_custom_tick() to make the "tick" over 1
+ * frame.
+ */
+EAPI void ecore_animator_source_set(Ecore_Animator_Source source);
+/**
+ * @brief Get the animator source currently set.
+ *
+ * @return The current animator source
+ *
+ * This gets the current animator source.
+ *
+ * @see ecore_animator_source_set()
+ */
+EAPI Ecore_Animator_Source ecore_animator_source_get(void);
+/**
+ * @brief Set the function that begins a custom animator tick source
+ *
+ * @param func The function to call when ticking is to begin
+ * @param data The data passed to the tick begin function as its parameter
+ *
+ * The Ecore Animator infrastructure handles tracking if animators are needed
+ * or not and which ones need to be called and when, but when the tick source
+ * is custom, you have to provide a tick source by calling
+ *ecore_animator_custom_tick() to indicate a frame tick happened. In order
+ * to allow the source of ticks to be dynamically enabled or disabled as
+ * needed, the @p func when set is called to enable the tick source to
+ * produce tick events that call ecore_animator_custom_tick(). If @p func
+ * is @c NULL then no function is called to begin custom ticking.
+ *
+ * @see ecore_animator_source_set()
+ * @see ecore_animator_custom_source_tick_end_callback_set()
+ * @see ecore_animator_custom_tick()
+ */
+EAPI void ecore_animator_custom_source_tick_begin_callback_set(Ecore_Cb func, const void *data);
+/**
+ * @brief Set the function that ends a custom animator tick source
+ *
+ * @param func The function to call when ticking is to end
+ * @param data The data passed to the tick end function as its parameter
+ *
+ * This function is a matching pair to the function set by
+ * ecore_animator_custom_source_tick_begin_callback_set() and is called
+ * when ticking is to stop. If @p func is @c NULL then no function will be
+ * called to stop ticking. For more information please see
+ * ecore_animator_custom_source_tick_begin_callback_set().
+ *
+ * @see ecore_animator_source_set()
+ * @see ecore_animator_custom_source_tick_begin_callback_set()
+ * @see ecore_animator_custom_tick()
+ */
+EAPI void ecore_animator_custom_source_tick_end_callback_set(Ecore_Cb func, const void *data);
+/**
+ * @brief Trigger a custom animator tick
+ *
+ * When animator source is set to ECORE_ANIMATOR_SOURCE_CUSTOM, then calling
+ * this function triggers a run of all animators currently registered with
+ * Ecore as this indicates a "frame tick" happened. This will do nothing if
+ * the animator source(set by ecore_animator_source_set()) is not set to
+ * ECORE_ANIMATOR_SOURCE_CUSTOM.
+ *
+ * @see ecore_animator_source_set()
+ * @see ecore_animator_custom_source_tick_begin_callback_set
+ * @see ecore_animator_custom_source_tick_end_callback_set()()
+ */
+EAPI void ecore_animator_custom_tick(void);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup Ecore_Time_Group Ecore time functions
+ *
+ * These are function to retrieve time in a given format.
+ *
+ * Examples:
+ * @li @ref ecore_time_functions_example_c
+ * @{
+ */
+EAPI double ecore_time_get(void);
+EAPI double ecore_time_unix_get(void);
+EAPI double ecore_loop_time_get(void);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup Ecore_Timer_Group Ecore Timer functions
+ *
+ * Ecore provides very flexible timer functionality. The basic usage of timers,
+ * to call a certain function at a certain interval can be achieved with a
+ * single line:
+ * @code
+ * Eina_Bool my_func(void *data) {
+ * do_funky_stuff_with_data(data);
+ * return EINA_TRUE;
+ * }
+ * ecore_timer_add(interval_in_seconds, my_func, data_given_to_function);
+ * @endcode
+ * @note If the function was to be executed only once simply return
+ * @c EINA_FALSE instead.
+ *
+ * An example that shows the usage of a lot of these:
+ * @li @ref ecore_timer_example_c
+ *
+ * @ingroup Ecore_Main_Loop_Group
+ *
+ * @{
+ */
+
+/*
+ * @since 1.8
+ */
+typedef Eo Ecore_Timer; /**< A handle for timers */
+
+#define ECORE_TIMER_CLASS ecore_timer_class_get()
+const Eo_Class *ecore_timer_class_get(void) EINA_CONST;
+extern EAPI Eo_Op ECORE_TIMER_BASE_ID;
+
+enum
+{
+ ECORE_TIMER_SUB_ID_CONSTRUCTOR,
+ ECORE_TIMER_SUB_ID_LOOP_CONSTRUCTOR,
+ ECORE_TIMER_SUB_ID_INTERVAL_SET,
+ ECORE_TIMER_SUB_ID_INTERVAL_GET,
+ ECORE_TIMER_SUB_ID_DELAY,
+ ECORE_TIMER_SUB_ID_RESET,
+ ECORE_TIMER_SUB_ID_PENDING_GET,
+ ECORE_TIMER_SUB_ID_LAST,
+};
+
+#define ECORE_TIMER_ID(sub_id) (ECORE_TIMER_BASE_ID + sub_id)
+
+/**
+ * @def ecore_timer_constructor
+ * @since 1.8
+ *
+ * Contructor.
+ *
+ * @param[in] in
+ * @param[in] func
+ * @param[in] data
+ *
+ */
+#define ecore_timer_constructor(in, func, data) ECORE_TIMER_ID(ECORE_TIMER_SUB_ID_CONSTRUCTOR), EO_TYPECHECK(double, in), EO_TYPECHECK(Ecore_Task_Cb, func), EO_TYPECHECK(const void *, data)
+
+/**
+ * @def ecore_timer_loop_constructor
+ * @since 1.8
+ *
+ * Contructor.
+ *
+ * @param[in] in
+ * @param[in] func
+ * @param[in] data
+ *
+ */
+#define ecore_timer_loop_constructor(in, func, data) ECORE_TIMER_ID(ECORE_TIMER_SUB_ID_LOOP_CONSTRUCTOR), EO_TYPECHECK(double, in), EO_TYPECHECK(Ecore_Task_Cb, func), EO_TYPECHECK(const void *, data)
+
+/**
+ * @def ecore_obj_timer_interval_set
+ * @since 1.8
+ *
+ * Change the interval the timer ticks of.
+ *
+ * @param[in] in
+ *
+ * @see ecore_timer_interval_set
+ */
+#define ecore_obj_timer_interval_set(in) ECORE_TIMER_ID(ECORE_TIMER_SUB_ID_INTERVAL_SET), EO_TYPECHECK(double, in)
+
+/**
+ * @def ecore_obj_timer_interval_get
+ * @since 1.8
+ *
+ * Get the interval the timer ticks on.
+ *
+ * @param[out] ret
+ *
+ * @see ecore_timer_interval_get
+ */
+#define ecore_obj_timer_interval_get(ret) ECORE_TIMER_ID(ECORE_TIMER_SUB_ID_INTERVAL_GET), EO_TYPECHECK(double *, ret)
+
+/**
+ * @def ecore_obj_timer_delay
+ * @since 1.8
+ *
+ * Add some delay for the next occurrence of a timer.
+ *
+ * @param[in] add
+ *
+ * @see ecore_timer_delay
+ */
+#define ecore_obj_timer_delay(add) ECORE_TIMER_ID(ECORE_TIMER_SUB_ID_DELAY), EO_TYPECHECK(double, add)
+
+/**
+ * @def ecore_obj_timer_reset
+ * @since 1.8
+ *
+ * Reset a timer to its full interval.
+ *
+ * @see ecore_timer_reset
+ */
+#define ecore_obj_timer_reset() ECORE_TIMER_ID(ECORE_TIMER_SUB_ID_RESET)
+
+/**
+ * @def ecore_obj_timer_pending_get
+ * @since 1.8
+ *
+ * Get the pending time regarding a timer.
+ *
+ * @param[out] ret
+ *
+ * @see ecore_timer_pending_get
+ */
+#define ecore_obj_timer_pending_get(ret) ECORE_TIMER_ID(ECORE_TIMER_SUB_ID_PENDING_GET), EO_TYPECHECK(double *, ret)
+
+EAPI Ecore_Timer *ecore_timer_add(double in, Ecore_Task_Cb func, const void *data);
+EAPI Ecore_Timer *ecore_timer_loop_add(double in, Ecore_Task_Cb func, const void *data);
+EAPI void *ecore_timer_del(Ecore_Timer *timer);
+EAPI void ecore_timer_interval_set(Ecore_Timer *timer, double in);
+EAPI double ecore_timer_interval_get(Ecore_Timer *timer);
+EAPI void ecore_timer_freeze(Ecore_Timer *timer);
+EAPI void ecore_timer_thaw(Ecore_Timer *timer);
+EAPI void ecore_timer_delay(Ecore_Timer *timer, double add);
+EAPI void ecore_timer_reset(Ecore_Timer *timer);
+EAPI double ecore_timer_pending_get(Ecore_Timer *timer);
+EAPI double ecore_timer_precision_get(void);
+EAPI void ecore_timer_precision_set(double precision);
+EAPI char *ecore_timer_dump(void);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup Ecore_Idle_Group Ecore Idle functions
+ *
+ * The idler functionality in Ecore allows for callbacks to be called when the
+ * program isn't handling @ref Ecore_Event_Group "events", @ref Ecore_Timer_Group
+ * "timers" or @ref Ecore_FD_Handler_Group "fd handlers".
+ *
+ * There are three types of idlers: Enterers, Idlers(proper) and Exiters. They
+ * are called, respectively, when the program is about to enter an idle state,
+ * when the program is in an idle state and when the program has just left an
+ * idle state and will begin processing @ref Ecore_Event_Group "events", @ref
+ * Ecore_Timer_Group "timers" or @ref Ecore_FD_Handler_Group "fd handlers".
+ *
+ * Enterer callbacks are good for updating your program's state, if
+ * it has a state engine. Once all of the enterer handlers are
+ * called, the program will enter a "sleeping" state.
+ *
+ * Idler callbacks are called when the main loop has called all
+ * enterer handlers. They are useful for interfaces that require
+ * polling and timers would be too slow to use.
+ *
+ * Exiter callbacks are called when the main loop wakes up from an idle state.
+ *
+ * If no idler callbacks are specified, then the process literally
+ * goes to sleep. Otherwise, the idler callbacks are called
+ * continuously while the loop is "idle", using as much CPU as is
+ * available to the process.
+ *
+ * @note Idle state doesn't mean that the @b program is idle, but
+ * that the <b>main loop</b> is idle. It doesn't have any timers,
+ * events, fd handlers or anything else to process (which in most
+ * <em>event driven</em> programs also means that the @b program is
+ * idle too, but it's not a rule). The program itself may be doing
+ * a lot of processing in the idler, or in another thread, for
+ * example.
+ *
+ * Example with functions that deal with idle state:
+ *
+ * @li @ref ecore_idler_example_c
+ *
+ * @ingroup Ecore_Main_Loop_Group
+ *
+ * @{
+ */
+
+/*
+ * @since 1.8
+ */
+typedef Eo Ecore_Idler; /**< A handle for idlers */
+#define ECORE_IDLER_CLASS ecore_idler_class_get()
+const Eo_Class *ecore_idler_class_get(void) EINA_CONST;
+
+extern EAPI Eo_Op ECORE_IDLER_BASE_ID;
+
+enum
+{
+ ECORE_IDLER_SUB_ID_CONSTRUCTOR,
+ ECORE_IDLER_SUB_ID_LAST
+};
+
+#define ECORE_IDLER_ID(sub_id) (ECORE_IDLER_BASE_ID + sub_id)
+
+/**
+ * @def ecore_idler_constructor
+ * @since 1.8
+ *
+ * Contructor.
+ *
+ * @param[in] func
+ * @param[in] data
+ *
+ */
+#define ecore_idler_constructor(func, data) ECORE_IDLER_ID(ECORE_IDLER_SUB_ID_CONSTRUCTOR), EO_TYPECHECK(Ecore_Task_Cb, func), EO_TYPECHECK(const void *, data)
+
+/**
+ *
+ */
+
+typedef Eo Ecore_Idle_Enterer; /**< A handle for idle enterers */
+#define ECORE_IDLE_ENTERER_CLASS ecore_idle_enterer_class_get()
+const Eo_Class *ecore_idle_enterer_class_get(void) EINA_CONST;
+
+extern EAPI Eo_Op ECORE_IDLE_ENTERER_BASE_ID;
+
+enum
+{
+ ECORE_IDLE_ENTERER_SUB_ID_AFTER_CONSTRUCTOR,
+ ECORE_IDLE_ENTERER_SUB_ID_BEFORE_CONSTRUCTOR,
+ ECORE_IDLE_ENTERER_SUB_ID_LAST
+};
+
+#define ECORE_IDLE_ENTERER_ID(sub_id) (ECORE_IDLE_ENTERER_BASE_ID + sub_id)
+
+/**
+ * @def ecore_idle_enterer_after_constructor
+ * @since 1.8
+ *
+ * Contructor. Will insert the handler at the end of the list.
+ *
+ * @param[in] func
+ * @param[in] data
+ *
+ */
+#define ecore_idle_enterer_after_constructor(func, data) ECORE_IDLE_ENTERER_ID(ECORE_IDLE_ENTERER_SUB_ID_AFTER_CONSTRUCTOR), EO_TYPECHECK(Ecore_Task_Cb, func), EO_TYPECHECK(const void *, data)
+
+/**
+ * @def ecore_idle_enterer_before_constructor
+ * @since 1.8
+ *
+ * Contructor. Will insert the handler at the beginning of the list.
+ *
+ * @param[in] func
+ * @param[in] data
+ *
+ */
+#define ecore_idle_enterer_before_constructor(func, data) ECORE_IDLE_ENTERER_ID(ECORE_IDLE_ENTERER_SUB_ID_BEFORE_CONSTRUCTOR), EO_TYPECHECK(Ecore_Task_Cb, func), EO_TYPECHECK(const void *, data)
+
+/**
+ *
+ */
+
+/*
+ * @since 1.8
+ */
+typedef Eo Ecore_Idle_Exiter; /**< A handle for idle exiters */
+#define ECORE_IDLE_EXITER_CLASS ecore_idle_exiter_class_get()
+const Eo_Class *ecore_idle_exiter_class_get(void) EINA_CONST;
+
+extern EAPI Eo_Op ECORE_IDLE_EXITER_BASE_ID;
+
+enum
+{
+ ECORE_IDLE_EXITER_SUB_ID_CONSTRUCTOR,
+ ECORE_IDLE_EXITER_SUB_ID_LAST
+};
+
+#define ECORE_IDLE_EXITER_ID(sub_id) (ECORE_IDLE_EXITER_BASE_ID + sub_id)
+
+/**
+ * @def ecore_idle_exiter_constructor
+ * @since 1.8
+ *
+ * Contructor.
+ *
+ * @param[in] func
+ * @param[in] data
+ *
+ */
+#define ecore_idle_exiter_constructor(func, data) ECORE_IDLE_EXITER_ID(ECORE_IDLE_EXITER_SUB_ID_CONSTRUCTOR), EO_TYPECHECK(Ecore_Task_Cb, func), EO_TYPECHECK(const void *, data)
+
+/**
+ * Add an idler handler.
+ * @param func The function to call when idling.
+ * @param data The data to be passed to this @p func call.
+ * @return A idler handle if successfully added, @c NULL otherwise.
+ *
+ * Add an idler handle to the event loop, returning a handle on
+ * success and @c NULL otherwise. The function @p func will be called
+ * repeatedly while no other events are ready to be processed, as
+ * long as it returns @c 1 (or ECORE_CALLBACK_RENEW). A return of @c 0
+ * (or ECORE_CALLBACK_CANCEL) deletes the idler.
+ *
+ * Idlers are useful for progressively prossessing data without blocking.
+ */
+EAPI Ecore_Idler *ecore_idler_add(Ecore_Task_Cb func, const void *data);
+
+/**
+ * Delete an idler callback from the list to be executed.
+ * @param idler The handle of the idler callback to delete
+ * @return The data pointer passed to the idler callback on success, @c NULL
+ * otherwise.
+ */
+EAPI void *ecore_idler_del(Ecore_Idler *idler);
+
+EAPI Ecore_Idle_Enterer *ecore_idle_enterer_add(Ecore_Task_Cb func, const void *data);
+EAPI Ecore_Idle_Enterer *ecore_idle_enterer_before_add(Ecore_Task_Cb func, const void *data);
+EAPI void *ecore_idle_enterer_del(Ecore_Idle_Enterer *idle_enterer);
+
+EAPI Ecore_Idle_Exiter *ecore_idle_exiter_add(Ecore_Task_Cb func, const void *data);
+EAPI void *ecore_idle_exiter_del(Ecore_Idle_Exiter *idle_exiter);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup Ecore_Thread_Group Ecore Thread functions
+ *
+ * Facilities to run heavy tasks in different threads to avoid blocking
+ * the main loop.
+ *
+ * The EFL is, for the most part, not thread safe. This means that if you
+ * have some task running in another thread and you have, for example, an
+ * Evas object to show the status progress of this task, you cannot update
+ * the object from within the thread. This can only be done from the main
+ * thread, the one running the main loop. This problem can be solved
+ * by running a thread that sends messages to the main one using an
+ * @ref Ecore_Pipe_Group "Ecore_Pipe", but when you need to handle other
+ * things like cancelling the thread, your code grows in complexity and gets
+ * much harder to maintain.
+ *
+ * Ecore Thread is here to solve that problem. It is @b not a simple wrapper
+ * around standard POSIX threads (or the equivalent in other systems) and
+ * it's not meant to be used to run parallel tasks throughout the entire
+ * duration of the program, especially when these tasks are performance
+ * critical, as Ecore manages these tasks using a pool of threads based on
+ * system configuration.
+ *
+ * What Ecore Thread does, is make it a lot easier to dispatch a worker
+ * function to perform some heavy task and then get the result once it
+ * completes, without blocking the application's UI. In addition, cancelling
+ * and rescheduling comes practically for free and the developer needs not
+ * worry about how many threads are launched, since Ecore will schedule
+ * them according to the number of processors the system has and maximum
+ * amount of concurrent threads set for the application.
+ *
+ * At the system level, Ecore will start a new thread on an as-needed basis
+ * until the maximum set is reached. When no more threads can be launched,
+ * new worker functions will be queued in a waiting list until a thread
+ * becomes available. This way, system threads will be shared throughout
+ * different worker functions, but running only one at a time. At the same
+ * time, a worker function that is rescheduled may be run on a different
+ * thread the next time.
+ *
+ * The ::Ecore_Thread handler has two meanings, depending on what context
+ * it is on. The one returned when starting a worker with any of the
+ * functions ecore_thread_run() or ecore_thread_feedback_run() is an
+ * identifier of that specific instance of the function and can be used from
+ * the main loop with the ecore_thread_cancel() and ecore_thread_check()
+ * functions. This handler must not be shared with the worker function
+ * function running in the thread. This same handler will be the one received
+ * on the @c end, @c cancel and @c feedback callbacks.
+ *
+ * The worker function, that's the one running in the thread, also receives
+ * an ::Ecore_Thread handler that can be used with ecore_thread_cancel() and
+ *ecore_thread_check(), sharing the flag with the main loop. But this
+ * handler is also associated with the thread where the function is running.
+ * This has strong implications when working with thread local data.
+ *
+ * There are two kinds of worker threads Ecore handles: simple, or short,
+ * workers and feedback workers.
+ *
+ * The first kind is for simple functions that perform a
+ * usually small but time consuming task. Ecore will run this function in
+ * a thread as soon as one becomes available and notify the calling user of
+ * its completion once the task is done.
+ *
+ * The following image shows the flow of a program running four tasks on
+ * a pool of two threads.
+ *
+ * @image html ecore_thread.png
+ * @image rtf ecore_thread.png
+ * @image latex ecore_thread.eps width=\textwidth
+ *
+ * For larger tasks that may require continuous communication with the main
+ * program, the feedback workers provide the same functionality plus a way
+ * for the function running in the thread to send messages to the main
+ * thread.
+ *
+ * The next diagram omits some details shown in the previous one regarding
+ * how threads are spawned and tasks are queued, but illustrates how feedback
+ * jobs communicate with the main loop and the special case of threads
+ * running out of pool.
+ *
+ * @image html ecore_thread_feedback.png
+ * @image rtf ecore_thread_feedback.png
+ * @image latex ecore_thread_feedback.eps width=\textwidth
+ *
+ * See an overview example in @ref ecore_thread_example_c.
+ *
+ * @ingroup Ecore_Main_Loop_Group
+ *
+ * @{
+ */
+
+typedef struct _Ecore_Thread Ecore_Thread; /**< A handle for threaded jobs */
+
+/**
+ * @typedef Ecore_Thread_Cb Ecore_Thread_Cb
+ * A callback used by Ecore_Thread helper.
+ */
+typedef void (*Ecore_Thread_Cb)(void *data, Ecore_Thread *thread);
+/**
+ * @typedef Ecore_Thread_Notify_Cb Ecore_Thread_Notify_Cb
+ * A callback used by the main loop to receive data sent by an
+ * @ref Ecore_Thread_Group.
+ */
+typedef void (*Ecore_Thread_Notify_Cb)(void *data, Ecore_Thread *thread, void *msg_data);
+
+/**
+ * Schedule a task to run in a parallel thread to avoid locking the main loop
+ *
+ * @param func_blocking The function that should run in another thread.
+ * @param func_end Function to call from main loop when @p func_blocking
+ * completes its task successfully (may be NULL)
+ * @param func_cancel Function to call from main loop if the thread running
+ * @p func_blocking is cancelled or fails to start (may be NULL)
+ * @param data User context data to pass to all callbacks.
+ * @return A new thread handler, or @c NULL on failure.
+ *
+ * This function will try to create a new thread to run @p func_blocking in,
+ * or if the maximum number of concurrent threads has been reached, will
+ * add it to the pending list, where it will wait until a thread becomes
+ * available. The return value will be an ::Ecore_Thread handle that can
+ * be used to cancel the thread before its completion.
+ *
+ * @note This function should always return immediately, but in the rare
+ * case that Ecore is built with no thread support, @p func_blocking will
+ * be called here, actually blocking the main loop.
+ *
+ * Once a thread becomes available, @p func_blocking will be run in it until
+ * it finishes, then @p func_end is called from the thread containing the
+ * main loop to inform the user of its completion. While in @p func_blocking,
+ * no functions from the EFL can be used, except for those from Eina that are
+ * marked to be thread-safe. Even for the latter, caution needs to be taken
+ * if the data is shared across several threads.
+ *
+ * @p func_end will be called from the main thread when @p func_blocking ends,
+ * so here it's safe to use anything from the EFL freely.
+ *
+ * The thread can also be cancelled before its completion calling
+ *ecore_thread_cancel(), either from the main thread or @p func_blocking.
+ * In this case, @p func_cancel will be called, also from the main thread
+ * to inform of this happening. If the thread could not be created, this
+ * function will be called and it's @c thread parameter will be NULL. It's
+ * also safe to call any EFL function here, as it will be running in the
+ * main thread.
+ *
+ * Inside @p func_blocking, it's possible to call ecore_thread_reschedule()
+ * to tell Ecore that this function should be called again.
+ *
+ * Be aware that no assumptions can be made about the order in which the
+ * @p func_end callbacks for each task will be called. Once the function is
+ * running in a different thread, it's the OS that will handle its running
+ * schedule, and different functions may take longer to finish than others.
+ * Also remember that just starting several tasks together doesn't mean they
+ * will be running at the same time. Ecore will schedule them based on the
+ * number of threads available for the particular system it's running in,
+ * so some of the jobs started may be waiting until another one finishes
+ * before it can execute its own @p func_blocking.
+ *
+ * @see ecore_thread_feedback_run()
+ * @see ecore_thread_cancel()
+ * @see ecore_thread_reschedule()
+ * @see ecore_thread_max_set()
+ */
+EAPI Ecore_Thread *ecore_thread_run(Ecore_Thread_Cb func_blocking, Ecore_Thread_Cb func_end, Ecore_Thread_Cb func_cancel, const void *data);
+/**
+ * Launch a thread to run a task that can talk back to the main thread
+ *
+ * @param func_heavy The function that should run in another thread.
+ * @param func_notify Function that receives the data sent from the thread
+ * @param func_end Function to call from main loop when @p func_heavy
+ * completes its task successfully
+ * @param func_cancel Function to call from main loop if the thread running
+ * @p func_heavy is cancelled or fails to start
+ * @param data User context data to pass to all callback.
+ * @param try_no_queue If you want to run outside of the thread pool.
+ * @return A new thread handler, or @c NULL on failure.
+ *
+ * See ecore_thread_run() for a general description of this function.
+ *
+ * The difference with the above is that ecore_thread_run() is meant for
+ * tasks that don't need to communicate anything until they finish, while
+ * this function is provided with a new callback, @p func_notify, that will
+ * be called from the main thread for every message sent from @p func_heavy
+ * with ecore_thread_feedback().
+ *
+ * Like with ecore_thread_run(), a new thread will be launched to run
+ * @p func_heavy unless the maximum number of simultaneous threads has been
+ * reached, in which case the function will be scheduled to run whenever a
+ * running task ends and a thread becomes free. But if @p try_no_queue is
+ * set, Ecore will first try to launch a thread outside of the pool to run
+ * the task. If it fails, it will revert to the normal behaviour of using a
+ * thread from the pool as if @p try_no_queue had not been set.
+ *
+ * Keep in mind that Ecore handles the thread pool based on the number of
+ * CPUs available, but running a thread outside of the pool doesn't count for
+ * this, so having too many of them may have drastic effects over the
+ * program's performance.
+ *
+ * @see ecore_thread_feedback()
+ * @see ecore_thread_run()
+ * @see ecore_thread_cancel()
+ * @see ecore_thread_reschedule()
+ * @see ecore_thread_max_set()
+ */
+EAPI Ecore_Thread *ecore_thread_feedback_run(Ecore_Thread_Cb func_heavy, Ecore_Thread_Notify_Cb func_notify,
+ Ecore_Thread_Cb func_end, Ecore_Thread_Cb func_cancel,
+ const void *data, Eina_Bool try_no_queue);
+/**
+ * Cancel a running thread.
+ *
+ * @param thread The thread to cancel.
+ * @return Will return @c EINA_TRUE if the thread has been cancelled,
+ * @c EINA_FALSE if it is pending.
+ *
+ * This function can be called both in the main loop or in the running thread.
+ *
+ * This function cancels a running thread. If @p thread can be immediately
+ * cancelled (it's still pending execution after creation or rescheduling),
+ * then the @c cancel callback will be called, @p thread will be freed and
+ * the function will return @c EINA_TRUE.
+ *
+ * If the thread is already running, then this function returns @c EINA_FALSE
+ * after marking the @p thread as pending cancellation. For the thread to
+ * actually be terminated, it needs to return from the user function back
+ * into Ecore control. This can happen in several ways:
+ * @li The function ends and returns normally. If it hadn't been cancelled,
+ * @c func_end would be called here, but instead @c func_cancel will happen.
+ * @li The function returns after requesting to be rescheduled with
+ * ecore_thread_reschedule().
+ * @li The function is prepared to leave early by checking if
+ * ecore_thread_check() returns @c EINA_TRUE.
+ *
+ * The user function can cancel itself by calling ecore_thread_cancel(), but
+ * it should always use the ::Ecore_Thread handle passed to it and never
+ * share it with the main loop thread by means of shared user data or any
+ * other way.
+ *
+ * @p thread will be freed and should not be used again if this function
+ * returns @c EINA_TRUE or after the @c func_cancel callback returns.
+ *
+ * @see ecore_thread_check()
+ */
+EAPI Eina_Bool ecore_thread_cancel(Ecore_Thread *thread);
+/**
+ * Checks if a thread is pending cancellation
+ *
+ * @param thread The thread to test.
+ * @return @c EINA_TRUE if the thread is pending cancellation,
+ * @c EINA_FALSE if it is not.
+ *
+ * This function can be called both in the main loop or in the running thread.
+ *
+ * When ecore_thread_cancel() is called on an already running task, the
+ * thread is marked as pending cancellation. This function returns @c EINA_TRUE
+ * if this mark is set for the given @p thread and can be used from the
+ * main loop thread to check if a still active thread has been cancelled,
+ * or from the user function running in the thread to check if it should
+ * stop doing what it's doing and return early, effectively cancelling the
+ * task.
+ *
+ * @see ecore_thread_cancel()
+ */
+EAPI Eina_Bool ecore_thread_check(Ecore_Thread *thread);
+/**
+ * Sends data from the worker thread to the main loop
+ *
+ * @param thread The current ::Ecore_Thread context to send data from
+ * @param msg_data Data to be transmitted to the main loop
+ * @return @c EINA_TRUE if @p msg_data was successfully sent to main loop,
+ * @c EINA_FALSE if anything goes wrong.
+ *
+ * You should use this function only in the @c func_heavy call.
+ *
+ * Only the address to @p msg_data will be sent and once this function
+ * returns @c EINA_TRUE, the job running in the thread should never touch the
+ * contents of it again. The data sent should be malloc()'ed or something
+ * similar, as long as it's not memory local to the thread that risks being
+ * overwritten or deleted once it goes out of scope or the thread finishes.
+ *
+ * Care must be taken that @p msg_data is properly freed in the @c func_notify
+ * callback set when creating the thread.
+ *
+ * @see ecore_thread_feedback_run()
+ */
+EAPI Eina_Bool ecore_thread_feedback(Ecore_Thread *thread, const void *msg_data);
+/**
+ * Asks for the function in the thread to be called again at a later time
+ *
+ * @param thread The current ::Ecore_Thread context to rescheduled
+ * @return @c EINA_TRUE if the task was successfully rescheduled,
+ * @c EINA_FALSE if anything goes wrong.
+ *
+ * This function should be called only from the same function represented
+ * by @p thread.
+ *
+ * Calling this function will mark the thread for a reschedule, so as soon
+ * as it returns, it will be added to the end of the list of pending tasks.
+ * If no other tasks are waiting or there are sufficient threads available,
+ * the rescheduled task will be launched again immediately.
+ *
+ * This should never return @c EINA_FALSE, unless it was called from the wrong
+ * thread or with the wrong arguments.
+ *
+ * The @c func_end callback set when the thread is created will not be
+ * called until the function in the thread returns without being rescheduled.
+ * Similarly, if the @p thread is cancelled, the reschedule will not take
+ * effect.
+ */
+EAPI Eina_Bool ecore_thread_reschedule(Ecore_Thread *thread);
+/**
+ * Gets the number of active threads running jobs
+ *
+ * @return Number of active threads running jobs
+ *
+ * This returns the number of threads currently running jobs of any type
+ * through the Ecore_Thread API.
+ *
+ * @note Jobs started through the ecore_thread_feedback_run() function with
+ * the @c try_no_queue parameter set to @c EINA_TRUE will not be accounted for
+ * in the return of this function unless the thread creation fails and it
+ * falls back to using one from the pool.
+ */
+EAPI int ecore_thread_active_get(void);
+/**
+ * Gets the number of short jobs waiting for a thread to run
+ *
+ * @return Number of pending threads running "short" jobs
+ *
+ * This returns the number of tasks started with ecore_thread_run() that are
+ * pending, waiting for a thread to become available to run them.
+ */
+EAPI int ecore_thread_pending_get(void);
+/**
+ * Gets the number of feedback jobs waiting for a thread to run
+ *
+ * @return Number of pending threads running "feedback" jobs
+ *
+ * This returns the number of tasks started with ecore_thread_feedback_run()
+ * that are pending, waiting for a thread to become available to run them.
+ */
+EAPI int ecore_thread_pending_feedback_get(void);
+/**
+ * Gets the total number of pending jobs
+ *
+ * @return Number of pending threads running jobs
+ *
+ * Same as the sum of ecore_thread_pending_get() and
+ *ecore_thread_pending_feedback_get().
+ */
+EAPI int ecore_thread_pending_total_get(void);
+/**
+ * Gets the maximum number of threads that can run simultaneously
+ *
+ * @return Max possible number of Ecore_Thread's running concurrently
+ *
+ * This returns the maximum number of Ecore_Thread's that may be running at
+ * the same time. If this number is reached, new jobs started by either
+ *ecore_thread_run() or ecore_thread_feedback_run() will be added to the
+ * respective pending queue until one of the running threads finishes its
+ * task and becomes available to run a new one.
+ *
+ * By default, this will be the number of available CPUs for the
+ * running program (as returned by eina_cpu_count()), or 1 if this value
+ * could not be fetched.
+ *
+ * @see ecore_thread_max_set()
+ * @see ecore_thread_max_reset()
+ */
+EAPI int ecore_thread_max_get(void);
+/**
+ * Sets the maximum number of threads allowed to run simultaneously
+ *
+ * @param num The new maximum
+ *
+ * This sets a new value for the maximum number of concurrently running
+ * Ecore_Thread's. It @b must an integer between 1 and (16 * @c x), where @c x
+ * is the number for CPUs available.
+ *
+ * @see ecore_thread_max_get()
+ * @see ecore_thread_max_reset()
+ */
+EAPI void ecore_thread_max_set(int num);
+/**
+ * Resets the maximum number of concurrently running threads to the default
+ *
+ * This resets the value returned by ecore_thread_max_get() back to its
+ * default.
+ *
+ * @see ecore_thread_max_get()
+ * @see ecore_thread_max_set()
+ */
+EAPI void ecore_thread_max_reset(void);
+/**
+ * Gets the number of threads available for running tasks
+ *
+ * @return The number of available threads
+ *
+ * Same as doing ecore_thread_max_get() - ecore_thread_active_get().
+ *
+ * This function may return a negative number only in the case the user
+ * changed the maximum number of running threads while other tasks are
+ * running.
+ */
+EAPI int ecore_thread_available_get(void);
+/**
+ * Adds some data to a hash local to the thread
+ *
+ * @param thread The thread context the data belongs to
+ * @param key The name under which the data will be stored
+ * @param value The data to add
+ * @param cb Function to free the data when removed from the hash
+ * @param direct If true, this will not copy the key string (like
+ * eina_hash_direct_add())
+ * @return @c EINA_TRUE on success, @c EINA_FALSE on failure.
+ *
+ * Ecore Thread has a mechanism to share data across several worker functions
+ * that run on the same system thread. That is, the data is stored per
+ * thread and for a worker function to have access to it, it must be run
+ * by the same thread that stored the data.
+ *
+ * When there are no more workers pending, the thread will be destroyed
+ * along with the internal hash and any data left in it will be freed with
+ * the @p cb function given.
+ *
+ * This set of functions is useful to share things around several instances
+ * of a function when that thing is costly to create and can be reused, but
+ * may only be used by one function at a time.
+ *
+ * For example, if you have a program doing requisitions to a database,
+ * these requisitions can be done in threads so that waiting for the
+ * database to respond doesn't block the UI. Each of these threads will
+ * run a function, and each function will be dependent on a connection to
+ * the database, which may not be able to handle more than one request at
+ * a time so for each running function you will need one connection handle.
+ * The options then are:
+ * @li Each function opens a connection when it's called, does the work and
+ * closes the connection when it finishes. This may be costly, wasting a lot
+ * of time on resolving hostnames, negotiating permissions and allocating
+ * memory.
+ * @li Open the connections in the main loop and pass it to the threads
+ * using the data pointer. Even worse, it's just as costly as before and now
+ * it may even be kept with connections open doing nothing until a thread
+ * becomes available to run the function.
+ * @li Have a way to share connection handles, so that each instance of the
+ * function can check if an available connection exists, and if it doesn't,
+ * create one and add it to the pool. When no more connections are needed,
+ * they are all closed.
+ *
+ * The last option is the most efficient, but it requires a lot of work to
+ * implement properly. Using thread local data helps to achieve the same
+ * result while avoiding doing all the tracking work on your code. The way
+ * to use it would be, at the worker function, to ask for the connection
+ * with ecore_thread_local_data_find() and if it doesn't exist, then open
+ * a new one and save it with ecore_thread_local_data_add(). Do the work and
+ * forget about the connection handle, when everything is done the function
+ * just ends. The next worker to run on that thread will check if a
+ * connection exists and find that it does, so the process of opening a
+ * new one has been spared. When no more workers exist, the thread is
+ * destroyed and the callback used when saving the connection will be called
+ * to close it.
+ *
+ * This function adds the data @p value to the thread data under the given
+ * @p key.
+ * No other value in the hash may have the same @p key. If you need to
+ * change the value under a @p key, or you don't know if one exists already,
+ * you can use ecore_thread_local_data_set().
+ *
+ * Neither @p key nor @p value may be @c NULL and @p key will be copied in the
+ * hash, unless @p direct is set, in which case the string used should not
+ * be freed until the data is removed from the hash.
+ *
+ * The @p cb function will be called when the data in the hash needs to be
+ * freed, be it because it got deleted with ecore_thread_local_data_del() or
+ * because @p thread was terminated and the hash destroyed. This parameter
+ * may be NULL, in which case @p value needs to be manually freed after
+ * removing it from the hash with either ecore_thread_local_data_del() or
+ * ecore_thread_local_data_set(), but it's very unlikely that this is what
+ * you want.
+ *
+ * This function, and all of the others in the @c ecore_thread_local_data
+ * family of functions, can only be called within the worker function running
+ * in the thread. Do not call them from the main loop or from a thread
+ * other than the one represented by @p thread.
+ *
+ * @see ecore_thread_local_data_set()
+ * @see ecore_thread_local_data_find()
+ * @see ecore_thread_local_data_del()
+ */
+EAPI Eina_Bool ecore_thread_local_data_add(Ecore_Thread *thread, const char *key, void *value,
+ Eina_Free_Cb cb, Eina_Bool direct);
+/**
+ * Sets some data in the hash local to the given thread
+ *
+ * @param thread The thread context the data belongs to
+ * @param key The name under which the data will be stored
+ * @param value The data to add
+ * @param cb Function to free the data when removed from the hash
+ *
+ * If no data exists in the hash under the @p key, this function adds
+ * @p value in the hash under the given @p key and returns NULL.
+ * The key itself is copied.
+ *
+ * If the hash already contains something under @p key, the data will be
+ * replaced by @p value and the old value will be returned.
+ *
+ * @c NULL will also be returned if either @p key or @p value are @c NULL, or
+ * if an error occurred.
+ *
+ * This function, and all of the others in the @c ecore_thread_local_data
+ * family of functions, can only be called within the worker function running
+ * in the thread. Do not call them from the main loop or from a thread
+ * other than the one represented by @p thread.
+ *
+ * @see ecore_thread_local_data_add()
+ * @see ecore_thread_local_data_del()
+ * @see ecore_thread_local_data_find()
+ */
+EAPI void *ecore_thread_local_data_set(Ecore_Thread *thread, const char *key, void *value, Eina_Free_Cb cb);
+/**
+ * Gets data stored in the hash local to the given thread
+ *
+ * @param thread The thread context the data belongs to
+ * @param key The name under which the data is stored
+ * @return The value under the given key, or @c NULL on error.
+ *
+ * Finds and return the data stored in the shared hash under the key @p key.
+ *
+ * This function, and all of the others in the @c ecore_thread_local_data
+ * family of functions, can only be called within the worker function running
+ * in the thread. Do not call them from the main loop or from a thread
+ * other than the one represented by @p thread.
+ *
+ * @see ecore_thread_local_data_add()
+ * @see ecore_thread_local_data_wait()
+ */
+EAPI void *ecore_thread_local_data_find(Ecore_Thread *thread, const char *key);
+/**
+ * Deletes from the thread's hash the data corresponding to the given key
+ *
+ * @param thread The thread context the data belongs to
+ * @param key The name under which the data is stored
+ * @return @c EINA_TRUE on success, @c EINA_FALSE on failure.
+ *
+ * If there's any data stored associated with @p key in the global hash,
+ * this function will remove it from it and return @c EINA_TRUE. If no data
+ * exists or an error occurs, it returns @c EINA_FALSE.
+ *
+ * If the data was added to the hash with a free function, then it will
+ * also be freed after removing it from the hash, otherwise it requires
+ * to be manually freed by the user, which means that if no other reference
+ * to it exists before calling this function, it will result in a memory
+ * leak.
+ *
+ * This function, and all of the others in the @c ecore_thread_local_data
+ * family of functions, can only be called within the worker function running
+ * in the thread. Do not call them from the main loop or from a thread
+ * other than the one represented by @p thread.
+ *
+ * @see ecore_thread_local_data_add()
+ */
+EAPI Eina_Bool ecore_thread_local_data_del(Ecore_Thread *thread, const char *key);
+
+/**
+ * Adds some data to a hash shared by all threads
+ *
+ * @param key The name under which the data will be stored
+ * @param value The data to add
+ * @param cb Function to free the data when removed from the hash
+ * @param direct If true, this will not copy the key string (like
+ * eina_hash_direct_add())
+ * @return @c EINA_TRUE on success, @c EINA_FALSE on failure.
+ *
+ * Ecore Thread keeps a hash that can be used to share data across several
+ * threads, including the main loop one, without having to manually handle
+ * mutexes to do so safely.
+ *
+ * This function adds the data @p value to this hash under the given @p key.
+ * No other value in the hash may have the same @p key. If you need to
+ * change the value under a @p key, or you don't know if one exists already,
+ * you can use ecore_thread_global_data_set().
+ *
+ * Neither @p key nor @p value may be @c NULL and @p key will be copied in the
+ * hash, unless @p direct is set, in which case the string used should not
+ * be freed until the data is removed from the hash.
+ *
+ * The @p cb function will be called when the data in the hash needs to be
+ * freed, be it because it got deleted with ecore_thread_global_data_del() or
+ * because Ecore Thread was shut down and the hash destroyed. This parameter
+ * may be NULL, in which case @p value needs to be manually freed after
+ * removing it from the hash with either ecore_thread_global_data_del() or
+ *ecore_thread_global_data_set().
+ *
+ * Manually freeing any data that was added to the hash with a @p cb function
+ * is likely to produce a segmentation fault, or any other strange
+ * happenings, later on in the program.
+ *
+ * @see ecore_thread_global_data_del()
+ * @see ecore_thread_global_data_set()
+ * @see ecore_thread_global_data_find()
+ */
+EAPI Eina_Bool ecore_thread_global_data_add(const char *key, void *value, Eina_Free_Cb cb, Eina_Bool direct);
+/**
+ * Sets some data in the hash shared by all threads
+ *
+ * @param key The name under which the data will be stored
+ * @param value The data to add
+ * @param cb Function to free the data when removed from the hash
+ *
+ * If no data exists in the hash under the @p key, this function adds
+ * @p value in the hash under the given @p key and returns NULL.
+ * The key itself is copied.
+ *
+ * If the hash already contains something under @p key, the data will be
+ * replaced by @p value and the old value will be returned.
+ *
+ * @c NULL will also be returned if either @p key or @p value are @c NULL, or
+ * if an error occurred.
+ *
+ * @see ecore_thread_global_data_add()
+ * @see ecore_thread_global_data_del()
+ * @see ecore_thread_global_data_find()
+ */
+EAPI void *ecore_thread_global_data_set(const char *key, void *value, Eina_Free_Cb cb);
+/**
+ * Gets data stored in the hash shared by all threads
+ *
+ * @param key The name under which the data is stored
+ * @return The value under the given key, or @c NULL on error.
+ *
+ * Finds and return the data stored in the shared hash under the key @p key.
+ *
+ * Keep in mind that the data returned may be used by more than one thread
+ * at the same time and no reference counting is done on it by Ecore.
+ * Freeing the data or modifying its contents may require additional
+ * precautions to be considered, depending on the application's design.
+ *
+ * @see ecore_thread_global_data_add()
+ * @see ecore_thread_global_data_wait()
+ */
+EAPI void *ecore_thread_global_data_find(const char *key);
+/**
+ * Deletes from the shared hash the data corresponding to the given key
+ *
+ * @param key The name under which the data is stored
+ * @return @c EINA_TRUE on success, @c EINA_FALSE on failure.
+ *
+ * If there's any data stored associated with @p key in the global hash,
+ * this function will remove it from it and return @c EINA_TRUE. If no data
+ * exists or an error occurs, it returns @c EINA_FALSE.
+ *
+ * If the data was added to the hash with a free function, then it will
+ * also be freed after removing it from the hash, otherwise it requires
+ * to be manually freed by the user, which means that if no other reference
+ * to it exists before calling this function, it will result in a memory
+ * leak.
+ *
+ * Note, also, that freeing data that other threads may be using will result
+ * in a crash, so appropriate care must be taken by the application when
+ * that possibility exists.
+ *
+ * @see ecore_thread_global_data_add()
+ */
+EAPI Eina_Bool ecore_thread_global_data_del(const char *key);
+/**
+ * Gets data stored in the shared hash, or wait for it if it doesn't exist
+ *
+ * @param key The name under which the data is stored
+ * @param seconds The amount of time in seconds to wait for the data.
+ * @return The value under the given key, or @c NULL on error.
+ *
+ * Finds and return the data stored in the shared hash under the key @p key.
+ *
+ * If there's nothing in the hash under the given @p key, the function
+ * will block and wait up to @p seconds seconds for some other thread to
+ * add it with either ecore_thread_global_data_add() or
+ * ecore_thread_global_data_set(). If after waiting there's still no data
+ * to get, @c NULL will be returned.
+ *
+ * If @p seconds is 0, then no waiting will happen and this function works
+ * like ecore_thread_global_data_find(). If @p seconds is less than 0, then
+ * the function will wait indefinitely.
+ *
+ * Keep in mind that the data returned may be used by more than one thread
+ * at the same time and no reference counting is done on it by Ecore.
+ * Freeing the data or modifying its contents may require additional
+ * precautions to be considered, depending on the application's design.
+ *
+ * @see ecore_thread_global_data_add()
+ * @see ecore_thread_global_data_find()
+ */
+EAPI void *ecore_thread_global_data_wait(const char *key, double seconds);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup Ecore_Pipe_Group Pipe wrapper
+ *
+ * These functions wrap the pipe / write / read functions to easily
+ * integrate its use into ecore's main loop.
+ *
+ * The ecore_pipe_add() function creates file descriptors (sockets
+ * on Windows) and attach a handle to the ecore main loop. That
+ * handle is called when data is read in the pipe. To write data in
+ * the pipe, just call ecore_pipe_write(). When you are done, just
+ * call ecore_pipe_del().
+ *
+ * For examples see here:
+ * @li @ref tutorial_ecore_pipe_gstreamer_example
+ * @li @ref tutorial_ecore_pipe_simple_example
+ *
+ * @ingroup Ecore_Main_Loop_Group
+ *
+ * @{
+ */
+
+typedef struct _Ecore_Pipe Ecore_Pipe; /**< A handle for pipes */
+
+/**
+ * @typedef Ecore_Pipe_Cb Ecore_Pipe_Cb
+ * The callback that data written to the pipe is sent to.
+ */
+typedef void (*Ecore_Pipe_Cb)(void *data, void *buffer, unsigned int nbyte);
+
+EAPI Ecore_Pipe *ecore_pipe_add(Ecore_Pipe_Cb handler, const void *data);
+EAPI void *ecore_pipe_del(Ecore_Pipe *p);
+EAPI Eina_Bool ecore_pipe_write(Ecore_Pipe *p, const void *buffer, unsigned int nbytes);
+EAPI void ecore_pipe_write_close(Ecore_Pipe *p);
+EAPI void ecore_pipe_read_close(Ecore_Pipe *p);
+EAPI void ecore_pipe_thaw(Ecore_Pipe *p);
+EAPI void ecore_pipe_freeze(Ecore_Pipe *p);
+EAPI int ecore_pipe_wait(Ecore_Pipe *p, int message_count, double wait);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup Ecore_Job_Group Ecore Job functions
+ *
+ * You can queue jobs that are to be done by the main loop when the
+ * current event is dealt with.
+ *
+ * Jobs are processed by the main loop similarly to events. They
+ * also will be executed in the order in which they were added.
+ *
+ * A good use for them is when you don't want to execute an action
+ * immediately, but want to give the control back to the main loop
+ * so that it will call your job callback when jobs start being
+ * processed (and if there are other jobs added before yours, they
+ * will be processed first). This also gives the chance to other
+ * actions in your program to cancel the job before it is started.
+ *
+ * Examples of using @ref Ecore_Job :
+ * @li @ref ecore_job_example_c
+ *
+ * @ingroup Ecore_Main_Loop_Group
+ *
+ * @{
+ */
+
+/*
+ * @since 1.8
+ */
+typedef Eo Ecore_Job; /**< A job handle */
+#define ECORE_JOB_CLASS ecore_job_class_get()
+const Eo_Class *ecore_job_class_get(void) EINA_CONST;
+
+extern EAPI Eo_Op ECORE_JOB_BASE_ID;
+
+enum
+{
+ ECORE_JOB_SUB_ID_CONSTRUCTOR,
+ ECORE_JOB_SUB_ID_LAST
+};
+
+#define ECORE_JOB_ID(sub_id) (ECORE_JOB_BASE_ID + sub_id)
+
+/**
+ * @def ecore_job_constructor
+ * @since 1.8
+ *
+ * Contructor.
+ *
+ * @param[in] func
+ * @param[in] data
+ *
+ */
+#define ecore_job_constructor(func, data) ECORE_JOB_ID(ECORE_JOB_SUB_ID_CONSTRUCTOR), EO_TYPECHECK(Ecore_Cb, func), EO_TYPECHECK(const void *, data)
+
+EAPI Ecore_Job *ecore_job_add(Ecore_Cb func, const void *data);
+EAPI void *ecore_job_del(Ecore_Job *job);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup Ecore_Application_Group Ecore Application functions
+ *
+ * @{
+ */
+
+EAPI void ecore_app_args_set(int argc, const char **argv);
+EAPI void ecore_app_args_get(int *argc, char ***argv);
+EAPI void ecore_app_restart(void);
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup Ecore_Throttle_Group Ecore Throttle functions
+ *
+ * @ingroup Ecore_Main_Loop_Group
+ *
+ * @{
+ */
+
+EAPI void ecore_throttle_adjust(double amount);
+EAPI double ecore_throttle_get(void);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/src/lib/ecore/Ecore_Getopt.h b/src/lib/ecore/Ecore_Getopt.h
new file mode 100644
index 0000000000..0a11787d4d
--- /dev/null
+++ b/src/lib/ecore/Ecore_Getopt.h
@@ -0,0 +1,419 @@
+#ifndef _ECORE_GETOPT_H
+#define _ECORE_GETOPT_H
+
+#include <stdio.h>
+#include <Eina.h>
+
+#ifdef EAPI
+# undef EAPI
+#endif
+
+#ifdef _WIN32
+# ifdef EFL_ECORE_BUILD
+# ifdef DLL_EXPORT
+# define EAPI __declspec(dllexport)
+# else
+# define EAPI
+# endif /* ! DLL_EXPORT */
+# else
+# define EAPI __declspec(dllimport)
+# endif /* ! EFL_ECORE_BUILD */
+#else
+# ifdef __GNUC__
+# if __GNUC__ >= 4
+# define EAPI __attribute__ ((visibility("default")))
+# else
+# define EAPI
+# endif
+# else
+# define EAPI
+# endif
+#endif /* ! _WIN32 */
+
+/**
+ * @file Ecore_Getopt.h
+ * @brief Contains powerful getopt replacement.
+ *
+ * This replacement handles both short (-X) or long options (--ABC)
+ * options, with various actions supported, like storing one value and
+ * already converting to required type, counting number of
+ * occurrences, setting true or false values, show help, license,
+ * copyright and even support user-defined callbacks.
+ *
+ * It is provided a set of C Pre Processor macros so definition is
+ * straightforward.
+ *
+ * Values will be stored elsewhere indicated by an array of pointers
+ * to values, it is given in separate to parser description so you can
+ * use multiple values with the same parser.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+ ECORE_GETOPT_ACTION_STORE,
+ ECORE_GETOPT_ACTION_STORE_CONST,
+ ECORE_GETOPT_ACTION_STORE_TRUE,
+ ECORE_GETOPT_ACTION_STORE_FALSE,
+ ECORE_GETOPT_ACTION_CHOICE,
+ ECORE_GETOPT_ACTION_APPEND,
+ ECORE_GETOPT_ACTION_COUNT,
+ ECORE_GETOPT_ACTION_CALLBACK,
+ ECORE_GETOPT_ACTION_HELP,
+ ECORE_GETOPT_ACTION_VERSION,
+ ECORE_GETOPT_ACTION_COPYRIGHT,
+ ECORE_GETOPT_ACTION_LICENSE
+} Ecore_Getopt_Action;
+
+typedef enum {
+ ECORE_GETOPT_TYPE_STR,
+ ECORE_GETOPT_TYPE_BOOL,
+ ECORE_GETOPT_TYPE_SHORT,
+ ECORE_GETOPT_TYPE_INT,
+ ECORE_GETOPT_TYPE_LONG,
+ ECORE_GETOPT_TYPE_USHORT,
+ ECORE_GETOPT_TYPE_UINT,
+ ECORE_GETOPT_TYPE_ULONG,
+ ECORE_GETOPT_TYPE_DOUBLE
+} Ecore_Getopt_Type;
+
+typedef enum {
+ ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO = 0,
+ ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES = 1,
+ ECORE_GETOPT_DESC_ARG_REQUIREMENT_OPTIONAL = 3
+} Ecore_Getopt_Desc_Arg_Requirement;
+
+typedef union _Ecore_Getopt_Value Ecore_Getopt_Value;
+
+typedef struct _Ecore_Getopt_Desc_Store Ecore_Getopt_Desc_Store;
+typedef struct _Ecore_Getopt_Desc_Callback Ecore_Getopt_Desc_Callback;
+typedef struct _Ecore_Getopt_Desc Ecore_Getopt_Desc;
+typedef struct _Ecore_Getopt Ecore_Getopt;
+
+union _Ecore_Getopt_Value
+{
+ char **strp;
+ unsigned char *boolp;
+ short *shortp;
+ int *intp;
+ long *longp;
+ unsigned short *ushortp;
+ unsigned int *uintp;
+ unsigned long *ulongp;
+ double *doublep;
+ Eina_List **listp;
+ void **ptrp;
+};
+
+struct _Ecore_Getopt_Desc_Store
+{
+ Ecore_Getopt_Type type; /**< type of data being handled */
+ Ecore_Getopt_Desc_Arg_Requirement arg_req;
+ union
+ {
+ const char *strv;
+ Eina_Bool boolv;
+ short shortv;
+ int intv;
+ long longv;
+ unsigned short ushortv;
+ unsigned int uintv;
+ unsigned long ulongv;
+ double doublev;
+ } def;
+};
+
+struct _Ecore_Getopt_Desc_Callback
+{
+ Eina_Bool (*func)(const Ecore_Getopt *parser,
+ const Ecore_Getopt_Desc *desc,
+ const char *str,
+ void *data,
+ Ecore_Getopt_Value *storage);
+ const void *data;
+ Ecore_Getopt_Desc_Arg_Requirement arg_req;
+ const char *def;
+};
+
+struct _Ecore_Getopt_Desc
+{
+ char shortname; /**< used with a single dash */
+ const char *longname; /**< used with double dashes */
+ const char *help; /**< used by --help/ecore_getopt_help() */
+ const char *metavar; /**< used by ecore_getopt_help() with nargs > 0 */
+
+ Ecore_Getopt_Action action; /**< define how to handle it */
+ union
+ {
+ const Ecore_Getopt_Desc_Store store;
+ const void *store_const;
+ const char *const *choices; /* NULL terminated. */
+ const Ecore_Getopt_Type append_type;
+ const Ecore_Getopt_Desc_Callback callback;
+ const void *dummy;
+ } action_param;
+};
+
+struct _Ecore_Getopt
+{
+ const char *prog; /**< to be used when ecore_app_args_get() fails */
+ const char *usage; /**< usage example, %prog is replaced */
+ const char *version; /**< if exists, --version will work */
+ const char *copyright; /**< if exists, --copyright will work */
+ const char *license; /**< if exists, --license will work */
+ const char *description; /**< long description, possible multiline */
+ Eina_Bool strict : 1; /**< fail on errors */
+ const Ecore_Getopt_Desc descs[]; /* NULL terminated. */
+};
+
+#define ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar, type, arg_requirement, default_value) \
+ {shortname, longname, help, metavar, ECORE_GETOPT_ACTION_STORE, \
+ {.store = {type, arg_requirement, default_value}}}
+
+#define ECORE_GETOPT_STORE(shortname, longname, help, type) \
+ ECORE_GETOPT_STORE_FULL(shortname, longname, help, NULL, type, \
+ ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES, {})
+
+#define ECORE_GETOPT_STORE_STR(shortname, longname, help) \
+ ECORE_GETOPT_STORE(shortname, longname, help, ECORE_GETOPT_TYPE_STR)
+#define ECORE_GETOPT_STORE_BOOL(shortname, longname, help) \
+ ECORE_GETOPT_STORE(shortname, longname, help, ECORE_GETOPT_TYPE_BOOL)
+#define ECORE_GETOPT_STORE_SHORT(shortname, longname, help) \
+ ECORE_GETOPT_STORE(shortname, longname, help, ECORE_GETOPT_TYPE_SHORT)
+#define ECORE_GETOPT_STORE_INT(shortname, longname, help) \
+ ECORE_GETOPT_STORE(shortname, longname, help, ECORE_GETOPT_TYPE_INT)
+#define ECORE_GETOPT_STORE_LONG(shortname, longname, help) \
+ ECORE_GETOPT_STORE(shortname, longname, help, ECORE_GETOPT_TYPE_LONG)
+#define ECORE_GETOPT_STORE_USHORT(shortname, longname, help) \
+ ECORE_GETOPT_STORE(shortname, longname, help, ECORE_GETOPT_TYPE_USHORT)
+#define ECORE_GETOPT_STORE_UINT(shortname, longname, help) \
+ ECORE_GETOPT_STORE(shortname, longname, help, ECORE_GETOPT_TYPE_UINT)
+#define ECORE_GETOPT_STORE_ULONG(shortname, longname, help) \
+ ECORE_GETOPT_STORE(shortname, longname, help, ECORE_GETOPT_TYPE_ULONG)
+#define ECORE_GETOPT_STORE_DOUBLE(shortname, longname, help) \
+ ECORE_GETOPT_STORE(shortname, longname, help, ECORE_GETOPT_TYPE_DOUBLE)
+
+#define ECORE_GETOPT_STORE_METAVAR(shortname, longname, help, metavar, type) \
+ ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar, type, \
+ ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES, {})
+
+#define ECORE_GETOPT_STORE_METAVAR_STR(shortname, longname, help, metavar) \
+ ECORE_GETOPT_STORE_METAVAR(shortname, longname, help, metavar, ECORE_GETOPT_TYPE_STR)
+#define ECORE_GETOPT_STORE_METAVAR_BOOL(shortname, longname, help, metavar) \
+ ECORE_GETOPT_STORE_METAVAR(shortname, longname, help, metavar, ECORE_GETOPT_TYPE_BOOL)
+#define ECORE_GETOPT_STORE_METAVAR_SHORT(shortname, longname, help, metavar) \
+ ECORE_GETOPT_STORE_METAVAR(shortname, longname, help, metavar, ECORE_GETOPT_TYPE_SHORT)
+#define ECORE_GETOPT_STORE_METAVAR_INT(shortname, longname, help, metavar) \
+ ECORE_GETOPT_STORE_METAVAR(shortname, longname, help, metavar, ECORE_GETOPT_TYPE_INT)
+#define ECORE_GETOPT_STORE_METAVAR_LONG(shortname, longname, help, metavar) \
+ ECORE_GETOPT_STORE_METAVAR(shortname, longname, help, metavar, ECORE_GETOPT_TYPE_LONG)
+#define ECORE_GETOPT_STORE_METAVAR_USHORT(shortname, longname, help, metavar) \
+ ECORE_GETOPT_STORE_METAVAR(shortname, longname, help, metavar, ECORE_GETOPT_TYPE_USHORT)
+#define ECORE_GETOPT_STORE_METAVAR_UINT(shortname, longname, help, metavar) \
+ ECORE_GETOPT_STORE_METAVAR(shortname, longname, help, metavar, ECORE_GETOPT_TYPE_UINT)
+#define ECORE_GETOPT_STORE_METAVAR_ULONG(shortname, longname, help, metavar) \
+ ECORE_GETOPT_STORE_METAVAR(shortname, longname, help, metavar, ECORE_GETOPT_TYPE_ULONG)
+#define ECORE_GETOPT_STORE_METAVAR_DOUBLE(shortname, longname, help, metavar) \
+ ECORE_GETOPT_STORE_METAVAR(shortname, longname, help, metavar, ECORE_GETOPT_TYPE_DOUBLE)
+
+#define ECORE_GETOPT_STORE_DEF(shortname, longname, help, type, default_value) \
+ ECORE_GETOPT_STORE_FULL(shortname, longname, help, NULL, type, \
+ ECORE_GETOPT_DESC_ARG_REQUIREMENT_OPTIONAL, \
+ default_value)
+
+#define ECORE_GETOPT_STORE_DEF_STR(shortname, longname, help, default_value) \
+ ECORE_GETOPT_STORE_DEF(shortname, longname, help, \
+ ECORE_GETOPT_TYPE_STR, \
+ {.strv = default_value})
+#define ECORE_GETOPT_STORE_DEF_BOOL(shortname, longname, help, default_value) \
+ ECORE_GETOPT_STORE_DEF(shortname, longname, help, \
+ ECORE_GETOPT_TYPE_BOOL, \
+ {.boolv = default_value})
+#define ECORE_GETOPT_STORE_DEF_SHORT(shortname, longname, help, default_value) \
+ ECORE_GETOPT_STORE_DEF(shortname, longname, help, \
+ ECORE_GETOPT_TYPE_SHORT, \
+ {.shortv = default_value})
+#define ECORE_GETOPT_STORE_DEF_INT(shortname, longname, help, default_value) \
+ ECORE_GETOPT_STORE_DEF(shortname, longname, help, \
+ ECORE_GETOPT_TYPE_INT, \
+ {.intv = default_value})
+#define ECORE_GETOPT_STORE_DEF_LONG(shortname, longname, help, default_value) \
+ ECORE_GETOPT_STORE_DEF(shortname, longname, help, \
+ ECORE_GETOPT_TYPE_LONG, \
+ {.longv = default_value})
+#define ECORE_GETOPT_STORE_DEF_USHORT(shortname, longname, help, default_value) \
+ ECORE_GETOPT_STORE_DEF(shortname, longname, help, \
+ ECORE_GETOPT_TYPE_USHORT, \
+ {.ushortv = default_value})
+#define ECORE_GETOPT_STORE_DEF_UINT(shortname, longname, help, default_value) \
+ ECORE_GETOPT_STORE_DEF(shortname, longname, help, \
+ ECORE_GETOPT_TYPE_UINT, \
+ {.uintv = default_value})
+#define ECORE_GETOPT_STORE_DEF_ULONG(shortname, longname, help, default_value) \
+ ECORE_GETOPT_STORE_DEF(shortname, longname, help, \
+ ECORE_GETOPT_TYPE_ULONG, \
+ {.ulongv = default_value})
+#define ECORE_GETOPT_STORE_DEF_DOUBLE(shortname, longname, help, default_value) \
+ ECORE_GETOPT_STORE_DEF(shortname, longname, help, \
+ ECORE_GETOPT_TYPE_DOUBLE, \
+ {.doublev = default_value})
+
+#define ECORE_GETOPT_STORE_FULL_STR(shortname, longname, help, metavar, arg_requirement, default_value) \
+ ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar, \
+ ECORE_GETOPT_TYPE_STR, \
+ arg_requirement, \
+ {.strv = default_value})
+#define ECORE_GETOPT_STORE_FULL_BOOL(shortname, longname, help, metavar, arg_requirement, default_value) \
+ ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar, \
+ ECORE_GETOPT_TYPE_BOOL, \
+ arg_requirement, \
+ {.boolv = default_value})
+#define ECORE_GETOPT_STORE_FULL_SHORT(shortname, longname, help, metavar, arg_requirement, default_value) \
+ ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar, \
+ ECORE_GETOPT_TYPE_SHORT, \
+ arg_requirement, \
+ {.shortv = default_value})
+#define ECORE_GETOPT_STORE_FULL_INT(shortname, longname, help, metavar, arg_requirement, default_value) \
+ ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar, \
+ ECORE_GETOPT_TYPE_INT, \
+ arg_requirement, \
+ {.intv = default_value})
+#define ECORE_GETOPT_STORE_FULL_LONG(shortname, longname, help, metavar, arg_requirement, default_value) \
+ ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar, \
+ ECORE_GETOPT_TYPE_LONG, \
+ arg_requirement, \
+ {.longv = default_value})
+#define ECORE_GETOPT_STORE_FULL_USHORT(shortname, longname, help, metavar, arg_requirement, default_value) \
+ ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar, \
+ ECORE_GETOPT_TYPE_USHORT, \
+ arg_requirement, \
+ {.ushortv = default_value})
+#define ECORE_GETOPT_STORE_FULL_UINT(shortname, longname, help, metavar, arg_requirement, default_value) \
+ ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar, \
+ ECORE_GETOPT_TYPE_UINT, \
+ arg_requirement, \
+ {.uintv = default_value})
+#define ECORE_GETOPT_STORE_FULL_ULONG(shortname, longname, help, metavar, arg_requirement, default_value) \
+ ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar, \
+ ECORE_GETOPT_TYPE_ULONG, \
+ arg_requirement, \
+ {.ulongv = default_value})
+#define ECORE_GETOPT_STORE_FULL_DOUBLE(shortname, longname, help, metavar, arg_requirement, default_value) \
+ ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar, \
+ ECORE_GETOPT_TYPE_DOUBLE, \
+ arg_requirement, \
+ {.doublev = default_value})
+
+#define ECORE_GETOPT_STORE_CONST(shortname, longname, help, value) \
+ {shortname, longname, help, NULL, ECORE_GETOPT_ACTION_STORE_CONST, \
+ {.store_const = value}}
+#define ECORE_GETOPT_STORE_TRUE(shortname, longname, help) \
+ {shortname, longname, help, NULL, ECORE_GETOPT_ACTION_STORE_TRUE, \
+ {.dummy = NULL}}
+#define ECORE_GETOPT_STORE_FALSE(shortname, longname, help) \
+ {shortname, longname, help, NULL, ECORE_GETOPT_ACTION_STORE_FALSE, \
+ {.dummy = NULL}}
+
+#define ECORE_GETOPT_CHOICE(shortname, longname, help, choices_array) \
+ {shortname, longname, help, NULL, ECORE_GETOPT_ACTION_CHOICE, \
+ {.choices = choices_array}}
+#define ECORE_GETOPT_CHOICE_METAVAR(shortname, longname, help, metavar, choices_array) \
+ {shortname, longname, help, metavar, ECORE_GETOPT_ACTION_CHOICE, \
+ {.choices = choices_array}}
+
+#define ECORE_GETOPT_APPEND(shortname, longname, help, sub_type) \
+ {shortname, longname, help, NULL, ECORE_GETOPT_ACTION_APPEND, \
+ {.append_type = sub_type}}
+#define ECORE_GETOPT_APPEND_METAVAR(shortname, longname, help, metavar, type) \
+ {shortname, longname, help, metavar, ECORE_GETOPT_ACTION_APPEND, \
+ {.append_type = type}}
+
+#define ECORE_GETOPT_COUNT(shortname, longname, help) \
+ {shortname, longname, help, NULL, ECORE_GETOPT_ACTION_COUNT, \
+ {.dummy = NULL}}
+
+#define ECORE_GETOPT_CALLBACK_FULL(shortname, longname, help, metavar, callback_func, callback_data, argument_requirement, default_value) \
+ {shortname, longname, help, metavar, ECORE_GETOPT_ACTION_CALLBACK, \
+ {.callback = {callback_func, callback_data, \
+ argument_requirement, default_value}}}
+#define ECORE_GETOPT_CALLBACK_NOARGS(shortname, longname, help, callback_func, callback_data) \
+ ECORE_GETOPT_CALLBACK_FULL(shortname, longname, help, NULL, \
+ callback_func, callback_data, \
+ ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO, \
+ NULL)
+#define ECORE_GETOPT_CALLBACK_ARGS(shortname, longname, help, metavar, callback_func, callback_data) \
+ ECORE_GETOPT_CALLBACK_FULL(shortname, longname, help, metavar, \
+ callback_func, callback_data, \
+ ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES, \
+ NULL)
+
+#define ECORE_GETOPT_HELP(shortname, longname) \
+ {shortname, longname, "show this message.", NULL, \
+ ECORE_GETOPT_ACTION_HELP, \
+ {.dummy = NULL}}
+
+#define ECORE_GETOPT_VERSION(shortname, longname) \
+ {shortname, longname, "show program version.", NULL, \
+ ECORE_GETOPT_ACTION_VERSION, \
+ {.dummy = NULL}}
+
+#define ECORE_GETOPT_COPYRIGHT(shortname, longname) \
+ {shortname, longname, "show copyright.", NULL, \
+ ECORE_GETOPT_ACTION_COPYRIGHT, \
+ {.dummy = NULL}}
+
+#define ECORE_GETOPT_LICENSE(shortname, longname) \
+ {shortname, longname, "show license.", NULL, \
+ ECORE_GETOPT_ACTION_LICENSE, \
+ {.dummy = NULL}}
+
+#define ECORE_GETOPT_SENTINEL {0, NULL, NULL, NULL, 0, {.dummy = NULL}}
+
+#define ECORE_GETOPT_VALUE_STR(val) {.strp = &(val)}
+#define ECORE_GETOPT_VALUE_BOOL(val) {.boolp = &(val)}
+#define ECORE_GETOPT_VALUE_SHORT(val) {.shortp = &(val)}
+#define ECORE_GETOPT_VALUE_INT(val) {.intp = &(val)}
+#define ECORE_GETOPT_VALUE_LONG(val) {.longp = &(val)}
+#define ECORE_GETOPT_VALUE_USHORT(val) {.ushortp = &(val)}
+#define ECORE_GETOPT_VALUE_UINT(val) {.uintp = &(val)}
+#define ECORE_GETOPT_VALUE_ULONG(val) {.ulongp = &(val)}
+#define ECORE_GETOPT_VALUE_DOUBLE(val) {.doublep = &(val)}
+#define ECORE_GETOPT_VALUE_PTR(val) {.ptrp = &(val)}
+#define ECORE_GETOPT_VALUE_PTR_CAST(val) {.ptrp = (void **)&(val)}
+#define ECORE_GETOPT_VALUE_LIST(val) {.listp = &(val)}
+#define ECORE_GETOPT_VALUE_NONE {.ptrp = NULL}
+
+EAPI void
+ecore_getopt_help(FILE *fp,
+ const Ecore_Getopt *info);
+
+EAPI Eina_Bool
+ ecore_getopt_parser_has_duplicates(const Ecore_Getopt *parser);
+EAPI int
+ ecore_getopt_parse(const Ecore_Getopt *parser,
+ Ecore_Getopt_Value *values,
+ int argc,
+ char **argv);
+
+EAPI Eina_List *ecore_getopt_list_free(Eina_List *list);
+
+/* helper functions to be used with ECORE_GETOPT_CALLBACK_*() */
+EAPI Eina_Bool
+ecore_getopt_callback_geometry_parse(const Ecore_Getopt *parser,
+ const Ecore_Getopt_Desc *desc,
+ const char *str,
+ void *data,
+ Ecore_Getopt_Value *storage);
+EAPI Eina_Bool
+ecore_getopt_callback_size_parse(const Ecore_Getopt *parser,
+ const Ecore_Getopt_Desc *desc,
+ const char *str,
+ void *data,
+ Ecore_Getopt_Value *storage);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _ECORE_GETOPT_H */
diff --git a/src/lib/ecore/ecore.c b/src/lib/ecore/ecore.c
new file mode 100644
index 0000000000..3aa15bf77c
--- /dev/null
+++ b/src/lib/ecore/ecore.c
@@ -0,0 +1,878 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#ifdef HAVE_LOCALE_H
+# include <locale.h>
+#endif
+
+#ifdef HAVE_LANGINFO_H
+# include <langinfo.h>
+#endif
+
+#ifdef HAVE_SYS_MMAN_H
+# include <sys/mman.h>
+#endif
+
+#ifdef HAVE_EVIL
+# include <Evil.h>
+#endif
+#include <Eina.h>
+
+#include "Ecore.h"
+#include "ecore_private.h"
+
+#if HAVE_MALLINFO
+#include <malloc.h>
+
+
+static Ecore_Version _version = { VMAJ, VMIN, VMIC, VREV };
+EAPI Ecore_Version *ecore_version = &_version;
+
+#define KEEP_MAX(Global, Local) \
+ if (Global < (Local)) \
+ Global = Local;
+
+static Eina_Bool _ecore_memory_statistic(void *data);
+static int _ecore_memory_max_total = 0;
+static int _ecore_memory_max_free = 0;
+static pid_t _ecore_memory_pid = 0;
+#endif
+
+Eo *_ecore_parent = NULL;
+
+static const char *_ecore_magic_string_get(Ecore_Magic m);
+static int _ecore_init_count = 0;
+int _ecore_log_dom = -1;
+int _ecore_fps_debug = 0;
+
+typedef struct _Ecore_Safe_Call Ecore_Safe_Call;
+struct _Ecore_Safe_Call
+{
+ union {
+ Ecore_Cb async;
+ Ecore_Data_Cb sync;
+ } cb;
+ void *data;
+
+ Eina_Lock m;
+ Eina_Condition c;
+
+ int current_id;
+
+ Eina_Bool sync : 1;
+ Eina_Bool suspend : 1;
+};
+
+static void _ecore_main_loop_thread_safe_call(Ecore_Safe_Call *order);
+static void _thread_safe_cleanup(void *data);
+static void _thread_callback(void *data,
+ void *buffer,
+ unsigned int nbyte);
+static Eina_List *_thread_cb = NULL;
+static Ecore_Pipe *_thread_call = NULL;
+static Eina_Lock _thread_safety;
+static const int wakeup = 42;
+
+static int _thread_loop = 0;
+static Eina_Lock _thread_mutex;
+static Eina_Condition _thread_cond;
+static Eina_Lock _thread_feedback_mutex;
+static Eina_Condition _thread_feedback_cond;
+
+static Eina_Lock _thread_id_lock;
+static int _thread_id = -1;
+static int _thread_id_max = 0;
+static int _thread_id_update = 0;
+
+Eina_Lock _ecore_main_loop_lock;
+int _ecore_main_lock_count;
+
+/** OpenBSD does not define CODESET
+ * FIXME ??
+ */
+
+#ifndef CODESET
+# define CODESET "INVALID"
+#endif
+
+/**
+ * @addtogroup Ecore_Init_Group
+ *
+ * @{
+ */
+
+/**
+ * Set up connections, signal handlers, sockets etc.
+ * @return 1 or greater on success, 0 otherwise
+ *
+ * This function sets up all singal handlers and the basic event loop. If it
+ * succeeds, 1 will be returned, otherwise 0 will be returned.
+ *
+ * @code
+ * #include <Ecore.h>
+ *
+ * int main(int argc, char **argv)
+ * {
+ * if (!ecore_init())
+ * {
+ * printf("ERROR: Cannot init Ecore!\n");
+ * return -1;
+ * }
+ * ecore_main_loop_begin();
+ * ecore_shutdown();
+ * }
+ * @endcode
+ */
+EAPI int
+ecore_init(void)
+{
+ if (++_ecore_init_count != 1)
+ return _ecore_init_count;
+
+ eo_init();
+
+#ifdef HAVE_LOCALE_H
+ setlocale(LC_CTYPE, "");
+#endif
+ /*
+ if (strcmp(nl_langinfo(CODESET), "UTF-8"))
+ {
+ WRN("Not a utf8 locale!");
+ }
+ */
+#ifdef HAVE_EVIL
+ if (!evil_init())
+ return --_ecore_init_count;
+#endif
+ if (!eina_init())
+ goto shutdown_evil;
+ _ecore_log_dom = eina_log_domain_register("ecore", ECORE_DEFAULT_LOG_COLOR);
+ if (_ecore_log_dom < 0)
+ {
+ EINA_LOG_ERR("Ecore was unable to create a log domain.");
+ goto shutdown_log_dom;
+ }
+ if (getenv("ECORE_FPS_DEBUG")) _ecore_fps_debug = 1;
+ if (_ecore_fps_debug) _ecore_fps_debug_init();
+ if (!ecore_mempool_init()) goto shutdown_mempool;
+ _ecore_main_loop_init();
+ _ecore_signal_init();
+#ifndef HAVE_EXOTIC
+ _ecore_exe_init();
+#endif
+ _ecore_thread_init();
+ _ecore_glib_init();
+ _ecore_job_init();
+ _ecore_time_init();
+
+ eina_lock_new(&_thread_mutex);
+ eina_condition_new(&_thread_cond, &_thread_mutex);
+ eina_lock_new(&_thread_feedback_mutex);
+ eina_condition_new(&_thread_feedback_cond, &_thread_feedback_mutex);
+ _thread_call = _ecore_pipe_add(_thread_callback, NULL);
+ eina_lock_new(&_thread_safety);
+
+ eina_lock_new(&_thread_id_lock);
+
+ eina_lock_new(&_ecore_main_loop_lock);
+
+#if HAVE_MALLINFO
+ if (getenv("ECORE_MEM_STAT"))
+ {
+ _ecore_memory_pid = getpid();
+ ecore_animator_add(_ecore_memory_statistic, NULL);
+ }
+#endif
+
+#if defined(GLIB_INTEGRATION_ALWAYS)
+ if (_ecore_glib_always_integrate) ecore_main_loop_glib_integrate();
+#endif
+ _ecore_parent = eo_add(ECORE_PARENT_CLASS, NULL);
+
+ return _ecore_init_count;
+
+shutdown_mempool:
+ ecore_mempool_shutdown();
+shutdown_log_dom:
+ eina_shutdown();
+shutdown_evil:
+#ifdef HAVE_EVIL
+ evil_shutdown();
+#endif
+
+ eo_shutdown();
+
+ return --_ecore_init_count;
+}
+
+/**
+ * Shut down connections, signal handlers sockets etc.
+ *
+ * @return 0 if ecore shuts down, greater than 0 otherwise.
+ * This function shuts down all things set up in ecore_init() and cleans up all
+ * event queues, handlers, filters, timers, idlers, idle enterers/exiters
+ * etc. set up after ecore_init() was called.
+ *
+ * Do not call this function from any callback that may be called from the main
+ * loop, as the main loop will then fall over and not function properly.
+ */
+EAPI int
+ecore_shutdown(void)
+{
+ Ecore_Pipe *p;
+ /*
+ * take a lock here because _ecore_event_shutdown() does callbacks
+ */
+ _ecore_lock();
+ if (_ecore_init_count <= 0)
+ {
+ ERR("Init count not greater than 0 in shutdown.");
+ _ecore_unlock();
+ return 0;
+ }
+ if (--_ecore_init_count != 0)
+ goto unlock;
+
+ if (_ecore_fps_debug) _ecore_fps_debug_shutdown();
+ _ecore_poller_shutdown();
+ _ecore_animator_shutdown();
+ _ecore_glib_shutdown();
+ _ecore_job_shutdown();
+ _ecore_thread_shutdown();
+
+ /* this looks horrible - a hack for now, but something to note. as
+ * we delete the _thread_call pipe a thread COULD be doing
+ * ecore_pipe_write() or what not to it at the same time - we
+ * must ensure all possible users of this _thread_call are finished
+ * and exited before we delete it here */
+ /*
+ * ok - this causes other valgrind complaints regarding glib aquiring
+ * locks internally. so fix bug a or bug b. let's leave the original
+ * bug in then and leave this as a note for now
+ */
+ /*
+ * It should be fine now as we do wait for thread to shutdown before
+ * we try to destroy the pipe.
+ */
+ p = _thread_call;
+ _thread_call = NULL;
+ _ecore_pipe_wait(p, 1, 0.1);
+ _ecore_pipe_del(p);
+ eina_lock_free(&_thread_safety);
+ eina_condition_free(&_thread_cond);
+ eina_lock_free(&_thread_mutex);
+ eina_condition_free(&_thread_feedback_cond);
+ eina_lock_free(&_thread_feedback_mutex);
+ eina_lock_free(&_thread_id_lock);
+
+
+#ifndef HAVE_EXOTIC
+ _ecore_exe_shutdown();
+#endif
+ _ecore_idle_enterer_shutdown();
+ _ecore_idle_exiter_shutdown();
+ _ecore_idler_shutdown();
+ _ecore_timer_shutdown();
+ _ecore_event_shutdown();
+ _ecore_main_shutdown();
+ _ecore_signal_shutdown();
+ _ecore_main_loop_shutdown();
+
+#if HAVE_MALLINFO
+ if (getenv("ECORE_MEM_STAT"))
+ {
+ _ecore_memory_statistic(NULL);
+
+ ERR("[%i] Memory MAX total: %i, free: %i",
+ _ecore_memory_pid,
+ _ecore_memory_max_total,
+ _ecore_memory_max_free);
+ }
+#endif
+ ecore_mempool_shutdown();
+ eina_log_domain_unregister(_ecore_log_dom);
+ _ecore_log_dom = -1;
+ eina_shutdown();
+#ifdef HAVE_EVIL
+ evil_shutdown();
+#endif
+
+ eo_unref(_ecore_parent);
+ eo_shutdown();
+unlock:
+ _ecore_unlock();
+
+ return _ecore_init_count;
+}
+
+struct _Ecore_Fork_Cb
+{
+ Ecore_Cb func;
+ void *data;
+ Eina_Bool delete_me : 1;
+};
+
+typedef struct _Ecore_Fork_Cb Ecore_Fork_Cb;
+
+static int fork_cbs_walking = 0;
+static Eina_List *fork_cbs = NULL;
+
+EAPI Eina_Bool
+ecore_fork_reset_callback_add(Ecore_Cb func, const void *data)
+{
+ Ecore_Fork_Cb *fcb;
+
+ fcb = calloc(1, sizeof(Ecore_Fork_Cb));
+ if (!fcb) return EINA_FALSE;
+ fcb->func = func;
+ fcb->data = (void *)data;
+ fork_cbs = eina_list_append(fork_cbs, fcb);
+ return EINA_TRUE;
+}
+
+EAPI Eina_Bool
+ecore_fork_reset_callback_del(Ecore_Cb func, const void *data)
+{
+ Eina_List *l;
+ Ecore_Fork_Cb *fcb;
+
+ EINA_LIST_FOREACH(fork_cbs, l, fcb)
+ {
+ if ((fcb->func == func) && (fcb->data == data))
+ {
+ if (!fork_cbs_walking)
+ {
+ fork_cbs = eina_list_remove_list(fork_cbs, l);
+ free(fcb);
+ }
+ else
+ fcb->delete_me = EINA_TRUE;
+ return EINA_TRUE;
+ }
+ }
+ return EINA_FALSE;
+}
+
+EAPI void
+ecore_fork_reset(void)
+{
+ Eina_List *l, *ln;
+ Ecore_Fork_Cb *fcb;
+
+ eina_lock_take(&_thread_safety);
+
+ ecore_pipe_del(_thread_call);
+ _thread_call = ecore_pipe_add(_thread_callback, NULL);
+ /* If there was something in the pipe, trigger a wakeup again */
+ if (_thread_cb) ecore_pipe_write(_thread_call, &wakeup, sizeof (int));
+
+ eina_lock_release(&_thread_safety);
+
+ // should this be done withing the eina lock stuff?
+
+ fork_cbs_walking++;
+ EINA_LIST_FOREACH(fork_cbs, l, fcb)
+ {
+ fcb->func(fcb->data);
+ }
+ fork_cbs_walking--;
+
+ EINA_LIST_FOREACH_SAFE(fork_cbs, l, ln, fcb)
+ {
+ if (fcb->delete_me)
+ {
+ fork_cbs = eina_list_remove_list(fork_cbs, l);
+ free(fcb);
+ }
+ }
+}
+
+/**
+ * @}
+ */
+
+EAPI void
+ecore_main_loop_thread_safe_call_async(Ecore_Cb callback,
+ void *data)
+{
+ Ecore_Safe_Call *order;
+
+ if (!callback) return;
+
+ if (eina_main_loop_is())
+ {
+ callback(data);
+ return;
+ }
+
+ order = malloc(sizeof (Ecore_Safe_Call));
+ if (!order) return;
+
+ order->cb.async = callback;
+ order->data = data;
+ order->sync = EINA_FALSE;
+ order->suspend = EINA_FALSE;
+
+ _ecore_main_loop_thread_safe_call(order);
+}
+
+EAPI void *
+ecore_main_loop_thread_safe_call_sync(Ecore_Data_Cb callback,
+ void *data)
+{
+ Ecore_Safe_Call *order;
+ void *ret;
+
+ if (!callback) return NULL;
+
+ if (eina_main_loop_is())
+ {
+ return callback(data);
+ }
+
+ order = malloc(sizeof (Ecore_Safe_Call));
+ if (!order) return NULL;
+
+ order->cb.sync = callback;
+ order->data = data;
+ eina_lock_new(&order->m);
+ eina_condition_new(&order->c, &order->m);
+ order->sync = EINA_TRUE;
+ order->suspend = EINA_FALSE;
+
+ _ecore_main_loop_thread_safe_call(order);
+
+ eina_lock_take(&order->m);
+ eina_condition_wait(&order->c);
+ eina_lock_release(&order->m);
+
+ ret = order->data;
+
+ order->sync = EINA_FALSE;
+ order->cb.async = _thread_safe_cleanup;
+ order->data = order;
+
+ _ecore_main_loop_thread_safe_call(order);
+
+ return ret;
+}
+
+EAPI int
+ecore_thread_main_loop_begin(void)
+{
+ Ecore_Safe_Call *order;
+
+ if (eina_main_loop_is())
+ {
+ return ++_thread_loop;
+ }
+
+ order = malloc(sizeof (Ecore_Safe_Call));
+ if (!order) return -1;
+
+ eina_lock_take(&_thread_id_lock);
+ order->current_id = ++_thread_id_max;
+ if (order->current_id < 0)
+ {
+ _thread_id_max = 0;
+ order->current_id = ++_thread_id_max;
+ }
+ eina_lock_release(&_thread_id_lock);
+
+ eina_lock_new(&order->m);
+ eina_condition_new(&order->c, &order->m);
+ order->suspend = EINA_TRUE;
+
+ _ecore_main_loop_thread_safe_call(order);
+
+ eina_lock_take(&order->m);
+ while (order->current_id != _thread_id)
+ eina_condition_wait(&order->c);
+ eina_lock_release(&order->m);
+
+ eina_main_loop_define();
+
+ _thread_loop = 1;
+
+ return EINA_TRUE;
+}
+
+EAPI int
+ecore_thread_main_loop_end(void)
+{
+ int current_id;
+
+ if (_thread_loop == 0)
+ {
+ ERR("the main loop is not locked ! No matching call to ecore_thread_main_loop_begin().");
+ return -1;
+ }
+
+ /* until we unlock the main loop, this thread has the main loop id */
+ if (!eina_main_loop_is())
+ {
+ ERR("Not in a locked thread !");
+ return -1;
+ }
+
+ _thread_loop--;
+ if (_thread_loop > 0)
+ return _thread_loop;
+
+ current_id = _thread_id;
+
+ eina_lock_take(&_thread_mutex);
+ _thread_id_update = _thread_id;
+ eina_condition_broadcast(&_thread_cond);
+ eina_lock_release(&_thread_mutex);
+
+ eina_lock_take(&_thread_feedback_mutex);
+ while (current_id == _thread_id && _thread_id != -1)
+ eina_condition_wait(&_thread_feedback_cond);
+ eina_lock_release(&_thread_feedback_mutex);
+
+ return 0;
+}
+
+EAPI void
+ecore_print_warning(const char *function EINA_UNUSED,
+ const char *sparam EINA_UNUSED)
+{
+ WRN("***** Developer Warning ***** :\n"
+ "\tThis program is calling:\n\n"
+ "\t%s();\n\n"
+ "\tWith the parameter:\n\n"
+ "\t%s\n\n"
+ "\tbeing NULL. Please fix your program.", function, sparam);
+ if (getenv("ECORE_ERROR_ABORT")) abort();
+}
+
+EAPI void
+_ecore_magic_fail(const void *d,
+ Ecore_Magic m,
+ Ecore_Magic req_m,
+ const char *fname EINA_UNUSED)
+{
+ ERR("\n"
+ "*** ECORE ERROR: Ecore Magic Check Failed!!!\n"
+ "*** IN FUNCTION: %s()", fname);
+ if (!d)
+ ERR(" Input handle pointer is NULL!");
+ else if (m == ECORE_MAGIC_NONE)
+ ERR(" Input handle has already been freed!");
+ else if (m != req_m)
+ ERR(" Input handle is wrong type\n"
+ " Expected: %08x - %s\n"
+ " Supplied: %08x - %s",
+ (unsigned int)req_m, _ecore_magic_string_get(req_m),
+ (unsigned int)m, _ecore_magic_string_get(m));
+ ERR("*** NAUGHTY PROGRAMMER!!!\n"
+ "*** SPANK SPANK SPANK!!!\n"
+ "*** Now go fix your code. Tut tut tut!");
+ if (getenv("ECORE_ERROR_ABORT")) abort();
+}
+
+static const char *
+_ecore_magic_string_get(Ecore_Magic m)
+{
+ switch (m)
+ {
+ case ECORE_MAGIC_NONE:
+ return "None (Freed Object)";
+ break;
+
+ case ECORE_MAGIC_EXE:
+ return "Ecore_Exe (Executable)";
+ break;
+
+ case ECORE_MAGIC_TIMER:
+ return "Ecore_Timer (Timer)";
+ break;
+
+ case ECORE_MAGIC_IDLER:
+ return "Ecore_Idler (Idler)";
+ break;
+
+ case ECORE_MAGIC_IDLE_ENTERER:
+ return "Ecore_Idle_Enterer (Idler Enterer)";
+ break;
+
+ case ECORE_MAGIC_IDLE_EXITER:
+ return "Ecore_Idle_Exiter (Idler Exiter)";
+ break;
+
+ case ECORE_MAGIC_FD_HANDLER:
+ return "Ecore_Fd_Handler (Fd Handler)";
+ break;
+
+ case ECORE_MAGIC_WIN32_HANDLER:
+ return "Ecore_Win32_Handler (Win32 Handler)";
+ break;
+
+ case ECORE_MAGIC_EVENT_HANDLER:
+ return "Ecore_Event_Handler (Event Handler)";
+ break;
+
+ case ECORE_MAGIC_EVENT:
+ return "Ecore_Event (Event)";
+ break;
+
+ default:
+ return "<UNKNOWN>";
+ }
+}
+
+/* fps debug calls - for debugging how much time your app actually spends */
+/* "running" (and the inverse being time spent running)... this does not */
+/* account for other apps and multitasking... */
+
+static int _ecore_fps_debug_init_count = 0;
+static int _ecore_fps_debug_fd = -1;
+unsigned int *_ecore_fps_runtime_mmap = NULL;
+
+void
+_ecore_fps_debug_init(void)
+{
+ char buf[PATH_MAX];
+ const char *tmp;
+ int pid;
+
+ _ecore_fps_debug_init_count++;
+ if (_ecore_fps_debug_init_count > 1) return;
+
+#ifndef HAVE_EVIL
+ tmp = "/tmp";
+#else
+ tmp = evil_tmpdir_get ();
+#endif /* HAVE_EVIL */
+ pid = (int)getpid();
+ snprintf(buf, sizeof(buf), "%s/.ecore_fps_debug-%i", tmp, pid);
+ _ecore_fps_debug_fd = open(buf, O_CREAT | O_TRUNC | O_RDWR, 0644);
+ if (_ecore_fps_debug_fd < 0)
+ {
+ unlink(buf);
+ _ecore_fps_debug_fd = open(buf, O_CREAT | O_TRUNC | O_RDWR, 0644);
+ }
+ if (_ecore_fps_debug_fd >= 0)
+ {
+ unsigned int zero = 0;
+ char *buf2 = (char *)&zero;
+ ssize_t todo = sizeof(unsigned int);
+
+ while (todo > 0)
+ {
+ ssize_t r = write(_ecore_fps_debug_fd, buf2, todo);
+ if (r > 0)
+ {
+ todo -= r;
+ buf2 += r;
+ }
+ else if ((r < 0) && (errno == EINTR))
+ continue;
+ else
+ {
+ ERR("could not write to file '%s' fd %d: %s",
+ tmp, _ecore_fps_debug_fd, strerror(errno));
+ close(_ecore_fps_debug_fd);
+ _ecore_fps_debug_fd = -1;
+ return;
+ }
+ }
+ _ecore_fps_runtime_mmap = mmap(NULL, sizeof(unsigned int),
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED,
+ _ecore_fps_debug_fd, 0);
+ if (_ecore_fps_runtime_mmap == MAP_FAILED)
+ _ecore_fps_runtime_mmap = NULL;
+ }
+}
+
+void
+_ecore_fps_debug_shutdown(void)
+{
+ _ecore_fps_debug_init_count--;
+ if (_ecore_fps_debug_init_count > 0) return;
+ if (_ecore_fps_debug_fd >= 0)
+ {
+ char buf[4096];
+ const char *tmp;
+ int pid;
+
+#ifndef HAVE_EVIL
+ tmp = "/tmp";
+#else
+ tmp = (char *)evil_tmpdir_get ();
+#endif /* HAVE_EVIL */
+ pid = (int)getpid();
+ snprintf(buf, sizeof(buf), "%s/.ecore_fps_debug-%i", tmp, pid);
+ unlink(buf);
+ if (_ecore_fps_runtime_mmap)
+ {
+ munmap(_ecore_fps_runtime_mmap, sizeof(int));
+ _ecore_fps_runtime_mmap = NULL;
+ }
+ close(_ecore_fps_debug_fd);
+ _ecore_fps_debug_fd = -1;
+ }
+}
+
+void
+_ecore_fps_debug_runtime_add(double t)
+{
+ if ((_ecore_fps_debug_fd >= 0) &&
+ (_ecore_fps_runtime_mmap))
+ {
+ unsigned int tm;
+
+ tm = (unsigned int)(t * 1000000.0);
+ /* i know its not 100% theoretically guaranteed, but i'd say a write */
+ /* of an int could be considered atomic for all practical purposes */
+ /* oh and since this is cumulative, 1 second = 1,000,000 ticks, so */
+ /* this can run for about 4294 seconds becore looping. if you are */
+ /* doing performance testing in one run for over an hour... well */
+ /* time to restart or handle a loop condition :) */
+ *(_ecore_fps_runtime_mmap) += tm;
+ }
+}
+
+#if HAVE_MALLINFO
+static Eina_Bool
+_ecore_memory_statistic(EINA_UNUSED void *data)
+{
+ struct mallinfo mi;
+ static int uordblks = 0;
+ static int fordblks = 0;
+ Eina_Bool changed = EINA_FALSE;
+
+ mi = mallinfo();
+
+#define HAS_CHANGED(Global, Local) \
+ if (Global != Local) \
+ { \
+ Global = Local; \
+ changed = EINA_TRUE; \
+ }
+
+ HAS_CHANGED(uordblks, mi.uordblks);
+ HAS_CHANGED(fordblks, mi.fordblks);
+
+ if (changed)
+ ERR("[%i] Memory total: %i, free: %i",
+ _ecore_memory_pid,
+ mi.uordblks,
+ mi.fordblks);
+
+ KEEP_MAX(_ecore_memory_max_total, mi.uordblks);
+ KEEP_MAX(_ecore_memory_max_free, mi.fordblks);
+
+ return ECORE_CALLBACK_RENEW;
+}
+
+#endif
+
+static void
+_ecore_main_loop_thread_safe_call(Ecore_Safe_Call *order)
+{
+ Eina_Bool count;
+
+ eina_lock_take(&_thread_safety);
+
+ count = _thread_cb ? 0 : 1;
+ _thread_cb = eina_list_append(_thread_cb, order);
+ if (count) ecore_pipe_write(_thread_call, &wakeup, sizeof (int));
+
+ eina_lock_release(&_thread_safety);
+}
+
+static void
+_thread_safe_cleanup(void *data)
+{
+ Ecore_Safe_Call *call = data;
+
+ eina_condition_free(&call->c);
+ eina_lock_free(&call->m);
+}
+
+void
+_ecore_main_call_flush(void)
+{
+ Ecore_Safe_Call *call;
+ Eina_List *callback;
+
+ eina_lock_take(&_thread_safety);
+ callback = _thread_cb;
+ _thread_cb = NULL;
+ eina_lock_release(&_thread_safety);
+
+ EINA_LIST_FREE(callback, call)
+ {
+ if (call->suspend)
+ {
+ eina_lock_take(&_thread_mutex);
+
+ eina_lock_take(&call->m);
+ _thread_id = call->current_id;
+ eina_condition_broadcast(&call->c);
+ eina_lock_release(&call->m);
+
+ while (_thread_id_update != _thread_id)
+ eina_condition_wait(&_thread_cond);
+ eina_lock_release(&_thread_mutex);
+
+ eina_main_loop_define();
+
+ eina_lock_take(&_thread_feedback_mutex);
+
+ _thread_id = -1;
+
+ eina_condition_broadcast(&_thread_feedback_cond);
+ eina_lock_release(&_thread_feedback_mutex);
+
+ _thread_safe_cleanup(call);
+ free(call);
+ }
+ else if (call->sync)
+ {
+ call->data = call->cb.sync(call->data);
+ eina_condition_broadcast(&call->c);
+ }
+ else
+ {
+ call->cb.async(call->data);
+ free(call);
+ }
+ }
+}
+
+static void
+_thread_callback(void *data EINA_UNUSED,
+ void *buffer EINA_UNUSED,
+ unsigned int nbyte EINA_UNUSED)
+{
+ _ecore_main_call_flush();
+}
+
+static const Eo_Class_Description parent_class_desc = {
+ EO_VERSION,
+ "ecore_parent",
+ EO_CLASS_TYPE_REGULAR,
+ EO_CLASS_DESCRIPTION_OPS(NULL, NULL, 0),
+ NULL,
+ 0,
+ NULL,
+ NULL
+};
+
+EO_DEFINE_CLASS(ecore_parent_class_get, &parent_class_desc, EO_BASE_CLASS, NULL);
diff --git a/src/lib/ecore/ecore_alloc.c b/src/lib/ecore/ecore_alloc.c
new file mode 100644
index 0000000000..bec66ef0d5
--- /dev/null
+++ b/src/lib/ecore/ecore_alloc.c
@@ -0,0 +1,132 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+
+#include <Eina.h>
+
+#include "Ecore.h"
+#include "ecore_private.h"
+
+typedef struct _Ecore_Mempool Ecore_Mempool;
+struct _Ecore_Mempool
+{
+ const char *name;
+ Eina_Mempool *mp;
+ size_t size;
+};
+
+#define GENERIC_ALLOC_FREE(TYPE, Type) \
+ extern size_t _ecore_sizeof_##TYPE; \
+ Ecore_Mempool Type##_mp = { #TYPE, NULL, 0 }; \
+ TYPE * \
+ Type##_calloc(unsigned int num) \
+ { \
+ return eina_mempool_calloc(Type##_mp.mp, \
+ num * _ecore_sizeof_##TYPE); \
+ } \
+ void \
+ Type##_mp_free(TYPE *e) \
+ { \
+ eina_mempool_free(Type##_mp.mp, e); \
+ }
+
+//GENERIC_ALLOC_FREE(Ecore_Animator, ecore_animator);
+GENERIC_ALLOC_FREE(Ecore_Event_Handler, ecore_event_handler);
+GENERIC_ALLOC_FREE(Ecore_Event_Filter, ecore_event_filter);
+GENERIC_ALLOC_FREE(Ecore_Event, ecore_event);
+//GENERIC_ALLOC_FREE(Ecore_Idle_Exiter, ecore_idle_exiter);
+//GENERIC_ALLOC_FREE(Ecore_Idle_Enterer, ecore_idle_enterer);
+//GENERIC_ALLOC_FREE(Ecore_Idler, ecore_idler);
+//GENERIC_ALLOC_FREE(Ecore_Job, ecore_job);
+//GENERIC_ALLOC_FREE(Ecore_Timer, ecore_timer);
+//GENERIC_ALLOC_FREE(Ecore_Poller, ecore_poller);
+GENERIC_ALLOC_FREE(Ecore_Pipe, ecore_pipe);
+GENERIC_ALLOC_FREE(Ecore_Fd_Handler, ecore_fd_handler);
+#ifdef _WIN32
+GENERIC_ALLOC_FREE(Ecore_Win32_Handler, ecore_win32_handler);
+#endif
+
+static Ecore_Mempool *mempool_array[] = {
+// &ecore_animator_mp,
+ &ecore_event_handler_mp,
+ &ecore_event_filter_mp,
+ &ecore_event_mp,
+// &ecore_idle_exiter_mp,
+// &ecore_idle_enterer_mp,
+// &ecore_idler_mp,
+// &ecore_job_mp,
+// &ecore_timer_mp,
+// &ecore_poller_mp,
+ &ecore_pipe_mp,
+ &ecore_fd_handler_mp,
+#ifdef _WIN32
+ &ecore_win32_handler_mp
+#endif
+};
+
+Eina_Bool
+ecore_mempool_init(void)
+{
+ const char *choice;
+ unsigned int i;
+
+#define MP_SIZE_INIT(TYPE, Type) \
+ Type##_mp.size = _ecore_sizeof_##TYPE
+
+// MP_SIZE_INIT(Ecore_Animator, ecore_animator);
+ MP_SIZE_INIT(Ecore_Event_Handler, ecore_event_handler);
+ MP_SIZE_INIT(Ecore_Event_Filter, ecore_event_filter);
+ MP_SIZE_INIT(Ecore_Event, ecore_event);
+// MP_SIZE_INIT(Ecore_Idle_Exiter, ecore_idle_exiter);
+// MP_SIZE_INIT(Ecore_Idle_Enterer, ecore_idle_enterer);
+// MP_SIZE_INIT(Ecore_Idler, ecore_idler);
+// MP_SIZE_INIT(Ecore_Job, ecore_job);
+// MP_SIZE_INIT(Ecore_Timer, ecore_timer);
+// MP_SIZE_INIT(Ecore_Poller, ecore_poller);
+ MP_SIZE_INIT(Ecore_Pipe, ecore_pipe);
+ MP_SIZE_INIT(Ecore_Fd_Handler, ecore_fd_handler);
+#ifdef _WIN32
+ MP_SIZE_INIT(Ecore_Win32_Handler, ecore_win32_handler);
+#endif
+#undef MP_SIZE_INIT
+
+ choice = getenv("EINA_MEMPOOL");
+ if ((!choice) || (!choice[0]))
+ choice = "chained_mempool";
+
+ for (i = 0; i < sizeof (mempool_array) / sizeof (mempool_array[0]); ++i)
+ {
+ retry:
+ mempool_array[i]->mp = eina_mempool_add(choice, mempool_array[i]->name, NULL, mempool_array[i]->size, 16);
+ if (!mempool_array[i]->mp)
+ {
+ if (!(!strcmp(choice, "pass_through")))
+ {
+ ERR("Falling back to pass through ! Previously tried '%s' mempool.", choice);
+ choice = "pass_through";
+ goto retry;
+ }
+ else
+ {
+ ERR("Impossible to allocate mempool '%s' !", choice);
+ return EINA_FALSE;
+ }
+ }
+ }
+ return EINA_TRUE;
+}
+
+void
+ecore_mempool_shutdown(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < sizeof (mempool_array) / sizeof (mempool_array[0]); ++i)
+ {
+ eina_mempool_del(mempool_array[i]->mp);
+ mempool_array[i]->mp = NULL;
+ }
+}
+
diff --git a/src/lib/ecore/ecore_anim.c b/src/lib/ecore/ecore_anim.c
new file mode 100644
index 0000000000..c06d139add
--- /dev/null
+++ b/src/lib/ecore/ecore_anim.c
@@ -0,0 +1,633 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <math.h>
+
+#include <Eo.h>
+
+#include "Ecore.h"
+#include "ecore_private.h"
+
+#define MY_CLASS ECORE_ANIMATOR_CLASS
+
+#define MY_CLASS_NAME "ecore_animator"
+
+#define ECORE_ANIMATOR_CHECK(obj) \
+ if (!eo_isa((obj), ECORE_ANIMATOR_CLASS)) \
+ return
+
+EAPI Eo_Op ECORE_ANIMATOR_BASE_ID = EO_NOOP;
+
+struct _Ecore_Animator_Private_Data
+{
+ EINA_INLIST;
+ Ecore_Animator *obj;
+
+ Ecore_Task_Cb func;
+ void *data;
+
+ double start, run;
+ Ecore_Timeline_Cb run_func;
+ void *run_data;
+
+ Eina_Bool delete_me : 1;
+ Eina_Bool suspended : 1;
+ Eina_Bool just_added : 1;
+};
+
+typedef struct _Ecore_Animator_Private_Data Ecore_Animator_Private_Data;
+
+static Eina_Bool _ecore_animator_run(void *data);
+static Eina_Bool _ecore_animator(void *data);
+
+static int animators_delete_me = 0;
+static Ecore_Animator_Private_Data *animators = NULL;
+static double animators_frametime = 1.0 / 30.0;
+
+static Ecore_Animator_Source src = ECORE_ANIMATOR_SOURCE_TIMER;
+static Ecore_Timer *timer = NULL;
+static int ticking = 0;
+static Ecore_Cb begin_tick_cb = NULL;
+static const void *begin_tick_data = NULL;
+static Ecore_Cb end_tick_cb = NULL;
+static const void *end_tick_data = NULL;
+
+static void
+_begin_tick(void)
+{
+ if (ticking) return;
+ ticking = 1;
+ switch (src)
+ {
+ case ECORE_ANIMATOR_SOURCE_TIMER:
+ if (!timer)
+ {
+ double t_loop = ecore_loop_time_get();
+ double sync_0 = 0.0;
+ double d = -fmod(t_loop - sync_0, animators_frametime);
+
+ timer = _ecore_timer_loop_add(animators_frametime,
+ _ecore_animator, NULL);
+ _ecore_timer_delay(timer, d);
+ }
+ break;
+
+ case ECORE_ANIMATOR_SOURCE_CUSTOM:
+ if (begin_tick_cb) begin_tick_cb((void *)begin_tick_data);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void
+_end_tick(void)
+{
+ if (!ticking) return;
+ ticking = 0;
+ switch (src)
+ {
+ case ECORE_ANIMATOR_SOURCE_TIMER:
+ if (timer)
+ {
+ _ecore_timer_del(timer);
+ timer = NULL;
+ }
+ break;
+
+ case ECORE_ANIMATOR_SOURCE_CUSTOM:
+ if (end_tick_cb) end_tick_cb((void *)end_tick_data);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static Eina_Bool
+_do_tick(void)
+{
+ Ecore_Animator_Private_Data *animator;
+
+ EINA_INLIST_FOREACH(animators, animator)
+ {
+ animator->just_added = EINA_FALSE;
+ }
+ EINA_INLIST_FOREACH(animators, animator)
+ {
+ if ((!animator->delete_me) &&
+ (!animator->suspended) &&
+ (!animator->just_added))
+ {
+ if (!_ecore_call_task_cb(animator->func, animator->data))
+ {
+ animator->delete_me = EINA_TRUE;
+ animators_delete_me++;
+ }
+ }
+ else animator->just_added = EINA_FALSE;
+ }
+ if (animators_delete_me)
+ {
+ Ecore_Animator_Private_Data *l;
+ for (l = animators; l; )
+ {
+ animator = l;
+ l = (Ecore_Animator_Private_Data *)EINA_INLIST_GET(l)->next;
+ if (animator->delete_me)
+ {
+ animators = (Ecore_Animator_Private_Data *)
+ eina_inlist_remove(EINA_INLIST_GET(animators),
+ EINA_INLIST_GET(animator));
+
+ eo_parent_set(animator->obj, NULL);
+ if (eo_destructed_is(animator->obj))
+ eo_manual_free(animator->obj);
+ else
+ eo_manual_free_set(animator->obj, EINA_FALSE);
+
+ animators_delete_me--;
+ if (animators_delete_me == 0) break;
+ }
+ }
+ }
+ if (!animators)
+ {
+ _end_tick();
+ return ECORE_CALLBACK_CANCEL;
+ }
+ return ECORE_CALLBACK_RENEW;
+}
+
+static Eina_Bool
+_ecore_animator_add(Ecore_Animator *obj,
+ Ecore_Animator_Private_Data *animator,
+ Ecore_Task_Cb func,
+ const void *data)
+{
+ if (EINA_UNLIKELY(!eina_main_loop_is()))
+ {
+ eo_error_set(obj);
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(EINA_FALSE);
+ }
+
+ animator->obj = obj;
+ eo_do_super(obj, eo_constructor());
+ eo_manual_free_set(obj, EINA_TRUE);
+
+ if (!func)
+ {
+ eo_error_set(obj);
+ ERR("callback function must be set up for an object of class: '%s'", MY_CLASS_NAME);
+ return EINA_FALSE;
+ }
+
+ animator->func = func;
+ animator->data = (void *)data;
+ animator->just_added = EINA_TRUE;
+ animators = (Ecore_Animator_Private_Data *)eina_inlist_append(EINA_INLIST_GET(animators), EINA_INLIST_GET(animator));
+ _begin_tick();
+ return EINA_TRUE;
+}
+
+static void
+_constructor(Eo *obj, void *_pd EINA_UNUSED, va_list *list EINA_UNUSED)
+{
+ eo_error_set(obj);
+ ERR("only custom constructor can be used with '%s' class", MY_CLASS_NAME);
+}
+
+EAPI Ecore_Animator *
+ecore_animator_add(Ecore_Task_Cb func,
+ const void *data)
+{
+ Ecore_Animator *animator = NULL;
+
+ animator = eo_add_custom(MY_CLASS, _ecore_parent,
+ ecore_animator_constructor(func, data));
+ eo_unref(animator);
+ return animator;
+}
+
+static void
+_animator_constructor(Eo *obj, void *_pd, va_list *list)
+{
+ Ecore_Task_Cb func = va_arg(*list, Ecore_Task_Cb);
+ const void *data = va_arg(*list, const void *);
+
+ _ecore_lock();
+ Ecore_Animator_Private_Data *animator = _pd;
+ _ecore_animator_add(obj, animator, func, data);
+ _ecore_unlock();
+}
+
+EAPI Ecore_Animator *
+ecore_animator_timeline_add(double runtime,
+ Ecore_Timeline_Cb func,
+ const void *data)
+{
+ Ecore_Animator *animator;
+ animator = eo_add_custom(MY_CLASS, _ecore_parent,
+ ecore_animator_timeline_constructor(runtime, func, data));
+ eo_unref(animator);
+ return animator;
+}
+
+static void
+_animator_timeline_constructor(Eo *obj, void *_pd, va_list *list)
+{
+ _ecore_lock();
+ double runtime = va_arg(*list, double);
+ if (runtime <= 0.0) runtime = 0.0;
+ Ecore_Timeline_Cb func = va_arg(*list, Ecore_Timeline_Cb);
+ const void *data = va_arg(*list, const void *);
+
+ Ecore_Animator_Private_Data *animator = _pd;
+ if (!_ecore_animator_add(obj, animator, _ecore_animator_run, NULL)) goto unlock;
+
+ animator->data = obj;
+ animator->run_func = func;
+ animator->run_data = (void *)data;
+ animator->start = ecore_loop_time_get();
+ animator->run = runtime;
+
+unlock:
+ _ecore_unlock();
+}
+
+static double
+_pos_map_sin(double in)
+{
+ return eina_f32p32_double_to(eina_f32p32_sin(eina_f32p32_double_from(in)));
+}
+
+#if 0
+static double
+_pos_map_cos(double in)
+{
+ return eina_f32p32_double_to(eina_f32p32_cos(eina_f32p32_double_from(in)));
+}
+#endif
+
+static double
+_pos_map_accel_factor(double pos,
+ double v1)
+{
+ int i, fact = (int)v1;
+ double p, o1 = pos, o2, v;
+ p = 1.0 - _pos_map_sin((M_PI / 2.0) + ((pos * M_PI) / 2.0));
+ o2 = p;
+ for (i = 0; i < fact; i++)
+ {
+ o1 = o2;
+ o2 = o2 * p;
+ }
+ v = v1 - (double)fact;
+ pos = (v * o2) + ((1.0 - v) * o1);
+ return pos;
+}
+
+static double
+_pos_map_pow(double pos,
+ double divis,
+ int p)
+{
+ double v = 1.0;
+ int i;
+ for (i = 0; i < p; i++) v *= pos;
+ return ((pos * divis) * (1.0 - v)) + (pos * v);
+}
+
+static double
+_pos_map_spring(double pos,
+ int bounces,
+ double decfac)
+{
+ int segnum, segpos, b1, b2;
+ double len, decay, decpos, p2;
+ if (bounces < 0) bounces = 0;
+ p2 = _pos_map_pow(pos, 0.5, 3);
+ len = (M_PI / 2.0) + ((double)bounces * M_PI);
+ segnum = (bounces * 2) + 1;
+ segpos = 2 * (((int)(p2 * segnum) + 1) / 2);
+ b1 = segpos;
+ b2 = segnum + 1;
+ if (b1 < 0) b1 = 0;
+ decpos = (double)b1 / (double)b2;
+ decay = _pos_map_accel_factor(1.0 - decpos, decfac);
+ return _pos_map_sin((M_PI / 2.0) + (p2 * len)) * decay;
+}
+
+#define DBL_TO(Fp) eina_f32p32_double_to(Fp)
+#define DBL_FROM(D) eina_f32p32_double_from(D)
+#define INT_FROM(I) eina_f32p32_int_from(I)
+#define SIN(Fp) eina_f32p32_sin(Fp)
+#define COS(Fp) eina_f32p32_cos(Fp)
+#define ADD(A, B) eina_f32p32_add(A, B)
+#define SUB(A, B) eina_f32p32_sub(A, B)
+#define MUL(A, B) eina_f32p32_mul(A, B)
+
+EAPI double
+ecore_animator_pos_map(double pos,
+ Ecore_Pos_Map map,
+ double v1,
+ double v2)
+{
+ /* purely functional - locking not required */
+ if (pos > 1.0) pos = 1.0;
+ else if (pos < 0.0)
+ pos = 0.0;
+ switch (map)
+ {
+ case ECORE_POS_MAP_LINEAR:
+ return pos;
+
+ case ECORE_POS_MAP_ACCELERATE:
+ /* pos = 1 - sin(Pi / 2 + pos * Pi / 2); */
+ pos = DBL_TO(SUB(INT_FROM(1), SIN(ADD((EINA_F32P32_PI >> 1), MUL(DBL_FROM(pos), (EINA_F32P32_PI >> 1))))));
+ return pos;
+
+ case ECORE_POS_MAP_DECELERATE:
+ /* pos = sin(pos * Pi / 2); */
+ pos = DBL_TO(SIN(MUL(DBL_FROM(pos), (EINA_F32P32_PI >> 1))));
+ return pos;
+
+ case ECORE_POS_MAP_SINUSOIDAL:
+ /* pos = (1 - cos(pos * Pi)) / 2 */
+ pos = DBL_TO((SUB(INT_FROM(1), COS(MUL(DBL_FROM(pos), EINA_F32P32_PI)))) >> 1);
+ return pos;
+
+ case ECORE_POS_MAP_ACCELERATE_FACTOR:
+ pos = _pos_map_accel_factor(pos, v1);
+ return pos;
+
+ case ECORE_POS_MAP_DECELERATE_FACTOR:
+ pos = 1.0 - _pos_map_accel_factor(1.0 - pos, v1);
+ return pos;
+
+ case ECORE_POS_MAP_SINUSOIDAL_FACTOR:
+ if (pos < 0.5) pos = _pos_map_accel_factor(pos * 2.0, v1) / 2.0;
+ else pos = 1.0 - (_pos_map_accel_factor((1.0 - pos) * 2.0, v1) / 2.0);
+ return pos;
+
+ case ECORE_POS_MAP_DIVISOR_INTERP:
+ pos = _pos_map_pow(pos, v1, (int)v2);
+ return pos;
+
+ case ECORE_POS_MAP_BOUNCE:
+ pos = _pos_map_spring(pos, (int)v2, v1);
+ if (pos < 0.0) pos = -pos;
+ pos = 1.0 - pos;
+ return pos;
+
+ case ECORE_POS_MAP_SPRING:
+ pos = 1.0 - _pos_map_spring(pos, (int)v2, v1);
+ return pos;
+
+ default:
+ return pos;
+ }
+ return pos;
+}
+
+EAPI void *
+ecore_animator_del(Ecore_Animator *obj)
+{
+ void *data = NULL;
+
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL);
+ Ecore_Animator_Private_Data *animator = eo_data_get(obj, MY_CLASS);
+ _ecore_lock();
+
+ if (animator->delete_me)
+ {
+ data = animator->data;
+ goto unlock;
+ }
+ animator->delete_me = EINA_TRUE;
+ animators_delete_me++;
+ if (animator->run_func)
+ data = animator->run_data;
+ else
+ data = animator->data;
+unlock:
+ _ecore_unlock();
+ return data;
+}
+
+static void
+_destructor(Eo *obj, void *_pd, va_list *list EINA_UNUSED)
+{
+ Ecore_Animator_Private_Data *pd = _pd;
+
+ pd->delete_me = EINA_TRUE;
+ animators_delete_me++;
+
+ eo_do_super(obj, eo_destructor());
+}
+
+EAPI void
+ecore_animator_frametime_set(double frametime)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN;
+ _ecore_lock();
+ if (frametime < 0.0) frametime = 0.0;
+ if (animators_frametime == frametime) goto unlock;
+ animators_frametime = frametime;
+ _end_tick();
+ if (animators) _begin_tick();
+unlock:
+ _ecore_unlock();
+}
+
+EAPI double
+ecore_animator_frametime_get(void)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(0.0);
+ return animators_frametime;
+}
+
+EAPI void
+ecore_animator_freeze(Ecore_Animator *animator)
+{
+ ECORE_ANIMATOR_CHECK(animator);
+ eo_do(animator, eo_event_freeze());
+}
+
+static void
+_ecore_animator_freeze(Eo *obj EINA_UNUSED, void *_pd,
+ va_list *list EINA_UNUSED)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN;
+ _ecore_lock();
+ Ecore_Animator_Private_Data *animator = _pd;
+
+ if (animator->delete_me) goto unlock;
+ animator->suspended = EINA_TRUE;
+unlock:
+ _ecore_unlock();
+}
+
+EAPI void
+ecore_animator_thaw(Ecore_Animator *animator)
+{
+ ECORE_ANIMATOR_CHECK(animator);
+ eo_do(animator, eo_event_thaw());
+}
+
+static void
+_ecore_animator_thaw(Eo *obj EINA_UNUSED, void *_pd,
+ va_list *list EINA_UNUSED)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN;
+ Ecore_Animator_Private_Data *animator = _pd;
+
+ _ecore_lock();
+ if (animator->delete_me) goto unlock;
+ animator->suspended = EINA_FALSE;
+unlock:
+ _ecore_unlock();
+}
+
+EAPI void
+ecore_animator_source_set(Ecore_Animator_Source source)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN;
+ _ecore_lock();
+ src = source;
+ _end_tick();
+ if (animators) _begin_tick();
+ _ecore_unlock();
+}
+
+EAPI Ecore_Animator_Source
+ecore_animator_source_get(void)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(0);
+ return src;
+}
+
+EAPI void
+ecore_animator_custom_source_tick_begin_callback_set(Ecore_Cb func,
+ const void *data)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN;
+ _ecore_lock();
+ begin_tick_cb = func;
+ begin_tick_data = data;
+ _end_tick();
+ if (animators) _begin_tick();
+ _ecore_unlock();
+}
+
+EAPI void
+ecore_animator_custom_source_tick_end_callback_set(Ecore_Cb func,
+ const void *data)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN;
+ _ecore_lock();
+ end_tick_cb = func;
+ end_tick_data = data;
+ _end_tick();
+ if (animators) _begin_tick();
+ _ecore_unlock();
+}
+
+EAPI void
+ecore_animator_custom_tick(void)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN;
+ _ecore_lock();
+ if (src == ECORE_ANIMATOR_SOURCE_CUSTOM) _do_tick();
+ _ecore_unlock();
+}
+
+void
+_ecore_animator_shutdown(void)
+{
+ _end_tick();
+ while (animators)
+ {
+ Ecore_Animator_Private_Data *animator;
+
+ animator = animators;
+ animators = (Ecore_Animator_Private_Data *)eina_inlist_remove(EINA_INLIST_GET(animators), EINA_INLIST_GET(animators));
+
+ eo_parent_set(animator->obj, NULL);
+ if (eo_destructed_is(animator->obj))
+ eo_manual_free(animator->obj);
+ else
+ eo_manual_free_set(animator->obj, EINA_FALSE);
+ }
+}
+
+static Eina_Bool
+_ecore_animator_run(void *data)
+{
+ Ecore_Animator *obj = data;
+ Ecore_Animator_Private_Data *animator = eo_data_get(obj, MY_CLASS);
+
+ double pos = 0.0, t;
+ Eina_Bool run_ret;
+
+ t = ecore_loop_time_get();
+ if (animator->run > 0.0)
+ {
+ pos = (t - animator->start) / animator->run;
+ if (pos > 1.0) pos = 1.0;
+ else if (pos < 0.0)
+ pos = 0.0;
+ }
+ run_ret = animator->run_func(animator->run_data, pos);
+ if (t >= (animator->start + animator->run)) run_ret = EINA_FALSE;
+ return run_ret;
+}
+
+static Eina_Bool
+_ecore_animator(void *data EINA_UNUSED)
+{
+ Eina_Bool r;
+ _ecore_lock();
+ r = _do_tick();
+ _ecore_unlock();
+ return r;
+}
+
+static void
+_class_constructor(Eo_Class *klass)
+{
+ const Eo_Op_Func_Description func_desc[] = {
+ EO_OP_FUNC(EO_BASE_ID(EO_BASE_SUB_ID_CONSTRUCTOR), _constructor),
+ EO_OP_FUNC(EO_BASE_ID(EO_BASE_SUB_ID_DESTRUCTOR), _destructor),
+ EO_OP_FUNC(EO_BASE_ID(EO_BASE_SUB_ID_EVENT_FREEZE), _ecore_animator_freeze),
+ EO_OP_FUNC(EO_BASE_ID(EO_BASE_SUB_ID_EVENT_THAW), _ecore_animator_thaw),
+
+ EO_OP_FUNC(ECORE_ANIMATOR_ID(ECORE_ANIMATOR_SUB_ID_CONSTRUCTOR), _animator_constructor),
+ EO_OP_FUNC(ECORE_ANIMATOR_ID(ECORE_ANIMATOR_SUB_ID_TIMELINE_CONSTRUCTOR), _animator_timeline_constructor),
+ EO_OP_FUNC_SENTINEL
+ };
+
+ eo_class_funcs_set(klass, func_desc);
+}
+
+static const Eo_Op_Description op_desc[] = {
+ EO_OP_DESCRIPTION(ECORE_ANIMATOR_SUB_ID_CONSTRUCTOR, "Add an animator to call func at every animation tick during main loop execution."),
+ EO_OP_DESCRIPTION(ECORE_ANIMATOR_SUB_ID_TIMELINE_CONSTRUCTOR, "Add an animator that runs for a limited time"),
+ EO_OP_DESCRIPTION_SENTINEL
+};
+
+static const Eo_Class_Description class_desc = {
+ EO_VERSION,
+ MY_CLASS_NAME,
+ EO_CLASS_TYPE_REGULAR,
+ EO_CLASS_DESCRIPTION_OPS(&ECORE_ANIMATOR_BASE_ID, op_desc, ECORE_ANIMATOR_SUB_ID_LAST),
+ NULL,
+ sizeof(Ecore_Animator_Private_Data),
+ _class_constructor,
+ NULL
+};
+
+EO_DEFINE_CLASS(ecore_animator_class_get, &class_desc, EO_BASE_CLASS, NULL)
diff --git a/src/lib/ecore/ecore_app.c b/src/lib/ecore/ecore_app.c
new file mode 100644
index 0000000000..04df487d3c
--- /dev/null
+++ b/src/lib/ecore/ecore_app.c
@@ -0,0 +1,98 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#ifdef _MSC_VER
+# include <process.h>
+#endif
+
+#ifdef HAVE_EVIL
+# include <Evil.h>
+#endif
+
+#include "Ecore.h"
+#include "ecore_private.h"
+
+static int app_argc = 0;
+static char **app_argv = NULL;
+
+/**
+ * @addtogroup Ecore_Application_Group
+ *
+ * @{
+ */
+
+/**
+ * Set up the programs command-line arguments.
+ * @param argc The same as passed as argc to the programs main() function
+ * @param argv The same as passed as argv to the programs main() function
+ *
+ * A call to this function will store the programs command-line arguments
+ * for later use by ecore_app_restart() or ecore_app_args_get().
+ */
+EAPI void
+ecore_app_args_set(int argc,
+ const char **argv)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN;
+
+ if ((argc < 1) ||
+ (!argv)) return;
+ app_argc = argc;
+ app_argv = (char **)argv;
+}
+
+/**
+ * Return the programs stored command-line arguments.
+ * @param argc A pointer to the return value to hold argc
+ * @param argv A pointer to the return value to hold argv
+ *
+ * When called, this funciton returns the arguments for the program stored by
+ * ecore_app_args_set(). The integer pointed to by @p argc will be filled, if
+ * the pointer is not NULL, and the string array pointer @p argv will be filled
+ * also if the pointer is not NULL. The values they are filled with will be the
+ * same set by ecore_app_args_set().
+ */
+EAPI void
+ecore_app_args_get(int *argc,
+ char ***argv)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN;
+
+ if (argc) *argc = app_argc;
+ if (argv) *argv = app_argv;
+}
+
+/**
+ * Restart the program executable with the command-line arguments stored.
+ *
+ * This function will restart & re-execute this program in place of itself
+ * using the command-line arguments stored by ecore_app_args_set(). This is
+ * an easy way for a program to restart itself for cleanup purposes,
+ * configuration reasons or in the event of a crash.
+ */
+EAPI void
+ecore_app_restart(void)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN;
+#ifdef HAVE_EXECVP
+ char *args[4096];
+ int i;
+
+ if ((app_argc < 1) || (!app_argv)) return;
+ if (app_argc >= 4096) return;
+ for (i = 0; i < app_argc; i++) args[i] = app_argv[i];
+ args[i] = NULL;
+ execvp(app_argv[0], args);
+#endif
+}
+
+/**
+ * @}
+ */
diff --git a/src/lib/ecore/ecore_events.c b/src/lib/ecore/ecore_events.c
new file mode 100644
index 0000000000..2d8ac1f661
--- /dev/null
+++ b/src/lib/ecore/ecore_events.c
@@ -0,0 +1,648 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#include "Ecore.h"
+#include "ecore_private.h"
+
+static int inpurge = 0;
+
+struct _Ecore_Event_Handler
+{
+ EINA_INLIST;
+ ECORE_MAGIC;
+ int type;
+ Ecore_Event_Handler_Cb func;
+ void *data;
+ int references;
+ Eina_Bool delete_me : 1;
+};
+GENERIC_ALLOC_SIZE_DECLARE(Ecore_Event_Handler);
+
+struct _Ecore_Event_Filter
+{
+ EINA_INLIST;
+ ECORE_MAGIC;
+ Ecore_Data_Cb func_start;
+ Ecore_Filter_Cb func_filter;
+ Ecore_End_Cb func_end;
+ void *loop_data;
+ void *data;
+ int references;
+ Eina_Bool delete_me : 1;
+};
+GENERIC_ALLOC_SIZE_DECLARE(Ecore_Event_Filter);
+
+struct _Ecore_Event
+{
+ EINA_INLIST;
+ ECORE_MAGIC;
+ int type;
+ void *event;
+ Ecore_End_Cb func_free;
+ void *data;
+ int references;
+ Eina_Bool delete_me : 1;
+};
+GENERIC_ALLOC_SIZE_DECLARE(Ecore_Event);
+
+static int events_num = 0;
+static Ecore_Event *events = NULL;
+static Ecore_Event *event_current = NULL;
+static Ecore_Event *purge_events = NULL;
+
+static Ecore_Event_Handler **event_handlers = NULL;
+static Ecore_Event_Handler *event_handler_current = NULL;
+static int event_handlers_num = 0;
+static int event_handlers_alloc_num = 0;
+static Eina_List *event_handlers_delete_list = NULL;
+
+static Ecore_Event_Handler *event_handlers_add_list = NULL;
+
+static Ecore_Event_Filter *event_filters = NULL;
+static Ecore_Event_Filter *event_filter_current = NULL;
+static Ecore_Event *event_filter_event_current = NULL;
+static int event_filters_delete_me = 0;
+static int event_id_max = ECORE_EVENT_COUNT;
+static int ecore_raw_event_type = ECORE_EVENT_NONE;
+static void *ecore_raw_event_event = NULL;
+
+static void _ecore_event_purge_deleted(void);
+static void *_ecore_event_del(Ecore_Event *event);
+
+EAPI Ecore_Event_Handler *
+ecore_event_handler_add(int type,
+ Ecore_Event_Handler_Cb func,
+ const void *data)
+{
+ Ecore_Event_Handler *eh = NULL;
+
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL);
+ _ecore_lock();
+
+ if (!func) goto unlock;
+ if ((type <= ECORE_EVENT_NONE) || (type >= event_id_max)) goto unlock;
+ eh = ecore_event_handler_calloc(1);
+ if (!eh) goto unlock;
+ ECORE_MAGIC_SET(eh, ECORE_MAGIC_EVENT_HANDLER);
+ eh->type = type;
+ eh->func = func;
+ eh->data = (void *)data;
+ if (type >= (event_handlers_num - 1))
+ {
+ int p_alloc_num;
+
+ p_alloc_num = event_handlers_alloc_num;
+ event_handlers_num = type + 1;
+ if (event_handlers_num > event_handlers_alloc_num)
+ {
+ Ecore_Event_Handler **new_handlers;
+ int i;
+
+ event_handlers_alloc_num = ((event_handlers_num + 16) / 16) * 16;
+ new_handlers = realloc(event_handlers, event_handlers_alloc_num * sizeof(Ecore_Event_Handler *));
+ if (!new_handlers)
+ {
+ ecore_event_handler_mp_free(eh);
+ goto unlock;
+ }
+ event_handlers = new_handlers;
+ for (i = p_alloc_num; i < event_handlers_alloc_num; i++)
+ event_handlers[i] = NULL;
+ }
+ }
+ if (ecore_raw_event_type == type)
+ event_handlers_add_list = (Ecore_Event_Handler *)eina_inlist_append(EINA_INLIST_GET(event_handlers_add_list), EINA_INLIST_GET(eh));
+ else if (type < event_handlers_alloc_num)
+ event_handlers[type] = (Ecore_Event_Handler *)eina_inlist_append(EINA_INLIST_GET(event_handlers[type]), EINA_INLIST_GET(eh));
+
+unlock:
+ _ecore_unlock();
+ return eh;
+}
+
+EAPI void *
+ecore_event_handler_del(Ecore_Event_Handler *event_handler)
+{
+ void *data = NULL;
+
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL);
+ _ecore_lock();
+ if (!ECORE_MAGIC_CHECK(event_handler, ECORE_MAGIC_EVENT_HANDLER))
+ {
+ ECORE_MAGIC_FAIL(event_handler, ECORE_MAGIC_EVENT_HANDLER,
+ "ecore_event_handler_del");
+ goto unlock;
+ }
+ data = _ecore_event_handler_del(event_handler);
+unlock:
+ _ecore_unlock();
+
+ return data;
+}
+
+EAPI void *
+ecore_event_handler_data_get(Ecore_Event_Handler *eh)
+{
+ void *data = NULL;
+
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL);
+ _ecore_lock();
+ if (!ECORE_MAGIC_CHECK(eh, ECORE_MAGIC_EVENT_HANDLER))
+ {
+ ECORE_MAGIC_FAIL(eh, ECORE_MAGIC_EVENT_HANDLER, "ecore_event_handler_data_get");
+ goto unlock;
+ }
+ data = eh->data;
+unlock:
+ _ecore_unlock();
+ return data;
+}
+
+EAPI void *
+ecore_event_handler_data_set(Ecore_Event_Handler *eh,
+ const void *data)
+{
+ void *old = NULL;
+
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL);
+ _ecore_lock();
+ if (!ECORE_MAGIC_CHECK(eh, ECORE_MAGIC_EVENT_HANDLER))
+ {
+ ECORE_MAGIC_FAIL(eh, ECORE_MAGIC_EVENT_HANDLER, "ecore_event_handler_data_set");
+ goto unlock;
+ }
+ old = eh->data;
+ eh->data = (void *)data;
+unlock:
+ _ecore_unlock();
+
+ return old;
+}
+
+static void
+_ecore_event_generic_free(void *data EINA_UNUSED,
+ void *event)
+{ /* DO NOT MEMPOOL FREE THIS */
+ free(event);
+}
+
+EAPI Ecore_Event *
+ecore_event_add(int type,
+ void *ev,
+ Ecore_End_Cb func_free,
+ void *data)
+{
+ Ecore_Event *event = NULL;
+
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL);
+ _ecore_lock();
+
+/* if (!ev) goto unlock; */
+ if (type <= ECORE_EVENT_NONE) goto unlock;
+ if (type >= event_id_max) goto unlock;
+ if ((ev) && (!func_free)) func_free = _ecore_event_generic_free;
+ event = _ecore_event_add(type, ev, func_free, data);
+unlock:
+ _ecore_unlock();
+ return event;
+}
+
+EAPI void *
+ecore_event_del(Ecore_Event *event)
+{
+ void *data = NULL;
+
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL);
+ _ecore_lock();
+ if (!ECORE_MAGIC_CHECK(event, ECORE_MAGIC_EVENT))
+ {
+ ECORE_MAGIC_FAIL(event, ECORE_MAGIC_EVENT, "ecore_event_del");
+ goto unlock;
+ }
+ EINA_SAFETY_ON_TRUE_GOTO(event->delete_me, unlock);
+ event->delete_me = 1;
+ data = event->data;
+unlock:
+ _ecore_unlock();
+ return data;
+}
+
+EAPI int
+ecore_event_type_new(void)
+{
+ int id;
+
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(0);
+ _ecore_lock();
+ id = event_id_max++;
+ _ecore_unlock();
+
+ return id;
+}
+
+EAPI Ecore_Event_Filter *
+ecore_event_filter_add(Ecore_Data_Cb func_start,
+ Ecore_Filter_Cb func_filter,
+ Ecore_End_Cb func_end,
+ const void *data)
+{
+ Ecore_Event_Filter *ef = NULL;
+
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL);
+ _ecore_lock();
+ if (!func_filter) goto unlock;
+ ef = ecore_event_filter_calloc(1);
+ if (!ef) goto unlock;
+ ECORE_MAGIC_SET(ef, ECORE_MAGIC_EVENT_FILTER);
+ ef->func_start = func_start;
+ ef->func_filter = func_filter;
+ ef->func_end = func_end;
+ ef->data = (void *)data;
+ event_filters = (Ecore_Event_Filter *)eina_inlist_append(EINA_INLIST_GET(event_filters), EINA_INLIST_GET(ef));
+unlock:
+ _ecore_unlock();
+ return ef;
+}
+
+EAPI void *
+ecore_event_filter_del(Ecore_Event_Filter *ef)
+{
+ void *data = NULL;
+
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL);
+ _ecore_lock();
+ if (!ECORE_MAGIC_CHECK(ef, ECORE_MAGIC_EVENT_FILTER))
+ {
+ ECORE_MAGIC_FAIL(ef, ECORE_MAGIC_EVENT_FILTER, "ecore_event_filter_del");
+ goto unlock;
+ }
+ EINA_SAFETY_ON_TRUE_GOTO(ef->delete_me, unlock);
+ ef->delete_me = 1;
+ event_filters_delete_me = 1;
+ data = ef->data;
+unlock:
+ _ecore_unlock();
+
+ return data;
+}
+
+EAPI int
+ecore_event_current_type_get(void)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(0);
+ return ecore_raw_event_type;
+}
+
+EAPI void *
+ecore_event_current_event_get(void)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL);
+ return ecore_raw_event_event;
+}
+
+EAPI void *
+_ecore_event_handler_del(Ecore_Event_Handler *event_handler)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(event_handler->delete_me, NULL);
+ event_handler->delete_me = 1;
+ event_handlers_delete_list = eina_list_append(event_handlers_delete_list, event_handler);
+ return event_handler->data;
+}
+
+void
+_ecore_event_shutdown(void)
+{
+ int i;
+ Ecore_Event_Handler *eh;
+ Ecore_Event_Filter *ef;
+
+ while (events) _ecore_event_del(events);
+ event_current = NULL;
+ for (i = 0; i < event_handlers_num; i++)
+ {
+ while ((eh = event_handlers[i]))
+ {
+ event_handlers[i] = (Ecore_Event_Handler *)eina_inlist_remove(EINA_INLIST_GET(event_handlers[i]), EINA_INLIST_GET(event_handlers[i]));
+ ECORE_MAGIC_SET(eh, ECORE_MAGIC_NONE);
+ if (!eh->delete_me) ecore_event_handler_mp_free(eh);
+ }
+ }
+ EINA_LIST_FREE(event_handlers_delete_list, eh)
+ ecore_event_handler_mp_free(eh);
+ if (event_handlers) free(event_handlers);
+ event_handlers = NULL;
+ event_handlers_num = 0;
+ event_handlers_alloc_num = 0;
+ while ((ef = event_filters))
+ {
+ event_filters = (Ecore_Event_Filter *)eina_inlist_remove(EINA_INLIST_GET(event_filters), EINA_INLIST_GET(event_filters));
+ ECORE_MAGIC_SET(ef, ECORE_MAGIC_NONE);
+ ecore_event_filter_mp_free(ef);
+ }
+ event_filters_delete_me = 0;
+ event_filter_current = NULL;
+ event_filter_event_current = NULL;
+}
+
+int
+_ecore_event_exist(void)
+{
+ Ecore_Event *e;
+ EINA_INLIST_FOREACH(events, e)
+ if (!e->delete_me) return 1;
+ return 0;
+}
+
+Ecore_Event *
+_ecore_event_add(int type,
+ void *ev,
+ Ecore_End_Cb func_free,
+ void *data)
+{
+ Ecore_Event *e;
+
+ e = ecore_event_calloc(1);
+ if (!e) return NULL;
+ ECORE_MAGIC_SET(e, ECORE_MAGIC_EVENT);
+ e->type = type;
+ e->event = ev;
+ e->func_free = func_free;
+ e->data = data;
+ if (inpurge > 0)
+ {
+ purge_events = (Ecore_Event *)eina_inlist_append(EINA_INLIST_GET(purge_events), EINA_INLIST_GET(e));
+ events_num++;
+ }
+ else
+ {
+ events = (Ecore_Event *)eina_inlist_append(EINA_INLIST_GET(events), EINA_INLIST_GET(e));
+ events_num++;
+ }
+ return e;
+}
+
+void *
+_ecore_event_del(Ecore_Event *event)
+{
+ void *data;
+
+ data = event->data;
+ if (event->func_free) _ecore_call_end_cb(event->func_free, event->data, event->event);
+ events = (Ecore_Event *)eina_inlist_remove(EINA_INLIST_GET(events), EINA_INLIST_GET(event));
+ ECORE_MAGIC_SET(event, ECORE_MAGIC_NONE);
+ ecore_event_mp_free(event);
+ events_num--;
+ return data;
+}
+
+static void
+_ecore_event_purge_deleted(void)
+{
+ Ecore_Event *itr = events;
+
+ inpurge++;
+ while (itr)
+ {
+ Ecore_Event *next = (Ecore_Event *)EINA_INLIST_GET(itr)->next;
+ if ((!itr->references) && (itr->delete_me))
+ _ecore_event_del(itr);
+ itr = next;
+ }
+ inpurge--;
+ while (purge_events)
+ {
+ Ecore_Event *e = purge_events;
+ purge_events = (Ecore_Event *)eina_inlist_remove(EINA_INLIST_GET(purge_events), EINA_INLIST_GET(purge_events));
+ events = (Ecore_Event *)eina_inlist_append(EINA_INLIST_GET(events), EINA_INLIST_GET(e));
+ }
+}
+
+static inline void
+_ecore_event_filters_apply()
+{
+ if (!event_filter_current)
+ {
+ /* regular main loop, start from head */
+ event_filter_current = event_filters;
+ }
+ else
+ {
+ /* recursive main loop, continue from where we were */
+ event_filter_current = (Ecore_Event_Filter *)EINA_INLIST_GET(event_filter_current)->next;
+ }
+
+ while (event_filter_current)
+ {
+ Ecore_Event_Filter *ef = event_filter_current;
+
+ if (!ef->delete_me)
+ {
+ ef->references++;
+
+ if (ef->func_start)
+ ef->loop_data = _ecore_call_data_cb(ef->func_start, ef->data);
+
+ if (!event_filter_event_current)
+ {
+ /* regular main loop, start from head */
+ event_filter_event_current = events;
+ }
+ else
+ {
+ /* recursive main loop, continue from where we were */
+ event_filter_event_current = (Ecore_Event *)EINA_INLIST_GET(event_filter_event_current)->next;
+ }
+
+ while (event_filter_event_current)
+ {
+ Ecore_Event *e = event_filter_event_current;
+
+ if (!_ecore_call_filter_cb(ef->func_filter, ef->data,
+ ef->loop_data, e->type, e->event))
+ {
+ ecore_event_del(e);
+ }
+
+ if (event_filter_event_current) /* may have changed in recursive main loops */
+ event_filter_event_current = (Ecore_Event *)EINA_INLIST_GET(event_filter_event_current)->next;
+ }
+ if (ef->func_end)
+ _ecore_call_end_cb(ef->func_end, ef->data, ef->loop_data);
+
+ ef->references--;
+ }
+
+ if (event_filter_current) /* may have changed in recursive main loops */
+ event_filter_current = (Ecore_Event_Filter *)EINA_INLIST_GET(event_filter_current)->next;
+ }
+ if (event_filters_delete_me)
+ {
+ int deleted_in_use = 0;
+ Ecore_Event_Filter *l;
+ for (l = event_filters; l; )
+ {
+ Ecore_Event_Filter *ef = l;
+ l = (Ecore_Event_Filter *)EINA_INLIST_GET(l)->next;
+ if (ef->delete_me)
+ {
+ if (ef->references)
+ {
+ deleted_in_use++;
+ continue;
+ }
+
+ event_filters = (Ecore_Event_Filter *)eina_inlist_remove(EINA_INLIST_GET(event_filters), EINA_INLIST_GET(ef));
+ ECORE_MAGIC_SET(ef, ECORE_MAGIC_NONE);
+ ecore_event_filter_mp_free(ef);
+ }
+ }
+ if (!deleted_in_use)
+ event_filters_delete_me = 0;
+ }
+}
+
+void
+_ecore_event_call(void)
+{
+ Eina_List *l, *l_next;
+ Ecore_Event_Handler *eh;
+
+ _ecore_event_filters_apply();
+
+ if (!event_current)
+ {
+ /* regular main loop, start from head */
+ event_current = events;
+ event_handler_current = NULL;
+ }
+
+ while (event_current)
+ {
+ Ecore_Event *e = event_current;
+ int handle_count = 0;
+
+ if (e->delete_me)
+ {
+ event_current = (Ecore_Event *)EINA_INLIST_GET(event_current)->next;
+ continue;
+ }
+
+ ecore_raw_event_type = e->type;
+ ecore_raw_event_event = e->event;
+ e->references++;
+ if ((e->type >= 0) && (e->type < event_handlers_num))
+ {
+ if (!event_handler_current)
+ {
+ /* regular main loop, start from head */
+ event_handler_current = event_handlers[e->type];
+ }
+ else
+ {
+ /* recursive main loop, continue from where we were */
+ event_handler_current = (Ecore_Event_Handler *)EINA_INLIST_GET(event_handler_current)->next;
+ }
+
+ while ((event_handler_current) && (!e->delete_me))
+ {
+ eh = event_handler_current;
+ if (!eh->delete_me)
+ {
+ Eina_Bool ret;
+
+ handle_count++;
+
+ eh->references++;
+ ret = _ecore_call_handler_cb(eh->func, eh->data, e->type, e->event);
+ eh->references--;
+
+ if (!ret)
+ {
+ event_handler_current = NULL;
+ break; /* 0 == "call no further handlers" */
+ }
+ }
+
+ if (event_handler_current) /* may have changed in recursive main loops */
+ event_handler_current = (Ecore_Event_Handler *)EINA_INLIST_GET(event_handler_current)->next;
+ }
+ }
+ while (event_handlers_add_list)
+ {
+ eh = event_handlers_add_list;
+ event_handlers_add_list = (Ecore_Event_Handler *)eina_inlist_remove(EINA_INLIST_GET(event_handlers_add_list), EINA_INLIST_GET(eh));
+ event_handlers[eh->type] = (Ecore_Event_Handler *)eina_inlist_append(EINA_INLIST_GET(event_handlers[eh->type]), EINA_INLIST_GET(eh));
+ }
+ /* if no handlers were set for EXIT signal - then default is */
+ /* to quit the main loop */
+ if ((e->type == ECORE_EVENT_SIGNAL_EXIT) && (handle_count == 0))
+ ecore_main_loop_quit();
+ e->references--;
+ e->delete_me = 1;
+
+ if (event_current) /* may have changed in recursive main loops */
+ event_current = (Ecore_Event *)EINA_INLIST_GET(event_current)->next;
+ }
+
+ ecore_raw_event_type = ECORE_EVENT_NONE;
+ ecore_raw_event_event = NULL;
+
+ _ecore_event_purge_deleted();
+
+ EINA_LIST_FOREACH_SAFE(event_handlers_delete_list, l, l_next, eh)
+ {
+ if (eh->references) continue;
+
+ event_handlers_delete_list = eina_list_remove_list(event_handlers_delete_list, l);
+
+ event_handlers[eh->type] = (Ecore_Event_Handler *)eina_inlist_remove(EINA_INLIST_GET(event_handlers[eh->type]), EINA_INLIST_GET(eh));
+ ECORE_MAGIC_SET(eh, ECORE_MAGIC_NONE);
+ ecore_event_handler_mp_free(eh);
+ }
+}
+
+void *
+_ecore_event_signal_user_new(void)
+{
+ Ecore_Event_Signal_User *e;
+
+ e = calloc(1, sizeof(Ecore_Event_Signal_User));
+ return e;
+}
+
+void *
+_ecore_event_signal_hup_new(void)
+{
+ Ecore_Event_Signal_Hup *e;
+
+ e = calloc(1, sizeof(Ecore_Event_Signal_Hup));
+ return e;
+}
+
+void *
+_ecore_event_signal_exit_new(void)
+{
+ Ecore_Event_Signal_Exit *e;
+
+ e = calloc(1, sizeof(Ecore_Event_Signal_Exit));
+ return e;
+}
+
+void *
+_ecore_event_signal_power_new(void)
+{
+ Ecore_Event_Signal_Power *e;
+
+ e = calloc(1, sizeof(Ecore_Event_Signal_Power));
+ return e;
+}
+
+void *
+_ecore_event_signal_realtime_new(void)
+{
+ return calloc(1, sizeof(Ecore_Event_Signal_Realtime));
+}
+
diff --git a/src/lib/ecore/ecore_exe.c b/src/lib/ecore/ecore_exe.c
new file mode 100644
index 0000000000..19bc39b008
--- /dev/null
+++ b/src/lib/ecore/ecore_exe.c
@@ -0,0 +1,1913 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#ifdef HAVE_SYS_PRCTL_H
+# include <sys/prctl.h>
+#endif
+
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+
+#include "Ecore.h"
+#include "ecore_private.h"
+
+/* FIXME: Getting respawn to work
+ *
+ * There is no way that we can do anything about the internal state info of
+ * an external exe. The same can be said about the state of user code. User
+ * code in this context means the code that is using ecore_exe to manage exe's
+ * for it.
+ *
+ * Document that the exe must be respawnable, in other words, there is no
+ * state that it cannot regenerate by just killing it and starting it again.
+ * This includes state that the user code knows about, as the respawn is
+ * transparent to that code. On the other hand, maybe a respawn event might
+ * be useful, or maybe resend the currently non existent add event. For
+ * consistancy with ecore_con, an add event is good anyway.
+ *
+ * The Ecore_exe structure is reused for respawning, so that the (opaque)
+ * pointer held by the user remains valid. This means that the Ecore_Exe
+ * init and del functions may need to be split into two parts each to avoid
+ * duplicating code - common code part, and the rest. This implies that
+ * the unchanging members mentioned next should NEVER change.
+ *
+ * These structure members don't need to change -
+ * __list_data - we stay on the list
+ * ECORE_MAGIC - this is a constant
+ * data - passed in originally
+ * cmd - passed in originally
+ * flags - passed in originally
+ *
+ * These structure members need to change -
+ * tag - state that must be regenerated, zap it
+ * pid - it will be different
+ * child_fd_write - it will be different
+ * child_fd_read - it will be different
+ * child_fd_error - it will be different
+ * write_fd_handler - we cannot change the fd used by a handler, this changes coz the fd changes.
+ * read_fd_handler - we cannot change the fd used by a handler, this changes coz the fd changes.
+ * error_fd_handler - we cannot change the fd used by a handler, this changes coz the fd changes.
+ *
+ * Hmm, the read, write, and error buffers could be tricky.
+ * They are not atomic, and could be in a semi complete state.
+ * They fall into the "state must be regenerated" mentioned above.
+ * A respawn/add event should take care of it.
+ *
+ * These structure members need to change -
+ * write_data_buf - state that must be regenerated, zap it
+ * write_data_size - state that must be regenerated, zap it
+ * write_data_offset - state that must be regenerated, zap it
+ * read_data_buf - state that must be regenerated, zap it
+ * read_data_size - state that must be regenerated, zap it
+ * error_data_buf - state that must be regenerated, zap it
+ * error_data_size - state that must be regenerated, zap it
+ * close_write - state that must be regenerated, zap it
+ *
+ * There is the problem that an exe that fell over and needs respawning
+ * might keep falling over, keep needing to be respawned, and tie up system
+ * resources with the constant respawning. An exponentially increasing
+ * timeout (with maximum timeout) between respawns should take care of that.
+ * Although this is not a "contention for a resource" problem, the exe falling
+ * over may be, so a random element added to the timeout may help, and won't
+ * hurt. The user code may need to be informed that a timeout is in progress.
+ */
+
+struct _Ecore_Exe
+{
+ EINA_INLIST;
+ ECORE_MAGIC;
+ pid_t pid;
+ void *data;
+ char *tag, *cmd;
+ Ecore_Exe_Flags flags;
+ Ecore_Fd_Handler *write_fd_handler; /* the fd_handler to handle write to child - if this was used, or NULL if not */
+ Ecore_Fd_Handler *read_fd_handler; /* the fd_handler to handle read from child - if this was used, or NULL if not */
+ Ecore_Fd_Handler *error_fd_handler; /* the fd_handler to handle errors from child - if this was used, or NULL if not */
+ void *write_data_buf; /* a data buffer for data to write to the child -
+ * realloced as needed for more data and flushed when the fd handler says writes are possible
+ */
+ int write_data_size; /* the size in bytes of the data buffer */
+ int write_data_offset; /* the offset in bytes in the data buffer */
+ void *read_data_buf; /* data read from the child awating delivery to an event */
+ int read_data_size; /* data read from child in bytes */
+ void *error_data_buf; /* errors read from the child awating delivery to an event */
+ int error_data_size; /* errors read from child in bytes */
+ int child_fd_write; /* fd to write TO to send data to the child */
+ int child_fd_read; /* fd to read FROM when child has sent us (the parent) data */
+ int child_fd_error; /* fd to read FROM when child has sent us (the parent) errors */
+ int child_fd_write_x; /* fd to write TO to send data to the child */
+ int child_fd_read_x; /* fd to read FROM when child has sent us (the parent) data */
+ int child_fd_error_x; /* fd to read FROM when child has sent us (the parent) errors */
+ Eina_Bool close_stdin : 1;
+
+ int start_bytes, end_bytes, start_lines, end_lines; /* Number of bytes/lines to auto pipe at start/end of stdout/stderr. */
+
+ Ecore_Timer *doomsday_clock; /* The Timer of Death. Muahahahaha. */
+ void *doomsday_clock_dead; /* data for the doomsday clock */
+
+ Ecore_Exe_Cb pre_free_cb;
+};
+
+/* TODO: Something to let people build a command line and does auto escaping -
+ *
+ * ecore_exe_snprintf()
+ *
+ * OR
+ *
+ * cmd = ecore_exe_comand_parameter_append(cmd, "firefox");
+ * cmd = ecore_exe_comand_parameter_append(cmd, "http://www.foo.com/bar.html?baz=yes");
+ * each parameter appended is one argument, and it gets escaped, quoted, and
+ * appended with a preceding space. The first is the command off course.
+ */
+
+struct _ecore_exe_dead_exe
+{
+ pid_t pid;
+ char *cmd;
+};
+
+static inline void _ecore_exe_exec_it(const char *exe_cmd,
+ Ecore_Exe_Flags flags);
+static Eina_Bool _ecore_exe_data_generic_handler(void *data,
+ Ecore_Fd_Handler *fd_handler,
+ Ecore_Exe_Flags flags);
+static Eina_Bool _ecore_exe_data_error_handler(void *data,
+ Ecore_Fd_Handler *fd_handler);
+static Eina_Bool _ecore_exe_data_read_handler(void *data,
+ Ecore_Fd_Handler *fd_handler);
+static Eina_Bool _ecore_exe_data_write_handler(void *data,
+ Ecore_Fd_Handler *fd_handler);
+static void _ecore_exe_flush(Ecore_Exe *exe);
+static void _ecore_exe_event_exe_data_free(void *data EINA_UNUSED,
+ void *ev);
+static Ecore_Exe *_ecore_exe_is_it_alive(pid_t pid);
+static Eina_Bool _ecore_exe_make_sure_its_dead(void *data);
+static Eina_Bool _ecore_exe_make_sure_its_really_dead(void *data);
+static Ecore_Exe_Event_Add *_ecore_exe_event_add_new(void);
+static void _ecore_exe_event_add_free(void *data,
+ void *ev);
+static void _ecore_exe_dead_attach(Ecore_Exe *exe);
+
+EAPI int ECORE_EXE_EVENT_ADD = 0;
+EAPI int ECORE_EXE_EVENT_DEL = 0;
+EAPI int ECORE_EXE_EVENT_DATA = 0;
+EAPI int ECORE_EXE_EVENT_ERROR = 0;
+
+static Ecore_Exe *exes = NULL;
+static const char *shell = NULL;
+
+/* FIXME: This errno checking stuff should be put elsewhere for everybody to use.
+ * For now it lives here though, just to make testing easier.
+ */
+static int _ecore_exe_check_errno(int result,
+ const char *file,
+ int line);
+
+#define E_IF_NO_ERRNO(result, foo, ok) \
+ while (((ok) = _ecore_exe_check_errno((result) = (foo), __FILE__, __LINE__)) == -1) sleep(1); \
+ if (ok)
+
+#define E_NO_ERRNO(result, foo, ok) \
+ while (((ok) = _ecore_exe_check_errno((result) = (foo), __FILE__, __LINE__)) == -1) sleep(1)
+
+#define E_IF_NO_ERRNO_NOLOOP(result, foo, ok) \
+ if (((ok) = _ecore_exe_check_errno((result) = (foo), __FILE__, __LINE__)))
+
+static int
+_ecore_exe_check_errno(int result,
+ const char *file EINA_UNUSED,
+ int line EINA_UNUSED)
+{
+ int saved_errno = errno;
+
+ if (result == -1)
+ {
+ perror("*** errno reports ");
+/* What is currently supported -
+ *
+ * pipe
+ * EFAULT Argument is not valid.
+ * EMFILE Too many file descriptors used by process.
+ * ENFILE Too many open files by system.
+ * read
+ * EAGAIN No data now, try again.
+ * EBADF This is not an fd that can be read.
+ * EFAULT This is not a valid buffer.
+ * EINTR Interupted by signal, try again.
+ * EINVAL This is not an fd that can be read.
+ * EIO I/O error.
+ * EISDIR This is a directory, and cannot be read.
+ * others Depending on what sort of thing we are reading from.
+ * close
+ * EBADF This is not an fd that can be closed.
+ * EINTR Interupted by signal, try again.
+ * EIO I/O error.
+ * dup2
+ * EBADF This is not an fd that can be dup2'ed.
+ * EBUSY Race condition between open() and dup()
+ * EINTR Interupted by signal, try again.
+ * EMFILE Too many file descriptors used by process.
+ * fcntl
+ * EACCES, EAGAIN Locked or mapped by something else, try again later.
+ * EBADF This is not an fd that can be fcntl'ed.
+ * EDEADLK This will cause a deadlock.
+ * EFAULT This is not a valid lock.
+ * EINTR Interupted by signal, try again.
+ * EINVAL This is not a valid arg.
+ * EMFILE Too many file descriptors used by process.
+ * ENOLCK Problem getting a lock.
+ * EPERM Not allowed to do that.
+ * fsync
+ * EBADF This is not an fd that is open for writing.
+ * EINVAL, EROFS This is not an fd that can be fsynced.
+ * EIO I/O error.
+ *
+ * How to use it -
+ * int ok = 0;
+ * int result;
+ *
+ * E_IF_NO_ERRNO(result, foo(bar), ok)
+ * {
+ * E_IF_NO_ERRNO_NOLOOP(result, foo(bar), ok)
+ * {
+ * }
+ * }
+ *
+ * if (!ok)
+ * {
+ * // Something failed, cleanup.
+ * }
+ */
+ switch (saved_errno)
+ {
+ case EACCES:
+ case EAGAIN:
+ case EINTR:
+ { /* Not now, try later. */
+ ERR("*** Must try again in %s @%u.", file, line);
+ result = -1;
+ break;
+ }
+
+ case EMFILE:
+ case ENFILE:
+ case ENOLCK:
+ { /* Low on resources. */
+ ERR("*** Low on resources in %s @%u.", file,
+ line);
+ result = 0;
+ break;
+ }
+
+ case EIO:
+ { /* I/O error. */
+ ERR("*** I/O error in %s @%u.", file, line);
+ result = 0;
+ break;
+ }
+
+ case EFAULT:
+ case EBADF:
+ case EINVAL:
+ case EROFS:
+ case EISDIR:
+ case EDEADLK:
+ case EPERM:
+ case EBUSY:
+ { /* Programmer fucked up. */
+ ERR("*** NAUGHTY PROGRAMMER!!!\n"
+ "*** SPANK SPANK SPANK!!!\n"
+ "*** Now go fix your code in %s @%u. Tut tut tut!",
+ file, line);
+ result = 0;
+ break;
+ }
+
+ default:
+ { /* Unsupported errno code, please add this one. */
+ ERR("*** NAUGHTY PROGRAMMER!!!\n"
+ "*** SPANK SPANK SPANK!!!\n"
+ "*** Unsupported errno code %d, please add this one.\n"
+ "*** Now go fix your code in %s @%u, from %s @%u. Tut tut tut!",
+ saved_errno, __FILE__, __LINE__, file, line);
+ result = 0;
+ break;
+ }
+ }
+ }
+ else /* Everything is fine. */
+ result = 1;
+
+ errno = saved_errno;
+ return result;
+}
+
+/**
+ * @addtogroup Ecore_Exe_Group
+ *
+ * @{
+ */
+
+static int run_pri = ECORE_EXE_PRIORITY_INHERIT;
+
+/**
+ * Sets the priority at which to launch processes
+ *
+ * This sets the priority of processes run by ecore_exe_run() and
+ * ecore_exe_pipe_run().
+ * @li On Windows, the child process is created by default with the
+ * @ref ECORE_EXE_WIN32_PRIORITY_NORMAL priority, unless the calling
+ * process is in @ref ECORE_EXE_WIN32_PRIORITY_IDLE or
+ * @ref ECORE_EXE_WIN32_PRIORITY_BELOW_NORMAL priority. In that case, the
+ * child process inherits this priority.
+ * @li On other platforms, if set to @ref ECORE_EXE_PRIORITY_INHERIT child
+ * processes inherits the priority of their parent. This is the default.
+ *
+ * @param pri value a Ecore_Exe_Win32_Priority value on Windows, -20
+ * to 19 or @ref ECORE_EXE_PRIORITY_INHERIT on other OS.
+ */
+EAPI void
+ecore_exe_run_priority_set(int pri)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN;
+ run_pri = pri;
+}
+
+/**
+ * Gets the priority at which to launch processes
+ *
+ * This gets ths priority of launched processes. See
+ * ecore_exe_run_priority_set() for details. This just returns the value set
+ * by this call.
+ *
+ * @return the value set by ecore_exe_run_priority_set()
+ */
+EAPI int
+ecore_exe_run_priority_get(void)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(0);
+ return run_pri;
+}
+
+/**
+ * Spawns a child process.
+ *
+ * This is now just a thin wrapper around ecore_exe_pipe_run()
+ * @note When you use this function you will have no permissions
+ * to write or read on the pipe that connects you with the spwaned process.
+ * If you need to do that use ecore_exe_pipe_run() with the
+ * appropriated flags.
+ *
+ * @param exe_cmd The command to run with @c /bin/sh.
+ * @param data Data to attach to the returned process handle.
+ * @return A process handle to the spawned process.
+ */
+EAPI Ecore_Exe *
+ecore_exe_run(const char *exe_cmd,
+ const void *data)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL);
+ return ecore_exe_pipe_run(exe_cmd, 0, data);
+}
+
+/**
+ * Spawns a child process with its stdin/out available for communication.
+ *
+ * This function forks and runs the given command using @c /bin/sh.
+ *
+ * Note that the process handle is only valid until a child process
+ * terminated event is received. After all handlers for the child process
+ * terminated event have been called, the handle will be freed by Ecore.
+ *
+ * This function does the same thing as ecore_exe_run(), but also makes the
+ * standard in and/or out as well as stderr from the child process available
+ * for reading or writing. To write use ecore_exe_send(). To read listen to
+ * ECORE_EXE_EVENT_DATA or ECORE_EXE_EVENT_ERROR events (set up handlers).
+ * Ecore may buffer read and error data until a newline character if asked
+ * for with the @p flags. All data will be included in the events (newlines
+ * will be replaced with NULLS if line buffered). ECORE_EXE_EVENT_DATA events
+ * will only happen if the process is run with ECORE_EXE_PIPE_READ enabled
+ * in the flags. The same with the error version. Writing will only be
+ * allowed with ECORE_EXE_PIPE_WRITE enabled in the flags.
+ *
+ * @param exe_cmd The command to run with @c /bin/sh.
+ * @param flags The flag parameters for how to deal with inter-process I/O
+ * @param data Data to attach to the returned process handle.
+ * @return A process handle to the spawned process.
+ */
+EAPI Ecore_Exe *
+ecore_exe_pipe_run(const char *exe_cmd,
+ Ecore_Exe_Flags flags,
+ const void *data)
+{
+ Ecore_Exe *exe = NULL;
+ int statusPipe[2] = { -1, -1 };
+ int errorPipe[2] = { -1, -1 };
+ int readPipe[2] = { -1, -1 };
+ int writePipe[2] = { -1, -1 };
+ int n = 0;
+ int ok = 1;
+ int result;
+
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL);
+ if (!exe_cmd) return NULL;
+ exe = calloc(1, sizeof(Ecore_Exe));
+ if (!exe) return NULL;
+
+ if ((flags & ECORE_EXE_PIPE_AUTO) && (!(flags & ECORE_EXE_PIPE_ERROR))
+ && (!(flags & ECORE_EXE_PIPE_READ)))
+ /* We need something to auto pipe. */
+ flags |= ECORE_EXE_PIPE_READ | ECORE_EXE_PIPE_ERROR;
+
+ exe->child_fd_error = -1;
+ exe->child_fd_read = -1;
+ exe->child_fd_write = -1;
+ exe->child_fd_error_x = -1;
+ exe->child_fd_read_x = -1;
+ exe->child_fd_write_x = -1;
+
+ /* Create some pipes. */
+ if (ok)
+ {
+ E_IF_NO_ERRNO_NOLOOP(result, pipe(statusPipe), ok)
+ {
+ }
+ }
+ if (ok && (flags & ECORE_EXE_PIPE_ERROR))
+ {
+ E_IF_NO_ERRNO_NOLOOP(result, pipe(errorPipe), ok)
+ {
+ exe->child_fd_error = errorPipe[0];
+ exe->child_fd_error_x = errorPipe[1];
+ }
+ }
+ if (ok && (flags & ECORE_EXE_PIPE_READ))
+ {
+ E_IF_NO_ERRNO_NOLOOP(result, pipe(readPipe), ok)
+ {
+ exe->child_fd_read = readPipe[0];
+ exe->child_fd_read_x = readPipe[1];
+ }
+ }
+ if (ok && (flags & ECORE_EXE_PIPE_WRITE))
+ {
+ E_IF_NO_ERRNO_NOLOOP(result, pipe(writePipe), ok)
+ {
+ exe->child_fd_write = writePipe[1];
+ exe->child_fd_write_x = writePipe[0];
+ }
+ }
+ if (ok)
+ {
+ pid_t pid = 0;
+ volatile int vfork_exec_errno = 0;
+
+ /* FIXME: I should double check this. After a quick look around, this is already done, but via a more modern method. */
+ /* signal(SIGPIPE, SIG_IGN); We only want EPIPE on errors */
+ pid = fork();
+
+ if (pid == -1)
+ {
+ ERR("Failed to fork process");
+ pid = 0;
+ }
+ else if (pid == 0) /* child */
+ {
+ if (run_pri != ECORE_EXE_PRIORITY_INHERIT)
+ {
+#ifdef PRIO_PROCESS
+ if ((run_pri >= -20) && (run_pri <= 19))
+ setpriority(PRIO_PROCESS, 0, run_pri);
+#else
+#warning "Your OS/libc does not provide PRIO_PROCESS (and possibly setpriority())"
+#warning "This is a POSIX-1.2001 standard and it is highly encouraged that you"
+#warning "Have support for this"
+#endif
+ }
+ /* dup2 STDERR, STDIN, and STDOUT. dup2() allegedly closes the
+ * second pipe if it's open. On the other hand, there was the
+ * Great FD Leak Scare of '06, so let's be paranoid. */
+ if (ok && (flags & ECORE_EXE_PIPE_ERROR))
+ {
+ E_NO_ERRNO(result, close(STDERR_FILENO), ok);
+ E_NO_ERRNO(result, dup2(errorPipe[1], STDERR_FILENO), ok);
+ }
+ if (ok && (flags & ECORE_EXE_PIPE_READ))
+ {
+ E_NO_ERRNO(result, close(STDOUT_FILENO), ok);
+ E_NO_ERRNO(result, dup2(readPipe[1], STDOUT_FILENO), ok);
+ }
+ if (ok && (flags & ECORE_EXE_PIPE_WRITE))
+ {
+ E_NO_ERRNO(result, close(STDIN_FILENO), ok);
+ E_NO_ERRNO(result, dup2(writePipe[0], STDIN_FILENO), ok);
+ }
+
+ if (ok)
+ {
+ /* Setup the status pipe. */
+ E_NO_ERRNO(result, close(statusPipe[0]), ok);
+ E_IF_NO_ERRNO(result, fcntl(statusPipe[1], F_SETFD, FD_CLOEXEC), ok) /* close on exec shows success */
+ {
+ /* Run the actual command. */
+ _ecore_exe_exec_it(exe_cmd, flags); /* no return */
+ }
+ }
+
+ /* Something went 'orribly wrong. */
+ vfork_exec_errno = errno;
+
+ /* Close the pipes. */
+ if (flags & ECORE_EXE_PIPE_ERROR)
+ E_NO_ERRNO(result, close(errorPipe[1]), ok);
+ if (flags & ECORE_EXE_PIPE_READ)
+ E_NO_ERRNO(result, close(readPipe[1]), ok);
+ if (flags & ECORE_EXE_PIPE_WRITE)
+ E_NO_ERRNO(result, close(writePipe[0]), ok);
+ E_NO_ERRNO(result, close(statusPipe[1]), ok);
+
+ _exit(-1);
+ }
+ else /* parent */
+ {
+ /* Close the unused pipes. */
+ E_NO_ERRNO(result, close(statusPipe[1]), ok);
+
+ /* FIXME: after having a good look at the current e fd
+ * handling, investigate fcntl(dataPipe[x], F_SETSIG, ...) */
+ /* FIXME: above F_SETSIG etc. - this is async SIGIO based IO
+ * which is also linux specific so we probably don't want to
+ * do this as long as select() is working fine. the only time
+ * we really want to think of SIGIO async IO is when it all
+ * actually works basically everywhere and we can turn all
+ * IO into DMA async activities (i.e. you do a read() then
+ * the read is complete not on return but when you get a
+ * SIGIO - the read() just starts the transfer and it is
+ * completed in the background by DMA (or whatever mechanism
+ * the kernel choses)) */
+
+ /* Wait for it to start executing. */
+ /* FIXME: this doesn't seem very nice - we sit and block
+ * waiting on a child process... even though it's just
+ * the segment between the fork() and the exec) it just feels
+ * wrong */
+ for (;; )
+ {
+ char buf;
+
+ E_NO_ERRNO(result, read(statusPipe[0], &buf, 1), ok);
+ if (result == 0)
+ {
+ if (vfork_exec_errno != 0)
+ {
+ n = vfork_exec_errno;
+ ERR("Could not start \"%s\"", exe_cmd);
+ pid = 0;
+ }
+ break;
+ }
+ }
+
+ /* Close the status pipe. */
+ E_NO_ERRNO(result, close(statusPipe[0]), ok);
+ }
+
+ if (pid)
+ {
+ /* Setup the exe structure. */
+ ECORE_MAGIC_SET(exe, ECORE_MAGIC_EXE);
+ exe->start_bytes = -1;
+ exe->end_bytes = -1;
+ exe->start_lines = -1;
+ exe->end_lines = -1;
+ exe->pid = pid;
+ exe->flags = flags;
+ exe->data = (void *)data;
+ if ((exe->cmd = strdup(exe_cmd)))
+ {
+ if (flags & ECORE_EXE_PIPE_ERROR) /* Setup the error stuff. */
+ {
+ E_IF_NO_ERRNO(result,
+ fcntl(exe->child_fd_error, F_SETFL,
+ O_NONBLOCK), ok) {
+ }
+ E_IF_NO_ERRNO(result,
+ fcntl(exe->child_fd_error, F_SETFD,
+ FD_CLOEXEC), ok) {
+ }
+ E_IF_NO_ERRNO(result,
+ fcntl(exe->child_fd_error_x, F_SETFD,
+ FD_CLOEXEC), ok) {
+ }
+ {
+ exe->error_fd_handler =
+ ecore_main_fd_handler_add(exe->child_fd_error,
+ ECORE_FD_READ,
+ _ecore_exe_data_error_handler,
+ exe, NULL, NULL);
+ if (!exe->error_fd_handler)
+ ok = 0;
+ }
+ }
+ if (ok && (flags & ECORE_EXE_PIPE_READ)) /* Setup the read stuff. */
+ {
+ E_IF_NO_ERRNO(result,
+ fcntl(exe->child_fd_read, F_SETFL,
+ O_NONBLOCK), ok) {
+ }
+ E_IF_NO_ERRNO(result,
+ fcntl(exe->child_fd_read, F_SETFD,
+ FD_CLOEXEC), ok) {
+ }
+ E_IF_NO_ERRNO(result,
+ fcntl(exe->child_fd_read_x, F_SETFD,
+ FD_CLOEXEC), ok) {
+ }
+ {
+ exe->read_fd_handler =
+ ecore_main_fd_handler_add(exe->child_fd_read,
+ ECORE_FD_READ,
+ _ecore_exe_data_read_handler,
+ exe, NULL, NULL);
+ if (!exe->read_fd_handler)
+ ok = 0;
+ }
+ }
+ if (ok && (flags & ECORE_EXE_PIPE_WRITE)) /* Setup the write stuff. */
+ {
+ E_IF_NO_ERRNO(result,
+ fcntl(exe->child_fd_write, F_SETFL,
+ O_NONBLOCK), ok) {
+ }
+ E_IF_NO_ERRNO(result,
+ fcntl(exe->child_fd_write, F_SETFD,
+ FD_CLOEXEC), ok) {
+ }
+ E_IF_NO_ERRNO(result,
+ fcntl(exe->child_fd_write_x, F_SETFD,
+ FD_CLOEXEC), ok) {
+ }
+ {
+ exe->write_fd_handler =
+ ecore_main_fd_handler_add(exe->child_fd_write,
+ ECORE_FD_WRITE,
+ _ecore_exe_data_write_handler,
+ exe, NULL, NULL);
+ if (exe->write_fd_handler)
+ ecore_main_fd_handler_active_set(exe->write_fd_handler, 0); /* Nothing to write to start with. */
+ else
+ ok = 0;
+ }
+ }
+
+ exes = (Ecore_Exe *)eina_inlist_append(EINA_INLIST_GET(exes), EINA_INLIST_GET(exe));
+ n = 0;
+ }
+ else
+ ok = 0;
+ }
+ else
+ ok = 0;
+ }
+
+ if (!ok) /* Something went wrong, so pull down everything. */
+ {
+ if (exe->pid) ecore_exe_terminate(exe);
+ IF_FN_DEL(ecore_exe_free, exe);
+ }
+ else
+ {
+ Ecore_Exe_Event_Add *e;
+
+ e = _ecore_exe_event_add_new();
+ e->exe = exe;
+ if (e) /* Send the event. */
+ ecore_event_add(ECORE_EXE_EVENT_ADD, e,
+ _ecore_exe_event_add_free, NULL);
+ /* INF("Running as %d for %s.\n", exe->pid, exe->cmd); */
+ }
+
+ errno = n;
+ return exe;
+}
+
+/**
+ * Defines a function to be called before really freeing the handle data.
+ *
+ * This might be useful for language bindings such as Python and Perl
+ * that need to deallocate wrappers associated with this handle.
+ *
+ * This handle should never be modified by this call. It should be
+ * considered informative only. All getters are valid when the given
+ * function is called back.
+ *
+ * @param exe The child process to attach the pre_free function.
+ * @param func The function to call before @a exe is freed.
+ */
+EAPI void
+ecore_exe_callback_pre_free_set(Ecore_Exe *exe,
+ Ecore_Exe_Cb func)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN;
+ if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+ {
+ ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE,
+ "ecore_exe_callback_pre_free_set");
+ return;
+ }
+ exe->pre_free_cb = func;
+}
+
+/**
+ * Sends data to the given child process which it receives on stdin.
+ *
+ * This function writes to a child processes standard in, with unlimited
+ * buffering. This call will never block. It may fail if the system runs out
+ * of memory.
+ *
+ * @param exe The child process to send to
+ * @param data The data to send
+ * @param size The size of the data to send, in bytes
+ * @return @c EINA_TRUE if successful, @c EINA_FALSE on failure.
+ */
+EAPI Eina_Bool
+ecore_exe_send(Ecore_Exe *exe,
+ const void *data,
+ int size)
+{
+ void *buf;
+
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(EINA_FALSE);
+ if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+ {
+ ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_send");
+ return EINA_FALSE;
+ }
+
+ if (exe->close_stdin)
+ {
+ ERR("Ecore_Exe %p stdin is closed! Cannot send %d bytes from %p",
+ exe, size, data);
+ return EINA_FALSE;
+ }
+
+ if (exe->child_fd_write == -1)
+ {
+ ERR("Ecore_Exe %p created without ECORE_EXE_PIPE_WRITE! "
+ "Cannot send %d bytes from %p", exe, size, data);
+ return EINA_FALSE;
+ }
+
+ buf = realloc(exe->write_data_buf, exe->write_data_size + size);
+ if (!buf) return EINA_FALSE;
+
+ exe->write_data_buf = buf;
+ memcpy((char *)exe->write_data_buf + exe->write_data_size, data, size);
+ exe->write_data_size += size;
+
+ if (exe->write_fd_handler)
+ ecore_main_fd_handler_active_set(exe->write_fd_handler, ECORE_FD_WRITE);
+
+ return EINA_TRUE;
+}
+
+/**
+ * The stdin of the given child process will close when the write buffer is empty.
+ *
+ * @param exe The child process
+ */
+EAPI void
+ecore_exe_close_stdin(Ecore_Exe *exe)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN;
+ if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+ {
+ ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_close_stdin");
+ return;
+ }
+ exe->close_stdin = 1;
+}
+
+/**
+ * Sets the auto pipe limits for the given process handle. On Windows
+ * this function does nothing.
+ *
+ * @param exe The given process handle.
+ * @param start_bytes limit of bytes at start of output to buffer.
+ * @param end_bytes limit of bytes at end of output to buffer.
+ * @param start_lines limit of lines at start of output to buffer.
+ * @param end_lines limit of lines at end of output to buffer.
+ */
+EAPI void
+ecore_exe_auto_limits_set(Ecore_Exe *exe,
+ int start_bytes,
+ int end_bytes,
+ int start_lines,
+ int end_lines)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN;
+ if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+ {
+ ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_auto_limits_set");
+ return;
+ }
+ /* FIXME: sanitize the input. */
+ exe->start_bytes = start_bytes;
+ exe->end_bytes = end_bytes;
+ exe->start_lines = start_lines;
+ exe->end_lines = end_lines;
+
+ /* FIXME: get this can of worms working.
+ *
+ * capture stderr & stdout internally
+ *
+ * raster and onefang keep moving the goal posts on this one. It started out as
+ * "show users the error output if an exe fails" and is rapidly approaching
+ * "alternative method of getting the data, poll vs event driven". Some serious
+ * thinking needs to be applied to this. Do we really want to go that far? If
+ * so, we should change the names. The basic design will probably remain the
+ * same which ever way we go. The constant goal post moving is probably due to
+ * generic design methods leading to feature creep as we inspired each other to
+ * more generic designs. It does seem like the closer we get to poll driven,
+ * the more issues and corner cases there are.
+ *
+ * Instead of doing the usual register an event handler thing, we are ecore_exe,
+ * we can take some short cuts. Don't send the events, just leave the exe buffers
+ * as is until the user asks for them, then return the event.
+ *
+ * start = 0, end = 0; clogged arteries get flushed, everything is ignored.
+ * start = -1, end = -1; clogged arteries get transferred to internal buffers. Actually, either == -1 means buffer everything.
+ * start = X, end = 0; buffer first X out of clogged arteries, flush and ignore rest.
+ * start = 0, end = X; circular buffer X
+ * start = X, end = Y; buffer first X out of clogged arteries, circular buffer Y from beginning.
+ *
+ * bytes vs lines, which ever one reaches the limit first.
+ * Before we go beyond the start+end limit, leave the end buffer empty, and store both in the start buffer, coz they overlap.
+ * After we pass the the start+end limit, insert "\n...\n" at the end of the start buffer, copy the rest to the end buffer, then store in the end buffer.
+ *
+ * Other issues -
+ * Spank programmer for polling data if polling is not turned on.
+ * Spank programmer for setting up event callbacks if polling is turned on.
+ * Spank programmer for freeing the event data if it came from the event system, as that autofrees.
+ * Spank the programmer if they try to set the limits bigger than what has been gathered & ignored already, coz they just lost data.
+ * Spank onefang and raster for opening this can of worms.
+ * Should we have separate out/err limits?
+ * Should we remove from the internal buffer the data that was delivered already?
+ * If so, what to do about limits, start, and end? They could loose their meaning.
+ */
+}
+
+/**
+ * Gets the auto pipe data for the given process handle
+ *
+ * @param exe The given process handle.
+ * @param flags Is this a ECORE_EXE_PIPE_READ or ECORE_EXE_PIPE_ERROR?
+ * @return The event data.
+ */
+EAPI Ecore_Exe_Event_Data *
+ecore_exe_event_data_get(Ecore_Exe *exe,
+ Ecore_Exe_Flags flags)
+{
+ Ecore_Exe_Event_Data *e = NULL;
+ int is_buffered = 0;
+ unsigned char *inbuf;
+ int inbuf_num;
+
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL);
+ if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+ {
+ ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_event_data_get");
+ return NULL;
+ }
+
+ /* Sort out what sort of event we are. */
+ if (flags & ECORE_EXE_PIPE_READ)
+ {
+ flags = ECORE_EXE_PIPE_READ;
+ if (exe->flags & ECORE_EXE_PIPE_READ_LINE_BUFFERED)
+ is_buffered = 1;
+ }
+ else
+ {
+ flags = ECORE_EXE_PIPE_ERROR;
+ if (exe->flags & ECORE_EXE_PIPE_ERROR_LINE_BUFFERED)
+ is_buffered = 1;
+ }
+
+ /* Get the data. */
+ if (flags & ECORE_EXE_PIPE_READ)
+ {
+ inbuf = exe->read_data_buf;
+ inbuf_num = exe->read_data_size;
+ exe->read_data_buf = NULL;
+ exe->read_data_size = 0;
+ }
+ else
+ {
+ inbuf = exe->error_data_buf;
+ inbuf_num = exe->error_data_size;
+ exe->error_data_buf = NULL;
+ exe->error_data_size = 0;
+ }
+
+ e = calloc(1, sizeof(Ecore_Exe_Event_Data));
+ if (e)
+ {
+ e->exe = exe;
+ e->data = inbuf;
+ e->size = inbuf_num;
+
+ if (is_buffered) /* Deal with line buffering. */
+ {
+ int max = 0;
+ int count = 0;
+ int i;
+ int last = 0;
+ char *c;
+
+ c = (char *)inbuf;
+ for (i = 0; i < inbuf_num; i++) /* Find the lines. */
+ {
+ if (inbuf[i] == '\n')
+ {
+ if (count >= max)
+ {
+ /* In testing, the lines seem to arrive in batches of 500 to 1000 lines at most, roughly speaking. */
+ max += 10; /* FIXME: Maybe keep track of the largest number of lines ever sent, and add half that many instead of 10. */
+ e->lines = realloc(e->lines, sizeof(Ecore_Exe_Event_Data_Line) * (max + 1)); /* Allow room for the NULL termination. */
+ }
+ /* raster said to leave the line endings as line endings, however -
+ * This is line buffered mode, we are not dealing with binary here, but lines.
+ * If we are not dealing with binary, we must be dealing with ASCII, unicode, or some other text format.
+ * Thus the user is most likely gonna deal with this text as strings.
+ * Thus the user is most likely gonna pass this data to str functions.
+ * rasters way - the endings are always gonna be '\n'; onefangs way - they will always be '\0'
+ * We are handing them the string length as a convenience.
+ * Thus if they really want it in raw format, they can e->lines[i].line[e->lines[i].size - 1] = '\n'; easily enough.
+ * In the default case, we can do this conversion quicker than the user can, as we already have the index and pointer.
+ * Let's make it easy on them to use these as standard C strings.
+ *
+ * onefang is proud to announce that he has just set a new personal record for the
+ * most over documentation of a simple assignment statement. B-)
+ */
+ inbuf[i] = '\0';
+ e->lines[count].line = c;
+ e->lines[count].size = i - last;
+ last = i + 1;
+ c = (char *)&inbuf[last];
+ count++;
+ }
+ }
+ if (i > last) /* Partial line left over, save it for next time. */
+ {
+ if (count != 0) e->size = last;
+ if (flags & ECORE_EXE_PIPE_READ)
+ {
+ exe->read_data_size = i - last;
+ exe->read_data_buf = malloc(exe->read_data_size);
+ memcpy(exe->read_data_buf, c, exe->read_data_size);
+ }
+ else
+ {
+ exe->error_data_size = i - last;
+ exe->error_data_buf = malloc(exe->error_data_size);
+ memcpy(exe->error_data_buf, c, exe->error_data_size);
+ }
+ }
+ if (count == 0) /* No lines to send, cancel the event. */
+ {
+ _ecore_exe_event_exe_data_free(NULL, e);
+ e = NULL;
+ }
+ else /* NULL terminate the array, so that people know where the end is. */
+ {
+ e->lines[count].line = NULL;
+ e->lines[count].size = 0;
+ }
+ }
+ }
+
+ return e;
+}
+
+/**
+ * Sets the string tag for the given process handle
+ *
+ * @param exe The given process handle.
+ * @param tag The string tag to set on the process handle.
+ */
+EAPI void
+ecore_exe_tag_set(Ecore_Exe *exe,
+ const char *tag)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN;
+ if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+ {
+ ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_tag_set");
+ return;
+ }
+ IF_FREE(exe->tag);
+ if (tag)
+ exe->tag = strdup(tag);
+ else
+ exe->tag = NULL;
+}
+
+/**
+ * Retrieves the tag attached to the given process handle. There is no need to
+ * free it as it just returns the internal pointer value. This value is only
+ * valid as long as the @p exe is valid or until the tag is set to something
+ * else on this @p exe.
+ *
+ * @param exe The given process handle.
+ * @return The string attached to @p exe. It is a handle to existing
+ * internal string and should not be modified, use
+ * ecore_exe_tag_set() to change it. It might be @c NULL.
+ */
+EAPI const char *
+ecore_exe_tag_get(const Ecore_Exe *exe)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL);
+ if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+ {
+ ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_tag_get");
+ return NULL;
+ }
+ return exe->tag;
+}
+
+/**
+ * Frees the given process handle.
+ *
+ * Note that the process that the handle represents is unaffected by this
+ * function.
+ *
+ * @param exe The given process handle.
+ * @return The data attached to the handle when @ref ecore_exe_run was
+ * called.
+ */
+EAPI void *
+ecore_exe_free(Ecore_Exe *exe)
+{
+ void *data;
+ int ok = 0;
+ int result;
+
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL);
+ if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+ {
+ ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_free");
+ return NULL;
+ }
+
+ data = exe->data;
+
+ if (exe->pre_free_cb)
+ exe->pre_free_cb(data, exe);
+
+ if (exe->doomsday_clock)
+ {
+ struct _ecore_exe_dead_exe *dead;
+
+ ecore_timer_del(exe->doomsday_clock);
+ exe->doomsday_clock = NULL;
+ dead = exe->doomsday_clock_dead;
+ if (dead)
+ {
+ IF_FREE(dead->cmd);
+ free(dead);
+ exe->doomsday_clock_dead = NULL;
+ }
+ }
+ IF_FN_DEL(ecore_main_fd_handler_del, exe->write_fd_handler);
+ IF_FN_DEL(ecore_main_fd_handler_del, exe->read_fd_handler);
+ IF_FN_DEL(ecore_main_fd_handler_del, exe->error_fd_handler);
+ if (exe->child_fd_write_x != -1)
+ E_NO_ERRNO(result, close(exe->child_fd_write_x), ok);
+ if (exe->child_fd_read_x != -1)
+ E_NO_ERRNO(result, close(exe->child_fd_read_x), ok);
+ if (exe->child_fd_error_x != -1)
+ E_NO_ERRNO(result, close(exe->child_fd_error_x), ok);
+ if (exe->child_fd_write != -1)
+ E_NO_ERRNO(result, close(exe->child_fd_write), ok);
+ if (exe->child_fd_read != -1)
+ E_NO_ERRNO(result, close(exe->child_fd_read), ok);
+ if (exe->child_fd_error != -1)
+ E_NO_ERRNO(result, close(exe->child_fd_error), ok);
+ IF_FREE(exe->write_data_buf);
+ IF_FREE(exe->read_data_buf);
+ IF_FREE(exe->error_data_buf);
+ IF_FREE(exe->cmd);
+
+ exes = (Ecore_Exe *)eina_inlist_remove(EINA_INLIST_GET(exes), EINA_INLIST_GET(exe));
+ ECORE_MAGIC_SET(exe, ECORE_MAGIC_NONE);
+ IF_FREE(exe->tag);
+ free(exe);
+ return data;
+}
+
+/**
+ * Frees the given event data.
+ *
+ * @param e The given event data.
+ */
+EAPI void
+ecore_exe_event_data_free(Ecore_Exe_Event_Data *e)
+{
+ if (!e) return;
+ IF_FREE(e->lines);
+ IF_FREE(e->data);
+ free(e);
+}
+
+/**
+ * Retrieves the process ID of the given spawned process.
+ * @param exe Handle to the given spawned process.
+ * @return The process ID on success. @c -1 otherwise.
+ */
+EAPI pid_t
+ecore_exe_pid_get(const Ecore_Exe *exe)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(0);
+ if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+ {
+ ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_pid_get");
+ return -1;
+ }
+ return exe->pid;
+}
+
+/**
+ * Retrieves the command of the given spawned process.
+ * @param exe Handle to the given spawned process.
+ * @return The command on success, @c NULL otherwise. This string is the
+ * pointer to the internal value and must not be modified in
+ * any way.
+ */
+EAPI const char *
+ecore_exe_cmd_get(const Ecore_Exe *exe)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL);
+ if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+ {
+ ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_cmd_get");
+ return NULL;
+ }
+ return exe->cmd;
+}
+
+/**
+ * Retrieves the data attached to the given process handle.
+ * @param exe The given process handle.
+ * @return The data pointer attached to @p exe Given to
+ * ecore_exe_run() or ecore_exe_pipe_run()
+ */
+EAPI void *
+ecore_exe_data_get(const Ecore_Exe *exe)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL);
+ if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+ {
+ ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_data_get");
+ return NULL;
+ }
+ return exe->data;
+}
+
+/**
+ * Sets the data attached to the given process handle.
+ * @param exe The given process handle.
+ * @param data The pointer to attach
+ * @return The data pointer previously attached to @p exe with
+ * ecore_exe_run(), ecore_exe_pipe_run(), or ecore_exe_data_set()
+ * @since 1.1
+ */
+EAPI void *
+ecore_exe_data_set(Ecore_Exe *exe,
+ void *data)
+{
+ void *ret;
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL);
+ if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+ {
+ ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, __func__);
+ return NULL;
+ }
+ ret = exe->data;
+ exe->data = data;
+ return ret;
+}
+
+/**
+ * Retrieves the flags attached to the given process handle.
+ * @param exe The given process handle.
+ * @return The flags attached to @p exe.
+ */
+EAPI Ecore_Exe_Flags
+ecore_exe_flags_get(const Ecore_Exe *exe)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(0);
+ if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+ {
+ ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_data_get");
+ return 0;
+ }
+ return exe->flags;
+}
+
+/**
+ * Pauses the given process by sending it a @c SIGSTOP signal.
+ * @param exe Process handle to the given process.
+ */
+EAPI void
+ecore_exe_pause(Ecore_Exe *exe)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN;
+ if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+ {
+ ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_pause");
+ return;
+ }
+ kill(exe->pid, SIGSTOP);
+}
+
+/**
+ * Continues the given paused process by sending it a @c SIGCONT signal.
+ * @param exe Process handle to the given process.
+ */
+EAPI void
+ecore_exe_continue(Ecore_Exe *exe)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN;
+ if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+ {
+ ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_continue");
+ return;
+ }
+ kill(exe->pid, SIGCONT);
+}
+
+/**
+ * Sends the given spawned process a interrupt (@c SIGINT) signal.
+ * @param exe Process handle to the given process.
+ */
+EAPI void
+ecore_exe_interrupt(Ecore_Exe *exe)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN;
+ if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+ {
+ ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_interrupt");
+ return;
+ }
+ _ecore_exe_dead_attach(exe);
+ kill(exe->pid, SIGINT);
+}
+
+/**
+ * Sends the given spawned process a quit (@c SIGQUIT) signal.
+ * @param exe Process handle to the given process.
+ */
+EAPI void
+ecore_exe_quit(Ecore_Exe *exe)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN;
+ if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+ {
+ ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_quit");
+ return;
+ }
+ _ecore_exe_dead_attach(exe);
+ kill(exe->pid, SIGQUIT);
+}
+
+/**
+ * Sends the given spawned process a terminate (@c SIGTERM) signal.
+ * @param exe Process handle to the given process.
+ */
+EAPI void
+ecore_exe_terminate(Ecore_Exe *exe)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN;
+ if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+ {
+ ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_terminate");
+ return;
+ }
+ _ecore_exe_dead_attach(exe);
+ INF("Sending TERM signal to %s (%d).", exe->cmd, exe->pid);
+ kill(exe->pid, SIGTERM);
+}
+
+/**
+ * Kills the given spawned process by sending it a @c SIGKILL signal.
+ * @param exe Process handle to the given process.
+ */
+EAPI void
+ecore_exe_kill(Ecore_Exe *exe)
+{
+ struct _ecore_exe_dead_exe *dead;
+
+ EINA_MAIN_LOOP_CHECK_RETURN;
+ if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+ {
+ ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_kill");
+ return;
+ }
+
+ dead = calloc(1, sizeof(struct _ecore_exe_dead_exe));
+ if (dead)
+ {
+ dead->pid = exe->pid;
+ dead->cmd = strdup(exe->cmd);
+ IF_FN_DEL(ecore_timer_del, exe->doomsday_clock);
+ exe->doomsday_clock =
+ ecore_timer_add(10.0, _ecore_exe_make_sure_its_really_dead, dead);
+ }
+
+ INF("Sending KILL signal to %s (%d).", exe->cmd, exe->pid);
+ kill(exe->pid, SIGKILL);
+}
+
+/**
+ * Sends a @c SIGUSR signal to the given spawned process.
+ * @param exe Process handle to the given process.
+ * @param num The number user signal to send. Must be either 1 or 2, or
+ * the signal will be ignored.
+ */
+EAPI void
+ecore_exe_signal(Ecore_Exe *exe,
+ int num)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN;
+ if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+ {
+ ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_signal");
+ return;
+ }
+ if (num == 1)
+ kill(exe->pid, SIGUSR1);
+ else if (num == 2)
+ kill(exe->pid, SIGUSR2);
+}
+
+/**
+ * Sends a @c SIGHUP signal to the given spawned process.
+ * @param exe Process handle to the given process.
+ */
+EAPI void
+ecore_exe_hup(Ecore_Exe *exe)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN;
+ if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+ {
+ ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_hup");
+ return;
+ }
+ kill(exe->pid, SIGHUP);
+}
+
+/**
+ * @}
+ */
+
+static Ecore_Exe *
+_ecore_exe_is_it_alive(pid_t pid)
+{
+ Ecore_Exe *exe = NULL;
+
+ /* FIXME: There is no nice, safe, OS independent way to tell if a
+ * particular PID is still alive. I have written code to do so
+ * for my urunlevel busybox applet (http://urunlevel.sourceforge.net/),
+ * but it's for linux only, and still not guaranteed.
+ *
+ * So for now, we just check that a valid Ecore_Exe structure
+ * exists for it. Even that is not a guarantee, as the structure
+ * can be freed without killing the process.
+ *
+ * I think we can safely put exe's into two categories, those users
+ * that care about the life of the exe, and the run and forget type.
+ * The run and forget type starts up the exe, then free's the
+ * Ecore_Exe structure straight away. They can never call any of
+ * the functions that can call this, so we don't worry about them.
+ *
+ * Those user's that care about the life of exe's will keep the
+ * Ecore_Exe structure around, terminate them eventually, or
+ * register for exit events. For these ones the assumption
+ * that valid Ecore_Exe struct == live exe is almost valid.
+ *
+ * I will probably copy my urunlevel code into here someday.
+ */
+ exe = _ecore_exe_find(pid);
+ if (exe)
+ {
+ if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+ exe = NULL;
+ }
+
+ return exe;
+}
+
+static Eina_Bool
+_ecore_exe_make_sure_its_dead(void *data)
+{
+ struct _ecore_exe_dead_exe *dead;
+
+ dead = data;
+ if (dead)
+ {
+ Ecore_Exe *exe = NULL;
+
+ if ((exe = _ecore_exe_is_it_alive(dead->pid)))
+ {
+ if (dead->cmd)
+ INF("Sending KILL signal to allegedly dead %s (%d).",
+ dead->cmd, dead->pid);
+ else
+ INF("Sending KILL signal to allegedly dead PID %d.",
+ dead->pid);
+ exe->doomsday_clock =
+ ecore_timer_add(10.0, _ecore_exe_make_sure_its_really_dead,
+ dead);
+ kill(dead->pid, SIGKILL);
+ }
+ else
+ {
+ IF_FREE(dead->cmd);
+ free(dead);
+ }
+ }
+ return ECORE_CALLBACK_CANCEL;
+}
+
+static Eina_Bool
+_ecore_exe_make_sure_its_really_dead(void *data)
+{
+ struct _ecore_exe_dead_exe *dead;
+
+ dead = data;
+ if (dead)
+ {
+ Ecore_Exe *exe = NULL;
+
+ if ((exe = _ecore_exe_is_it_alive(dead->pid)))
+ {
+ ERR("RUN! The zombie wants to eat your brains! And your CPU!");
+ if (dead->cmd)
+ INF("%s (%d) is not really dead.", dead->cmd, dead->pid);
+ else
+ INF("PID %d is not really dead.", dead->pid);
+ exe->doomsday_clock = NULL;
+ }
+ IF_FREE(dead->cmd);
+ free(dead);
+ }
+ return ECORE_CALLBACK_CANCEL;
+}
+
+void
+_ecore_exe_init(void)
+{
+ ECORE_EXE_EVENT_ADD = ecore_event_type_new();
+ ECORE_EXE_EVENT_DEL = ecore_event_type_new();
+ ECORE_EXE_EVENT_DATA = ecore_event_type_new();
+ ECORE_EXE_EVENT_ERROR = ecore_event_type_new();
+}
+
+void
+_ecore_exe_shutdown(void)
+{
+ while (exes)
+ ecore_exe_free(exes);
+}
+
+Ecore_Exe *
+_ecore_exe_find(pid_t pid)
+{
+ Ecore_Exe *exe;
+
+ EINA_INLIST_FOREACH(exes, exe)
+ {
+ if (exe->pid == pid)
+ return exe;
+ }
+ return NULL;
+}
+
+Ecore_Timer *
+_ecore_exe_doomsday_clock_get(Ecore_Exe *exe)
+{
+ return exe->doomsday_clock;
+}
+
+void
+_ecore_exe_doomsday_clock_set(Ecore_Exe *exe,
+ Ecore_Timer *dc)
+{
+ exe->doomsday_clock = dc;
+}
+
+static inline void
+_ecore_exe_exec_it(const char *exe_cmd,
+ Ecore_Exe_Flags flags)
+{
+ char use_sh = 1;
+ char *buf = NULL;
+ char **args = NULL;
+ int save_errno = 0;
+
+ /* So what is this doing?
+ *
+ * We are trying to avoid wrapping the exe call with /bin/sh -c.
+ * We conservatively search for certain shell meta characters,
+ * If we don't find them, we can call the exe directly.
+ */
+ if (!strpbrk(exe_cmd, "|&;<>()$`\\\"'*?#"))
+ {
+ char *token;
+ char pre_command = 1;
+ int num_tokens = 0;
+
+ if (!(buf = strdup(exe_cmd)))
+ return;
+
+ token = strtok(buf, " \t\n\v");
+ while (token)
+ {
+ if (token[0] == '~')
+ break;
+ if (pre_command)
+ {
+ if (token[0] == '[')
+ break;
+ if (strchr(token, '='))
+ break;
+ else
+ pre_command = 0;
+ }
+ num_tokens++;
+ token = strtok(NULL, " \t\n\v");
+ }
+ IF_FREE(buf);
+ if ((!token) && (num_tokens))
+ {
+ int i = 0;
+
+ if (!(buf = strdup(exe_cmd)))
+ return;
+
+ token = strtok(buf, " \t\n\v");
+ use_sh = 0;
+ if (!(args = (char **)calloc(num_tokens + 1, sizeof(char *))))
+ {
+ IF_FREE(buf);
+ return;
+ }
+ for (i = 0; i < num_tokens; i++)
+ {
+ if (token)
+ args[i] = token;
+ token = strtok(NULL, " \t\n\v");
+ }
+ args[num_tokens] = NULL;
+ }
+ }
+
+#ifdef HAVE_SYS_PRCTL_H
+ if ((flags & ECORE_EXE_TERM_WITH_PARENT))
+ {
+ prctl(PR_SET_PDEATHSIG, SIGTERM);
+ }
+#endif
+
+ if (!(flags & ECORE_EXE_NOT_LEADER)) setsid();
+ if ((flags & ECORE_EXE_USE_SH))
+ {
+ errno = 0;
+ execl("/bin/sh", "/bin/sh", "-c", exe_cmd, (char *)NULL);
+ }
+ else if (use_sh) /* We have to use a shell to run this. */
+ {
+ if (!shell) /* Find users preferred shell. */
+ {
+ shell = getenv("SHELL");
+ if (!shell)
+ shell = "/bin/sh";
+ }
+ errno = 0;
+ execl(shell, shell, "-c", exe_cmd, (char *)NULL);
+ }
+ else
+ { /* We can run this directly. */
+ if (!args)
+ {
+ IF_FREE(buf);
+ IF_FREE(args);
+ ERR("arg[0] is NULL!");
+ return;
+ }
+ errno = 0;
+ execvp(args[0], args);
+ }
+
+ save_errno = errno;
+ IF_FREE(buf);
+ IF_FREE(args);
+ errno = save_errno;
+ return;
+}
+
+static Eina_Bool
+_ecore_exe_data_generic_handler(void *data,
+ Ecore_Fd_Handler *fd_handler,
+ Ecore_Exe_Flags flags)
+{
+ Ecore_Exe *exe;
+ int child_fd;
+ int event_type;
+
+ exe = data;
+
+ /* Sort out what sort of handler we are. */
+ if (flags & ECORE_EXE_PIPE_READ)
+ {
+ flags = ECORE_EXE_PIPE_READ;
+ event_type = ECORE_EXE_EVENT_DATA;
+ child_fd = exe->child_fd_read;
+ }
+ else
+ {
+ flags = ECORE_EXE_PIPE_ERROR;
+ event_type = ECORE_EXE_EVENT_ERROR;
+ child_fd = exe->child_fd_error;
+ }
+
+ if ((fd_handler)
+ && (ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_READ)))
+ {
+ unsigned char *inbuf;
+ int inbuf_num;
+
+ /* Get any left over data from last time. */
+ if (flags & ECORE_EXE_PIPE_READ)
+ {
+ inbuf = exe->read_data_buf;
+ inbuf_num = exe->read_data_size;
+ exe->read_data_buf = NULL;
+ exe->read_data_size = 0;
+ }
+ else
+ {
+ inbuf = exe->error_data_buf;
+ inbuf_num = exe->error_data_size;
+ exe->error_data_buf = NULL;
+ exe->error_data_size = 0;
+ }
+
+ for (;; )
+ {
+ int num, lost_exe;
+ char buf[READBUFSIZ];
+
+ lost_exe = 0;
+ errno = 0;
+ if ((num = read(child_fd, buf, READBUFSIZ)) < 1)
+ {
+ /* FIXME: SPEED/SIZE TRADE OFF - add a smaller READBUFSIZE
+ * (currently 64k) to inbuf, use that instead of buf, and
+ * save ourselves a memcpy(). */
+ lost_exe = ((errno == EIO) ||
+ (errno == EBADF) ||
+ (errno == EPIPE) ||
+ (errno == EINVAL) || (errno == ENOSPC));
+ if ((errno != EAGAIN) && (errno != EINTR))
+ perror("_ecore_exe_generic_handler() read problem ");
+ }
+ if (num > 0) /* data got read. */
+ {
+ inbuf = realloc(inbuf, inbuf_num + num);
+ memcpy(inbuf + inbuf_num, buf, num);
+ inbuf_num += num;
+ }
+ else
+ { /* No more data to read. */
+ if (inbuf)
+ {
+ Ecore_Exe_Event_Data *e;
+
+ /* Stash the data away for later. */
+ if (flags & ECORE_EXE_PIPE_READ)
+ {
+ exe->read_data_buf = inbuf;
+ exe->read_data_size = inbuf_num;
+ }
+ else
+ {
+ exe->error_data_buf = inbuf;
+ exe->error_data_size = inbuf_num;
+ }
+
+ if (!(exe->flags & ECORE_EXE_PIPE_AUTO))
+ {
+ e = ecore_exe_event_data_get(exe, flags);
+ if (e) /* Send the event. */
+ ecore_event_add(event_type, e,
+ _ecore_exe_event_exe_data_free,
+ NULL);
+ }
+ }
+ if (lost_exe)
+ {
+ if (flags & ECORE_EXE_PIPE_READ)
+ {
+ if (exe->read_data_size)
+ INF("There are %d bytes left unsent from the dead exe %s.",
+ exe->read_data_size, exe->cmd);
+ }
+ else
+ {
+ if (exe->error_data_size)
+ INF("There are %d bytes left unsent from the dead exe %s.",
+ exe->error_data_size, exe->cmd);
+ }
+ /* Thought about this a bit. If the exe has actually
+ * died, this won't do any harm as it must have died
+ * recently and the pid has not had a chance to recycle.
+ * It is also a paranoid catchall, coz the usual ecore_signal
+ * mechenism should kick in. But let's give it a good
+ * kick in the head anyway.
+ */
+ ecore_exe_terminate(exe);
+ }
+ break;
+ }
+ }
+ }
+
+ return ECORE_CALLBACK_RENEW;
+}
+
+static Eina_Bool
+_ecore_exe_data_error_handler(void *data,
+ Ecore_Fd_Handler *fd_handler)
+{
+ return _ecore_exe_data_generic_handler(data, fd_handler,
+ ECORE_EXE_PIPE_ERROR);
+}
+
+static Eina_Bool
+_ecore_exe_data_read_handler(void *data,
+ Ecore_Fd_Handler *fd_handler)
+{
+ return _ecore_exe_data_generic_handler(data, fd_handler,
+ ECORE_EXE_PIPE_READ);
+}
+
+static Eina_Bool
+_ecore_exe_data_write_handler(void *data,
+ Ecore_Fd_Handler *fd_handler EINA_UNUSED)
+{
+ Ecore_Exe *exe;
+
+ exe = data;
+ if ((exe->write_fd_handler) &&
+ (ecore_main_fd_handler_active_get
+ (exe->write_fd_handler, ECORE_FD_WRITE)))
+ _ecore_exe_flush(exe);
+
+ /* If we have sent all there is to send, and we need to close the pipe, then close it. */
+ if ((exe->close_stdin == 1)
+ && (exe->write_data_size == exe->write_data_offset))
+ {
+ int ok = 0;
+ int result;
+
+ INF("Closing stdin for %s", exe->cmd);
+ /* if (exe->child_fd_write != -1) E_NO_ERRNO(result, fsync(exe->child_fd_write), ok); This a) doesn't work, and b) isn't needed. */
+ IF_FN_DEL(ecore_main_fd_handler_del, exe->write_fd_handler);
+ if (exe->child_fd_write != -1)
+ E_NO_ERRNO(result, close(exe->child_fd_write), ok);
+ exe->child_fd_write = -1;
+ IF_FREE(exe->write_data_buf);
+ }
+
+ return ECORE_CALLBACK_RENEW;
+}
+
+static void
+_ecore_exe_flush(Ecore_Exe *exe)
+{
+ int count;
+
+ /* check whether we need to write anything at all. */
+ if ((exe->child_fd_write == -1) || (!exe->write_data_buf))
+ return;
+ if (exe->write_data_size == exe->write_data_offset)
+ return;
+
+ count = write(exe->child_fd_write,
+ (char *)exe->write_data_buf + exe->write_data_offset,
+ exe->write_data_size - exe->write_data_offset);
+ if (count < 1)
+ {
+ if (errno == EIO || errno == EBADF || errno == EPIPE || errno == EINVAL || errno == ENOSPC) /* we lost our exe! */
+ {
+ ecore_exe_terminate(exe);
+ if (exe->write_fd_handler)
+ ecore_main_fd_handler_active_set(exe->write_fd_handler, 0);
+ }
+ }
+ else
+ {
+ exe->write_data_offset += count;
+ if (exe->write_data_offset >= exe->write_data_size) /* Nothing left to write, clean up. */
+ {
+ exe->write_data_size = 0;
+ exe->write_data_offset = 0;
+ IF_FREE(exe->write_data_buf);
+ if (exe->write_fd_handler)
+ ecore_main_fd_handler_active_set(exe->write_fd_handler, 0);
+ }
+ }
+}
+
+static void
+_ecore_exe_event_exe_data_free(void *data EINA_UNUSED,
+ void *ev)
+{
+ Ecore_Exe_Event_Data *e;
+
+ e = ev;
+ ecore_exe_event_data_free(e);
+}
+
+static Ecore_Exe_Event_Add *
+_ecore_exe_event_add_new(void)
+{
+ Ecore_Exe_Event_Add *e;
+
+ e = calloc(1, sizeof(Ecore_Exe_Event_Add));
+ return e;
+}
+
+static void
+_ecore_exe_event_add_free(void *data EINA_UNUSED,
+ void *ev)
+{
+ Ecore_Exe_Event_Add *e;
+
+ e = ev;
+ free(e);
+}
+
+void *
+_ecore_exe_event_del_new(void)
+{
+ Ecore_Exe_Event_Del *e;
+
+ e = calloc(1, sizeof(Ecore_Exe_Event_Del));
+ return e;
+}
+
+void
+_ecore_exe_event_del_free(void *data EINA_UNUSED,
+ void *ev)
+{
+ Ecore_Exe_Event_Del *e;
+
+ e = ev;
+ if (e->exe)
+ ecore_exe_free(e->exe);
+ free(e);
+}
+
+static void
+_ecore_exe_dead_attach(Ecore_Exe *exe)
+{
+ struct _ecore_exe_dead_exe *dead;
+
+ if (exe->doomsday_clock_dead) return;
+ dead = calloc(1, sizeof(struct _ecore_exe_dead_exe));
+ if (dead)
+ {
+ dead->pid = exe->pid;
+ dead->cmd = strdup(exe->cmd);
+ IF_FN_DEL(ecore_timer_del, exe->doomsday_clock);
+ exe->doomsday_clock =
+ ecore_timer_add(10.0, _ecore_exe_make_sure_its_dead, dead);
+ exe->doomsday_clock_dead = dead;
+ }
+}
+
diff --git a/src/lib/ecore/ecore_exe_ps3.c b/src/lib/ecore/ecore_exe_ps3.c
new file mode 100644
index 0000000000..1ef1e81fcb
--- /dev/null
+++ b/src/lib/ecore/ecore_exe_ps3.c
@@ -0,0 +1,20 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef HAVE_ESCAPE
+# include <Escape.h>
+#endif
+
+#include "Ecore.h"
+#include "ecore_private.h"
+
+void
+_ecore_exe_init(void)
+{
+}
+
+void
+_ecore_exe_shutdown(void)
+{
+}
diff --git a/src/lib/ecore/ecore_exe_win32.c b/src/lib/ecore/ecore_exe_win32.c
new file mode 100644
index 0000000000..71557c3676
--- /dev/null
+++ b/src/lib/ecore/ecore_exe_win32.c
@@ -0,0 +1,1055 @@
+/*
+ * TODO:
+ * - manage I/O pipes (several ones, and stdin)
+ * - manage SetConsoleCtrlHandler ?
+ * - the child process seems to still run after the DEL event
+ * - add log messages
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef HAVE_EVIL
+# include <Evil.h>
+#endif
+
+#include "Ecore.h"
+#include "ecore_private.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#undef WIN32_LEAN_AND_MEAN
+#include <process.h>
+
+#define ECORE_EXE_WIN32_TIMEOUT 3000
+
+typedef enum
+{
+ ECORE_EXE_WIN32_SIGINT,
+ ECORE_EXE_WIN32_SIGQUIT,
+ ECORE_EXE_WIN32_SIGTERM,
+ ECORE_EXE_WIN32_SIGKILL
+} Ecore_Exe_Win32_Signal;
+
+struct _Ecore_Exe
+{
+ EINA_INLIST;
+ ECORE_MAGIC;
+
+ HANDLE process2;
+ HANDLE process; /* CloseHandle */
+ HANDLE process_thread;
+ DWORD process_id;
+ DWORD thread_id;
+ void *data;
+ char *tag;
+ char *cmd;
+ Ecore_Exe_Flags flags;
+ Ecore_Exe_Win32_Signal sig;
+ Ecore_Win32_Handler *h_close;
+ struct
+ {
+ HANDLE child_pipe;
+ HANDLE child_pipe_x;
+ Ecore_Pipe *p;
+ HANDLE thread;
+ void *data_buf;
+ int data_size;
+ } pipe_read;
+ struct
+ {
+ HANDLE child_pipe;
+ HANDLE child_pipe_x;
+ HANDLE thread;
+ Ecore_Win32_Handler *h;
+ void *data_buf;
+ int data_size;
+ } pipe_write;
+ struct
+ {
+ HANDLE child_pipe;
+ HANDLE child_pipe_x;
+ Ecore_Pipe *p;
+ HANDLE thread;
+ void *data_buf;
+ int data_size;
+ } pipe_error;
+ Eina_Bool close_stdin : 1;
+ Eina_Bool is_suspended : 1;
+
+ Ecore_Exe_Cb pre_free_cb;
+};
+
+static Ecore_Exe *exes = NULL;
+
+static int _ecore_exe_win32_pipes_set(Ecore_Exe *exe);
+static void _ecore_exe_win32_pipes_close(Ecore_Exe *exe);
+
+static BOOL CALLBACK _ecore_exe_enum_windows_procedure(HWND window,
+ LPARAM data);
+static void _ecore_exe_event_add_free(void *data,
+ void *ev);
+static void _ecore_exe_event_del_free(void *data,
+ void *ev);
+static void _ecore_exe_event_exe_data_free(void *data,
+ void *ev);
+static int _ecore_exe_win32_pipe_thread_generic_cb(void *data,
+ Ecore_Exe_Flags flags);
+static DWORD WINAPI _ecore_exe_win32_pipe_thread_read_cb(void *data);
+static DWORD WINAPI _ecore_exe_win32_pipe_thread_error_cb(void *data);
+static Eina_Bool _ecore_exe_close_cb(void *data,
+ Ecore_Win32_Handler *wh);
+static void _ecore_exe_pipe_read_cb(void *data,
+ void *buf,
+ unsigned int size);
+static int _ecore_exe_pipe_write_cb(void *data,
+ Ecore_Win32_Handler *wh);
+static void _ecore_exe_pipe_error_cb(void *data,
+ void *buf,
+ unsigned int size);
+
+EAPI int ECORE_EXE_EVENT_ADD = 0;
+EAPI int ECORE_EXE_EVENT_DEL = 0;
+EAPI int ECORE_EXE_EVENT_DATA = 0;
+EAPI int ECORE_EXE_EVENT_ERROR = 0;
+
+void
+_ecore_exe_init(void)
+{
+ ECORE_EXE_EVENT_ADD = ecore_event_type_new();
+ ECORE_EXE_EVENT_DEL = ecore_event_type_new();
+ ECORE_EXE_EVENT_DATA = ecore_event_type_new();
+ ECORE_EXE_EVENT_ERROR = ecore_event_type_new();
+}
+
+void
+_ecore_exe_shutdown(void)
+{
+ while (exes)
+ ecore_exe_free(exes);
+}
+
+static int run_pri = NORMAL_PRIORITY_CLASS;
+
+EAPI void
+ecore_exe_run_priority_set(int pri)
+{
+ switch (pri)
+ {
+ case ECORE_EXE_WIN32_PRIORITY_IDLE:
+ run_pri = IDLE_PRIORITY_CLASS;
+ break;
+
+ case ECORE_EXE_WIN32_PRIORITY_BELOW_NORMAL:
+ run_pri = BELOW_NORMAL_PRIORITY_CLASS;
+ break;
+
+ case ECORE_EXE_WIN32_PRIORITY_NORMAL:
+ run_pri = NORMAL_PRIORITY_CLASS;
+ break;
+
+ case ECORE_EXE_WIN32_PRIORITY_ABOVE_NORMAL:
+ run_pri = ABOVE_NORMAL_PRIORITY_CLASS;
+ break;
+
+ case ECORE_EXE_WIN32_PRIORITY_HIGH:
+ run_pri = HIGH_PRIORITY_CLASS;
+ break;
+
+ case ECORE_EXE_WIN32_PRIORITY_REALTIME:
+ run_pri = REALTIME_PRIORITY_CLASS;
+ break;
+
+ default:
+ break;
+ }
+}
+
+EAPI int
+ecore_exe_run_priority_get(void)
+{
+ switch (run_pri)
+ {
+ case IDLE_PRIORITY_CLASS:
+ return ECORE_EXE_WIN32_PRIORITY_IDLE;
+
+ case BELOW_NORMAL_PRIORITY_CLASS:
+ return ECORE_EXE_WIN32_PRIORITY_BELOW_NORMAL;
+
+ case NORMAL_PRIORITY_CLASS:
+ return ECORE_EXE_WIN32_PRIORITY_NORMAL;
+
+ case ABOVE_NORMAL_PRIORITY_CLASS:
+ return ECORE_EXE_WIN32_PRIORITY_ABOVE_NORMAL;
+
+ case HIGH_PRIORITY_CLASS:
+ return ECORE_EXE_WIN32_PRIORITY_HIGH;
+
+ case REALTIME_PRIORITY_CLASS:
+ return ECORE_EXE_WIN32_PRIORITY_REALTIME;
+
+ /* default should not be reached */
+ default:
+ return ECORE_EXE_WIN32_PRIORITY_NORMAL;
+ }
+}
+
+EAPI Ecore_Exe *
+ecore_exe_run(const char *exe_cmd,
+ const void *data)
+{
+ return ecore_exe_pipe_run(exe_cmd, 0, data);
+}
+
+EAPI Ecore_Exe *
+ecore_exe_pipe_run(const char *exe_cmd,
+ Ecore_Exe_Flags flags,
+ const void *data)
+{
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+ Ecore_Exe_Event_Add *e;
+ Ecore_Exe *exe;
+ char *ret = NULL;
+
+ exe = calloc(1, sizeof(Ecore_Exe));
+ if (!exe)
+ return NULL;
+
+ if ((flags & ECORE_EXE_PIPE_AUTO) && (!(flags & ECORE_EXE_PIPE_ERROR))
+ && (!(flags & ECORE_EXE_PIPE_READ)))
+ /* We need something to auto pipe. */
+ flags |= ECORE_EXE_PIPE_READ | ECORE_EXE_PIPE_ERROR;
+
+ exe->flags = flags;
+ if (exe->flags & ECORE_EXE_PIPE_READ)
+ if (!_ecore_exe_win32_pipes_set(exe))
+ goto free_exe;
+
+ if (exe->flags & ECORE_EXE_PIPE_WRITE)
+ if (!_ecore_exe_win32_pipes_set(exe))
+ goto close_pipes;
+
+ if (exe->flags & ECORE_EXE_PIPE_ERROR)
+ if (!_ecore_exe_win32_pipes_set(exe))
+ goto close_pipes;
+
+ if ((exe->flags & ECORE_EXE_USE_SH) ||
+ ((ret = strrstr(exe_cmd, ".bat")) && (ret[4] == '\0')))
+ {
+ char buf[PATH_MAX];
+ snprintf(buf, PATH_MAX, "cmd.exe /c %s", exe_cmd);
+ exe->cmd = strdup(buf);
+ }
+ else
+ exe->cmd = strdup(exe_cmd);
+
+ if (!exe->cmd)
+ goto close_pipes;
+
+ ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
+
+ ZeroMemory(&si, sizeof(STARTUPINFO));
+ si.cb = sizeof(STARTUPINFO);
+ si.hStdOutput = exe->pipe_read.child_pipe_x;
+ si.hStdInput = exe->pipe_write.child_pipe;
+ si.hStdError = exe->pipe_error.child_pipe_x;
+ si.dwFlags |= STARTF_USESTDHANDLES;
+
+ /* FIXME: gerer la priorite */
+
+ if (!CreateProcess(NULL, exe->cmd, NULL, NULL, EINA_TRUE,
+ run_pri | CREATE_SUSPENDED, NULL, NULL, &si, &pi))
+ goto free_exe_cmd;
+
+ /* be sure that the child process is running */
+ /* FIXME: This does not work if the child is an EFL-based app */
+ /* if (WaitForInputIdle(pi.hProcess, INFINITE) == WAIT_FAILED) */
+ /* goto free_exe_cmd; */
+
+ ECORE_MAGIC_SET(exe, ECORE_MAGIC_EXE);
+ exe->process = pi.hProcess;
+ exe->process_thread = pi.hThread;
+ exe->process_id = pi.dwProcessId;
+ exe->thread_id = pi.dwThreadId;
+ exe->data = (void *)data;
+
+ if (!(exe->process2 = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_SUSPEND_RESUME | PROCESS_TERMINATE | SYNCHRONIZE,
+ EINA_FALSE, pi.dwProcessId)))
+ goto close_thread;
+
+ exe->h_close = ecore_main_win32_handler_add(exe->process2, _ecore_exe_close_cb, exe);
+ if (!exe->h_close) goto close_process2;
+
+ if (ResumeThread(exe->process_thread) == ((DWORD)-1))
+ goto close_process2;
+
+ exes = (Ecore_Exe *)eina_inlist_append(EINA_INLIST_GET(exes), EINA_INLIST_GET(exe));
+
+ e = (Ecore_Exe_Event_Add *)calloc(1, sizeof(Ecore_Exe_Event_Add));
+ if (!e) goto delete_h_close;
+
+ e->exe = exe;
+ ecore_event_add(ECORE_EXE_EVENT_ADD, e,
+ _ecore_exe_event_add_free, NULL);
+
+ return exe;
+
+delete_h_close:
+ ecore_main_win32_handler_del(exe->h_close);
+close_process2:
+ CloseHandle(exe->process2);
+close_thread:
+ CloseHandle(exe->process_thread);
+ CloseHandle(exe->process);
+free_exe_cmd:
+ free(exe->cmd);
+close_pipes:
+ _ecore_exe_win32_pipes_close(exe);
+free_exe:
+ free(exe);
+ return NULL;
+}
+
+EAPI void
+ecore_exe_callback_pre_free_set(Ecore_Exe *exe,
+ Ecore_Exe_Cb func)
+{
+ if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+ {
+ ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE,
+ "ecore_exe_callback_pre_free_set");
+ return;
+ }
+ exe->pre_free_cb = func;
+}
+
+EAPI Eina_Bool
+ecore_exe_send(Ecore_Exe *exe,
+ const void *data,
+ int size)
+{
+ void *buf;
+
+ if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+ {
+ ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_send");
+ return 0;
+ }
+
+ if (exe->close_stdin)
+ {
+ ERR("Ecore_Exe %p stdin is closed! Cannot send %d bytes from %p",
+ exe, size, data);
+ return 0;
+ }
+
+ if (!exe->pipe_write.child_pipe)
+ {
+ ERR("Ecore_Exe %p created without ECORE_EXE_PIPE_WRITE! "
+ "Cannot send %d bytes from %p", exe, size, data);
+ return 0;
+ }
+
+ buf = realloc(exe->pipe_write.data_buf, exe->pipe_write.data_size + size);
+ if (!buf) return 0;
+
+ exe->pipe_write.data_buf = buf;
+ memcpy((char *)exe->pipe_write.data_buf + exe->pipe_write.data_size, data, size);
+ exe->pipe_write.data_size += size;
+
+ /* if (exe->pipe_write.) */
+ /* ecore_main_fd_handler_active_set(exe->pipe_write.h, ECORE_FD_WRITE); */
+
+ return 1;
+}
+
+EAPI void
+ecore_exe_close_stdin(Ecore_Exe *exe)
+{
+ if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+ {
+ ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_close_stdin");
+ return;
+ }
+ exe->close_stdin = 1;
+}
+
+/* Not used on Windows */
+EAPI void
+ecore_exe_auto_limits_set(Ecore_Exe *exe EINA_UNUSED,
+ int start_bytes EINA_UNUSED,
+ int end_bytes EINA_UNUSED,
+ int start_lines EINA_UNUSED,
+ int end_lines EINA_UNUSED)
+{
+}
+
+EAPI Ecore_Exe_Event_Data *
+ecore_exe_event_data_get(Ecore_Exe *exe,
+ Ecore_Exe_Flags flags)
+{
+ Ecore_Exe_Event_Data *e = NULL;
+ unsigned char *inbuf;
+ int inbuf_num;
+
+ if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+ {
+ ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_event_data_get");
+ return NULL;
+ }
+
+ /* Sort out what sort of event we are, */
+ /* And get the data. */
+ if (flags & ECORE_EXE_PIPE_READ)
+ {
+ inbuf = exe->pipe_read.data_buf;
+ inbuf_num = exe->pipe_read.data_size;
+ exe->pipe_read.data_buf = NULL;
+ exe->pipe_read.data_size = 0;
+ }
+ else
+ {
+ inbuf = exe->pipe_error.data_buf;
+ inbuf_num = exe->pipe_error.data_size;
+ exe->pipe_error.data_buf = NULL;
+ exe->pipe_error.data_size = 0;
+ }
+
+ e = calloc(1, sizeof(Ecore_Exe_Event_Data));
+ if (e)
+ {
+ e->exe = exe;
+ e->data = inbuf;
+ e->size = inbuf_num;
+ }
+
+ return e;
+}
+
+EAPI void
+ecore_exe_event_data_free(Ecore_Exe_Event_Data *e)
+{
+ if (!e) return;
+ IF_FREE(e->lines);
+ IF_FREE(e->data);
+ free(e);
+}
+
+EAPI void *
+ecore_exe_free(Ecore_Exe *exe)
+{
+ void *data;
+
+ if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+ {
+ ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_free");
+ return NULL;
+ }
+
+ data = exe->data;
+
+ if (exe->pre_free_cb)
+ exe->pre_free_cb(data, exe);
+
+ CloseHandle(exe->process2);
+ CloseHandle(exe->process_thread);
+ CloseHandle(exe->process);
+ free(exe->cmd);
+ _ecore_exe_win32_pipes_close(exe);
+ exes = (Ecore_Exe *)eina_inlist_remove(EINA_INLIST_GET(exes), EINA_INLIST_GET(exe));
+ ECORE_MAGIC_SET(exe, ECORE_MAGIC_NONE);
+ if (exe->tag) free(exe->tag);
+ free(exe);
+
+ return data;
+}
+
+EAPI pid_t
+ecore_exe_pid_get(const Ecore_Exe *exe)
+{
+ if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+ {
+ ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_pid_get");
+ return -1;
+ }
+ return exe->process_id;
+}
+
+EAPI void
+ecore_exe_tag_set(Ecore_Exe *exe,
+ const char *tag)
+{
+ if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+ {
+ ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_tag_set");
+ return;
+ }
+ IF_FREE(exe->tag);
+ if (tag)
+ exe->tag = strdup(tag);
+}
+
+EAPI const char *
+ecore_exe_tag_get(const Ecore_Exe *exe)
+{
+ if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+ {
+ ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_tag_get");
+ return NULL;
+ }
+ return exe->tag;
+}
+
+EAPI const char *
+ecore_exe_cmd_get(const Ecore_Exe *exe)
+{
+ if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+ {
+ ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_cmd_get");
+ return NULL;
+ }
+ return exe->cmd;
+}
+
+EAPI void *
+ecore_exe_data_get(const Ecore_Exe *exe)
+{
+ if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+ {
+ ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_data_get");
+ return NULL;
+ }
+ return exe->data;
+}
+
+EAPI Ecore_Exe_Flags
+ecore_exe_flags_get(const Ecore_Exe *exe)
+{
+ if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+ {
+ ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_data_get");
+ return 0;
+ }
+ return exe->flags;
+}
+
+EAPI void
+ecore_exe_pause(Ecore_Exe *exe)
+{
+ if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+ {
+ ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_pause");
+ return;
+ }
+
+ if (exe->is_suspended)
+ return;
+
+ if (SuspendThread(exe->process_thread) != (DWORD)-1)
+ exe->is_suspended = 1;
+}
+
+EAPI void
+ecore_exe_continue(Ecore_Exe *exe)
+{
+ if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+ {
+ ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_continue");
+ return;
+ }
+
+ if (!exe->is_suspended)
+ return;
+
+ if (ResumeThread(exe->process_thread) != (DWORD)-1)
+ exe->is_suspended = 0;
+}
+
+EAPI void
+ecore_exe_interrupt(Ecore_Exe *exe)
+{
+ if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+ {
+ ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_interrupt");
+ return;
+ }
+
+ CloseHandle(exe->process_thread);
+ CloseHandle(exe->process);
+ exe->sig = ECORE_EXE_WIN32_SIGINT;
+ while (EnumWindows(_ecore_exe_enum_windows_procedure, (LPARAM)exe)) ;
+}
+
+EAPI void
+ecore_exe_quit(Ecore_Exe *exe)
+{
+ if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+ {
+ ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_quit");
+ return;
+ }
+
+ CloseHandle(exe->process_thread);
+ CloseHandle(exe->process);
+ exe->sig = ECORE_EXE_WIN32_SIGQUIT;
+ while (EnumWindows(_ecore_exe_enum_windows_procedure, (LPARAM)exe)) ;
+}
+
+EAPI void
+ecore_exe_terminate(Ecore_Exe *exe)
+{
+ if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+ {
+ ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_terminate");
+ return;
+ }
+
+/* CloseHandle(exe->thread); */
+ CloseHandle(exe->process);
+ exe->sig = ECORE_EXE_WIN32_SIGTERM;
+ while (EnumWindows(_ecore_exe_enum_windows_procedure, (LPARAM)exe)) ;
+}
+
+EAPI void
+ecore_exe_kill(Ecore_Exe *exe)
+{
+ if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+ {
+ ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_kill");
+ return;
+ }
+
+ CloseHandle(exe->process_thread);
+ CloseHandle(exe->process);
+ exe->sig = ECORE_EXE_WIN32_SIGKILL;
+ while (EnumWindows(_ecore_exe_enum_windows_procedure, (LPARAM)exe)) ;
+}
+
+EAPI void
+ecore_exe_signal(Ecore_Exe *exe,
+ int num EINA_UNUSED)
+{
+ if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+ {
+ ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_signal");
+ return;
+ }
+
+ /* does nothing */
+}
+
+EAPI void
+ecore_exe_hup(Ecore_Exe *exe)
+{
+ if (!ECORE_MAGIC_CHECK(exe, ECORE_MAGIC_EXE))
+ {
+ ECORE_MAGIC_FAIL(exe, ECORE_MAGIC_EXE, "ecore_exe_hup");
+ return;
+ }
+
+ /* does nothing */
+}
+
+/* FIXME: manage error mode */
+static int
+_ecore_exe_win32_pipe_thread_generic_cb(void *data,
+ Ecore_Exe_Flags flags)
+{
+#define BUFSIZE 2048
+ char buf[BUFSIZE];
+ Ecore_Exe *exe;
+ char *current_buf = NULL;
+ HANDLE child_pipe;
+ Ecore_Pipe *ecore_pipe;
+ Ecore_Exe_Event_Data *event;
+ DWORD size;
+ DWORD current_size = 0;
+ BOOL res;
+
+ exe = (Ecore_Exe *)data;
+
+ /* Sort out what sort of handler we are. */
+ /* And get any left over data from last time. */
+ if ((exe->flags & ECORE_EXE_PIPE_READ) && (flags == ECORE_EXE_PIPE_READ))
+ {
+ child_pipe = exe->pipe_read.child_pipe;
+ ecore_pipe = exe->pipe_read.p;
+ flags = ECORE_EXE_PIPE_READ;
+ }
+ else if ((exe->flags & ECORE_EXE_PIPE_ERROR) && (flags == ECORE_EXE_PIPE_ERROR))
+ {
+ child_pipe = exe->pipe_error.child_pipe;
+ ecore_pipe = exe->pipe_error.p;
+ flags = ECORE_EXE_PIPE_ERROR;
+ }
+ else
+ return 0;
+
+ while (1)
+ {
+ if (!PeekNamedPipe(child_pipe, buf, sizeof(buf), &size, &current_size, NULL))
+ continue;
+ if (size == 0)
+ continue;
+ current_buf = (char *)malloc(current_size);
+ if (!current_buf)
+ continue;
+ res = ReadFile(child_pipe, current_buf, current_size, &size, NULL);
+ if (!res || (size == 0))
+ {
+ free(current_buf);
+ current_buf = NULL;
+ continue;
+ }
+ if (current_size != size)
+ {
+ free(current_buf);
+ current_buf = NULL;
+ continue;
+ }
+ current_size = size;
+
+ if (flags == ECORE_EXE_PIPE_READ)
+ {
+ exe->pipe_read.data_buf = current_buf;
+ exe->pipe_read.data_size = current_size;
+ }
+ else
+ {
+ exe->pipe_error.data_buf = current_buf;
+ exe->pipe_error.data_size = current_size;
+ }
+
+ event = ecore_exe_event_data_get(exe, flags);
+ if (event)
+ ecore_pipe_write(ecore_pipe, &event, sizeof(event));
+
+ current_buf = NULL;
+ current_size = 0;
+ }
+
+ return 1;
+}
+
+static DWORD WINAPI
+_ecore_exe_win32_pipe_thread_read_cb(void *data)
+{
+ return _ecore_exe_win32_pipe_thread_generic_cb(data, ECORE_EXE_PIPE_READ);
+}
+
+static DWORD WINAPI
+_ecore_exe_win32_pipe_thread_error_cb(void *data)
+{
+ return _ecore_exe_win32_pipe_thread_generic_cb(data, ECORE_EXE_PIPE_ERROR);
+}
+
+static int
+_ecore_exe_win32_pipes_set(Ecore_Exe *exe)
+{
+ SECURITY_ATTRIBUTES sa;
+ HANDLE child_pipe;
+ HANDLE child_pipe_x;
+
+ sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+ sa.bInheritHandle = EINA_TRUE;
+ sa.lpSecurityDescriptor = NULL;
+
+ if (!CreatePipe(&child_pipe, &child_pipe_x, &sa, 0))
+ return 0;
+ if (exe->flags & ECORE_EXE_PIPE_WRITE)
+ {
+ if (!SetHandleInformation(child_pipe_x, HANDLE_FLAG_INHERIT, 0))
+ goto close_pipe;
+ }
+ else
+ {
+ if (!SetHandleInformation(child_pipe, HANDLE_FLAG_INHERIT, 0))
+ goto close_pipe;
+ }
+
+ if (exe->flags & ECORE_EXE_PIPE_READ)
+ {
+ exe->pipe_read.child_pipe = child_pipe;
+ exe->pipe_read.child_pipe_x = child_pipe_x;
+ exe->pipe_read.p = ecore_pipe_add(_ecore_exe_pipe_read_cb, exe);
+ exe->pipe_read.thread = CreateThread(NULL, 0,
+ _ecore_exe_win32_pipe_thread_read_cb,
+ exe, 0, NULL);
+ }
+ else if (exe->flags & ECORE_EXE_PIPE_WRITE)
+ {
+ exe->pipe_write.child_pipe = child_pipe;
+ exe->pipe_write.child_pipe_x = child_pipe_x;
+/* exe->pipe_write.thread = CreateThread(NULL, 0, */
+/* _ecore_exe_win32_pipe_thread_cb, */
+/* exe, 0, NULL); */
+ }
+ else
+ {
+ exe->pipe_error.child_pipe = child_pipe;
+ exe->pipe_error.child_pipe_x = child_pipe_x;
+ exe->pipe_error.p = ecore_pipe_add(_ecore_exe_pipe_error_cb, exe);
+ exe->pipe_error.thread = CreateThread(NULL, 0,
+ _ecore_exe_win32_pipe_thread_error_cb,
+ exe, 0, NULL);
+ }
+
+ return 1;
+
+close_pipe:
+ CloseHandle(child_pipe);
+ CloseHandle(child_pipe_x);
+
+ return 0;
+}
+
+static void
+_ecore_exe_win32_pipes_close(Ecore_Exe *exe)
+{
+ if (exe->flags & ECORE_EXE_PIPE_READ)
+ {
+ if (exe->pipe_read.child_pipe)
+ {
+ CloseHandle(exe->pipe_read.child_pipe);
+ exe->pipe_read.child_pipe = NULL;
+ }
+ if (exe->pipe_read.child_pipe_x)
+ {
+ CloseHandle(exe->pipe_read.child_pipe_x);
+ exe->pipe_read.child_pipe_x = NULL;
+ }
+ }
+
+ if (exe->flags & ECORE_EXE_PIPE_WRITE)
+ {
+ if (exe->pipe_write.child_pipe)
+ {
+ CloseHandle(exe->pipe_write.child_pipe);
+ exe->pipe_write.child_pipe = NULL;
+ }
+ if (exe->pipe_write.child_pipe_x)
+ {
+ CloseHandle(exe->pipe_write.child_pipe_x);
+ exe->pipe_write.child_pipe_x = NULL;
+ }
+ }
+
+ if (exe->flags & ECORE_EXE_PIPE_ERROR)
+ {
+ if (exe->pipe_error.child_pipe)
+ {
+ CloseHandle(exe->pipe_error.child_pipe);
+ exe->pipe_error.child_pipe = NULL;
+ }
+ if (exe->pipe_error.child_pipe_x)
+ {
+ CloseHandle(exe->pipe_error.child_pipe_x);
+ exe->pipe_error.child_pipe_x = NULL;
+ }
+ }
+}
+
+static DWORD WINAPI
+_ecore_exe_thread_procedure(LPVOID data EINA_UNUSED)
+{
+ GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
+ GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, 0);
+ return 1;
+}
+
+static BOOL CALLBACK
+_ecore_exe_enum_windows_procedure(HWND window,
+ LPARAM data)
+{
+ Ecore_Exe *exe;
+ DWORD thread_id;
+
+ exe = (Ecore_Exe *)data;
+ thread_id = GetWindowThreadProcessId(window, NULL);
+
+ if (thread_id == exe->thread_id)
+ {
+ /* Ctrl-C or Ctrl-Break */
+ if (CreateRemoteThread(exe->process, NULL, 0,
+ (LPTHREAD_START_ROUTINE)_ecore_exe_thread_procedure, NULL,
+ 0, NULL))
+ {
+ printf ("remote thread\n");
+ return EINA_FALSE;
+ }
+
+ if ((exe->sig == ECORE_EXE_WIN32_SIGINT) ||
+ (exe->sig == ECORE_EXE_WIN32_SIGQUIT))
+ {
+ printf ("int or quit\n");
+ return EINA_FALSE;
+ }
+
+ /* WM_CLOSE message */
+ PostMessage(window, WM_CLOSE, 0, 0);
+ if (WaitForSingleObject(exe->process, ECORE_EXE_WIN32_TIMEOUT) == WAIT_OBJECT_0)
+ {
+ printf ("CLOSE\n");
+ return EINA_FALSE;
+ }
+
+ /* WM_QUIT message */
+ PostMessage(window, WM_QUIT, 0, 0);
+ if (WaitForSingleObject(exe->process, ECORE_EXE_WIN32_TIMEOUT) == WAIT_OBJECT_0)
+ {
+ printf ("QUIT\n");
+ return EINA_FALSE;
+ }
+
+ /* Exit process */
+ if (CreateRemoteThread(exe->process, NULL, 0,
+ (LPTHREAD_START_ROUTINE)ExitProcess, NULL,
+ 0, NULL))
+ {
+ printf ("remote thread 2\n");
+ return EINA_FALSE;
+ }
+
+ if (exe->sig == ECORE_EXE_WIN32_SIGTERM)
+ {
+ printf ("term\n");
+ return EINA_FALSE;
+ }
+
+ TerminateProcess(exe->process, 0);
+
+ return EINA_FALSE;
+ }
+
+ return EINA_TRUE;
+}
+
+static void
+_ecore_exe_event_add_free(void *data EINA_UNUSED,
+ void *ev)
+{
+ Ecore_Exe_Event_Add *e;
+
+ e = (Ecore_Exe_Event_Add *)ev;
+ free(e);
+}
+
+static void
+_ecore_exe_event_del_free(void *data EINA_UNUSED,
+ void *ev)
+{
+ Ecore_Exe_Event_Del *e;
+
+ e = (Ecore_Exe_Event_Del *)ev;
+ if (e->exe)
+ ecore_exe_free(e->exe);
+ free(e);
+}
+
+static void
+_ecore_exe_event_exe_data_free(void *data EINA_UNUSED,
+ void *ev)
+{
+ Ecore_Exe_Event_Data *e;
+
+ e = (Ecore_Exe_Event_Data *)ev;
+ ecore_exe_event_data_free(e);
+}
+
+static Eina_Bool
+_ecore_exe_close_cb(void *data,
+ Ecore_Win32_Handler *wh EINA_UNUSED)
+{
+ Ecore_Exe_Event_Del *e;
+ Ecore_Exe *exe;
+ DWORD exit_code = 0;
+
+ e = calloc(1, sizeof(Ecore_Exe_Event_Del));
+ if (!e) return 0;
+
+ exe = (Ecore_Exe *)data;
+
+ if (GetExitCodeProcess(exe->process2, &exit_code))
+ {
+ e->exit_code = exit_code;
+ e->exited = 1;
+ }
+ else
+ {
+ char *msg;
+
+ msg = evil_last_error_get();
+ printf("%s\n", msg);
+ free(msg);
+ }
+ e->pid = exe->process_id;
+ e->exe = exe;
+
+ ecore_event_add(ECORE_EXE_EVENT_DEL, e,
+ _ecore_exe_event_del_free, NULL);
+
+ return 0;
+}
+
+static void
+_ecore_exe_pipe_read_cb(void *data,
+ void *buf,
+ unsigned int size)
+{
+ Ecore_Exe_Event_Data *e;
+
+ e = *((Ecore_Exe_Event_Data **)buf);
+ if (e)
+ ecore_event_add(ECORE_EXE_EVENT_DATA, e,
+ _ecore_exe_event_exe_data_free,
+ NULL);
+}
+
+static int
+_ecore_exe_pipe_write_cb(void *data,
+ Ecore_Win32_Handler *wh EINA_UNUSED)
+{
+ char buf[READBUFSIZ];
+ Ecore_Exe *exe;
+ DWORD num_exe;
+ BOOL res;
+
+ exe = (Ecore_Exe *)data;
+
+ res = WriteFile(exe->pipe_write.child_pipe_x, buf, READBUFSIZ, &num_exe, NULL);
+ if (!res || num_exe == 0)
+ {
+ /* FIXME: what to do here ?? */
+ }
+
+ if (exe->close_stdin == 1)
+ {
+ if (exe->pipe_write.h)
+ {
+ ecore_main_win32_handler_del(exe->pipe_write.h);
+ exe->pipe_write.h = NULL;
+ }
+ exe->pipe_write.h = NULL;
+ CloseHandle(exe->pipe_write.child_pipe);
+ exe->pipe_write.child_pipe = NULL;
+ }
+
+ return 1;
+}
+
+static void
+_ecore_exe_pipe_error_cb(void *data,
+ void *buf,
+ unsigned int size)
+{
+ Ecore_Exe_Event_Data *e;
+
+ e = *((Ecore_Exe_Event_Data **)buf);
+ if (e)
+ ecore_event_add(ECORE_EXE_EVENT_ERROR, e,
+ _ecore_exe_event_exe_data_free,
+ NULL);
+}
+
diff --git a/src/lib/ecore/ecore_exe_wince.c b/src/lib/ecore/ecore_exe_wince.c
new file mode 100644
index 0000000000..c07fcbe31b
--- /dev/null
+++ b/src/lib/ecore/ecore_exe_wince.c
@@ -0,0 +1,21 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef HAVE_EVIL
+# include <Evil.h>
+#endif
+
+#include "Ecore.h"
+#include "ecore_private.h"
+
+void
+_ecore_exe_init(void)
+{
+}
+
+void
+_ecore_exe_shutdown(void)
+{
+}
+
diff --git a/src/lib/ecore/ecore_getopt.c b/src/lib/ecore/ecore_getopt.c
new file mode 100644
index 0000000000..42e43e8915
--- /dev/null
+++ b/src/lib/ecore/ecore_getopt.c
@@ -0,0 +1,1936 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+# endif
+#endif
+#ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+#elif !defined alloca
+# ifdef __GNUC__
+# define alloca __builtin_alloca
+# elif defined _AIX
+# define alloca __alloca
+# elif defined _MSC_VER
+# include <malloc.h>
+# define alloca _alloca
+# elif !defined HAVE_ALLOCA
+# ifdef __cplusplus
+extern "C"
+# endif
+void *alloca (size_t);
+# endif
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <ctype.h>
+
+#ifdef ENABLE_NLS
+# include <libintl.h>
+#else
+# define gettext(x) (x)
+# define dgettext(domain, x) (x)
+#endif
+
+#define _(x) dgettext("ecore", x)
+
+#ifdef _WIN32_WCE
+# include <Evil.h>
+#endif
+
+#ifdef HAVE_EXOTIC
+# include <Exotic.h>
+#endif
+
+#include "Ecore.h"
+#include "Ecore_Getopt.h"
+
+static const char *prog = NULL;
+static char **_argv = NULL;
+static int _argc = 0;
+static int cols = 80;
+static int helpcol = 80 / 3;
+
+static void
+_ecore_getopt_help_print_replace_program(FILE *fp,
+ const Ecore_Getopt *parser EINA_UNUSED,
+ const char *text)
+{
+ do
+ {
+ const char *d = strchr(text, '%');
+
+ if (!d)
+ {
+ fputs(text, fp);
+ break;
+ }
+
+ if (fwrite(text, 1, d - text, fp) != (size_t)(d - text))
+ return;
+ d++;
+ if (strncmp(d, "prog", sizeof("prog") - 1) == 0)
+ {
+ fputs(prog ? prog : "???", fp);
+ d += sizeof("prog") - 1;
+ }
+ else
+ {
+ if (d[0] == '%')
+ d++;
+ fputc('%', fp);
+ }
+
+ text = d;
+ }
+ while (text[0] != '\0');
+
+ fputc('\n', fp);
+}
+
+static void
+_ecore_getopt_version(FILE *fp,
+ const Ecore_Getopt *parser)
+{
+ fputs(_("Version:"), fp);
+ fputc(' ', fp);
+ _ecore_getopt_help_print_replace_program(fp, parser, parser->version);
+}
+
+static void
+_ecore_getopt_help_usage(FILE *fp,
+ const Ecore_Getopt *parser)
+{
+ fputs(_("Usage:"), fp);
+ fputc(' ', fp);
+
+ if (!parser->usage)
+ {
+ fprintf(fp, _("%s [options]\n"), prog);
+ return;
+ }
+
+ _ecore_getopt_help_print_replace_program(fp, parser, gettext(parser->usage));
+}
+
+static int
+_ecore_getopt_help_line(FILE *fp,
+ const int base,
+ const int total,
+ int used,
+ const char *text,
+ int len)
+{
+ int linebreak = 0;
+ do
+ {
+ /* process line considering spaces (new line and tabs are spaces!) */
+ while ((used < total) && (len > 0))
+ {
+ const char *space = NULL;
+ int i, todo;
+
+ todo = total - used;
+ if (todo > len)
+ todo = len;
+
+ for (i = 0; i < todo; i++)
+ if (isspace((unsigned char)text[i]))
+ {
+ space = text + i;
+ break;
+ }
+
+ if (space)
+ {
+ i = fwrite(text, 1, i, fp);
+ i++;
+ text += i;
+ len -= i;
+ used += i;
+
+ if (linebreak)
+ {
+ linebreak = 0;
+ continue;
+ }
+
+ if (space[0] == '\n')
+ break;
+ else if (space[0] == '\t')
+ {
+ int c;
+
+ used--;
+ c = ((used / 8) + 1) * 8;
+ if (c < total)
+ {
+ for (; used < c; used++)
+ fputc(' ', fp);
+ }
+ else
+ {
+ text--;
+ len++;
+ break;
+ }
+ }
+ else if (used < total)
+ fputc(space[0], fp);
+ }
+ else
+ {
+ i = fwrite(text, 1, i, fp);
+ text += i;
+ len -= i;
+ used += i;
+ }
+ linebreak = 0;
+ }
+ if (len <= 0)
+ break;
+ linebreak = 1;
+ fputc('\n', fp);
+ for (used = 0; used < base; used++)
+ fputc(' ', fp);
+ }
+ while (1);
+
+ return used;
+}
+
+static void
+_ecore_getopt_help_description(FILE *fp,
+ const Ecore_Getopt *parser)
+{
+ const char *p, *prg, *ver;
+ int used, prglen, verlen;
+
+ p = gettext(parser->description);
+ if (!p)
+ return;
+
+ fputc('\n', fp);
+
+ prg = prog ? prog : "???";
+ ver = parser->version ? parser->version : "???";
+
+ prglen = strlen(prg);
+ verlen = strlen(ver);
+
+ used = 0;
+
+ do
+ {
+ const char *d = strchr(p, '%');
+
+ if (!d)
+ {
+ _ecore_getopt_help_line(fp, 0, cols, used, p, strlen(p));
+ break;
+ }
+
+ used = _ecore_getopt_help_line(fp, 0, cols, used, p, d - p);
+ d++;
+ if (strncmp(d, "prog", sizeof("prog") - 1) == 0)
+ {
+ used = _ecore_getopt_help_line(fp, 0, cols, used, prg, prglen);
+ d += sizeof("prog") - 1;
+ }
+ else if (strncmp(d, "version", sizeof("version") - 1) == 0)
+ {
+ used = _ecore_getopt_help_line(fp, 0, cols, used, ver, verlen);
+ d += sizeof("version") - 1;
+ }
+ else
+ {
+ if (d[0] == '%')
+ d++;
+ used = _ecore_getopt_help_line(fp, 0, cols, used, "%", 1);
+ }
+
+ p = d;
+ }
+ while (p[0] != '\0');
+
+ fputs("\n\n", fp);
+}
+
+static void
+_ecore_getopt_copyright(FILE *fp,
+ const Ecore_Getopt *parser)
+{
+ const char *txt = gettext(parser->copyright);
+ fputs(_("Copyright:"), fp);
+ fputs("\n ", fp);
+ _ecore_getopt_help_line
+ (fp, 3, cols, 3, txt, strlen(txt));
+ fputc('\n', fp);
+}
+
+static void
+_ecore_getopt_license(FILE *fp,
+ const Ecore_Getopt *parser)
+{
+ const char *txt = gettext(parser->license);
+ fputs(_("License:"), fp);
+ fputs("\n ", fp);
+ _ecore_getopt_help_line
+ (fp, 3, cols, 3, txt, strlen(txt));
+ fputc('\n', fp);
+}
+
+static Ecore_Getopt_Desc_Arg_Requirement
+_ecore_getopt_desc_arg_requirement(const Ecore_Getopt_Desc *desc)
+{
+ switch (desc->action)
+ {
+ case ECORE_GETOPT_ACTION_STORE:
+ return desc->action_param.store.arg_req;
+
+ case ECORE_GETOPT_ACTION_STORE_CONST:
+ return ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO;
+
+ case ECORE_GETOPT_ACTION_STORE_TRUE:
+ return ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO;
+
+ case ECORE_GETOPT_ACTION_STORE_FALSE:
+ return ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO;
+
+ case ECORE_GETOPT_ACTION_CHOICE:
+ return ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES;
+
+ case ECORE_GETOPT_ACTION_APPEND:
+ return ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES;
+
+ case ECORE_GETOPT_ACTION_COUNT:
+ return ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO;
+
+ case ECORE_GETOPT_ACTION_CALLBACK:
+ return desc->action_param.callback.arg_req;
+
+ case ECORE_GETOPT_ACTION_HELP:
+ return ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO;
+
+ case ECORE_GETOPT_ACTION_VERSION:
+ return ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO;
+
+ default:
+ return ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO;
+ }
+}
+
+static void
+_ecore_getopt_help_desc_setup_metavar(const Ecore_Getopt_Desc *desc,
+ char *metavar,
+ int *metavarlen,
+ int maxsize)
+{
+ if (desc->metavar)
+ {
+ const char *txt = gettext(desc->metavar);
+ *metavarlen = strlen(txt);
+ if (*metavarlen > maxsize - 1)
+ *metavarlen = maxsize - 1;
+
+ memcpy(metavar, txt, *metavarlen);
+ metavar[*metavarlen] = '\0';
+ }
+ else if (desc->longname)
+ {
+ int i;
+
+ *metavarlen = strlen(desc->longname);
+ if (*metavarlen > maxsize - 1)
+ *metavarlen = maxsize - 1;
+
+ for (i = 0; i < *metavarlen; i++)
+ metavar[i] = toupper((int) desc->longname[i]);
+ metavar[i] = '\0';
+ }
+}
+
+static int
+_ecore_getopt_help_desc_show_arg(FILE *fp,
+ Ecore_Getopt_Desc_Arg_Requirement requirement,
+ const char *metavar,
+ int metavarlen)
+{
+ int used;
+
+ if (requirement == ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO)
+ return 0;
+
+ used = 0;
+
+ if (requirement == ECORE_GETOPT_DESC_ARG_REQUIREMENT_OPTIONAL)
+ {
+ fputc('[', fp);
+ used++;
+ }
+
+ if (requirement != ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO)
+ {
+ fputc('=', fp);
+ fputs(metavar, fp);
+ used += metavarlen + 1;
+ }
+
+ if (requirement == ECORE_GETOPT_DESC_ARG_REQUIREMENT_OPTIONAL)
+ {
+ fputc(']', fp);
+ used++;
+ }
+
+ return used;
+}
+
+static int
+_ecore_getopt_help_desc_store(FILE *fp,
+ const int base,
+ const int total,
+ int used,
+ const Ecore_Getopt_Desc *desc)
+{
+ const Ecore_Getopt_Desc_Store *store = &desc->action_param.store;
+ char buf[64];
+ const char *str;
+ size_t len;
+
+ fputc('\n', fp);
+ for (used = 0; used < base; used++)
+ fputc(' ', fp);
+
+ switch (store->type)
+ {
+ case ECORE_GETOPT_TYPE_STR:
+ str = "STR";
+ len = sizeof("STR") - 1;
+ break;
+
+ case ECORE_GETOPT_TYPE_BOOL:
+ str = "BOOL";
+ len = sizeof("BOOL") - 1;
+ break;
+
+ case ECORE_GETOPT_TYPE_SHORT:
+ str = "SHORT";
+ len = sizeof("SHORT") - 1;
+ break;
+
+ case ECORE_GETOPT_TYPE_INT:
+ str = "INT";
+ len = sizeof("INT") - 1;
+ break;
+
+ case ECORE_GETOPT_TYPE_LONG:
+ str = "LONG";
+ len = sizeof("LONG") - 1;
+ break;
+
+ case ECORE_GETOPT_TYPE_USHORT:
+ str = "USHORT";
+ len = sizeof("USHORT") - 1;
+ break;
+
+ case ECORE_GETOPT_TYPE_UINT:
+ str = "UINT";
+ len = sizeof("UINT") - 1;
+ break;
+
+ case ECORE_GETOPT_TYPE_ULONG:
+ str = "ULONG";
+ len = sizeof("ULONG") - 1;
+ break;
+
+ case ECORE_GETOPT_TYPE_DOUBLE:
+ str = "DOUBLE";
+ len = sizeof("DOUBLE") - 1;
+ break;
+
+ default:
+ str = "???";
+ len = sizeof("???") - 1;
+ }
+
+ used = _ecore_getopt_help_line
+ (fp, base, total, used, _("Type: "), strlen(_("Type: ")));
+ used = _ecore_getopt_help_line(fp, base, total, used, str, len);
+
+ if (store->arg_req == ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES)
+ goto end;
+
+ used = _ecore_getopt_help_line
+ (fp, base, total, used, ". ", sizeof(". ") - 1);
+
+ switch (store->type)
+ {
+ case ECORE_GETOPT_TYPE_STR:
+ str = store->def.strv;
+ len = str ? strlen(str) : 0;
+ break;
+
+ case ECORE_GETOPT_TYPE_BOOL:
+ str = store->def.boolv ? "true" : "false";
+ len = strlen(str);
+ break;
+
+ case ECORE_GETOPT_TYPE_SHORT:
+ str = buf;
+ len = snprintf(buf, sizeof(buf), "%hd", store->def.shortv);
+ if (len > sizeof(buf) - 1)
+ len = sizeof(buf) - 1;
+ break;
+
+ case ECORE_GETOPT_TYPE_INT:
+ str = buf;
+ len = snprintf(buf, sizeof(buf), "%d", store->def.intv);
+ if (len > sizeof(buf) - 1)
+ len = sizeof(buf) - 1;
+ break;
+
+ case ECORE_GETOPT_TYPE_LONG:
+ str = buf;
+ len = snprintf(buf, sizeof(buf), "%ld", store->def.longv);
+ if (len > sizeof(buf) - 1)
+ len = sizeof(buf) - 1;
+ break;
+
+ case ECORE_GETOPT_TYPE_USHORT:
+ str = buf;
+ len = snprintf(buf, sizeof(buf), "%hu", store->def.ushortv);
+ if (len > sizeof(buf) - 1)
+ len = sizeof(buf) - 1;
+ break;
+
+ case ECORE_GETOPT_TYPE_UINT:
+ str = buf;
+ len = snprintf(buf, sizeof(buf), "%u", store->def.uintv);
+ if (len > sizeof(buf) - 1)
+ len = sizeof(buf) - 1;
+ break;
+
+ case ECORE_GETOPT_TYPE_ULONG:
+ str = buf;
+ len = snprintf(buf, sizeof(buf), "%lu", store->def.ulongv);
+ if (len > sizeof(buf) - 1)
+ len = sizeof(buf) - 1;
+ break;
+
+ case ECORE_GETOPT_TYPE_DOUBLE:
+ str = buf;
+ len = snprintf(buf, sizeof(buf), "%f", store->def.doublev);
+ if (len > sizeof(buf) - 1)
+ len = sizeof(buf) - 1;
+ break;
+
+ default:
+ str = "???";
+ len = sizeof("???") - 1;
+ }
+
+ used = _ecore_getopt_help_line
+ (fp, base, total, used, _("Default: "), strlen(_("Default: ")));
+ used = _ecore_getopt_help_line(fp, base, total, used, str, len);
+
+end:
+ return _ecore_getopt_help_line(fp, base, total, used, ".", 1);
+}
+
+static int
+_ecore_getopt_help_desc_choices(FILE *fp,
+ const int base,
+ const int total,
+ int used,
+ const Ecore_Getopt_Desc *desc)
+{
+ const char *const *itr;
+ const char sep[] = ", ";
+ const int seplen = sizeof(sep) - 1;
+
+ if (used > 0)
+ {
+ fputc('\n', fp);
+ used = 0;
+ }
+ for (; used < base; used++)
+ fputc(' ', fp);
+
+ used = _ecore_getopt_help_line
+ (fp, base, total, used, _("Choices: "), strlen(_("Choices: ")));
+
+ for (itr = desc->action_param.choices; *itr; itr++)
+ {
+ used = _ecore_getopt_help_line
+ (fp, base, total, used, *itr, strlen(*itr));
+ if (itr[1])
+ used = _ecore_getopt_help_line(fp, base, total, used, sep, seplen);
+ }
+
+ return _ecore_getopt_help_line(fp, base, total, used, ".", 1);
+}
+
+static void
+_ecore_getopt_help_desc(FILE *fp,
+ const Ecore_Getopt_Desc *desc)
+{
+ Ecore_Getopt_Desc_Arg_Requirement arg_req;
+ char metavar[32] = "ARG";
+ int metavarlen = 3;
+ int used;
+
+ arg_req = _ecore_getopt_desc_arg_requirement(desc);
+ if (arg_req != ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO)
+ _ecore_getopt_help_desc_setup_metavar
+ (desc, metavar, &metavarlen, sizeof(metavar));
+
+ fputs(" ", fp);
+ used = 2;
+
+ if (desc->shortname)
+ {
+ fputc('-', fp);
+ fputc(desc->shortname, fp);
+ used += 2;
+ used += _ecore_getopt_help_desc_show_arg
+ (fp, arg_req, metavar, metavarlen);
+ }
+
+ if (desc->shortname && desc->longname)
+ {
+ fputs(", ", fp);
+ used += 2;
+ }
+
+ if (desc->longname)
+ {
+ int namelen = strlen(desc->longname);
+
+ fputs("--", fp);
+ fputs(desc->longname, fp);
+ used += 2 + namelen;
+ used += _ecore_getopt_help_desc_show_arg
+ (fp, arg_req, metavar, metavarlen);
+ }
+
+ if (!desc->help)
+ goto end;
+
+ if (used + 3 >= helpcol)
+ {
+ fputc('\n', fp);
+ used = 0;
+ }
+
+ for (; used < helpcol; used++)
+ fputc(' ', fp);
+
+ used = _ecore_getopt_help_line
+ (fp, helpcol, cols, used, desc->help, strlen(desc->help));
+
+ switch (desc->action)
+ {
+ case ECORE_GETOPT_ACTION_STORE:
+ _ecore_getopt_help_desc_store(fp, helpcol, cols, used, desc);
+ break;
+
+ case ECORE_GETOPT_ACTION_CHOICE:
+ _ecore_getopt_help_desc_choices(fp, helpcol, cols, used, desc);
+ break;
+
+ default:
+ break;
+ }
+
+end:
+ fputc('\n', fp);
+}
+
+static Eina_Bool
+_ecore_getopt_desc_is_sentinel(const Ecore_Getopt_Desc *desc)
+{
+ return (desc->shortname == '\0') && (!desc->longname);
+}
+
+static void
+_ecore_getopt_help_options(FILE *fp,
+ const Ecore_Getopt *parser)
+{
+ const Ecore_Getopt_Desc *desc;
+
+ fputs(_("Options:\n"), fp);
+
+ for (desc = parser->descs; !_ecore_getopt_desc_is_sentinel(desc); desc++)
+ _ecore_getopt_help_desc(fp, desc);
+
+ fputc('\n', fp);
+}
+
+/**
+ * Show nicely formatted help message for the given parser.
+ *
+ * @param fp The file the message will be printed on.
+ * @param parser The parser to be used.
+ */
+EAPI void
+ecore_getopt_help(FILE *fp,
+ const Ecore_Getopt *parser)
+{
+ const char *var;
+
+ EINA_MAIN_LOOP_CHECK_RETURN;
+ if (!parser) return;
+
+ if (_argc < 1)
+ {
+ ecore_app_args_get(&_argc, &_argv);
+ if ((_argc > 0) && (_argv[0]))
+ prog = _argv[0];
+ else
+ prog = parser->prog;
+ }
+
+ var = getenv("COLUMNS");
+ if (var)
+ {
+ cols = atoi(var);
+ if (cols < 20)
+ cols = 20;
+
+ helpcol = cols / 3;
+ }
+
+ _ecore_getopt_help_usage(fp, parser);
+ _ecore_getopt_help_description(fp, parser);
+ _ecore_getopt_help_options(fp, parser);
+}
+
+static const Ecore_Getopt_Desc *
+_ecore_getopt_parse_find_long(const Ecore_Getopt *parser,
+ const char *name)
+{
+ const Ecore_Getopt_Desc *desc = parser->descs;
+ const char *p = strchr(name, '=');
+ int len = 0;
+
+ if (p)
+ len = p - name;
+
+ for (; !_ecore_getopt_desc_is_sentinel(desc); desc++)
+ {
+ if (!desc->longname)
+ continue;
+
+ if (p)
+ {
+ if ((strncmp(name, desc->longname, len) == 0) &&
+ (desc->longname[len] == '\0'))
+ return desc;
+ }
+ else
+ {
+ if (strcmp(name, desc->longname) == 0)
+ return desc;
+ }
+ }
+
+ return NULL;
+}
+
+static const Ecore_Getopt_Desc *
+_ecore_getopt_parse_find_short(const Ecore_Getopt *parser,
+ char name)
+{
+ const Ecore_Getopt_Desc *desc = parser->descs;
+ for (; !_ecore_getopt_desc_is_sentinel(desc); desc++)
+ if (name == desc->shortname)
+ return desc;
+ return NULL;
+}
+
+static int
+_ecore_getopt_parse_find_nonargs_base(const Ecore_Getopt *parser,
+ int argc,
+ char **argv)
+{
+ char **nonargs;
+ int src, dst, used, base;
+
+ nonargs = alloca(sizeof(char *) * argc);
+ src = 1;
+ dst = 1;
+ used = 0;
+ base = 0;
+ while (src < argc)
+ {
+ const Ecore_Getopt_Desc *desc;
+ Ecore_Getopt_Desc_Arg_Requirement arg_req;
+ char *arg = argv[src];
+
+ if (arg[0] != '-')
+ goto found_nonarg;
+
+ if (arg[1] == '-')
+ {
+ if (arg[2] == '\0') /* explicit end of options, "--" */
+ {
+ base = 1;
+ break;
+ }
+ desc = _ecore_getopt_parse_find_long(parser, arg + 2);
+ }
+ else
+ desc = _ecore_getopt_parse_find_short(parser, arg[1]);
+
+ if (!desc)
+ {
+ if (arg[1] == '-')
+ fprintf(stderr, _("ERROR: unknown option --%s.\n"), arg + 2);
+ else
+ fprintf(stderr, _("ERROR: unknown option -%c.\n"), arg[1]);
+ if (parser->strict)
+ {
+ memmove(argv + dst, nonargs, used * sizeof(char *));
+ return -1;
+ }
+ else
+ goto found_nonarg;
+ }
+
+ if (src != dst)
+ argv[dst] = argv[src];
+ src++;
+ dst++;
+
+ arg_req = _ecore_getopt_desc_arg_requirement(desc);
+ if (arg_req == ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO)
+ continue;
+
+ if (strchr(arg, '='))
+ continue;
+
+ if ((src >= argc) || (argv[src][0] == '-'))
+ continue;
+
+ if (src != dst)
+ argv[dst] = argv[src];
+ src++;
+ dst++;
+ continue;
+
+found_nonarg:
+ nonargs[used] = arg;
+ used++;
+ src++;
+ }
+
+ if (!base) /* '--' not found */
+ base = dst;
+ else
+ {
+ base = dst;
+ if (src != dst)
+ argv[dst] = argv[src];
+ dst++;
+ }
+
+ memmove(argv + dst, nonargs, used * sizeof(char *));
+ return base;
+}
+
+static void
+_ecore_getopt_desc_print_error(const Ecore_Getopt_Desc *desc,
+ const char *fmt,
+ ...)
+{
+ va_list ap;
+
+ fputs(_("ERROR: "), stderr);
+
+ if (desc->shortname)
+ {
+ fputc('-', stderr);
+ fputc(desc->shortname, stderr);
+ }
+
+ if (desc->shortname && desc->longname)
+ fputs(", ", stderr);
+
+ if (desc->longname)
+ {
+ fputs("--", stderr);
+ fputs(desc->longname, stderr);
+ }
+
+ fputs(": ", stderr);
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+
+static Eina_Bool
+_ecore_getopt_parse_bool(const char *str,
+ Eina_Bool *v)
+{
+ if ((strcmp(str, "0") == 0) ||
+ (strcasecmp(str, "f") == 0) ||
+ (strcasecmp(str, "false") == 0) ||
+ (strcasecmp(str, "no") == 0) ||
+ (strcasecmp(str, "off") == 0)
+ )
+ {
+ *v = EINA_FALSE;
+ return EINA_TRUE;
+ }
+ else if ((strcmp(str, "1") == 0) ||
+ (strcasecmp(str, "t") == 0) ||
+ (strcasecmp(str, "true") == 0) ||
+ (strcasecmp(str, "yes") == 0) ||
+ (strcasecmp(str, "on") == 0)
+ )
+ {
+ *v = EINA_TRUE;
+ return EINA_TRUE;
+ }
+
+ return EINA_FALSE;
+}
+
+static Eina_Bool
+_ecore_getopt_parse_long(const char *str,
+ long int *v)
+{
+ char *endptr = NULL;
+ *v = strtol(str, &endptr, 0);
+ return endptr > str;
+}
+
+static Eina_Bool
+_ecore_getopt_parse_double(const char *str,
+ double *v)
+{
+ char *endptr = NULL;
+ *v = strtod(str, &endptr);
+ return endptr > str;
+}
+
+static Eina_Bool
+_ecore_getopt_parse_store(const Ecore_Getopt *parser EINA_UNUSED,
+ const Ecore_Getopt_Desc *desc,
+ Ecore_Getopt_Value *value,
+ const char *arg_val)
+{
+ const Ecore_Getopt_Desc_Store *store = &desc->action_param.store;
+ long int v;
+ double d;
+ Eina_Bool b;
+
+ if (!value->ptrp)
+ {
+ _ecore_getopt_desc_print_error(desc, _("value has no pointer set.\n"));
+ return EINA_FALSE;
+ }
+
+ switch (store->arg_req)
+ {
+ case ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO:
+ goto use_optional;
+
+ case ECORE_GETOPT_DESC_ARG_REQUIREMENT_OPTIONAL:
+ if (!arg_val)
+ goto use_optional;
+
+ case ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES:
+ break;
+ }
+
+ switch (store->type)
+ {
+ case ECORE_GETOPT_TYPE_STR:
+ *value->strp = (char *)arg_val;
+ return EINA_TRUE;
+
+ case ECORE_GETOPT_TYPE_BOOL:
+ if (_ecore_getopt_parse_bool(arg_val, &b))
+ {
+ *value->boolp = b;
+ return EINA_TRUE;
+ }
+ else
+ {
+ _ecore_getopt_desc_print_error
+ (desc, _("unknown boolean value %s.\n"), arg_val);
+ return EINA_FALSE;
+ }
+
+ case ECORE_GETOPT_TYPE_SHORT:
+ if (!_ecore_getopt_parse_long(arg_val, &v))
+ goto error;
+ *value->shortp = v;
+ return EINA_TRUE;
+
+ case ECORE_GETOPT_TYPE_INT:
+ if (!_ecore_getopt_parse_long(arg_val, &v))
+ goto error;
+ *value->intp = v;
+ return EINA_TRUE;
+
+ case ECORE_GETOPT_TYPE_LONG:
+ if (!_ecore_getopt_parse_long(arg_val, &v))
+ goto error;
+ *value->longp = v;
+ return EINA_TRUE;
+
+ case ECORE_GETOPT_TYPE_USHORT:
+ if (!_ecore_getopt_parse_long(arg_val, &v))
+ goto error;
+ *value->ushortp = v;
+ return EINA_TRUE;
+
+ case ECORE_GETOPT_TYPE_UINT:
+ if (!_ecore_getopt_parse_long(arg_val, &v))
+ goto error;
+ *value->uintp = v;
+ return EINA_TRUE;
+
+ case ECORE_GETOPT_TYPE_ULONG:
+ if (!_ecore_getopt_parse_long(arg_val, &v))
+ goto error;
+ *value->ulongp = v;
+ return EINA_TRUE;
+
+ case ECORE_GETOPT_TYPE_DOUBLE:
+ if (!_ecore_getopt_parse_double(arg_val, &d))
+ goto error;
+ *value->doublep = d;
+ break;
+ }
+
+ return EINA_TRUE;
+
+error:
+ _ecore_getopt_desc_print_error
+ (desc, _("invalid number format %s\n"), arg_val);
+ return EINA_FALSE;
+
+use_optional:
+ switch (store->type)
+ {
+ case ECORE_GETOPT_TYPE_STR:
+ *value->strp = (char *)store->def.strv;
+ break;
+
+ case ECORE_GETOPT_TYPE_BOOL:
+ *value->boolp = store->def.boolv;
+ break;
+
+ case ECORE_GETOPT_TYPE_SHORT:
+ *value->shortp = store->def.shortv;
+ break;
+
+ case ECORE_GETOPT_TYPE_INT:
+ *value->intp = store->def.intv;
+ break;
+
+ case ECORE_GETOPT_TYPE_LONG:
+ *value->longp = store->def.longv;
+ break;
+
+ case ECORE_GETOPT_TYPE_USHORT:
+ *value->ushortp = store->def.ushortv;
+ break;
+
+ case ECORE_GETOPT_TYPE_UINT:
+ *value->uintp = store->def.uintv;
+ break;
+
+ case ECORE_GETOPT_TYPE_ULONG:
+ *value->ulongp = store->def.ulongv;
+ break;
+
+ case ECORE_GETOPT_TYPE_DOUBLE:
+ *value->doublep = store->def.doublev;
+ break;
+ }
+
+ return EINA_TRUE;
+}
+
+static Eina_Bool
+_ecore_getopt_parse_store_const(const Ecore_Getopt *parser EINA_UNUSED,
+ const Ecore_Getopt_Desc *desc,
+ Ecore_Getopt_Value *val,
+ const char *arg_val EINA_UNUSED)
+{
+ if (!val->ptrp)
+ {
+ _ecore_getopt_desc_print_error(desc, _("value has no pointer set.\n"));
+ return EINA_FALSE;
+ }
+
+ *val->ptrp = (void *)desc->action_param.store_const;
+ return EINA_TRUE;
+}
+
+static Eina_Bool
+_ecore_getopt_parse_store_true(const Ecore_Getopt *parser EINA_UNUSED,
+ const Ecore_Getopt_Desc *desc,
+ Ecore_Getopt_Value *val,
+ const char *arg_val EINA_UNUSED)
+{
+ if (!val->boolp)
+ {
+ _ecore_getopt_desc_print_error(desc, _("value has no pointer set.\n"));
+ return EINA_FALSE;
+ }
+ *val->boolp = EINA_TRUE;
+ return EINA_TRUE;
+}
+
+static Eina_Bool
+_ecore_getopt_parse_store_false(const Ecore_Getopt *parser EINA_UNUSED,
+ const Ecore_Getopt_Desc *desc,
+ Ecore_Getopt_Value *val,
+ const char *arg_val EINA_UNUSED)
+{
+ if (!val->boolp)
+ {
+ _ecore_getopt_desc_print_error(desc, _("value has no pointer set.\n"));
+ return EINA_FALSE;
+ }
+ *val->boolp = EINA_FALSE;
+ return EINA_TRUE;
+}
+
+static Eina_Bool
+_ecore_getopt_parse_choice(const Ecore_Getopt *parser EINA_UNUSED,
+ const Ecore_Getopt_Desc *desc,
+ Ecore_Getopt_Value *val,
+ const char *arg_val)
+{
+ const char *const *pchoice;
+
+ if (!val->strp)
+ {
+ _ecore_getopt_desc_print_error(desc, _("value has no pointer set.\n"));
+ return EINA_FALSE;
+ }
+
+ pchoice = desc->action_param.choices;
+ for (; *pchoice; pchoice++)
+ if (strcmp(*pchoice, arg_val) == 0)
+ {
+ *val->strp = (char *)*pchoice;
+ return EINA_TRUE;
+ }
+
+ _ecore_getopt_desc_print_error
+ (desc, _("invalid choice \"%s\". Valid values are: "), arg_val);
+
+ pchoice = desc->action_param.choices;
+ for (; *pchoice; pchoice++)
+ {
+ fputs(*pchoice, stderr);
+ if (pchoice[1])
+ fputs(", ", stderr);
+ }
+
+ fputs(".\n", stderr);
+ return EINA_FALSE;
+}
+
+static Eina_Bool
+_ecore_getopt_parse_append(const Ecore_Getopt *parser EINA_UNUSED,
+ const Ecore_Getopt_Desc *desc,
+ Ecore_Getopt_Value *val,
+ const char *arg_val)
+{
+ void *data;
+ long int v;
+ double d;
+ Eina_Bool b;
+
+ if (!arg_val)
+ {
+ _ecore_getopt_desc_print_error
+ (desc, _("missing parameter to append.\n"));
+ return EINA_FALSE;
+ }
+
+ if (!val->listp)
+ {
+ _ecore_getopt_desc_print_error(desc, _("value has no pointer set.\n"));
+ return EINA_FALSE;
+ }
+
+ switch (desc->action_param.append_type)
+ {
+ case ECORE_GETOPT_TYPE_STR:
+ data = strdup(arg_val);
+ break;
+
+ case ECORE_GETOPT_TYPE_BOOL:
+ {
+ if (_ecore_getopt_parse_bool(arg_val, &b))
+ {
+ data = malloc(sizeof(Eina_Bool));
+ if (data)
+ *(Eina_Bool *)data = b;
+ }
+ else
+ {
+ _ecore_getopt_desc_print_error(desc, _("unknown boolean value %s.\n"), arg_val);
+ return EINA_FALSE;
+ }
+ }
+ break;
+
+ case ECORE_GETOPT_TYPE_SHORT:
+ {
+ if (!_ecore_getopt_parse_long(arg_val, &v))
+ goto error;
+ data = malloc(sizeof(short));
+ if (data)
+ *(short *)data = (short)v;
+ }
+ break;
+
+ case ECORE_GETOPT_TYPE_INT:
+ {
+ if (!_ecore_getopt_parse_long(arg_val, &v))
+ goto error;
+ data = malloc(sizeof(int));
+ if (data)
+ *(int *)data = (int)v;
+ }
+ break;
+
+ case ECORE_GETOPT_TYPE_LONG:
+ {
+ if (!_ecore_getopt_parse_long(arg_val, &v))
+ goto error;
+ data = malloc(sizeof(long));
+ if (data)
+ *(long *)data = v;
+ }
+ break;
+
+ case ECORE_GETOPT_TYPE_USHORT:
+ {
+ if (!_ecore_getopt_parse_long(arg_val, &v))
+ goto error;
+ data = malloc(sizeof(unsigned short));
+ if (data)
+ *(unsigned short *)data = (unsigned short)v;
+ }
+ break;
+
+ case ECORE_GETOPT_TYPE_UINT:
+ {
+ if (!_ecore_getopt_parse_long(arg_val, &v))
+ goto error;
+ data = malloc(sizeof(unsigned int));
+ if (data)
+ *(unsigned int *)data = (unsigned int)v;
+ }
+ break;
+
+ case ECORE_GETOPT_TYPE_ULONG:
+ {
+ if (!_ecore_getopt_parse_long(arg_val, &v))
+ goto error;
+ data = malloc(sizeof(unsigned long));
+ if (data)
+ *(unsigned long *)data = v;
+ }
+ break;
+
+ case ECORE_GETOPT_TYPE_DOUBLE:
+ {
+ if (!_ecore_getopt_parse_double(arg_val, &d))
+ goto error;
+ data = malloc(sizeof(double));
+ if (data)
+ *(double *)data = d;
+ }
+ break;
+
+ default:
+ {
+ _ecore_getopt_desc_print_error(desc, _("could not parse value.\n"));
+ return EINA_FALSE;
+ }
+ }
+
+ *val->listp = eina_list_append(*val->listp, data);
+ return EINA_TRUE;
+
+error:
+ _ecore_getopt_desc_print_error
+ (desc, _("invalid number format %s\n"), arg_val);
+ return EINA_FALSE;
+}
+
+static Eina_Bool
+_ecore_getopt_parse_count(const Ecore_Getopt *parser EINA_UNUSED,
+ const Ecore_Getopt_Desc *desc,
+ Ecore_Getopt_Value *val,
+ const char *arg_val EINA_UNUSED)
+{
+ if (!val->intp)
+ {
+ _ecore_getopt_desc_print_error(desc, _("value has no pointer set.\n"));
+ return EINA_FALSE;
+ }
+
+ (*val->intp)++;
+ return EINA_TRUE;
+}
+
+static Eina_Bool
+_ecore_getopt_parse_callback(const Ecore_Getopt *parser,
+ const Ecore_Getopt_Desc *desc,
+ Ecore_Getopt_Value *val,
+ const char *arg_val)
+{
+ const Ecore_Getopt_Desc_Callback *cb = &desc->action_param.callback;
+
+ switch (cb->arg_req)
+ {
+ case ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO:
+ arg_val = cb->def;
+ break;
+
+ case ECORE_GETOPT_DESC_ARG_REQUIREMENT_OPTIONAL:
+ if (!arg_val)
+ arg_val = cb->def;
+ break;
+
+ case ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES:
+ break;
+ }
+
+ if (cb->arg_req != ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO)
+ {
+ if ((!arg_val) || (arg_val[0] == '\0'))
+ {
+ _ecore_getopt_desc_print_error(desc, _("missing parameter.\n"));
+ return EINA_FALSE;
+ }
+
+ if (!val->ptrp)
+ {
+ _ecore_getopt_desc_print_error(desc, _("value has no pointer set.\n"));
+ return EINA_FALSE;
+ }
+ }
+
+ if (!cb->func)
+ {
+ _ecore_getopt_desc_print_error(desc, _("missing callback function!\n"));
+ return EINA_FALSE;
+ }
+
+ return cb->func(parser, desc, arg_val, (void *)cb->data, val);
+}
+
+static Eina_Bool
+_ecore_getopt_parse_help(const Ecore_Getopt *parser,
+ const Ecore_Getopt_Desc *desc EINA_UNUSED,
+ Ecore_Getopt_Value *val,
+ const char *arg_val EINA_UNUSED)
+{
+ if (val->boolp)
+ (*val->boolp) = EINA_TRUE;
+ ecore_getopt_help(stdout, parser);
+ return EINA_TRUE;
+}
+
+static Eina_Bool
+_ecore_getopt_parse_version(const Ecore_Getopt *parser,
+ const Ecore_Getopt_Desc *desc,
+ Ecore_Getopt_Value *val,
+ const char *arg_val EINA_UNUSED)
+{
+ if (val->boolp)
+ (*val->boolp) = EINA_TRUE;
+ if (!parser->version)
+ {
+ _ecore_getopt_desc_print_error(desc, _("no version was defined.\n"));
+ return EINA_FALSE;
+ }
+ _ecore_getopt_version(stdout, parser);
+ return EINA_TRUE;
+}
+
+static Eina_Bool
+_ecore_getopt_parse_copyright(const Ecore_Getopt *parser,
+ const Ecore_Getopt_Desc *desc,
+ Ecore_Getopt_Value *val,
+ const char *arg_val EINA_UNUSED)
+{
+ if (val->boolp)
+ (*val->boolp) = EINA_TRUE;
+ if (!parser->copyright)
+ {
+ _ecore_getopt_desc_print_error(desc, _("no copyright was defined.\n"));
+ return EINA_FALSE;
+ }
+ _ecore_getopt_copyright(stdout, parser);
+ return EINA_TRUE;
+}
+
+static Eina_Bool
+_ecore_getopt_parse_license(const Ecore_Getopt *parser,
+ const Ecore_Getopt_Desc *desc,
+ Ecore_Getopt_Value *val,
+ const char *arg_val EINA_UNUSED)
+{
+ if (val->boolp)
+ (*val->boolp) = EINA_TRUE;
+ if (!parser->license)
+ {
+ _ecore_getopt_desc_print_error(desc, _("no license was defined.\n"));
+ return EINA_FALSE;
+ }
+ _ecore_getopt_license(stdout, parser);
+ return EINA_TRUE;
+}
+
+static Eina_Bool
+_ecore_getopt_desc_handle(const Ecore_Getopt *parser,
+ const Ecore_Getopt_Desc *desc,
+ Ecore_Getopt_Value *value,
+ const char *arg_val)
+{
+ switch (desc->action)
+ {
+ case ECORE_GETOPT_ACTION_STORE:
+ return _ecore_getopt_parse_store(parser, desc, value, arg_val);
+
+ case ECORE_GETOPT_ACTION_STORE_CONST:
+ return _ecore_getopt_parse_store_const(parser, desc, value, arg_val);
+
+ case ECORE_GETOPT_ACTION_STORE_TRUE:
+ return _ecore_getopt_parse_store_true(parser, desc, value, arg_val);
+
+ case ECORE_GETOPT_ACTION_STORE_FALSE:
+ return _ecore_getopt_parse_store_false(parser, desc, value, arg_val);
+
+ case ECORE_GETOPT_ACTION_CHOICE:
+ return _ecore_getopt_parse_choice(parser, desc, value, arg_val);
+
+ case ECORE_GETOPT_ACTION_APPEND:
+ return _ecore_getopt_parse_append(parser, desc, value, arg_val);
+
+ case ECORE_GETOPT_ACTION_COUNT:
+ return _ecore_getopt_parse_count(parser, desc, value, arg_val);
+
+ case ECORE_GETOPT_ACTION_CALLBACK:
+ return _ecore_getopt_parse_callback(parser, desc, value, arg_val);
+
+ case ECORE_GETOPT_ACTION_HELP:
+ return _ecore_getopt_parse_help(parser, desc, value, arg_val);
+
+ case ECORE_GETOPT_ACTION_VERSION:
+ return _ecore_getopt_parse_version(parser, desc, value, arg_val);
+
+ case ECORE_GETOPT_ACTION_COPYRIGHT:
+ return _ecore_getopt_parse_copyright(parser, desc, value, arg_val);
+
+ case ECORE_GETOPT_ACTION_LICENSE:
+ return _ecore_getopt_parse_license(parser, desc, value, arg_val);
+
+ default:
+ return EINA_FALSE;
+ }
+}
+
+static Eina_Bool
+_ecore_getopt_parse_arg_long(const Ecore_Getopt *parser,
+ Ecore_Getopt_Value *values,
+ int argc EINA_UNUSED,
+ char **argv,
+ int *idx,
+ int *nonargs,
+ const char *arg)
+{
+ const Ecore_Getopt_Desc *desc;
+ Ecore_Getopt_Desc_Arg_Requirement arg_req;
+ const char *arg_val;
+ int desc_idx;
+ Ecore_Getopt_Value *value;
+ Eina_Bool ret;
+
+ desc = _ecore_getopt_parse_find_long(parser, arg);
+ if (!desc)
+ {
+ fprintf(stderr, _("ERROR: unknown option --%s, ignored.\n"), arg);
+ if (parser->strict)
+ return EINA_FALSE;
+
+ (*idx)++;
+ return EINA_TRUE;
+ }
+
+ (*idx)++;
+
+ arg_req = _ecore_getopt_desc_arg_requirement(desc);
+ if (arg_req != ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO)
+ {
+ arg_val = strchr(arg, '=');
+ if (arg_val)
+ arg_val++;
+ else
+ {
+ if ((*idx < *nonargs) && (argv[*idx][0] != '-'))
+ {
+ arg_val = argv[*idx];
+ (*idx)++;
+ }
+ else
+ arg_val = NULL;
+ }
+
+ if (arg_val && arg_val[0] == '\0')
+ arg_val = NULL;
+
+ if ((!arg_val) && (arg_req == ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES))
+ {
+ fprintf
+ (stderr, _("ERROR: option --%s requires an argument!\n"), arg);
+ if (parser->strict)
+ return EINA_FALSE;
+ return EINA_TRUE;
+ }
+ }
+ else
+ arg_val = NULL;
+
+ desc_idx = desc - parser->descs;
+ value = values + desc_idx;
+ ret = _ecore_getopt_desc_handle(parser, desc, value, arg_val);
+ if ((!ret) && parser->strict)
+ return EINA_FALSE;
+
+ return EINA_TRUE;
+}
+
+static Eina_Bool
+_ecore_getopt_parse_arg_short(const Ecore_Getopt *parser,
+ Ecore_Getopt_Value *values,
+ int argc EINA_UNUSED,
+ char **argv,
+ int *idx,
+ int *nonargs,
+ const char *arg)
+{
+ int run = 1;
+ while (run && (arg[0] != '\0'))
+ {
+ int opt = arg[0];
+ const Ecore_Getopt_Desc *desc;
+ Ecore_Getopt_Desc_Arg_Requirement arg_req;
+ const char *arg_val;
+ int desc_idx;
+ Ecore_Getopt_Value *value;
+ Eina_Bool ret;
+
+ desc = _ecore_getopt_parse_find_short(parser, arg[0]);
+ if (!desc)
+ {
+ fprintf
+ (stderr, _("ERROR: unknown option -%c, ignored.\n"), arg[0]);
+ if (parser->strict)
+ return EINA_FALSE;
+
+ arg++;
+ continue;
+ }
+
+ arg++;
+
+ arg_req = _ecore_getopt_desc_arg_requirement(desc);
+ if (arg_req != ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO)
+ {
+ (*idx)++;
+ run = 0;
+
+ if (arg[0] == '=')
+ arg_val = arg + 1;
+ else if (arg[0] != '\0')
+ arg_val = arg;
+ else
+ {
+ if ((*idx < *nonargs) && (argv[*idx][0] != '-'))
+ {
+ arg_val = argv[*idx];
+ (*idx)++;
+ }
+ else
+ arg_val = NULL;
+ }
+
+ if (arg_val && arg_val[0] == '\0')
+ arg_val = NULL;
+
+ if ((!arg_val) &&
+ (arg_req == ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES))
+ {
+ fprintf
+ (stderr, _("ERROR: option -%c requires an argument!\n"),
+ opt);
+ if (parser->strict)
+ return EINA_FALSE;
+ return EINA_TRUE;
+ }
+ }
+ else
+ arg_val = NULL;
+
+ desc_idx = desc - parser->descs;
+ value = values + desc_idx;
+ ret = _ecore_getopt_desc_handle(parser, desc, value, arg_val);
+ if ((!ret) && parser->strict)
+ return EINA_FALSE;
+ }
+
+ if (run)
+ (*idx)++;
+
+ return EINA_TRUE;
+}
+
+static Eina_Bool
+_ecore_getopt_parse_arg(const Ecore_Getopt *parser,
+ Ecore_Getopt_Value *values,
+ int argc,
+ char **argv,
+ int *idx,
+ int *nonargs)
+{
+ char *arg = argv[*idx];
+
+ if (arg[0] != '-')
+ {
+ char **dst, **src, **src_end;
+
+ dst = argv + *idx;
+ src = dst + 1;
+ src_end = src + *nonargs - *idx - 1;
+
+ for (; src < src_end; src++, dst++)
+ *dst = *src;
+
+ *dst = arg;
+ (*nonargs)--;
+ return EINA_TRUE;
+ }
+
+ if (arg[1] == '-')
+ return _ecore_getopt_parse_arg_long(parser, values, argc, argv, idx, nonargs, arg + 2);
+ else
+ return _ecore_getopt_parse_arg_short(parser, values, argc, argv, idx, nonargs, arg + 1);
+}
+
+static const Ecore_Getopt_Desc *
+_ecore_getopt_parse_find_short_other(const Ecore_Getopt *parser,
+ const Ecore_Getopt_Desc *orig)
+{
+ const Ecore_Getopt_Desc *desc = parser->descs;
+ const char c = orig->shortname;
+
+ for (; !_ecore_getopt_desc_is_sentinel(desc); desc++)
+ {
+ if (desc == orig)
+ return NULL;
+
+ if (c == desc->shortname)
+ return desc;
+ }
+
+ return NULL;
+}
+
+static const Ecore_Getopt_Desc *
+_ecore_getopt_parse_find_long_other(const Ecore_Getopt *parser,
+ const Ecore_Getopt_Desc *orig)
+{
+ const Ecore_Getopt_Desc *desc = parser->descs;
+ const char *name = orig->longname;
+
+ for (; !_ecore_getopt_desc_is_sentinel(desc); desc++)
+ {
+ if (desc == orig)
+ return NULL;
+
+ if (desc->longname && (strcmp(name, desc->longname) == 0))
+ return desc;
+ }
+
+ return NULL;
+}
+
+/**
+ * Check parser for duplicate entries, print them out.
+ *
+ * @return @c EINA_TRUE if there are duplicates, @c EINA_FALSE otherwise.
+ * @param parser The parser to be checked.
+ */
+EAPI Eina_Bool
+ecore_getopt_parser_has_duplicates(const Ecore_Getopt *parser)
+{
+ const Ecore_Getopt_Desc *desc = parser->descs;
+ for (; !_ecore_getopt_desc_is_sentinel(desc); desc++)
+ {
+ if (desc->shortname)
+ {
+ const Ecore_Getopt_Desc *other;
+ other = _ecore_getopt_parse_find_short_other(parser, desc);
+ if (other)
+ {
+ _ecore_getopt_desc_print_error(desc, "short name -%c already exists.", desc->shortname);
+
+ if (other->longname)
+ fprintf(stderr, " Other is --%s.\n", other->longname);
+ else
+ fputc('\n', stderr);
+ return EINA_TRUE;
+ }
+ }
+
+ if (desc->longname)
+ {
+ const Ecore_Getopt_Desc *other;
+ other = _ecore_getopt_parse_find_long_other(parser, desc);
+ if (other)
+ {
+ _ecore_getopt_desc_print_error(desc, "long name --%s already exists.", desc->longname);
+
+ if (other->shortname)
+ fprintf(stderr, " Other is -%c.\n", other->shortname);
+ else
+ fputc('\n', stderr);
+ return EINA_TRUE;
+ }
+ }
+ }
+ return EINA_FALSE;
+}
+
+static const Ecore_Getopt_Desc *
+_ecore_getopt_find_help(const Ecore_Getopt *parser)
+{
+ const Ecore_Getopt_Desc *desc = parser->descs;
+ for (; !_ecore_getopt_desc_is_sentinel(desc); desc++)
+ if (desc->action == ECORE_GETOPT_ACTION_HELP)
+ return desc;
+ return NULL;
+}
+
+/**
+ * Parse command line parameters.
+ *
+ * Walks the command line parameters and parse them based on @a parser
+ * description, doing actions based on @c parser->descs->action, like
+ * showing help text, license, copyright, storing values in values and
+ * so on.
+ *
+ * It is expected that values is of the same size than @c parser->descs,
+ * options that do not need a value it will be left untouched.
+ *
+ * All values are expected to be initialized before use. Options with
+ * action @c ECORE_GETOPT_ACTION_STORE and non required arguments
+ * (others than @c ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES), are expected
+ * to provide a value in @c def to be used.
+ *
+ * The following actions will store @c 1 on value as a boolean
+ * (@c value->boolp) if it's not @c NULL to indicate these actions were
+ * executed:
+ * - @c ECORE_GETOPT_ACTION_HELP
+ * - @c ECORE_GETOPT_ACTION_VERSION
+ * - @c ECORE_GETOPT_ACTION_COPYRIGHT
+ * - @c ECORE_GETOPT_ACTION_LICENSE
+ *
+ * Just @c ECORE_GETOPT_ACTION_APPEND will allocate memory and thus
+ * need to be freed. For consistency between all of appended subtypes,
+ * @c eina_list->data will contain an allocated memory with the value,
+ * that is, for @c ECORE_GETOPT_TYPE_STR it will contain a copy of the
+ * argument, @c ECORE_GETOPT_TYPE_INT a pointer to an allocated
+ * integer and so on.
+ *
+ * If parser is in strict mode (see @c Ecore_Getopt->strict), then any
+ * error will abort parsing and @c -1 is returned. Otherwise it will try
+ * to continue as far as possible.
+ *
+ * This function may reorder @a argv elements.
+ *
+ * Translation of help strings (description), metavar, usage, license
+ * and copyright may be translated, standard/global gettext() call
+ * will be applied on them if ecore was compiled with such support.
+ *
+ * @param parser description of how to work.
+ * @param values where to store values, it is assumed that this is a vector
+ * of the same size as @c parser->descs. Values should be previously
+ * initialized.
+ * @param argc how many elements in @a argv. If not provided it will be
+ * retrieved with ecore_app_args_get().
+ * @param argv command line parameters.
+ *
+ * @return index of first non-option parameter or -1 on error.
+ */
+EAPI int
+ecore_getopt_parse(const Ecore_Getopt *parser,
+ Ecore_Getopt_Value *values,
+ int argc,
+ char **argv)
+{
+ int i, nonargs;
+
+ if (!parser)
+ {
+ fputs(_("ERROR: no parser provided.\n"), stderr);
+ return -1;
+ }
+ if (!values)
+ {
+ fputs(_("ERROR: no values provided.\n"), stderr);
+ return -1;
+ }
+
+ if ((argc < 1) || (!argv))
+ ecore_app_args_get(&argc, &argv);
+
+ if (argc < 1)
+ {
+ fputs(_("ERROR: no arguments provided.\n"), stderr);
+ return -1;
+ }
+
+ if (argv[0])
+ prog = argv[0];
+ else
+ prog = parser->prog;
+
+ nonargs = _ecore_getopt_parse_find_nonargs_base(parser, argc, argv);
+ if (nonargs < 0)
+ goto error;
+
+ if (nonargs > argc)
+ nonargs = argc;
+
+ i = 1;
+ while (i < nonargs)
+ if (!_ecore_getopt_parse_arg(parser, values, argc, argv, &i, &nonargs))
+ goto error;
+
+ return nonargs;
+
+error:
+ {
+ const Ecore_Getopt_Desc *help;
+ fputs(_("ERROR: invalid options found."), stderr);
+
+ help = _ecore_getopt_find_help(parser);
+ if (!help)
+ fputc('\n', stderr);
+ else if (help->longname)
+ fprintf(stderr, _(" See --%s.\n"), help->longname);
+ else
+ fprintf(stderr, _(" See -%c.\n"), help->shortname);
+ }
+
+ return -1;
+}
+
+/**
+ * Utility to free list and nodes allocated by @a ECORE_GETOPT_ACTION_APPEND.
+ *
+ * @param list pointer to list to be freed.
+ * @return always @c NULL, so you can easily make your list head @c NULL.
+ */
+EAPI Eina_List *
+ecore_getopt_list_free(Eina_List *list)
+{
+ void *data;
+
+ EINA_LIST_FREE(list, data)
+ free(data);
+ return NULL;
+}
+
+/**
+ * Helper ecore_getopt callback to parse geometry (x:y:w:h).
+ *
+ * @param parser This parameter isn't in use.
+ * @param desc This parameter isn't in use.
+ * @param str Geometry value
+ * @param data This parameter isn't in use.
+ * @param storage must be a pointer to @c Eina_Rectangle and will be used to
+ * store the four values passed in the given string.
+ * @return @c EINA_TRUE on success, @c EINA_FALSE on incorrect geometry value.
+ *
+ * @c callback_data value is ignored, you can safely use @c NULL.
+ */
+EAPI Eina_Bool
+ecore_getopt_callback_geometry_parse(const Ecore_Getopt *parser EINA_UNUSED,
+ const Ecore_Getopt_Desc *desc EINA_UNUSED,
+ const char *str,
+ void *data EINA_UNUSED,
+ Ecore_Getopt_Value *storage)
+{
+ Eina_Rectangle *v = (Eina_Rectangle *)storage->ptrp;
+
+ if (sscanf(str, "%d:%d:%d:%d", &v->x, &v->y, &v->w, &v->h) != 4)
+ {
+ fprintf(stderr, _("ERROR: incorrect geometry value '%s'\n"), str);
+ return EINA_FALSE;
+ }
+
+ return EINA_TRUE;
+}
+
+/**
+ * Helper ecore_getopt callback to parse geometry size (WxH).
+ *
+ * @param parser This parameter isn't in use.
+ * @param desc This parameter isn't in use.
+ * @param str size value
+ * @param data This parameter isn't in use.
+ * @param storage must be a pointer to @c Eina_Rectangle and will be used to
+ * store the two values passed in the given string and @c 0 in the x and y
+ * fields.
+ * @return @c EINA_TRUE on success, @c EINA_FALSE on incorrect size value.
+ *
+ * @c callback_data value is ignored, you can safely use @c NULL.
+ */
+EAPI Eina_Bool
+ecore_getopt_callback_size_parse(const Ecore_Getopt *parser EINA_UNUSED,
+ const Ecore_Getopt_Desc *desc EINA_UNUSED,
+ const char *str,
+ void *data EINA_UNUSED,
+ Ecore_Getopt_Value *storage)
+{
+ Eina_Rectangle *v = (Eina_Rectangle *)storage->ptrp;
+
+ if (sscanf(str, "%dx%d", &v->w, &v->h) != 2)
+ {
+ fprintf(stderr, _("ERROR: incorrect size value '%s'\n"), str);
+ return EINA_FALSE;
+ }
+ v->x = 0;
+ v->y = 0;
+
+ return EINA_TRUE;
+}
+
diff --git a/src/lib/ecore/ecore_glib.c b/src/lib/ecore/ecore_glib.c
new file mode 100644
index 0000000000..5b73180951
--- /dev/null
+++ b/src/lib/ecore/ecore_glib.c
@@ -0,0 +1,346 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "Ecore.h"
+#include "ecore_private.h"
+
+#ifdef HAVE_GLIB
+# include <glib.h>
+
+static Eina_Bool _ecore_glib_active = EINA_FALSE;
+static Ecore_Select_Function _ecore_glib_select_original;
+static GCond *_ecore_glib_cond = NULL;
+static GPollFD *_ecore_glib_fds = NULL;
+static size_t _ecore_glib_fds_size = 0;
+static const size_t ECORE_GLIB_FDS_INITIAL = 128;
+static const size_t ECORE_GLIB_FDS_STEP = 8;
+static const size_t ECORE_GLIB_FDS_MAX_FREE = 256;
+
+static Eina_Bool
+_ecore_glib_fds_resize(size_t size)
+{
+ void *tmp = realloc(_ecore_glib_fds, sizeof(GPollFD) * size);
+
+ if (!tmp)
+ {
+ ERR("Could not realloc from %zu to %zu buckets.",
+ _ecore_glib_fds_size, size);
+ return EINA_FALSE;
+ }
+
+ _ecore_glib_fds = tmp;
+ _ecore_glib_fds_size = size;
+ return EINA_TRUE;
+}
+
+static int
+_ecore_glib_context_query(GMainContext *ctx,
+ int priority,
+ int *p_timer)
+{
+ int reqfds;
+
+ if (_ecore_glib_fds_size == 0)
+ {
+ if (!_ecore_glib_fds_resize(ECORE_GLIB_FDS_INITIAL)) return -1;
+ }
+
+ while (1)
+ {
+ size_t size;
+
+ reqfds = g_main_context_query
+ (ctx, priority, p_timer, _ecore_glib_fds, _ecore_glib_fds_size);
+ if (reqfds <= (int)_ecore_glib_fds_size) break;
+
+ size = (1 + reqfds / ECORE_GLIB_FDS_STEP) * ECORE_GLIB_FDS_STEP;
+ if (!_ecore_glib_fds_resize(size)) return -1;
+ }
+
+ if (reqfds + ECORE_GLIB_FDS_MAX_FREE < _ecore_glib_fds_size)
+ {
+ size_t size;
+
+ size = (1 + reqfds / ECORE_GLIB_FDS_MAX_FREE) * ECORE_GLIB_FDS_MAX_FREE;
+ _ecore_glib_fds_resize(size);
+ }
+
+ return reqfds;
+}
+
+static int
+_ecore_glib_context_poll_from(const GPollFD *pfds,
+ int count,
+ fd_set *rfds,
+ fd_set *wfds,
+ fd_set *efds)
+{
+ const GPollFD *itr = pfds, *itr_end = pfds + count;
+ int glib_fds = -1;
+
+ for (; itr < itr_end; itr++)
+ {
+ if (glib_fds < itr->fd)
+ glib_fds = itr->fd;
+
+ if (itr->events & G_IO_IN)
+ FD_SET(itr->fd, rfds);
+ if (itr->events & G_IO_OUT)
+ FD_SET(itr->fd, wfds);
+ if (itr->events & (G_IO_HUP | G_IO_ERR))
+ FD_SET(itr->fd, efds);
+ }
+
+ return glib_fds + 1;
+}
+
+static int
+_ecore_glib_context_poll_to(GPollFD *pfds,
+ int count,
+ const fd_set *rfds,
+ const fd_set *wfds,
+ const fd_set *efds,
+ int ready)
+{
+ GPollFD *itr = pfds, *itr_end = pfds + count;
+
+ for (; (itr < itr_end) && (ready > 0); itr++)
+ {
+ itr->revents = 0;
+ if (FD_ISSET(itr->fd, rfds) && (itr->events & G_IO_IN))
+ {
+ itr->revents |= G_IO_IN;
+ ready--;
+ }
+ if (FD_ISSET(itr->fd, wfds) && (itr->events & G_IO_OUT))
+ {
+ itr->revents |= G_IO_OUT;
+ ready--;
+ }
+ if (FD_ISSET(itr->fd, efds) && (itr->events & (G_IO_HUP | G_IO_ERR)))
+ {
+ itr->revents |= G_IO_ERR;
+ ready--;
+ }
+ }
+ return ready;
+}
+
+static int
+_ecore_glib_select__locked(GMainContext *ctx,
+ int ecore_fds,
+ fd_set *rfds,
+ fd_set *wfds,
+ fd_set *efds,
+ struct timeval *ecore_timeout)
+{
+ int priority, maxfds, glib_fds, reqfds, reqtimeout, ret;
+ struct timeval *timeout, glib_timeout;
+
+ g_main_context_prepare(ctx, &priority);
+ reqfds = _ecore_glib_context_query(ctx, priority, &reqtimeout);
+ if (reqfds < 0) goto error;
+
+ glib_fds = _ecore_glib_context_poll_from
+ (_ecore_glib_fds, reqfds, rfds, wfds, efds);
+
+ if (reqtimeout == -1)
+ timeout = ecore_timeout;
+ else
+ {
+ glib_timeout.tv_sec = reqtimeout / 1000;
+ glib_timeout.tv_usec = (reqtimeout % 1000) * 1000;
+
+ if (!ecore_timeout || timercmp(ecore_timeout, &glib_timeout, >))
+ timeout = &glib_timeout;
+ else
+ timeout = ecore_timeout;
+ }
+
+ maxfds = (ecore_fds >= glib_fds) ? ecore_fds : glib_fds;
+ ret = _ecore_glib_select_original(maxfds, rfds, wfds, efds, timeout);
+
+ ret = _ecore_glib_context_poll_to
+ (_ecore_glib_fds, reqfds, rfds, wfds, efds, ret);
+
+ if (g_main_context_check(ctx, priority, _ecore_glib_fds, reqfds))
+ g_main_context_dispatch(ctx);
+
+ return ret;
+
+error:
+ return _ecore_glib_select_original
+ (ecore_fds, rfds, wfds, efds, ecore_timeout);
+}
+
+static int
+_ecore_glib_select(int ecore_fds,
+ fd_set *rfds,
+ fd_set *wfds,
+ fd_set *efds,
+ struct timeval *ecore_timeout)
+{
+ GStaticMutex lock;
+ GMutex *mutex;
+ GMainContext *ctx;
+ int ret;
+
+ g_static_mutex_init(&lock);
+ mutex = g_static_mutex_get_mutex(&lock);
+ ctx = g_main_context_default();
+
+ if (g_main_context_acquire(ctx))
+ {
+ if (mutex) g_mutex_lock(mutex);
+ }
+ else
+ {
+ if (!_ecore_glib_cond)
+ _ecore_glib_cond = g_cond_new();
+
+ while (!g_main_context_wait(ctx, _ecore_glib_cond, mutex))
+ g_thread_yield();
+ }
+
+ ret = _ecore_glib_select__locked
+ (ctx, ecore_fds, rfds, wfds, efds, ecore_timeout);
+
+ if (mutex) g_mutex_unlock(mutex);
+ g_main_context_release(ctx);
+ g_static_mutex_free(&lock);
+
+ return ret;
+}
+
+#endif
+
+void
+_ecore_glib_init(void)
+{
+}
+
+void
+_ecore_glib_shutdown(void)
+{
+#ifdef HAVE_GLIB
+ if (!_ecore_glib_active) return;
+ _ecore_glib_active = EINA_FALSE;
+
+ if (ecore_main_loop_select_func_get() == _ecore_glib_select)
+ ecore_main_loop_select_func_set(_ecore_glib_select_original);
+
+ if (_ecore_glib_fds)
+ {
+ free(_ecore_glib_fds);
+ _ecore_glib_fds = NULL;
+ }
+ _ecore_glib_fds_size = 0;
+
+ if (_ecore_glib_cond)
+ {
+ g_cond_free(_ecore_glib_cond);
+ _ecore_glib_cond = NULL;
+ }
+#endif
+}
+
+/**
+ * @addtogroup Ecore_Main_Loop_Group
+ *
+ * @}
+ */
+
+/**
+ * Request ecore to integrate GLib's main loop.
+ *
+ * This will add a small overhead during every main loop interaction
+ * by checking glib's default main context (used by its main loop). If
+ * it have events to be checked (timers, file descriptors or idlers),
+ * then these will be polled alongside with Ecore's own events, then
+ * dispatched before Ecore's. This is done by calling
+ * ecore_main_loop_select_func_set().
+ *
+ * This will cooperate with previously set
+ * ecore_main_loop_select_func_set() by calling the old
+ * function. Similarly, if you want to override
+ * ecore_main_loop_select_func_set() after main loop is integrated,
+ * call the new select function set by this call (get it by calling
+ * ecore_main_loop_select_func_get() right after
+ * ecore_main_loop_glib_integrate()).
+ *
+ * This is useful to use GMainLoop libraries, like GTK, GUPnP,
+ * LibSoup, GConf and more. Adobe Flash plugin and other plugins
+ * systems depend on this as well.
+ *
+ * Once initialized/integrated, it will be valid until Ecore is
+ * completely shut down.
+ *
+ * Example of use:
+ * @code
+ *
+ * int main(void)
+ * {
+ * ecore_init();
+ * ecore_main_loop_glib_integrate();
+ *
+ * // some code here
+ *
+ * ecore_main_loop_begin();
+ *
+ * ecore_shutdown();
+ *
+ * return 0;
+ * }
+ *
+ * @endcode
+ *
+ * @note This is only available if Ecore was compiled with GLib support.
+ * @note You don't need to call this function if Ecore was compiled with
+ * --enable-glib-integration-always.
+ *
+ * @return @c EINA_TRUE on success of @c EINA_FALSE if it failed,
+ * likely no GLib support in Ecore.
+ */
+EAPI Eina_Bool
+ecore_main_loop_glib_integrate(void)
+{
+#ifdef HAVE_GLIB
+ void *func;
+
+ if (_ecore_glib_active) return EINA_TRUE;
+ func = ecore_main_loop_select_func_get();
+ if (func == _ecore_glib_select) return EINA_TRUE;
+ _ecore_glib_select_original = func;
+ ecore_main_loop_select_func_set(_ecore_glib_select);
+ _ecore_glib_active = EINA_TRUE;
+ return EINA_TRUE;
+#else
+ ERR("No glib support");
+ return EINA_FALSE;
+#endif
+}
+
+Eina_Bool _ecore_glib_always_integrate = 1;
+
+/**
+ * Disable always integrating glib
+ *
+ * If ecore is compiled with --enable-glib-integration-always (to always
+ * call ecore_main_loop_glib_integrate() when ecore_init() is called), then
+ * calling this before calling ecore_init() will disable the integration.
+ * This is for apps that explicitly do not want this to happen for whatever
+ * reasons they may have.
+ */
+EAPI void
+ecore_main_loop_glib_always_integrate_disable(void)
+{
+ _ecore_glib_always_integrate = 0;
+}
+
+/**
+ * @}
+ */
diff --git a/src/lib/ecore/ecore_idle_enterer.c b/src/lib/ecore/ecore_idle_enterer.c
new file mode 100644
index 0000000000..e7d10e555a
--- /dev/null
+++ b/src/lib/ecore/ecore_idle_enterer.c
@@ -0,0 +1,313 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#include <Eo.h>
+
+#include "Ecore.h"
+#include "ecore_private.h"
+
+#define MY_CLASS ECORE_IDLE_ENTERER_CLASS
+
+#define MY_CLASS_NAME "ecore_idle_enterer"
+
+EAPI Eo_Op ECORE_IDLE_ENTERER_BASE_ID = EO_NOOP;
+
+struct _Ecore_Idle_Enterer_Private_Data
+{
+ EINA_INLIST;
+ Ecore_Idle_Enterer *obj;
+ Ecore_Task_Cb func;
+ void *data;
+ int references;
+ Eina_Bool delete_me : 1;
+};
+typedef struct _Ecore_Idle_Enterer_Private_Data Ecore_Idle_Enterer_Private_Data;
+
+static Ecore_Idle_Enterer_Private_Data *idle_enterers = NULL;
+static Ecore_Idle_Enterer_Private_Data *idle_enterer_current = NULL;
+static int idle_enterers_delete_me = 0;
+
+static void *
+_ecore_idle_enterer_del(Ecore_Idle_Enterer *idle_enterer);
+
+/**
+ * @addtogroup Ecore_Idle_Group
+ *
+ * @{
+ */
+
+static Eina_Bool
+_ecore_idle_enterer_add(Ecore_Idle_Enterer *obj,
+ Ecore_Idle_Enterer_Private_Data *ie,
+ Ecore_Task_Cb func,
+ const void *data)
+{
+ if (EINA_UNLIKELY(!eina_main_loop_is()))
+ {
+ eo_error_set(obj);
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(EINA_FALSE);
+ }
+
+ ie->obj = obj;
+ eo_do_super(obj, eo_constructor());
+ eo_manual_free_set(obj, EINA_TRUE);
+
+ if (!func)
+ {
+ eo_error_set(obj);
+ ERR("callback function must be set up for an object of class: '%s'", MY_CLASS_NAME);
+ return EINA_FALSE;
+ }
+
+ ie->func = func;
+ ie->data = (void *)data;
+ return EINA_TRUE;
+}
+
+/**
+ * Add an idle enterer handler.
+ * @param func The function to call when entering an idle state.
+ * @param data The data to be passed to the @p func call
+ * @return A handle to the idle enterer callback if successful. Otherwise,
+ * NULL is returned.
+ * @note The function func will be called every time the main loop is entering
+ * idle state, as long as it returns 1 (or ECORE_CALLBACK_RENEW). A return of 0
+ * (or ECORE_CALLBACK_CANCEL) deletes the idle enterer.
+ */
+EAPI Ecore_Idle_Enterer *
+ecore_idle_enterer_add(Ecore_Task_Cb func,
+ const void *data)
+{
+ Ecore_Idle_Enterer *ie = NULL;
+ ie = eo_add_custom(MY_CLASS, _ecore_parent, ecore_idle_enterer_after_constructor(func, data));
+ eo_unref(ie);
+ return ie;
+}
+
+static void
+_idle_enterer_after_constructor(Eo *obj, void *_pd, va_list *list)
+{
+ Ecore_Task_Cb func = va_arg(*list, Ecore_Task_Cb);
+ const void *data = va_arg(*list, const void *);
+
+ _ecore_lock();
+ Ecore_Idle_Enterer_Private_Data *ie = _pd;
+ if (!_ecore_idle_enterer_add(obj, ie, func, data)) goto unlock;
+
+ idle_enterers = (Ecore_Idle_Enterer_Private_Data *)eina_inlist_append(EINA_INLIST_GET(idle_enterers), EINA_INLIST_GET(ie));
+
+unlock:
+ _ecore_unlock();
+}
+
+/**
+ * Add an idle enterer handler at the start of the list so it gets called earlier than others.
+ * @param func The function to call when entering an idle state.
+ * @param data The data to be passed to the @p func call
+ * @return A handle to the idle enterer callback if successful. Otherwise,
+ * NULL is returned.
+ * @note The function func will be called every time the main loop is entering
+ * idle state, as long as it returns 1 (or ECORE_CALLBACK_RENEW). A return of 0
+ * (or ECORE_CALLBACK_CANCEL) deletes the idle enterer.
+ */
+EAPI Ecore_Idle_Enterer *
+ecore_idle_enterer_before_add(Ecore_Task_Cb func,
+ const void *data)
+{
+ Ecore_Idle_Enterer *ie = NULL;
+ ie = eo_add_custom(MY_CLASS, _ecore_parent, ecore_idle_enterer_before_constructor(func, data));
+ eo_unref(ie);
+ return ie;
+}
+
+static void
+_idle_enterer_before_constructor(Eo *obj, void *_pd, va_list *list)
+{
+ Ecore_Task_Cb func = va_arg(*list, Ecore_Task_Cb);
+ const void *data = va_arg(*list, const void *);
+
+ _ecore_lock();
+ Ecore_Idle_Enterer_Private_Data *ie = _pd;
+ if (!_ecore_idle_enterer_add(obj, ie, func, data)) goto unlock;
+
+ idle_enterers = (Ecore_Idle_Enterer_Private_Data *)eina_inlist_prepend(EINA_INLIST_GET(idle_enterers), EINA_INLIST_GET(ie));
+
+unlock:
+ _ecore_unlock();
+}
+
+static void
+_constructor(Eo *obj, void *_pd EINA_UNUSED, va_list *list EINA_UNUSED)
+{
+ eo_error_set(obj);
+ ERR("only custom constructor can be used with '%s' class", MY_CLASS_NAME);
+}
+
+/**
+ * Delete an idle enterer callback.
+ * @param idle_enterer The idle enterer to delete
+ * @return The data pointer passed to the idler enterer callback on success.
+ * NULL otherwise.
+ */
+EAPI void *
+ecore_idle_enterer_del(Ecore_Idle_Enterer *idle_enterer)
+{
+ void *data = NULL;
+
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL);
+
+ _ecore_lock();
+ data = _ecore_idle_enterer_del(idle_enterer);
+ _ecore_unlock();
+ return data;
+}
+
+/**
+ * @}
+ */
+
+
+static void *
+_ecore_idle_enterer_del(Ecore_Idle_Enterer *obj)
+{
+ Ecore_Idle_Enterer_Private_Data *idle_enterer = eo_data_get(obj, MY_CLASS);
+
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(idle_enterer->delete_me, NULL);
+ idle_enterer->delete_me = 1;
+ idle_enterers_delete_me = 1;
+ return idle_enterer->data;
+}
+
+static void
+_destructor(Eo *obj, void *_pd, va_list *list EINA_UNUSED)
+{
+ Ecore_Idle_Enterer_Private_Data *idle_enterer = _pd;
+
+ idle_enterer->delete_me = 1;
+ idle_enterers_delete_me = 1;
+
+ eo_do_super(obj, eo_destructor());
+}
+
+void
+_ecore_idle_enterer_shutdown(void)
+{
+ Ecore_Idle_Enterer_Private_Data *ie;
+ while ((ie = idle_enterers))
+ {
+ idle_enterers = (Ecore_Idle_Enterer_Private_Data *)eina_inlist_remove(EINA_INLIST_GET(idle_enterers), EINA_INLIST_GET(idle_enterers));
+
+ eo_parent_set(ie->obj, NULL);
+ if (eo_destructed_is(ie->obj))
+ eo_manual_free(ie->obj);
+ else
+ eo_manual_free_set(ie->obj, EINA_FALSE);
+ }
+ idle_enterers_delete_me = 0;
+ idle_enterer_current = NULL;
+}
+
+void
+_ecore_idle_enterer_call(void)
+{
+ if (!idle_enterer_current)
+ {
+ /* regular main loop, start from head */
+ idle_enterer_current = idle_enterers;
+ }
+ else
+ {
+ /* recursive main loop, continue from where we were */
+ idle_enterer_current =
+ (Ecore_Idle_Enterer_Private_Data *)EINA_INLIST_GET(idle_enterer_current)->next;
+ }
+
+ while (idle_enterer_current)
+ {
+ Ecore_Idle_Enterer_Private_Data *ie = (Ecore_Idle_Enterer_Private_Data *)idle_enterer_current;
+ if (!ie->delete_me)
+ {
+ ie->references++;
+ if (!_ecore_call_task_cb(ie->func, ie->data))
+ {
+ if (!ie->delete_me) _ecore_idle_enterer_del(ie->obj);
+ }
+ ie->references--;
+ }
+ if (idle_enterer_current) /* may have changed in recursive main loops */
+ idle_enterer_current =
+ (Ecore_Idle_Enterer_Private_Data *)EINA_INLIST_GET(idle_enterer_current)->next;
+ }
+ if (idle_enterers_delete_me)
+ {
+ Ecore_Idle_Enterer_Private_Data *l;
+ int deleted_idler_enterers_in_use = 0;
+
+ for (l = idle_enterers; l; )
+ {
+ Ecore_Idle_Enterer_Private_Data *ie = l;
+ l = (Ecore_Idle_Enterer_Private_Data *)EINA_INLIST_GET(l)->next;
+ if (ie->delete_me)
+ {
+ if (ie->references)
+ {
+ deleted_idler_enterers_in_use++;
+ continue;
+ }
+
+ idle_enterers = (Ecore_Idle_Enterer_Private_Data *)eina_inlist_remove(EINA_INLIST_GET(idle_enterers), EINA_INLIST_GET(ie));
+
+ eo_parent_set(ie->obj, NULL);
+ if (eo_destructed_is(ie->obj))
+ eo_manual_free(ie->obj);
+ else
+ eo_manual_free_set(ie->obj, EINA_FALSE);
+ }
+ }
+ if (!deleted_idler_enterers_in_use)
+ idle_enterers_delete_me = 0;
+ }
+}
+
+int
+_ecore_idle_enterer_exist(void)
+{
+ if (idle_enterers) return 1;
+ return 0;
+}
+
+static void
+_class_constructor(Eo_Class *klass)
+{
+ const Eo_Op_Func_Description func_desc[] = {
+ EO_OP_FUNC(EO_BASE_ID(EO_BASE_SUB_ID_CONSTRUCTOR), _constructor),
+ EO_OP_FUNC(EO_BASE_ID(EO_BASE_SUB_ID_DESTRUCTOR), _destructor),
+
+ EO_OP_FUNC(ECORE_IDLE_ENTERER_ID(ECORE_IDLE_ENTERER_SUB_ID_AFTER_CONSTRUCTOR), _idle_enterer_after_constructor),
+ EO_OP_FUNC(ECORE_IDLE_ENTERER_ID(ECORE_IDLE_ENTERER_SUB_ID_BEFORE_CONSTRUCTOR), _idle_enterer_before_constructor),
+ EO_OP_FUNC_SENTINEL
+ };
+
+ eo_class_funcs_set(klass, func_desc);
+}
+
+static const Eo_Op_Description op_desc[] = {
+ EO_OP_DESCRIPTION(ECORE_IDLE_ENTERER_SUB_ID_AFTER_CONSTRUCTOR, "Add an idle enterer handler."),
+ EO_OP_DESCRIPTION(ECORE_IDLE_ENTERER_SUB_ID_BEFORE_CONSTRUCTOR, "Add an idle enterer handler at the start of the list so it gets called earlier than others."),
+ EO_OP_DESCRIPTION_SENTINEL
+};
+static const Eo_Class_Description class_desc = {
+ EO_VERSION,
+ MY_CLASS_NAME,
+ EO_CLASS_TYPE_REGULAR,
+ EO_CLASS_DESCRIPTION_OPS(&ECORE_IDLE_ENTERER_BASE_ID, op_desc, ECORE_IDLE_ENTERER_SUB_ID_LAST),
+ NULL,
+ sizeof(Ecore_Idle_Enterer_Private_Data),
+ _class_constructor,
+ NULL
+};
+
+EO_DEFINE_CLASS(ecore_idle_enterer_class_get, &class_desc, EO_BASE_CLASS, NULL)
diff --git a/src/lib/ecore/ecore_idle_exiter.c b/src/lib/ecore/ecore_idle_exiter.c
new file mode 100644
index 0000000000..8fd20e7a0a
--- /dev/null
+++ b/src/lib/ecore/ecore_idle_exiter.c
@@ -0,0 +1,264 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#include <Eo.h>
+
+#include "Ecore.h"
+#include "ecore_private.h"
+
+#define MY_CLASS ECORE_IDLE_EXITER_CLASS
+
+#define MY_CLASS_NAME "ecore_idle_exiter"
+
+EAPI Eo_Op ECORE_IDLE_EXITER_BASE_ID = EO_NOOP;
+
+struct _Ecore_Idle_Exiter_Private_Data
+{
+ EINA_INLIST;
+ Ecore_Idle_Exiter *obj;
+ Ecore_Task_Cb func;
+ void *data;
+ int references;
+ Eina_Bool delete_me : 1;
+};
+
+typedef struct _Ecore_Idle_Exiter_Private_Data Ecore_Idle_Exiter_Private_Data;
+
+static Ecore_Idle_Exiter_Private_Data *idle_exiters = NULL;
+static Ecore_Idle_Exiter_Private_Data *idle_exiter_current = NULL;
+static int idle_exiters_delete_me = 0;
+
+static void *
+_ecore_idle_exiter_del(Ecore_Idle_Exiter *idle_exiter);
+
+/**
+ * @addtogroup Ecore_Idle_Group
+ *
+ * @{
+ */
+
+/**
+ * Add an idle exiter handler.
+ * @param func The function to call when exiting an idle state.
+ * @param data The data to be passed to the @p func call
+ * @return A handle to the idle exiter callback on success. NULL otherwise.
+ * @note The function func will be called every time the main loop is exiting
+ * idle state, as long as it returns 1 (or ECORE_CALLBACK_RENEW). A return of 0
+ * (or ECORE_CALLBACK_CANCEL) deletes the idle exiter.
+ */
+EAPI Ecore_Idle_Exiter *
+ecore_idle_exiter_add(Ecore_Task_Cb func,
+ const void *data)
+{
+ Ecore_Idle_Exiter *ie = NULL;
+ ie = eo_add_custom(MY_CLASS, _ecore_parent, ecore_idle_exiter_constructor(func, data));
+ eo_unref(ie);
+ return ie;
+}
+
+static void
+_idle_exiter_constructor(Eo *obj, void *_pd, va_list *list EINA_UNUSED)
+{
+ Ecore_Task_Cb func = va_arg(*list, Ecore_Task_Cb);
+ const void *data = va_arg(*list, const void *);
+
+ _ecore_lock();
+ if (EINA_UNLIKELY(!eina_main_loop_is()))
+ {
+ eo_error_set(obj);
+ EINA_MAIN_LOOP_CHECK_RETURN;
+ }
+
+ Ecore_Idle_Exiter_Private_Data *ie = _pd;
+
+ ie->obj = obj;
+ eo_do_super(obj, eo_constructor());
+ eo_manual_free_set(obj, EINA_TRUE);
+
+ if (!func)
+ {
+ eo_error_set(obj);
+ ERR("callback function must be set up for an object of class: '%s'", MY_CLASS_NAME);
+ return;
+ }
+
+ ie->func = func;
+ ie->data = (void *)data;
+
+ idle_exiters = (Ecore_Idle_Exiter_Private_Data *)eina_inlist_append(EINA_INLIST_GET(idle_exiters), EINA_INLIST_GET(ie));
+ _ecore_unlock();
+}
+
+static void
+_constructor(Eo *obj, void *_pd EINA_UNUSED, va_list *list EINA_UNUSED)
+{
+ eo_error_set(obj);
+ ERR("only custom constructor can be used with '%s' class", MY_CLASS_NAME);
+}
+
+/**
+ * Delete an idle exiter handler from the list to be run on exiting idle state.
+ * @param idle_exiter The idle exiter to delete
+ * @return The data pointer that was being being passed to the handler if
+ * successful. NULL otherwise.
+ */
+EAPI void *
+ecore_idle_exiter_del(Ecore_Idle_Exiter *idle_exiter)
+{
+ void *data;
+
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL);
+
+ _ecore_lock();
+ data = _ecore_idle_exiter_del(idle_exiter);
+ _ecore_unlock();
+ return data;
+}
+
+/**
+ * @}
+ */
+static void *
+_ecore_idle_exiter_del(Ecore_Idle_Exiter *obj)
+{
+ Ecore_Idle_Exiter_Private_Data *idle_exiter = eo_data_get(obj, MY_CLASS);
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(idle_exiter->delete_me, NULL);
+ idle_exiter->delete_me = 1;
+ idle_exiters_delete_me = 1;
+ return idle_exiter->data;
+}
+
+
+static void
+_destructor(Eo *obj, void *_pd, va_list *list EINA_UNUSED)
+{
+ Ecore_Idle_Exiter_Private_Data *idle_exiter = _pd;
+
+ idle_exiter->delete_me = 1;
+ idle_exiters_delete_me = 1;
+
+ eo_do_super(obj, eo_destructor());
+}
+
+void
+_ecore_idle_exiter_shutdown(void)
+{
+ Ecore_Idle_Exiter_Private_Data *ie;
+ while ((ie = idle_exiters))
+ {
+ idle_exiters = (Ecore_Idle_Exiter_Private_Data *)eina_inlist_remove(EINA_INLIST_GET(idle_exiters), EINA_INLIST_GET(idle_exiters));
+
+ eo_parent_set(ie->obj, NULL);
+ if (eo_destructed_is(ie->obj))
+ eo_manual_free(ie->obj);
+ else
+ eo_manual_free_set(ie->obj, EINA_FALSE);
+ }
+ idle_exiters_delete_me = 0;
+ idle_exiter_current = NULL;
+}
+
+void
+_ecore_idle_exiter_call(void)
+{
+ if (!idle_exiter_current)
+ {
+ /* regular main loop, start from head */
+ idle_exiter_current = idle_exiters;
+ }
+ else
+ {
+ /* recursive main loop, continue from where we were */
+ idle_exiter_current =
+ (Ecore_Idle_Exiter_Private_Data *)EINA_INLIST_GET(idle_exiter_current)->next;
+ }
+
+ while (idle_exiter_current)
+ {
+ Ecore_Idle_Exiter_Private_Data *ie = (Ecore_Idle_Exiter_Private_Data *)idle_exiter_current;
+ if (!ie->delete_me)
+ {
+ ie->references++;
+ if (!_ecore_call_task_cb(ie->func, ie->data))
+ {
+ if (!ie->delete_me) _ecore_idle_exiter_del(ie->obj);
+ }
+ ie->references--;
+ }
+ if (idle_exiter_current) /* may have changed in recursive main loops */
+ idle_exiter_current =
+ (Ecore_Idle_Exiter_Private_Data *)EINA_INLIST_GET(idle_exiter_current)->next;
+ }
+ if (idle_exiters_delete_me)
+ {
+ Ecore_Idle_Exiter_Private_Data *l;
+ int deleted_idler_exiters_in_use = 0;
+
+ for (l = idle_exiters; l; )
+ {
+ Ecore_Idle_Exiter_Private_Data *ie = l;
+
+ l = (Ecore_Idle_Exiter_Private_Data *)EINA_INLIST_GET(l)->next;
+ if (ie->delete_me)
+ {
+ if (ie->references)
+ {
+ deleted_idler_exiters_in_use++;
+ continue;
+ }
+
+ idle_exiters = (Ecore_Idle_Exiter_Private_Data *)eina_inlist_remove(EINA_INLIST_GET(idle_exiters), EINA_INLIST_GET(ie));
+
+ eo_parent_set(ie->obj, NULL);
+ if (eo_destructed_is(ie->obj))
+ eo_manual_free(ie->obj);
+ else
+ eo_manual_free_set(ie->obj, EINA_FALSE);
+ }
+ }
+ if (!deleted_idler_exiters_in_use)
+ idle_exiters_delete_me = 0;
+ }
+}
+
+int
+_ecore_idle_exiter_exist(void)
+{
+ if (idle_exiters) return 1;
+ return 0;
+}
+
+static void
+_class_constructor(Eo_Class *klass)
+{
+ const Eo_Op_Func_Description func_desc[] = {
+ EO_OP_FUNC(EO_BASE_ID(EO_BASE_SUB_ID_CONSTRUCTOR), _constructor),
+ EO_OP_FUNC(EO_BASE_ID(EO_BASE_SUB_ID_DESTRUCTOR), _destructor),
+
+ EO_OP_FUNC(ECORE_IDLE_EXITER_ID(ECORE_IDLE_EXITER_SUB_ID_CONSTRUCTOR), _idle_exiter_constructor),
+ EO_OP_FUNC_SENTINEL
+ };
+
+ eo_class_funcs_set(klass, func_desc);
+}
+
+static const Eo_Op_Description op_desc[] = {
+ EO_OP_DESCRIPTION(ECORE_IDLE_EXITER_SUB_ID_CONSTRUCTOR, "Add an idle exiter handler."),
+ EO_OP_DESCRIPTION_SENTINEL
+};
+
+static const Eo_Class_Description class_desc = {
+ EO_VERSION,
+ MY_CLASS_NAME,
+ EO_CLASS_TYPE_REGULAR,
+ EO_CLASS_DESCRIPTION_OPS(&ECORE_IDLE_EXITER_BASE_ID, op_desc, ECORE_IDLE_EXITER_SUB_ID_LAST),
+ NULL,
+ sizeof(Ecore_Idle_Exiter_Private_Data),
+ _class_constructor,
+ NULL
+};
+
+EO_DEFINE_CLASS(ecore_idle_exiter_class_get, &class_desc, EO_BASE_CLASS, NULL)
diff --git a/src/lib/ecore/ecore_idler.c b/src/lib/ecore/ecore_idler.c
new file mode 100644
index 0000000000..147b3c1880
--- /dev/null
+++ b/src/lib/ecore/ecore_idler.c
@@ -0,0 +1,247 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#include <Eo.h>
+
+#include "Ecore.h"
+#include "ecore_private.h"
+
+#define MY_CLASS ECORE_IDLER_CLASS
+
+#define MY_CLASS_NAME "ecore_idler"
+
+EAPI Eo_Op ECORE_IDLER_BASE_ID = EO_NOOP;
+
+struct _Ecore_Idler_Private_Data
+{
+ EINA_INLIST;
+ Ecore_Idler *obj;
+ Ecore_Task_Cb func;
+ void *data;
+ int references;
+ Eina_Bool delete_me : 1;
+};
+
+typedef struct _Ecore_Idler_Private_Data Ecore_Idler_Private_Data;
+static Ecore_Idler_Private_Data *idlers = NULL;
+static Ecore_Idler_Private_Data *idler_current = NULL;
+static int idlers_delete_me = 0;
+
+static void *
+_ecore_idler_del(Ecore_Idler *idler);
+
+EAPI Ecore_Idler *
+ecore_idler_add(Ecore_Task_Cb func,
+ const void *data)
+{
+ Ecore_Idler *ie = NULL;
+
+ _ecore_lock();
+
+ ie = eo_add_custom(MY_CLASS, _ecore_parent, ecore_idler_constructor(func, data));
+ eo_unref(ie);
+
+ _ecore_unlock();
+ return ie;
+}
+
+static void
+_idler_constructor(Eo *obj, void *_pd, va_list *list EINA_UNUSED)
+{
+ Ecore_Task_Cb func = va_arg(*list, Ecore_Task_Cb);
+ const void *data = va_arg(*list, const void *);
+
+ if (EINA_UNLIKELY(!eina_main_loop_is()))
+ {
+ eo_error_set(obj);
+ EINA_MAIN_LOOP_CHECK_RETURN;
+ }
+
+ Ecore_Idler_Private_Data *ie = _pd;
+
+ ie->obj = obj;
+ eo_do_super(obj, eo_constructor());
+ eo_manual_free_set(obj, EINA_TRUE);
+
+ if (!func)
+ {
+ eo_error_set(obj);
+ ERR("callback function must be set up for an object of class: '%s'", MY_CLASS_NAME);
+ return;
+ }
+
+ ie->func = func;
+ ie->data = (void *)data;
+ idlers = (Ecore_Idler_Private_Data *)eina_inlist_append(EINA_INLIST_GET(idlers), EINA_INLIST_GET(ie));
+}
+
+
+static void
+_constructor(Eo *obj, void *_pd EINA_UNUSED, va_list *list EINA_UNUSED)
+{
+ eo_error_set(obj);
+ ERR("only custom constructor can be used with '%s' class", MY_CLASS_NAME);
+}
+
+EAPI void *
+ecore_idler_del(Ecore_Idler *idler)
+{
+ void *data = NULL;
+
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL);
+
+ _ecore_lock();
+ data = _ecore_idler_del(idler);
+ _ecore_unlock();
+ return data;
+}
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+static void *
+_ecore_idler_del(Ecore_Idler *obj)
+{
+ Ecore_Idler_Private_Data *idler = eo_data_get(obj, MY_CLASS);
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(idler->delete_me, NULL);
+ idler->delete_me = 1;
+ idlers_delete_me = 1;
+ return idler->data;
+}
+
+static void
+_destructor(Eo *obj, void *_pd, va_list *list EINA_UNUSED)
+{
+ Ecore_Idler_Private_Data *idler = _pd;
+
+ idler->delete_me = 1;
+ idlers_delete_me = 1;
+
+ eo_do_super(obj, eo_destructor());
+}
+
+void
+_ecore_idler_shutdown(void)
+{
+ Ecore_Idler_Private_Data *ie;
+ while ((ie = idlers))
+ {
+ idlers = (Ecore_Idler_Private_Data *)eina_inlist_remove(EINA_INLIST_GET(idlers), EINA_INLIST_GET(idlers));
+
+ eo_parent_set(ie->obj, NULL);
+ if (eo_destructed_is(ie->obj))
+ eo_manual_free(ie->obj);
+ else
+ eo_manual_free_set(ie->obj, EINA_FALSE);
+ }
+ idlers_delete_me = 0;
+ idler_current = NULL;
+}
+
+int
+_ecore_idler_all_call(void)
+{
+ if (!idler_current)
+ {
+ /* regular main loop, start from head */
+ idler_current = idlers;
+ }
+ else
+ {
+ /* recursive main loop, continue from where we were */
+ idler_current = (Ecore_Idler_Private_Data *)EINA_INLIST_GET(idler_current)->next;
+ }
+
+ while (idler_current)
+ {
+ Ecore_Idler_Private_Data *ie = (Ecore_Idler_Private_Data *)idler_current;
+ if (!ie->delete_me)
+ {
+ ie->references++;
+ if (!_ecore_call_task_cb(ie->func, ie->data))
+ {
+ if (!ie->delete_me) _ecore_idler_del(ie->obj);
+ }
+ ie->references--;
+ }
+ if (idler_current) /* may have changed in recursive main loops */
+ idler_current = (Ecore_Idler_Private_Data *)EINA_INLIST_GET(idler_current)->next;
+ }
+ if (idlers_delete_me)
+ {
+ Ecore_Idler_Private_Data *l;
+ int deleted_idlers_in_use = 0;
+ for (l = idlers; l; )
+ {
+ Ecore_Idler_Private_Data *ie = l;
+ l = (Ecore_Idler_Private_Data *)EINA_INLIST_GET(l)->next;
+ if (ie->delete_me)
+ {
+ if (ie->references)
+ {
+ deleted_idlers_in_use++;
+ continue;
+ }
+
+ idlers = (Ecore_Idler_Private_Data *)eina_inlist_remove(EINA_INLIST_GET(idlers), EINA_INLIST_GET(ie));
+
+ eo_parent_set(ie->obj, NULL);
+ if (eo_destructed_is(ie->obj))
+ eo_manual_free(ie->obj);
+ else
+ eo_manual_free_set(ie->obj, EINA_FALSE);
+ }
+ }
+ if (!deleted_idlers_in_use)
+ idlers_delete_me = 0;
+ }
+ if (idlers) return 1;
+ return 0;
+}
+
+int
+_ecore_idler_exist(void)
+{
+ if (idlers) return 1;
+ return 0;
+}
+
+static void
+_class_constructor(Eo_Class *klass)
+{
+ const Eo_Op_Func_Description func_desc[] = {
+ EO_OP_FUNC(EO_BASE_ID(EO_BASE_SUB_ID_CONSTRUCTOR), _constructor),
+ EO_OP_FUNC(EO_BASE_ID(EO_BASE_SUB_ID_DESTRUCTOR), _destructor),
+
+ EO_OP_FUNC(ECORE_IDLER_ID(ECORE_IDLER_SUB_ID_CONSTRUCTOR), _idler_constructor),
+ EO_OP_FUNC_SENTINEL
+ };
+
+ eo_class_funcs_set(klass, func_desc);
+}
+
+static const Eo_Op_Description op_desc[] = {
+ EO_OP_DESCRIPTION(ECORE_IDLER_SUB_ID_CONSTRUCTOR, "Add an idler handler."),
+ EO_OP_DESCRIPTION_SENTINEL
+};
+
+static const Eo_Class_Description class_desc = {
+ EO_VERSION,
+ MY_CLASS_NAME,
+ EO_CLASS_TYPE_REGULAR,
+ EO_CLASS_DESCRIPTION_OPS(&ECORE_IDLER_BASE_ID, op_desc, ECORE_IDLER_SUB_ID_LAST),
+ NULL,
+ sizeof(Ecore_Idler_Private_Data),
+ _class_constructor,
+ NULL
+};
+
+EO_DEFINE_CLASS(ecore_idler_class_get, &class_desc, EO_BASE_CLASS, NULL)
diff --git a/src/lib/ecore/ecore_job.c b/src/lib/ecore/ecore_job.c
new file mode 100644
index 0000000000..bc438b7209
--- /dev/null
+++ b/src/lib/ecore/ecore_job.c
@@ -0,0 +1,198 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#include <Eo.h>
+
+#include "Ecore.h"
+#include "ecore_private.h"
+
+#define MY_CLASS ECORE_JOB_CLASS
+
+#define MY_CLASS_NAME "ecore_job"
+
+EAPI Eo_Op ECORE_JOB_BASE_ID = EO_NOOP;
+
+static Eina_Bool _ecore_job_event_handler(void *data,
+ int type,
+ void *ev);
+static void _ecore_job_event_free(void *data,
+ void *ev);
+
+static int ecore_event_job_type = 0;
+static Ecore_Event_Handler *_ecore_job_handler = NULL;
+
+typedef struct _Ecore_Job_Private_Data Ecore_Job_Private_Data;
+
+struct _Ecore_Job_Private_Data
+{
+ Ecore_Event *event;
+ Ecore_Cb func;
+ void *data;
+};
+
+void
+_ecore_job_init(void)
+{
+ ecore_event_job_type = ecore_event_type_new();
+ _ecore_job_handler = ecore_event_handler_add(ecore_event_job_type, _ecore_job_event_handler, NULL);
+}
+
+void
+_ecore_job_shutdown(void)
+{
+ _ecore_event_handler_del(_ecore_job_handler);
+ _ecore_job_handler = NULL;
+}
+
+/**
+ * @addtogroup Ecore_Job_Group
+ *
+ * @{
+ */
+
+/**
+ * Add a job to the event queue.
+ * @param func The function to call when the job gets handled.
+ * @param data Data pointer to be passed to the job function when the job is
+ * handled.
+ * @return The handle of the job. @c NULL is returned if the job could not be
+ * added to the queue.
+ * @note Once the job has been executed, the job handle is invalid.
+ */
+EAPI Ecore_Job *
+ecore_job_add(Ecore_Cb func,
+ const void *data)
+{
+ Ecore_Job *job = eo_add_custom(MY_CLASS, _ecore_parent, ecore_job_constructor(func, data));
+ eo_unref(job);
+ return job;
+}
+
+static void
+_job_constructor(Eo *obj, void *_pd, va_list *list EINA_UNUSED)
+{
+ Ecore_Cb func = va_arg(*list, Ecore_Cb);
+ const void *data = va_arg(*list, const void *);
+
+ if (EINA_UNLIKELY(!eina_main_loop_is()))
+ {
+ eo_error_set(obj);
+ EINA_MAIN_LOOP_CHECK_RETURN;
+ }
+ eo_do_super(obj, eo_constructor());
+ eo_manual_free_set(obj, EINA_TRUE);
+
+ if (!func)
+ {
+ eo_error_set(obj);
+ ERR("callback function must be set up for an object of class: '%s'", MY_CLASS_NAME);
+ return;
+ }
+
+ Ecore_Job_Private_Data *job = _pd;
+
+ job->event = ecore_event_add(ecore_event_job_type, job, _ecore_job_event_free, obj);
+ if (!job->event)
+ {
+ eo_error_set(obj);
+ ERR("no event was assigned to object '%p' of class '%s'", obj, MY_CLASS_NAME);
+ return;
+ }
+ job->func = func;
+ job->data = (void *)data;
+}
+
+static void
+_constructor(Eo *obj, void *_pd EINA_UNUSED, va_list *list EINA_UNUSED)
+{
+ eo_error_set(obj);
+ ERR("only custom constructor can be used with '%s' class", MY_CLASS_NAME);
+}
+
+/**
+ * Delete a queued job that has not yet been executed.
+ * @param job Handle of the job to delete.
+ * @return The data pointer that was to be passed to the job.
+ */
+EAPI void *
+ecore_job_del(Ecore_Job *obj)
+{
+ void *data;
+
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL);
+ Ecore_Job_Private_Data *job = eo_data_get(obj, MY_CLASS);
+ data = job->data;
+ ecore_event_del(job->event);
+ eo_parent_set(obj, NULL);
+ return data;
+}
+
+static void
+_destructor(Eo *obj, void *_pd EINA_UNUSED, va_list *list EINA_UNUSED)
+{
+ /*FIXME: check if ecore_event_del should be called from here*/
+ eo_do_super(obj, eo_destructor());
+}
+
+/**
+ * @}
+ */
+
+static Eina_Bool
+_ecore_job_event_handler(void *data EINA_UNUSED,
+ int type EINA_UNUSED,
+ void *ev)
+{
+ Ecore_Job_Private_Data *job;
+
+ job = ev;
+ job->func(job->data);
+ return ECORE_CALLBACK_CANCEL;
+}
+
+static void
+_ecore_job_event_free(void *data,
+ void *job EINA_UNUSED)
+{
+ eo_parent_set(data, NULL);
+
+ Ecore_Job *obj = data;
+
+ if (eo_destructed_is(obj))
+ eo_manual_free(obj);
+ else
+ eo_manual_free_set(obj, EINA_FALSE);
+}
+
+static void
+_class_constructor(Eo_Class *klass)
+{
+ const Eo_Op_Func_Description func_desc[] = {
+ EO_OP_FUNC(EO_BASE_ID(EO_BASE_SUB_ID_CONSTRUCTOR), _constructor),
+ EO_OP_FUNC(EO_BASE_ID(EO_BASE_SUB_ID_DESTRUCTOR), _destructor),
+ EO_OP_FUNC(ECORE_JOB_ID(ECORE_JOB_SUB_ID_CONSTRUCTOR), _job_constructor),
+ EO_OP_FUNC_SENTINEL
+ };
+
+ eo_class_funcs_set(klass, func_desc);
+}
+
+static const Eo_Op_Description op_desc[] = {
+ EO_OP_DESCRIPTION(ECORE_JOB_SUB_ID_CONSTRUCTOR, "Add a job to the event queue."),
+ EO_OP_DESCRIPTION_SENTINEL
+};
+static const Eo_Class_Description class_desc = {
+ EO_VERSION,
+ MY_CLASS_NAME,
+ EO_CLASS_TYPE_REGULAR,
+ EO_CLASS_DESCRIPTION_OPS(&ECORE_JOB_BASE_ID, op_desc, ECORE_JOB_SUB_ID_LAST),
+ NULL,
+ sizeof(Ecore_Job_Private_Data),
+ _class_constructor,
+ NULL
+};
+
+EO_DEFINE_CLASS(ecore_job_class_get, &class_desc, EO_BASE_CLASS, NULL);
diff --git a/src/lib/ecore/ecore_main.c b/src/lib/ecore/ecore_main.c
new file mode 100644
index 0000000000..cc7b0654a5
--- /dev/null
+++ b/src/lib/ecore/ecore_main.c
@@ -0,0 +1,2111 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef _WIN32
+# define WIN32_LEAN_AND_MEAN
+# include <winsock2.h>
+# undef WIN32_LEAN_AND_MEAN
+# ifndef USER_TIMER_MINIMUM
+# define USER_TIMER_MINIMUM 0x0a
+# endif
+#endif
+
+#ifdef __SUNPRO_C
+# include <ieeefp.h>
+# include <string.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#ifdef _MSC_VER
+# include <float.h>
+#endif
+
+#ifdef HAVE_ISFINITE
+# define ECORE_FINITE(t) isfinite(t)
+#else
+# ifdef _MSC_VER
+# define ECORE_FINITE(t) _finite(t)
+# else
+# define ECORE_FINITE(t) finite(t)
+# endif
+#endif
+
+//#define FIX_HZ 1
+
+#ifdef FIX_HZ
+# ifndef _MSC_VER
+# include <sys/param.h>
+# endif
+# ifndef HZ
+# define HZ 100
+# endif
+#endif
+
+#ifdef HAVE_EVIL
+# include <Evil.h>
+#endif
+
+#include "Ecore.h"
+#include "ecore_private.h"
+
+#ifdef HAVE_SYS_EPOLL_H
+# define HAVE_EPOLL 1
+# include <sys/epoll.h>
+#else
+
+# define HAVE_EPOLL 0
+# define EPOLLIN 1
+# define EPOLLPRI 2
+# define EPOLLOUT 4
+# define EPOLLERR 8
+
+#define EPOLL_CTL_ADD 1
+#define EPOLL_CTL_DEL 2
+#define EPOLL_CTL_MOD 3
+
+typedef union epoll_data {
+ void *ptr;
+ int fd;
+ uint32_t u32;
+ uint64_t u64;
+} epoll_data_t;
+
+struct epoll_event
+{
+ uint32_t events;
+ epoll_data_t data;
+};
+
+static inline int
+epoll_create(int size EINA_UNUSED)
+{
+ return -1;
+}
+
+static inline int
+epoll_wait(int epfd EINA_UNUSED,
+ struct epoll_event *events EINA_UNUSED,
+ int maxevents EINA_UNUSED,
+ int timeout EINA_UNUSED)
+{
+ return -1;
+}
+
+static inline int
+epoll_ctl(int epfd EINA_UNUSED,
+ int op EINA_UNUSED,
+ int fd EINA_UNUSED,
+ struct epoll_event *event EINA_UNUSED)
+{
+ return -1;
+}
+
+#endif
+
+#ifdef HAVE_SYS_TIMERFD_H
+# include <sys/timerfd.h>
+#else
+/* fallback code if we don't have real timerfd - reduces number of ifdefs */
+# ifndef CLOCK_MONOTONIC
+# define CLOCK_MONOTONIC 0 /* bogus value */
+# endif
+# ifndef TFD_NONBLOCK
+# define TFD_NONBLOCK 0 /* bogus value */
+# endif
+#endif /* HAVE_SYS_TIMERFD_H */
+
+#ifndef HAVE_TIMERFD_CREATE
+static inline int
+timerfd_create(int clockid EINA_UNUSED,
+ int flags EINA_UNUSED)
+{
+ return -1;
+}
+
+static inline int
+timerfd_settime(int fd EINA_UNUSED,
+ int flags EINA_UNUSED,
+ const struct itimerspec *new_value EINA_UNUSED,
+ struct itimerspec *old_value EINA_UNUSED)
+{
+ return -1;
+}
+
+#endif /* HAVE_TIMERFD_CREATE */
+
+#ifdef USE_G_MAIN_LOOP
+# include <glib.h>
+#endif
+
+#define NS_PER_SEC (1000.0 * 1000.0 * 1000.0)
+
+struct _Ecore_Fd_Handler
+{
+ EINA_INLIST;
+ ECORE_MAGIC;
+ Ecore_Fd_Handler *next_ready;
+ int fd;
+ Ecore_Fd_Handler_Flags flags;
+ Ecore_Fd_Cb func;
+ void *data;
+ Ecore_Fd_Cb buf_func;
+ void *buf_data;
+ Ecore_Fd_Prep_Cb prep_func;
+ void *prep_data;
+ int references;
+ Eina_Bool read_active : 1;
+ Eina_Bool write_active : 1;
+ Eina_Bool error_active : 1;
+ Eina_Bool delete_me : 1;
+ Eina_Bool file : 1;
+#if defined(USE_G_MAIN_LOOP)
+ GPollFD gfd;
+#endif
+};
+GENERIC_ALLOC_SIZE_DECLARE(Ecore_Fd_Handler);
+
+#ifdef _WIN32
+struct _Ecore_Win32_Handler
+{
+ EINA_INLIST;
+ ECORE_MAGIC;
+ HANDLE h;
+ Ecore_Win32_Handle_Cb func;
+ void *data;
+ int references;
+ Eina_Bool delete_me : 1;
+};
+GENERIC_ALLOC_SIZE_DECLARE(Ecore_Win32_Handler);
+#endif
+
+#ifndef USE_G_MAIN_LOOP
+static int _ecore_main_select(double timeout);
+#endif
+static void _ecore_main_prepare_handlers(void);
+static void _ecore_main_fd_handlers_cleanup(void);
+#ifndef _WIN32
+# ifndef USE_G_MAIN_LOOP
+static void _ecore_main_fd_handlers_bads_rem(void);
+# endif
+#endif
+static void _ecore_main_fd_handlers_call(void);
+static int _ecore_main_fd_handlers_buf_call(void);
+#ifndef USE_G_MAIN_LOOP
+static void _ecore_main_loop_iterate_internal(int once_only);
+#endif
+
+#ifdef _WIN32
+static int _ecore_main_win32_select(int nfds,
+ fd_set *readfds,
+ fd_set *writefds,
+ fd_set *exceptfds,
+ struct timeval *timeout);
+static void _ecore_main_win32_handlers_cleanup(void);
+#endif
+
+static int in_main_loop = 0;
+static int do_quit = 0;
+static Ecore_Fd_Handler *fd_handlers = NULL;
+static Ecore_Fd_Handler *fd_handler_current = NULL;
+static Eina_List *fd_handlers_with_prep = NULL;
+static Eina_List *file_fd_handlers = NULL;
+static Eina_List *fd_handlers_with_buffer = NULL;
+static Eina_List *fd_handlers_to_delete = NULL;
+
+/* single linked list of ready fdhs, terminated by loop to self */
+static Ecore_Fd_Handler *fd_handlers_to_call;
+static Ecore_Fd_Handler *fd_handlers_to_call_current;
+
+#ifdef _WIN32
+static Ecore_Win32_Handler *win32_handlers = NULL;
+static Ecore_Win32_Handler *win32_handler_current = NULL;
+static Eina_Bool win32_handlers_delete_me = EINA_FALSE;
+#endif
+
+#ifdef _WIN32
+Ecore_Select_Function main_loop_select = _ecore_main_win32_select;
+#else
+# if !defined EXOTIC_NO_SELECT
+# ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+# endif
+Ecore_Select_Function main_loop_select = select;
+# else
+Ecore_Select_Function main_loop_select = NULL;
+# endif
+#endif
+
+#ifndef USE_G_MAIN_LOOP
+static double t1 = 0.0;
+static double t2 = 0.0;
+#endif
+
+static int timer_fd = -1;
+static int epoll_fd = -1;
+static pid_t epoll_pid;
+
+#ifdef USE_G_MAIN_LOOP
+static GPollFD ecore_epoll_fd;
+static GPollFD ecore_timer_fd;
+static GSource *ecore_glib_source;
+static guint ecore_glib_source_id;
+static GMainLoop *ecore_main_loop;
+static gboolean ecore_idling;
+static gboolean _ecore_glib_idle_enterer_called;
+static gboolean ecore_fds_ready;
+#endif
+
+static inline void
+_ecore_fd_valid(void)
+{
+ if (HAVE_EPOLL && epoll_fd >= 0)
+ {
+ if (fcntl(epoll_fd, F_GETFD) < 0)
+ {
+ ERR("arghhh you caught me! report a backtrace to edevel!");
+ pause();
+ }
+ }
+}
+
+static inline void
+_ecore_try_add_to_call_list(Ecore_Fd_Handler *fdh)
+{
+ /* check if this fdh is already in the list */
+ if (fdh->next_ready)
+ return;
+ if (fdh->read_active || fdh->write_active || fdh->error_active)
+ {
+ /*
+ * make sure next_ready is non-null by pointing to ourselves
+ * use that to indicate this fdh is in the ready list
+ * insert at the head of the list to avoid trouble
+ */
+ fdh->next_ready = fd_handlers_to_call ? fd_handlers_to_call : fdh;
+ fd_handlers_to_call = fdh;
+ }
+}
+
+static inline int
+_ecore_get_epoll_fd(void)
+{
+ if (epoll_pid && epoll_pid != getpid())
+ {
+ /* forked! */
+ _ecore_main_loop_shutdown();
+ }
+ if (epoll_pid == 0 && epoll_fd < 0)
+ {
+ _ecore_main_loop_init();
+ }
+ return epoll_fd;
+}
+
+static inline int
+_ecore_epoll_add(int efd,
+ int fd,
+ int events,
+ void *ptr)
+{
+ struct epoll_event ev;
+
+ memset(&ev, 0, sizeof (ev));
+ ev.events = events;
+ ev.data.ptr = ptr;
+ INF("adding poll on %d %08x", fd, events);
+ return epoll_ctl(efd, EPOLL_CTL_ADD, fd, &ev);
+}
+
+static inline int
+_ecore_poll_events_from_fdh(Ecore_Fd_Handler *fdh)
+{
+ int events = 0;
+ if (fdh->flags & ECORE_FD_READ) events |= EPOLLIN;
+ if (fdh->flags & ECORE_FD_WRITE) events |= EPOLLOUT;
+ if (fdh->flags & ECORE_FD_ERROR) events |= EPOLLERR | EPOLLPRI;
+ return events;
+}
+
+#ifdef USE_G_MAIN_LOOP
+static inline int
+_gfd_events_from_fdh(Ecore_Fd_Handler *fdh)
+{
+ int events = 0;
+ if (fdh->flags & ECORE_FD_READ) events |= G_IO_IN;
+ if (fdh->flags & ECORE_FD_WRITE) events |= G_IO_OUT;
+ if (fdh->flags & ECORE_FD_ERROR) events |= G_IO_ERR;
+ return events;
+}
+
+#endif
+
+static inline int
+_ecore_main_fdh_poll_add(Ecore_Fd_Handler *fdh)
+{
+ int r = 0;
+
+ if ((!fdh->file) && HAVE_EPOLL && epoll_fd >= 0)
+ {
+ r = _ecore_epoll_add(_ecore_get_epoll_fd(), fdh->fd,
+ _ecore_poll_events_from_fdh(fdh), fdh);
+ }
+ else
+ {
+#ifdef USE_G_MAIN_LOOP
+ fdh->gfd.fd = fdh->fd;
+ fdh->gfd.events = _gfd_events_from_fdh(fdh);
+ fdh->gfd.revents = 0;
+ INF("adding gpoll on %d %08x", fdh->fd, fdh->gfd.events);
+ g_source_add_poll(ecore_glib_source, &fdh->gfd);
+#endif
+ }
+ return r;
+}
+
+static inline void
+_ecore_main_fdh_poll_del(Ecore_Fd_Handler *fdh)
+{
+ if ((!fdh->file) && HAVE_EPOLL && epoll_fd >= 0)
+ {
+ struct epoll_event ev;
+ int efd = _ecore_get_epoll_fd();
+
+ memset(&ev, 0, sizeof (ev));
+ INF("removing poll on %d", fdh->fd);
+ /* could get an EBADF if somebody closed the FD before removing it */
+ if ((epoll_ctl(efd, EPOLL_CTL_DEL, fdh->fd, &ev) < 0))
+ {
+ if (errno == EBADF)
+ {
+ WRN("fd %d was closed, can't remove from epoll - reinit!",
+ fdh->fd);
+ _ecore_main_loop_shutdown();
+ _ecore_main_loop_init();
+ }
+ else
+ {
+ ERR("Failed to delete epoll fd %d! (errno=%d)", fdh->fd, errno);
+ }
+ }
+ }
+ else
+ {
+#ifdef USE_G_MAIN_LOOP
+ fdh->gfd.fd = fdh->fd;
+ fdh->gfd.events = _gfd_events_from_fdh(fdh);
+ fdh->gfd.revents = 0;
+ INF("adding gpoll on %d %08x", fdh->fd, fdh->gfd.events);
+ g_source_add_poll(ecore_glib_source, &fdh->gfd);
+#endif
+ }
+}
+
+static inline int
+_ecore_main_fdh_poll_modify(Ecore_Fd_Handler *fdh)
+{
+ int r = 0;
+ if ((!fdh->file) && HAVE_EPOLL && epoll_fd >= 0)
+ {
+ struct epoll_event ev;
+ int efd = _ecore_get_epoll_fd();
+
+ memset(&ev, 0, sizeof (ev));
+ ev.events = _ecore_poll_events_from_fdh(fdh);
+ ev.data.ptr = fdh;
+ INF("modifing epoll on %d to %08x", fdh->fd, ev.events);
+ r = epoll_ctl(efd, EPOLL_CTL_MOD, fdh->fd, &ev);
+ }
+ else
+ {
+#ifdef USE_G_MAIN_LOOP
+ fdh->gfd.fd = fdh->fd;
+ fdh->gfd.events = _gfd_events_from_fdh(fdh);
+ fdh->gfd.revents = 0;
+ INF("modifing gpoll on %d to %08x", fdh->fd, fdh->gfd.events);
+#endif
+ }
+ return r;
+}
+
+static inline int
+_ecore_main_fdh_epoll_mark_active(void)
+{
+ struct epoll_event ev[32];
+ int i, ret;
+ int efd = _ecore_get_epoll_fd();
+
+ memset(&ev, 0, sizeof (ev));
+ ret = epoll_wait(efd, ev, sizeof(ev) / sizeof(struct epoll_event), 0);
+ if (ret < 0)
+ {
+ if (errno == EINTR) return -1;
+ ERR("epoll_wait failed %d", errno);
+ return -1;
+ }
+
+ for (i = 0; i < ret; i++)
+ {
+ Ecore_Fd_Handler *fdh;
+
+ fdh = ev[i].data.ptr;
+ if (!ECORE_MAGIC_CHECK(fdh, ECORE_MAGIC_FD_HANDLER))
+ {
+ ECORE_MAGIC_FAIL(fdh, ECORE_MAGIC_FD_HANDLER,
+ "_ecore_main_fdh_epoll_mark_active");
+ continue;
+ }
+ if (fdh->delete_me)
+ {
+ ERR("deleted fd in epoll");
+ continue;
+ }
+
+ if (ev[i].events & EPOLLIN)
+ fdh->read_active = EINA_TRUE;
+ if (ev[i].events & EPOLLOUT)
+ fdh->write_active = EINA_TRUE;
+ if (ev[i].events & EPOLLERR)
+ fdh->error_active = EINA_TRUE;
+
+ _ecore_try_add_to_call_list(fdh);
+ }
+
+ return ret;
+}
+
+#ifdef USE_G_MAIN_LOOP
+
+static inline int
+_ecore_main_fdh_glib_mark_active(void)
+{
+ Ecore_Fd_Handler *fdh;
+ int ret = 0;
+
+ /* call the prepare callback for all handlers */
+ EINA_INLIST_FOREACH(fd_handlers, fdh)
+ {
+ if (fdh->delete_me)
+ continue;
+
+ if (fdh->gfd.revents & G_IO_IN)
+ fdh->read_active = EINA_TRUE;
+ if (fdh->gfd.revents & G_IO_OUT)
+ fdh->write_active = EINA_TRUE;
+ if (fdh->gfd.revents & G_IO_ERR)
+ fdh->error_active = EINA_TRUE;
+
+ _ecore_try_add_to_call_list(fdh);
+
+ if (fdh->gfd.revents & (G_IO_IN | G_IO_OUT | G_IO_ERR)) ret++;
+ }
+
+ return ret;
+}
+
+/* like we are about to enter main_loop_select in _ecore_main_select */
+static gboolean
+_ecore_main_gsource_prepare(GSource *source EINA_UNUSED,
+ gint *next_time)
+{
+ gboolean ready = FALSE;
+
+ _ecore_lock();
+ in_main_loop++;
+
+ if (!ecore_idling && !_ecore_glib_idle_enterer_called)
+ {
+ _ecore_time_loop_time = ecore_time_get();
+ _ecore_timer_expired_timers_call(_ecore_time_loop_time);
+ _ecore_timer_cleanup();
+
+ _ecore_idle_enterer_call();
+ _ecore_throttle();
+ _ecore_glib_idle_enterer_called = FALSE;
+
+ if (fd_handlers_with_buffer)
+ _ecore_main_fd_handlers_buf_call();
+ }
+
+ _ecore_signal_received_process();
+
+ /* don't check fds if somebody quit */
+ if (g_main_loop_is_running(ecore_main_loop))
+ {
+ /* only set idling state in dispatch */
+ if (ecore_idling && !_ecore_idler_exist() && !_ecore_event_exist())
+ {
+ if (_ecore_timers_exists())
+ {
+ int r = -1;
+ double t = _ecore_timer_next_get();
+ if (timer_fd >= 0 && t > 0.0)
+ {
+ struct itimerspec ts;
+
+ ts.it_interval.tv_sec = 0;
+ ts.it_interval.tv_nsec = 0;
+ ts.it_value.tv_sec = t;
+ ts.it_value.tv_nsec = fmod(t * NS_PER_SEC, NS_PER_SEC);
+
+ /* timerfd cannot sleep for 0 time */
+ if (ts.it_value.tv_sec || ts.it_value.tv_nsec)
+ {
+ r = timerfd_settime(timer_fd, 0, &ts, NULL);
+ if (r < 0)
+ {
+ ERR("timer set returned %d (errno=%d)", r, errno);
+ close(timer_fd);
+ timer_fd = -1;
+ }
+ else
+ INF("sleeping for %ld s %06ldus",
+ ts.it_value.tv_sec,
+ ts.it_value.tv_nsec / 1000);
+ }
+ }
+ if (r == -1)
+ {
+ *next_time = ceil(t * 1000.0);
+ if (t == 0.0)
+ ready = TRUE;
+ }
+ }
+ else
+ *next_time = -1;
+ }
+ else
+ {
+ *next_time = 0;
+ if (_ecore_event_exist())
+ ready = TRUE;
+ }
+
+ if (fd_handlers_with_prep)
+ _ecore_main_prepare_handlers();
+ }
+ else
+ ready = TRUE;
+
+ in_main_loop--;
+ INF("leave, timeout = %d", *next_time);
+ _ecore_unlock();
+
+ /* ready if we're not running (about to quit) */
+ return ready;
+}
+
+static gboolean
+_ecore_main_gsource_check(GSource *source EINA_UNUSED)
+{
+ gboolean ret = FALSE;
+
+ _ecore_lock();
+ in_main_loop++;
+
+ /* check if old timers expired */
+ if (ecore_idling && !_ecore_idler_exist() && !_ecore_event_exist())
+ {
+ if (timer_fd >= 0)
+ {
+ uint64_t count = 0;
+ int r = read(timer_fd, &count, sizeof count);
+ if (r == -1 && errno == EAGAIN)
+ ;
+ else if (r == sizeof count)
+ ret = TRUE;
+ else
+ {
+ /* unexpected things happened... fail back to old way */
+ ERR("timer read returned %d (errno=%d)", r, errno);
+ close(timer_fd);
+ timer_fd = -1;
+ }
+ }
+ }
+ else
+ ret = TRUE;
+
+ /* check if fds are ready */
+ if (HAVE_EPOLL && epoll_fd >= 0)
+ ecore_fds_ready = (_ecore_main_fdh_epoll_mark_active() > 0);
+ else
+ ecore_fds_ready = (_ecore_main_fdh_glib_mark_active() > 0);
+ _ecore_main_fd_handlers_cleanup();
+ if (ecore_fds_ready)
+ ret = TRUE;
+
+ /* check timers after updating loop time */
+ if (!ret && _ecore_timers_exists())
+ ret = (0.0 == _ecore_timer_next_get());
+
+ in_main_loop--;
+ _ecore_unlock();
+
+ return ret;
+}
+
+/* like we just came out of main_loop_select in _ecore_main_select */
+static gboolean
+_ecore_main_gsource_dispatch(GSource *source EINA_UNUSED,
+ GSourceFunc callback EINA_UNUSED,
+ gpointer user_data EINA_UNUSED)
+{
+ gboolean events_ready, timers_ready, idlers_ready;
+ double next_time;
+
+ _ecore_lock();
+ _ecore_time_loop_time = ecore_time_get();
+ _ecore_timer_enable_new();
+ next_time = _ecore_timer_next_get();
+
+ events_ready = _ecore_event_exist();
+ timers_ready = _ecore_timers_exists() && (0.0 == next_time);
+ idlers_ready = _ecore_idler_exist();
+
+ in_main_loop++;
+ INF("enter idling=%d fds=%d events=%d timers=%d (next=%.2f) idlers=%d",
+ ecore_idling, ecore_fds_ready, events_ready,
+ timers_ready, next_time, idlers_ready);
+
+ if (ecore_idling && events_ready)
+ {
+ _ecore_idle_exiter_call();
+ ecore_idling = 0;
+ }
+ else if (!ecore_idling && !events_ready)
+ {
+ ecore_idling = 1;
+ }
+
+ if (ecore_idling)
+ {
+ _ecore_idler_all_call();
+
+ events_ready = _ecore_event_exist();
+
+ if (ecore_fds_ready || events_ready || timers_ready)
+ {
+ _ecore_idle_exiter_call();
+ ecore_idling = 0;
+ }
+ }
+
+ /* process events */
+ if (!ecore_idling)
+ {
+ _ecore_main_fd_handlers_call();
+ if (fd_handlers_with_buffer)
+ _ecore_main_fd_handlers_buf_call();
+ _ecore_signal_received_process();
+ _ecore_event_call();
+ _ecore_main_fd_handlers_cleanup();
+
+ _ecore_timer_expired_timers_call(_ecore_time_loop_time);
+ _ecore_timer_cleanup();
+
+ _ecore_idle_enterer_call();
+ _ecore_throttle();
+ _ecore_glib_idle_enterer_called = TRUE;
+
+ if (fd_handlers_with_buffer)
+ _ecore_main_fd_handlers_buf_call();
+ }
+
+ in_main_loop--;
+ _ecore_unlock();
+
+ return TRUE; /* what should be returned here? */
+}
+
+static void
+_ecore_main_gsource_finalize(GSource *source EINA_UNUSED)
+{
+}
+
+static GSourceFuncs ecore_gsource_funcs =
+{
+ .prepare = _ecore_main_gsource_prepare,
+ .check = _ecore_main_gsource_check,
+ .dispatch = _ecore_main_gsource_dispatch,
+ .finalize = _ecore_main_gsource_finalize,
+};
+
+#endif
+
+void
+_ecore_main_loop_init(void)
+{
+ epoll_fd = epoll_create(1);
+ if (epoll_fd < 0)
+ WRN("Failed to create epoll fd!");
+ epoll_pid = getpid();
+
+ /* add polls on all our file descriptors */
+ Ecore_Fd_Handler *fdh;
+ EINA_INLIST_FOREACH(fd_handlers, fdh)
+ {
+ if (fdh->delete_me)
+ continue;
+ _ecore_epoll_add(epoll_fd, fdh->fd,
+ _ecore_poll_events_from_fdh(fdh), fdh);
+ _ecore_main_fdh_poll_add(fdh);
+ }
+
+ /* setup for the g_main_loop only integration */
+#ifdef USE_G_MAIN_LOOP
+ ecore_glib_source = g_source_new(&ecore_gsource_funcs, sizeof (GSource));
+ if (!ecore_glib_source)
+ CRIT("Failed to create glib source for epoll!");
+ else
+ {
+ g_source_set_priority(ecore_glib_source, G_PRIORITY_HIGH_IDLE + 20);
+ if (HAVE_EPOLL && epoll_fd >= 0)
+ {
+ /* epoll multiplexes fds into the g_main_loop */
+ ecore_epoll_fd.fd = epoll_fd;
+ ecore_epoll_fd.events = G_IO_IN;
+ ecore_epoll_fd.revents = 0;
+ g_source_add_poll(ecore_glib_source, &ecore_epoll_fd);
+ }
+
+ /* timerfd gives us better than millisecond accuracy in g_main_loop */
+ timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
+ if (timer_fd < 0)
+ WRN("failed to create timer fd!");
+ else
+ {
+ ecore_timer_fd.fd = timer_fd;
+ ecore_timer_fd.events = G_IO_IN;
+ ecore_timer_fd.revents = 0;
+ g_source_add_poll(ecore_glib_source, &ecore_timer_fd);
+ }
+
+ ecore_glib_source_id = g_source_attach(ecore_glib_source, NULL);
+ if (ecore_glib_source_id <= 0)
+ CRIT("Failed to attach glib source to default context");
+ }
+#endif
+}
+
+void
+_ecore_main_loop_shutdown(void)
+{
+#ifdef USE_G_MAIN_LOOP
+ if (ecore_glib_source)
+ {
+ g_source_destroy(ecore_glib_source);
+ ecore_glib_source = NULL;
+ }
+#endif
+
+ if (epoll_fd >= 0)
+ {
+ close(epoll_fd);
+ epoll_fd = -1;
+ }
+ epoll_pid = 0;
+
+ if (timer_fd >= 0)
+ {
+ close(timer_fd);
+ timer_fd = -1;
+ }
+}
+
+void *
+_ecore_main_fd_handler_del(Ecore_Fd_Handler *fd_handler)
+{
+ if (fd_handler->delete_me)
+ {
+ ERR("fdh %p deleted twice", fd_handler);
+ return NULL;
+ }
+
+ _ecore_main_fdh_poll_del(fd_handler);
+ fd_handler->delete_me = EINA_TRUE;
+ fd_handlers_to_delete = eina_list_append(fd_handlers_to_delete, fd_handler);
+ if (fd_handler->prep_func && fd_handlers_with_prep)
+ fd_handlers_with_prep = eina_list_remove(fd_handlers_with_prep, fd_handler);
+ if (fd_handler->buf_func && fd_handlers_with_buffer)
+ fd_handlers_with_buffer = eina_list_remove(fd_handlers_with_buffer, fd_handler);
+ return fd_handler->data;
+}
+
+/**
+ * @addtogroup Ecore_Main_Loop_Group
+ *
+ * @{
+ */
+
+/**
+ * Runs a single iteration of the main loop to process everything on the
+ * queue.
+ *
+ * It does everything that is already done inside an @c Ecore main loop, like
+ * checking for expired timers, idlers, etc. But it will do it only once and
+ * return, instead of keep watching for new events.
+ *
+ * DO NOT use this function unless you are the person God comes to ask for
+ * advice when He has trouble managing the Universe.
+ *
+ * @see ecore_main_loop_iterate_may_block()
+ */
+EAPI void
+ecore_main_loop_iterate(void)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN;
+#ifndef USE_G_MAIN_LOOP
+ _ecore_lock();
+ _ecore_time_loop_time = ecore_time_get();
+ _ecore_main_loop_iterate_internal(1);
+ _ecore_unlock();
+#else
+ g_main_context_iteration(NULL, 0);
+#endif
+}
+
+/**
+ * Runs a single iteration of the main loop to process everything on the
+ * queue with block/non-blocking status.
+ *
+ * @param may_block A flag if the main loop has a possibility of blocking.
+ * (@c EINA_TRUE = may block/@c EINA_FALSE = non block)
+ *
+ * This is an extension API for ecore_main_loop_iterate() with additional
+ * parameter. It does everything that is already done inside an
+ * @c Ecore main loop, like checking for expired timers, idlers, etc. But it
+ * will do it only once and return, instead of keep watching for new events.
+ *
+ * DO NOT use this function unless you are the person God comes to ask for
+ * advice when He has trouble managing the Universe.
+ *
+ * @see ecore_main_loop_iterate()
+ */
+EAPI int
+ecore_main_loop_iterate_may_block(int may_block)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(0);
+#ifndef USE_G_MAIN_LOOP
+ _ecore_lock();
+ _ecore_time_loop_time = ecore_time_get();
+in_main_loop++;
+ _ecore_main_loop_iterate_internal(!may_block);
+in_main_loop--;
+ _ecore_unlock();
+ return _ecore_event_exist();
+#else
+ return g_main_context_iteration(NULL, may_block);
+#endif
+}
+
+/**
+ * Runs the application main loop.
+ *
+ * This function will not return until @ref ecore_main_loop_quit is called. It
+ * will check for expired timers, idlers, file descriptors being watched by fd
+ * handlers, etc. Once everything is done, before entering again on idle state,
+ * any callback set as @c Idle_Enterer will be called.
+ *
+ * Each main loop iteration is done by calling ecore_main_loop_iterate()
+ * internally.
+ *
+ * The polling (select) function used can be changed with
+ * ecore_main_loop_select_func_set().
+ *
+ * The function used to check for file descriptors, events, and that has a
+ * timeout for the timers can be changed using
+ * ecore_main_loop_select_func_set().
+ */
+EAPI void
+ecore_main_loop_begin(void)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN;
+#ifndef USE_G_MAIN_LOOP
+ _ecore_lock();
+ in_main_loop++;
+ _ecore_time_loop_time = ecore_time_get();
+ while (do_quit == 0) _ecore_main_loop_iterate_internal(0);
+ do_quit = 0;
+ in_main_loop--;
+ _ecore_unlock();
+#else
+ if (!do_quit)
+ {
+ if (!ecore_main_loop)
+ ecore_main_loop = g_main_loop_new(NULL, FALSE);
+ g_main_loop_run(ecore_main_loop);
+ }
+ do_quit = 0;
+#endif
+}
+
+/**
+ * Quits the main loop once all the events currently on the queue have
+ * been processed.
+ *
+ * This function returns immediately, but will mark the ecore_main_loop_begin()
+ * function to return at the end of the current main loop iteration.
+ */
+EAPI void
+ecore_main_loop_quit(void)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN;
+ do_quit = 1;
+#ifdef USE_G_MAIN_LOOP
+ if (ecore_main_loop)
+ g_main_loop_quit(ecore_main_loop);
+#endif
+}
+
+/**
+ * Sets the function to use when monitoring multiple file descriptors,
+ * and waiting until one of more of the file descriptors before ready
+ * for some class of I/O operation.
+ *
+ * This function will be used instead of the system call select and
+ * could possible be used to integrate the Ecore event loop with an
+ * external event loop.
+ *
+ * @warning you don't know how to use, don't even try to use it.
+ *
+ * @param func The function to be used.
+ */
+EAPI void
+ecore_main_loop_select_func_set(Ecore_Select_Function func)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN;
+ main_loop_select = func;
+}
+
+/**
+ * Gets the select function set by ecore_select_func_set(),
+ * or the native select function if none was set.
+ *
+ */
+EAPI Ecore_Select_Function
+ecore_main_loop_select_func_get(void)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL);
+ return main_loop_select;
+}
+
+Ecore_Fd_Handler *
+_ecore_main_fd_handler_add(int fd,
+ Ecore_Fd_Handler_Flags flags,
+ Ecore_Fd_Cb func,
+ const void *data,
+ Ecore_Fd_Cb buf_func,
+ const void *buf_data)
+{
+ Ecore_Fd_Handler *fdh = NULL;
+
+ if ((fd < 0) || (flags == 0) || (!func)) return NULL;
+
+ fdh = ecore_fd_handler_calloc(1);
+ if (!fdh) return NULL;
+ ECORE_MAGIC_SET(fdh, ECORE_MAGIC_FD_HANDLER);
+ fdh->next_ready = NULL;
+ fdh->fd = fd;
+ fdh->flags = flags;
+ if (_ecore_main_fdh_poll_add(fdh) < 0)
+ {
+ int err = errno;
+ ERR("Failed to add poll on fd %d (errno = %d: %s)!", fd, err, strerror(err));
+ ecore_fd_handler_mp_free(fdh);
+ return NULL;
+ }
+ fdh->read_active = EINA_FALSE;
+ fdh->write_active = EINA_FALSE;
+ fdh->error_active = EINA_FALSE;
+ fdh->delete_me = EINA_FALSE;
+ fdh->func = func;
+ fdh->data = (void *)data;
+ fdh->buf_func = buf_func;
+ if (buf_func)
+ fd_handlers_with_buffer = eina_list_append(fd_handlers_with_buffer, fdh);
+ fdh->buf_data = (void *)buf_data;
+ fd_handlers = (Ecore_Fd_Handler *)
+ eina_inlist_append(EINA_INLIST_GET(fd_handlers),
+ EINA_INLIST_GET(fdh));
+
+ return fdh;
+}
+
+EAPI Ecore_Fd_Handler *
+ecore_main_fd_handler_add(int fd,
+ Ecore_Fd_Handler_Flags flags,
+ Ecore_Fd_Cb func,
+ const void *data,
+ Ecore_Fd_Cb buf_func,
+ const void *buf_data)
+{
+ Ecore_Fd_Handler *fdh = NULL;
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL);
+ _ecore_lock();
+ fdh = _ecore_main_fd_handler_add(fd, flags, func, data, buf_func, buf_data);
+ _ecore_unlock();
+ return fdh;
+}
+
+EAPI Ecore_Fd_Handler *
+ecore_main_fd_handler_file_add(int fd,
+ Ecore_Fd_Handler_Flags flags,
+ Ecore_Fd_Cb func,
+ const void *data,
+ Ecore_Fd_Cb buf_func,
+ const void *buf_data)
+{
+ Ecore_Fd_Handler *fdh = NULL;
+
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL);
+ _ecore_lock();
+
+ if ((fd < 0) || (flags == 0) || (!func)) goto unlock;
+
+ fdh = ecore_fd_handler_calloc(1);
+ if (!fdh) goto unlock;
+ ECORE_MAGIC_SET(fdh, ECORE_MAGIC_FD_HANDLER);
+ fdh->next_ready = NULL;
+ fdh->fd = fd;
+ fdh->flags = flags;
+ fdh->file = EINA_TRUE;
+ if (_ecore_main_fdh_poll_add(fdh) < 0)
+ {
+ int err = errno;
+ ERR("Failed to add poll on fd %d (errno = %d: %s)!", fd, err, strerror(err));
+ ecore_fd_handler_mp_free(fdh);
+ fdh = NULL;
+ goto unlock;
+ }
+ fdh->read_active = EINA_FALSE;
+ fdh->write_active = EINA_FALSE;
+ fdh->error_active = EINA_FALSE;
+ fdh->delete_me = EINA_FALSE;
+ fdh->func = func;
+ fdh->data = (void *)data;
+ fdh->buf_func = buf_func;
+ if (buf_func)
+ fd_handlers_with_buffer = eina_list_append(fd_handlers_with_buffer, fdh);
+ fdh->buf_data = (void *)buf_data;
+ fd_handlers = (Ecore_Fd_Handler *)
+ eina_inlist_append(EINA_INLIST_GET(fd_handlers),
+ EINA_INLIST_GET(fdh));
+ file_fd_handlers = eina_list_append(file_fd_handlers, fdh);
+unlock:
+ _ecore_unlock();
+
+ return fdh;
+}
+
+#ifdef _WIN32
+EAPI Ecore_Win32_Handler *
+ecore_main_win32_handler_add(void *h,
+ Ecore_Win32_Handle_Cb func,
+ const void *data)
+{
+ Ecore_Win32_Handler *wh;
+
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL);
+ if (!h || !func) return NULL;
+
+ wh = ecore_win32_handler_calloc(1);
+ if (!wh) return NULL;
+ ECORE_MAGIC_SET(wh, ECORE_MAGIC_WIN32_HANDLER);
+ wh->h = (HANDLE)h;
+ wh->delete_me = EINA_FALSE;
+ wh->func = func;
+ wh->data = (void *)data;
+ win32_handlers = (Ecore_Win32_Handler *)
+ eina_inlist_append(EINA_INLIST_GET(win32_handlers),
+ EINA_INLIST_GET(wh));
+ return wh;
+}
+
+#else
+EAPI Ecore_Win32_Handler *
+ecore_main_win32_handler_add(void *h EINA_UNUSED,
+ Ecore_Win32_Handle_Cb func EINA_UNUSED,
+ const void *data EINA_UNUSED)
+{
+ return NULL;
+}
+
+#endif
+
+EAPI void *
+ecore_main_fd_handler_del(Ecore_Fd_Handler *fd_handler)
+{
+ void *ret = NULL;
+
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL);
+ _ecore_lock();
+
+ if (!ECORE_MAGIC_CHECK(fd_handler, ECORE_MAGIC_FD_HANDLER))
+ {
+ ECORE_MAGIC_FAIL(fd_handler, ECORE_MAGIC_FD_HANDLER,
+ "ecore_main_fd_handler_del");
+ goto unlock;
+ }
+ ret = _ecore_main_fd_handler_del(fd_handler);
+unlock:
+ _ecore_unlock();
+ return ret;
+}
+
+#ifdef _WIN32
+EAPI void *
+ecore_main_win32_handler_del(Ecore_Win32_Handler *win32_handler)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL);
+ if (!ECORE_MAGIC_CHECK(win32_handler, ECORE_MAGIC_WIN32_HANDLER))
+ {
+ ECORE_MAGIC_FAIL(win32_handler, ECORE_MAGIC_WIN32_HANDLER,
+ "ecore_main_win32_handler_del");
+ return NULL;
+ }
+ win32_handler->delete_me = EINA_TRUE;
+ win32_handlers_delete_me = EINA_TRUE;
+ return win32_handler->data;
+}
+
+#else
+EAPI void *
+ecore_main_win32_handler_del(Ecore_Win32_Handler *win32_handler EINA_UNUSED)
+{
+ return NULL;
+}
+
+#endif
+
+EAPI void
+ecore_main_fd_handler_prepare_callback_set(Ecore_Fd_Handler *fd_handler,
+ Ecore_Fd_Prep_Cb func,
+ const void *data)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN;
+ _ecore_lock();
+
+ if (!ECORE_MAGIC_CHECK(fd_handler, ECORE_MAGIC_FD_HANDLER))
+ {
+ ECORE_MAGIC_FAIL(fd_handler, ECORE_MAGIC_FD_HANDLER,
+ "ecore_main_fd_handler_prepare_callback_set");
+ goto unlock;
+ }
+ fd_handler->prep_func = func;
+ fd_handler->prep_data = (void *)data;
+ if ((!fd_handlers_with_prep) ||
+ (fd_handlers_with_prep && (!eina_list_data_find(fd_handlers_with_prep, fd_handler))))
+ /* FIXME: THIS WILL NOT SCALE WITH LOTS OF PREP FUNCTIONS!!! */
+ fd_handlers_with_prep = eina_list_append(fd_handlers_with_prep, fd_handler);
+unlock:
+ _ecore_unlock();
+}
+
+EAPI int
+ecore_main_fd_handler_fd_get(Ecore_Fd_Handler *fd_handler)
+{
+ int fd = -1;
+
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(-1);
+ _ecore_lock();
+
+ if (!ECORE_MAGIC_CHECK(fd_handler, ECORE_MAGIC_FD_HANDLER))
+ {
+ ECORE_MAGIC_FAIL(fd_handler, ECORE_MAGIC_FD_HANDLER,
+ "ecore_main_fd_handler_fd_get");
+ goto unlock;
+ }
+ fd = fd_handler->fd;
+unlock:
+ _ecore_unlock();
+ return fd;
+}
+
+EAPI Eina_Bool
+ecore_main_fd_handler_active_get(Ecore_Fd_Handler *fd_handler,
+ Ecore_Fd_Handler_Flags flags)
+{
+ int ret = EINA_FALSE;
+
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(EINA_FALSE);
+ _ecore_lock();
+
+ if (!ECORE_MAGIC_CHECK(fd_handler, ECORE_MAGIC_FD_HANDLER))
+ {
+ ECORE_MAGIC_FAIL(fd_handler, ECORE_MAGIC_FD_HANDLER,
+ "ecore_main_fd_handler_active_get");
+ goto unlock;
+ }
+ if ((flags & ECORE_FD_READ) && (fd_handler->read_active)) ret = EINA_TRUE;
+ if ((flags & ECORE_FD_WRITE) && (fd_handler->write_active)) ret = EINA_TRUE;
+ if ((flags & ECORE_FD_ERROR) && (fd_handler->error_active)) ret = EINA_TRUE;
+unlock:
+ _ecore_unlock();
+ return ret;
+}
+
+EAPI void
+ecore_main_fd_handler_active_set(Ecore_Fd_Handler *fd_handler,
+ Ecore_Fd_Handler_Flags flags)
+{
+ int ret;
+
+ EINA_MAIN_LOOP_CHECK_RETURN;
+ _ecore_lock();
+
+ if (!ECORE_MAGIC_CHECK(fd_handler, ECORE_MAGIC_FD_HANDLER))
+ {
+ ECORE_MAGIC_FAIL(fd_handler, ECORE_MAGIC_FD_HANDLER,
+ "ecore_main_fd_handler_active_set");
+ goto unlock;
+ }
+ fd_handler->flags = flags;
+ ret = _ecore_main_fdh_poll_modify(fd_handler);
+ if (ret < 0)
+ {
+ ERR("Failed to mod epoll fd %d: %s!", fd_handler->fd, strerror(ret));
+ }
+unlock:
+ _ecore_unlock();
+}
+
+/**
+ * @}
+ */
+
+void
+_ecore_main_shutdown(void)
+{
+ if (in_main_loop)
+ {
+ ERR("\n"
+ "*** ECORE WARNING: Calling ecore_shutdown() while still in the main loop.\n"
+ "*** Program may crash or behave strangely now.");
+ return;
+ }
+ while (fd_handlers)
+ {
+ Ecore_Fd_Handler *fdh;
+
+ fdh = fd_handlers;
+ fd_handlers = (Ecore_Fd_Handler *)eina_inlist_remove(EINA_INLIST_GET(fd_handlers),
+ EINA_INLIST_GET(fdh));
+ ECORE_MAGIC_SET(fdh, ECORE_MAGIC_NONE);
+ ecore_fd_handler_mp_free(fdh);
+ }
+ if (fd_handlers_with_buffer)
+ fd_handlers_with_buffer = eina_list_free(fd_handlers_with_buffer);
+ if (fd_handlers_with_prep)
+ fd_handlers_with_prep = eina_list_free(fd_handlers_with_prep);
+ if (fd_handlers_to_delete)
+ fd_handlers_to_delete = eina_list_free(fd_handlers_to_delete);
+ if (file_fd_handlers)
+ file_fd_handlers = eina_list_free(file_fd_handlers);
+
+ fd_handlers_to_call = NULL;
+ fd_handlers_to_call_current = NULL;
+ fd_handlers_to_delete = NULL;
+ fd_handler_current = NULL;
+
+#ifdef _WIN32
+ while (win32_handlers)
+ {
+ Ecore_Win32_Handler *wh;
+
+ wh = win32_handlers;
+ win32_handlers = (Ecore_Win32_Handler *)eina_inlist_remove(EINA_INLIST_GET(win32_handlers),
+ EINA_INLIST_GET(wh));
+ ECORE_MAGIC_SET(wh, ECORE_MAGIC_NONE);
+ ecore_win32_handler_mp_free(wh);
+ }
+ win32_handlers_delete_me = EINA_FALSE;
+ win32_handler_current = NULL;
+#endif
+}
+
+static void
+_ecore_main_prepare_handlers(void)
+{
+ Ecore_Fd_Handler *fdh;
+ Eina_List *l, *l2;
+
+ /* call the prepare callback for all handlers with prep functions */
+ EINA_LIST_FOREACH_SAFE(fd_handlers_with_prep, l, l2, fdh)
+ {
+ if (!fdh)
+ {
+ fd_handlers_with_prep = eina_list_remove_list(l, fd_handlers_with_prep);
+ continue;
+ }
+ if (!fdh->delete_me && fdh->prep_func)
+ {
+ fdh->references++;
+ _ecore_call_prep_cb(fdh->prep_func, fdh->prep_data, fdh);
+ fdh->references--;
+ }
+ else
+ fd_handlers_with_prep = eina_list_remove_list(fd_handlers_with_prep, l);
+ }
+}
+
+#ifndef USE_G_MAIN_LOOP
+static int
+_ecore_main_select(double timeout)
+{
+ struct timeval tv, *t;
+ fd_set rfds, wfds, exfds;
+ Ecore_Fd_Handler *fdh;
+ Eina_List *l;
+ int max_fd;
+ int ret;
+
+ t = NULL;
+ if ((!ECORE_FINITE(timeout)) || (timeout == 0.0)) /* finite() tests for NaN, too big, too small, and infinity. */
+ {
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ t = &tv;
+ }
+ else if (timeout > 0.0)
+ {
+ int sec, usec;
+
+#ifdef FIX_HZ
+ timeout += (0.5 / HZ);
+ sec = (int)timeout;
+ usec = (int)((timeout - (double)sec) * 1000000);
+#else
+ sec = (int)timeout;
+ usec = (int)((timeout - (double)sec) * 1000000);
+#endif
+ tv.tv_sec = sec;
+ tv.tv_usec = usec;
+ t = &tv;
+ }
+ max_fd = 0;
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+ FD_ZERO(&exfds);
+
+ /* call the prepare callback for all handlers */
+ if (fd_handlers_with_prep)
+ _ecore_main_prepare_handlers();
+
+ if (!HAVE_EPOLL || epoll_fd < 0)
+ {
+ EINA_INLIST_FOREACH(fd_handlers, fdh)
+ {
+ if (!fdh->delete_me)
+ {
+ if (fdh->flags & ECORE_FD_READ)
+ {
+ FD_SET(fdh->fd, &rfds);
+ if (fdh->fd > max_fd) max_fd = fdh->fd;
+ }
+ if (fdh->flags & ECORE_FD_WRITE)
+ {
+ FD_SET(fdh->fd, &wfds);
+ if (fdh->fd > max_fd) max_fd = fdh->fd;
+ }
+ if (fdh->flags & ECORE_FD_ERROR)
+ {
+ FD_SET(fdh->fd, &exfds);
+ if (fdh->fd > max_fd) max_fd = fdh->fd;
+ }
+ }
+ }
+ }
+ else
+ {
+ /* polling on the epoll fd will wake when an fd in the epoll set is active */
+ max_fd = _ecore_get_epoll_fd();
+ FD_SET(max_fd, &rfds);
+ }
+ EINA_LIST_FOREACH(file_fd_handlers, l, fdh)
+ if (!fdh->delete_me)
+ {
+ if (fdh->flags & ECORE_FD_READ)
+ {
+ FD_SET(fdh->fd, &rfds);
+ if (fdh->fd > max_fd) max_fd = fdh->fd;
+ }
+ if (fdh->flags & ECORE_FD_WRITE)
+ {
+ FD_SET(fdh->fd, &wfds);
+ if (fdh->fd > max_fd) max_fd = fdh->fd;
+ }
+ if (fdh->flags & ECORE_FD_ERROR)
+ {
+ FD_SET(fdh->fd, &exfds);
+ if (fdh->fd > max_fd) max_fd = fdh->fd;
+ }
+ if (fdh->fd > max_fd) max_fd = fdh->fd;
+ }
+ if (_ecore_signal_count_get()) return -1;
+
+ _ecore_unlock();
+ ret = main_loop_select(max_fd + 1, &rfds, &wfds, &exfds, t);
+ _ecore_lock();
+
+ _ecore_time_loop_time = ecore_time_get();
+ if (ret < 0)
+ {
+#ifndef _WIN32
+ if (errno == EINTR) return -1;
+ else if (errno == EBADF)
+ _ecore_main_fd_handlers_bads_rem();
+#endif
+ }
+ if (ret > 0)
+ {
+ if (HAVE_EPOLL && epoll_fd >= 0)
+ _ecore_main_fdh_epoll_mark_active();
+ else
+ {
+ EINA_INLIST_FOREACH(fd_handlers, fdh)
+ {
+ if (!fdh->delete_me)
+ {
+ if (FD_ISSET(fdh->fd, &rfds))
+ fdh->read_active = EINA_TRUE;
+ if (FD_ISSET(fdh->fd, &wfds))
+ fdh->write_active = EINA_TRUE;
+ if (FD_ISSET(fdh->fd, &exfds))
+ fdh->error_active = EINA_TRUE;
+ _ecore_try_add_to_call_list(fdh);
+ }
+ }
+ }
+ EINA_LIST_FOREACH(file_fd_handlers, l, fdh)
+ {
+ if (!fdh->delete_me)
+ {
+ if (FD_ISSET(fdh->fd, &rfds))
+ fdh->read_active = EINA_TRUE;
+ if (FD_ISSET(fdh->fd, &wfds))
+ fdh->write_active = EINA_TRUE;
+ if (FD_ISSET(fdh->fd, &exfds))
+ fdh->error_active = EINA_TRUE;
+ _ecore_try_add_to_call_list(fdh);
+ }
+ }
+ _ecore_main_fd_handlers_cleanup();
+#ifdef _WIN32
+ _ecore_main_win32_handlers_cleanup();
+#endif
+ return 1;
+ }
+ return 0;
+}
+
+#endif
+
+#ifndef _WIN32
+# ifndef USE_G_MAIN_LOOP
+static void
+_ecore_main_fd_handlers_bads_rem(void)
+{
+ Ecore_Fd_Handler *fdh;
+ Eina_Inlist *l;
+ int found = 0;
+
+ ERR("Removing bad fds");
+ for (l = EINA_INLIST_GET(fd_handlers); l; )
+ {
+ fdh = (Ecore_Fd_Handler *)l;
+ l = l->next;
+ errno = 0;
+
+ if ((fcntl(fdh->fd, F_GETFD) < 0) && (errno == EBADF))
+ {
+ ERR("Found bad fd at index %d", fdh->fd);
+ if (fdh->flags & ECORE_FD_ERROR)
+ {
+ ERR("Fd set for error! calling user");
+ fdh->references++;
+ if (!_ecore_call_fd_cb(fdh->func, fdh->data, fdh))
+ {
+ ERR("Fd function err returned 0, remove it");
+ if (!fdh->delete_me)
+ {
+ fdh->delete_me = EINA_TRUE;
+ fd_handlers_to_delete = eina_list_append(fd_handlers_to_delete, fdh);
+ }
+ found++;
+ }
+ fdh->references--;
+ }
+ else
+ {
+ ERR("Problematic fd found at %d! setting it for delete", fdh->fd);
+ if (!fdh->delete_me)
+ {
+ fdh->delete_me = EINA_TRUE;
+ fd_handlers_to_delete = eina_list_append(fd_handlers_to_delete, fdh);
+ }
+
+ found++;
+ }
+ }
+ }
+ if (found == 0)
+ {
+# ifdef HAVE_GLIB
+ ERR("No bad fd found. Maybe a foreign fd from glib?");
+# else
+ ERR("No bad fd found. EEEK!");
+# endif
+ }
+ _ecore_main_fd_handlers_cleanup();
+}
+
+# endif
+#endif
+
+static void
+_ecore_main_fd_handlers_cleanup(void)
+{
+ Ecore_Fd_Handler *fdh;
+ Eina_List *l, *l2;
+
+ if (!fd_handlers_to_delete) return;
+ EINA_LIST_FOREACH_SAFE(fd_handlers_to_delete, l, l2, fdh)
+ {
+ if (!fdh)
+ {
+ fd_handlers_to_delete = eina_list_remove_list(l, fd_handlers_to_delete);
+ continue;
+ }
+ /* fdh->delete_me should be set for all fdhs at the start of the list */
+ if (fdh->references)
+ continue;
+ if (fdh->buf_func && fd_handlers_with_buffer)
+ fd_handlers_with_buffer = eina_list_remove(fd_handlers_with_buffer, fdh);
+ if (fdh->prep_func && fd_handlers_with_prep)
+ fd_handlers_with_prep = eina_list_remove(fd_handlers_with_prep, fdh);
+ fd_handlers = (Ecore_Fd_Handler *)
+ eina_inlist_remove(EINA_INLIST_GET(fd_handlers), EINA_INLIST_GET(fdh));
+ if (fdh->file)
+ file_fd_handlers = eina_list_remove(file_fd_handlers, fdh);
+ ECORE_MAGIC_SET(fdh, ECORE_MAGIC_NONE);
+ ecore_fd_handler_mp_free(fdh);
+ fd_handlers_to_delete = eina_list_remove_list(fd_handlers_to_delete, l);
+ }
+}
+
+#ifdef _WIN32
+static void
+_ecore_main_win32_handlers_cleanup(void)
+{
+ Ecore_Win32_Handler *wh;
+ Eina_Inlist *l;
+ int deleted_in_use = 0;
+
+ if (!win32_handlers_delete_me) return;
+ for (l = EINA_INLIST_GET(win32_handlers); l; )
+ {
+ wh = (Ecore_Win32_Handler *)l;
+
+ l = l->next;
+ if (wh->delete_me)
+ {
+ if (wh->references)
+ {
+ deleted_in_use++;
+ continue;
+ }
+
+ win32_handlers = (Ecore_Win32_Handler *)
+ eina_inlist_remove(EINA_INLIST_GET(win32_handlers),
+ EINA_INLIST_GET(wh));
+ ECORE_MAGIC_SET(wh, ECORE_MAGIC_NONE);
+ ecore_win32_handler_mp_free(wh);
+ }
+ }
+ if (!deleted_in_use) win32_handlers_delete_me = EINA_FALSE;
+}
+
+#endif
+
+static void
+_ecore_main_fd_handlers_call(void)
+{
+ /* grab a new list */
+ if (!fd_handlers_to_call_current)
+ {
+ fd_handlers_to_call_current = fd_handlers_to_call;
+ fd_handlers_to_call = NULL;
+ }
+
+ while (fd_handlers_to_call_current)
+ {
+ Ecore_Fd_Handler *fdh = fd_handlers_to_call_current;
+
+ if (!fdh->delete_me)
+ {
+ if ((fdh->read_active) ||
+ (fdh->write_active) ||
+ (fdh->error_active))
+ {
+ fdh->references++;
+ if (!_ecore_call_fd_cb(fdh->func, fdh->data, fdh))
+ {
+ if (!fdh->delete_me)
+ {
+ fdh->delete_me = EINA_TRUE;
+ fd_handlers_to_delete = eina_list_append(fd_handlers_to_delete, fdh);
+ }
+ }
+ fdh->references--;
+ _ecore_fd_valid();
+
+ fdh->read_active = EINA_FALSE;
+ fdh->write_active = EINA_FALSE;
+ fdh->error_active = EINA_FALSE;
+ }
+ }
+
+ /* stop when we point to ourselves */
+ if (fdh->next_ready == fdh)
+ {
+ fdh->next_ready = NULL;
+ fd_handlers_to_call_current = NULL;
+ break;
+ }
+
+ fd_handlers_to_call_current = fdh->next_ready;
+ fdh->next_ready = NULL;
+ }
+}
+
+static int
+_ecore_main_fd_handlers_buf_call(void)
+{
+ Ecore_Fd_Handler *fdh;
+ Eina_List *l, *l2;
+ int ret;
+
+ ret = 0;
+ EINA_LIST_FOREACH_SAFE(fd_handlers_with_buffer, l, l2, fdh)
+ {
+ if (!fdh)
+ {
+ fd_handlers_with_buffer = eina_list_remove_list(l, fd_handlers_with_buffer);
+ continue;
+ }
+ if ((!fdh->delete_me) && fdh->buf_func)
+ {
+ fdh->references++;
+ if (_ecore_call_fd_cb(fdh->buf_func, fdh->buf_data, fdh))
+ {
+ ret |= _ecore_call_fd_cb(fdh->func, fdh->data, fdh);
+ fdh->read_active = EINA_TRUE;
+ _ecore_try_add_to_call_list(fdh);
+ }
+ fdh->references--;
+ }
+ else
+ fd_handlers_with_buffer = eina_list_remove_list(fd_handlers_with_buffer, l);
+ }
+ return ret;
+}
+
+#ifndef USE_G_MAIN_LOOP
+
+enum {
+ SPIN_MORE,
+ SPIN_RESTART,
+ LOOP_CONTINUE
+};
+
+static int
+_ecore_main_loop_spin_core(void)
+{
+ /* as we are spinning we need to update loop time per spin */
+ _ecore_time_loop_time = ecore_time_get();
+ /* call all idlers, which returns false if no more idelrs exist */
+ if (!_ecore_idler_all_call()) return SPIN_RESTART;
+ /* sneaky - drop through or if checks - the first one to succeed
+ * drops through and returns "continue" so further ones dont run */
+ if ((_ecore_main_select(0.0) > 0) || (_ecore_event_exist()) ||
+ (_ecore_signal_count_get() > 0) || (do_quit))
+ return LOOP_CONTINUE;
+ /* default - spin more */
+ return SPIN_MORE;
+}
+
+static int
+_ecore_main_loop_spin_no_timers(void)
+{
+ /* if we have idlers we HAVE to spin and handle everything
+ * in a polling way - spin in a tight polling loop */
+ for (;; )
+ {
+ int action = _ecore_main_loop_spin_core();
+ if (action != SPIN_MORE) return action;
+ /* if an idler has added a timer then we need to go through
+ * the start of the spin cycle again to handle cases properly */
+ if (_ecore_timers_exists()) return SPIN_RESTART;
+ }
+ /* just contiune handling events etc. */
+ return LOOP_CONTINUE;
+}
+
+static int
+_ecore_main_loop_spin_timers(void)
+{
+ /* if we have idlers we HAVE to spin and handle everything
+ * in a polling way - spin in a tight polling loop */
+ for (;; )
+ {
+ int action = _ecore_main_loop_spin_core();
+ if (action != SPIN_MORE) return action;
+ /* if next timer expires now or in the past - stop spinning and
+ * continue the mainloop walk as our "select" timeout has
+ * expired now */
+ if (_ecore_timer_next_get() <= 0.0) return LOOP_CONTINUE;
+ }
+ /* just contiune handling events etc. */
+ return LOOP_CONTINUE;
+}
+
+static void
+_ecore_fps_marker_1(void)
+{
+ if (!_ecore_fps_debug) return;
+ t2 = ecore_time_get();
+ if ((t1 > 0.0) && (t2 > 0.0)) _ecore_fps_debug_runtime_add(t2 - t1);
+}
+
+static void
+_ecore_fps_marker_2(void)
+{
+ if (!_ecore_fps_debug) return;
+ t1 = ecore_time_get();
+}
+
+static void
+_ecore_main_loop_iterate_internal(int once_only)
+{
+ double next_time = -1.0;
+
+ in_main_loop++;
+ /* expire any timers */
+ _ecore_timer_expired_timers_call(_ecore_time_loop_time);
+ _ecore_timer_cleanup();
+
+ /* process signals into events .... */
+ _ecore_signal_received_process();
+ /* if as a result of timers/animators or signals we have accumulated
+ * events, then instantly handle them */
+ if (_ecore_event_exist())
+ {
+ /* but first conceptually enter an idle state */
+ _ecore_idle_enterer_call();
+ _ecore_throttle();
+ /* now quickly poll to see which input fd's are active */
+ _ecore_main_select(0.0);
+ /* allow newly queued timers to expire from now on */
+ _ecore_timer_enable_new();
+ /* go straight to processing the events we had queued */
+ goto process_all;
+ }
+
+ if (once_only)
+ {
+ /* in once_only mode we should quickly poll for inputs, signals
+ * if we got any events or signals, allow new timers to process.
+ * use bitwise or to force both conditions to be tested and
+ * merged together */
+ if (_ecore_main_select(0.0) | _ecore_signal_count_get())
+ {
+ _ecore_timer_enable_new();
+ goto process_all;
+ }
+ }
+ else
+ {
+ /* call idle enterers ... */
+ _ecore_idle_enterer_call();
+ _ecore_throttle();
+ }
+
+ /* if these calls caused any buffered events to appear - deal with them */
+ if (fd_handlers_with_buffer)
+ _ecore_main_fd_handlers_buf_call();
+
+ /* if there are any (buffered fd handling may generate them)
+ * then jump to processing them */
+ if (_ecore_event_exist())
+ {
+ _ecore_main_select(0.0);
+ _ecore_timer_enable_new();
+ goto process_all;
+ }
+
+ if (once_only)
+ {
+ /* in once_only mode enter idle here instead and then return */
+ _ecore_idle_enterer_call();
+ _ecore_throttle();
+ _ecore_timer_enable_new();
+ goto done;
+ }
+
+ _ecore_fps_marker_1();
+
+ /* start of the sleeping or looping section */
+start_loop: /***************************************************************/
+ /* any timers re-added as a result of these are allowed to go */
+ _ecore_timer_enable_new();
+ /* if we have been asked to quit the mainloop then exit at this point */
+ if (do_quit)
+ {
+ _ecore_timer_enable_new();
+ goto done;
+ }
+ if (!_ecore_event_exist())
+ {
+ /* init flags */
+ next_time = _ecore_timer_next_get();
+ /* no idlers */
+ if (!_ecore_idler_exist())
+ {
+ /* sleep until timeout or forever (-1.0) waiting for on fds */
+ _ecore_main_select(next_time);
+ }
+ else
+ {
+ int action = LOOP_CONTINUE;
+
+ /* no timers - spin */
+ if (next_time < 0) action = _ecore_main_loop_spin_no_timers();
+ /* timers - spin */
+ else action = _ecore_main_loop_spin_timers();
+ if (action == SPIN_RESTART) goto start_loop;
+ }
+ }
+ _ecore_fps_marker_2();
+
+ /* actually wake up and deal with input, events etc. */
+process_all: /***********************************************************/
+
+ /* we came out of our "wait state" so idle has exited */
+ if (!once_only) _ecore_idle_exiter_call();
+ /* call the fd handler per fd that became alive... */
+ /* this should read or write any data to the monitored fd and then */
+ /* post events onto the ecore event pipe if necessary */
+ _ecore_main_fd_handlers_call();
+ if (fd_handlers_with_buffer) _ecore_main_fd_handlers_buf_call();
+ /* process signals into events .... */
+ _ecore_signal_received_process();
+ /* handle events ... */
+ _ecore_event_call();
+ _ecore_main_fd_handlers_cleanup();
+
+ if (once_only)
+ {
+ /* if in once_only mode handle idle exiting */
+ _ecore_idle_enterer_call();
+ _ecore_throttle();
+ }
+
+done: /*******************************************************************/
+ in_main_loop--;
+}
+
+#endif
+
+#ifdef _WIN32
+static int
+_ecore_main_win32_select(int nfds EINA_UNUSED,
+ fd_set *readfds,
+ fd_set *writefds,
+ fd_set *exceptfds,
+ struct timeval *tv)
+{
+ HANDLE objects[MAXIMUM_WAIT_OBJECTS];
+ int sockets[MAXIMUM_WAIT_OBJECTS];
+ Ecore_Fd_Handler *fdh;
+ Ecore_Win32_Handler *wh;
+ unsigned int objects_nbr = 0;
+ unsigned int handles_nbr = 0;
+ unsigned int events_nbr = 0;
+ DWORD result;
+ DWORD timeout;
+ MSG msg;
+ unsigned int i;
+ int res;
+
+ /* Create an event object per socket */
+ EINA_INLIST_FOREACH(fd_handlers, fdh)
+ {
+ WSAEVENT event;
+ long network_event;
+
+ network_event = 0;
+ if (readfds)
+ {
+ if (FD_ISSET(fdh->fd, readfds))
+ network_event |= FD_READ;
+ }
+ if (writefds)
+ {
+ if (FD_ISSET(fdh->fd, writefds))
+ network_event |= FD_WRITE;
+ }
+ if (exceptfds)
+ {
+ if (FD_ISSET(fdh->fd, exceptfds))
+ network_event |= FD_OOB;
+ }
+
+ if (network_event)
+ {
+ event = WSACreateEvent();
+ WSAEventSelect(fdh->fd, event, network_event);
+ objects[objects_nbr] = event;
+ sockets[events_nbr] = fdh->fd;
+ events_nbr++;
+ objects_nbr++;
+ }
+ }
+
+ /* store the HANDLEs in the objects to wait for */
+ EINA_INLIST_FOREACH(win32_handlers, wh)
+ {
+ objects[objects_nbr] = wh->h;
+ handles_nbr++;
+ objects_nbr++;
+ }
+
+ /* Empty the queue before waiting */
+ while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ /* Wait for any message sent or posted to this queue */
+ /* or for one of the passed handles be set to signaled. */
+ if (!tv)
+ timeout = INFINITE;
+ else
+ timeout = (DWORD)((tv->tv_sec * 1000.0) + (tv->tv_usec / 1000.0));
+
+ if (timeout == 0) return 0;
+
+ result = MsgWaitForMultipleObjects(objects_nbr, (const HANDLE *)objects, EINA_FALSE,
+ timeout, QS_ALLINPUT);
+
+ if (readfds)
+ FD_ZERO(readfds);
+ if (writefds)
+ FD_ZERO(writefds);
+ if (exceptfds)
+ FD_ZERO(exceptfds);
+
+ /* The result tells us the type of event we have. */
+ if (result == WAIT_FAILED)
+ {
+ char *m;
+
+ m = evil_last_error_get();
+ ERR("%s", m);
+ free(m);
+ res = -1;
+ }
+ else if (result == WAIT_TIMEOUT)
+ {
+ /* ERR("time out\n"); */
+ res = 0;
+ }
+ else if (result == (WAIT_OBJECT_0 + objects_nbr))
+ {
+ while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ res = 0;
+ }
+ else if ((result >= 0) && (result < WAIT_OBJECT_0 + events_nbr))
+ {
+ WSANETWORKEVENTS network_event;
+
+ WSAEnumNetworkEvents(sockets[result], objects[result], &network_event);
+
+ if ((network_event.lNetworkEvents & FD_READ) && readfds)
+ FD_SET(sockets[result], readfds);
+ if ((network_event.lNetworkEvents & FD_WRITE) && writefds)
+ FD_SET(sockets[result], writefds);
+ if ((network_event.lNetworkEvents & FD_OOB) && exceptfds)
+ FD_SET(sockets[result], exceptfds);
+
+ res = 1;
+ }
+ else if ((result >= (WAIT_OBJECT_0 + events_nbr)) &&
+ (result < (WAIT_OBJECT_0 + objects_nbr)))
+ {
+ if (!win32_handler_current)
+ {
+ /* regular main loop, start from head */
+ win32_handler_current = win32_handlers;
+ }
+ else
+ {
+ /* recursive main loop, continue from where we were */
+ win32_handler_current = (Ecore_Win32_Handler *)EINA_INLIST_GET(win32_handler_current)->next;
+ }
+
+ while (win32_handler_current)
+ {
+ wh = win32_handler_current;
+
+ if (objects[result - WAIT_OBJECT_0] == wh->h)
+ {
+ if (!wh->delete_me)
+ {
+ wh->references++;
+ if (!wh->func(wh->data, wh))
+ {
+ wh->delete_me = EINA_TRUE;
+ win32_handlers_delete_me = EINA_TRUE;
+ }
+ wh->references--;
+ }
+ }
+ if (win32_handler_current) /* may have changed in recursive main loops */
+ win32_handler_current = (Ecore_Win32_Handler *)EINA_INLIST_GET(win32_handler_current)->next;
+ }
+ res = 1;
+ }
+ else
+ {
+ ERR("unknown result...\n");
+ res = -1;
+ }
+
+ /* Remove event objects again */
+ for (i = 0; i < events_nbr; i++) WSACloseEvent(objects[i]);
+
+ return res;
+}
+
+#endif
diff --git a/src/lib/ecore/ecore_pipe.c b/src/lib/ecore/ecore_pipe.c
new file mode 100644
index 0000000000..c6962a5d58
--- /dev/null
+++ b/src/lib/ecore/ecore_pipe.c
@@ -0,0 +1,748 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <math.h>
+
+#ifdef HAVE_ISFINITE
+# define ECORE_FINITE(t) isfinite(t)
+#else
+# ifdef _MSC_VER
+# define ECORE_FINITE(t) _finite(t)
+# else
+# define ECORE_FINITE(t) finite(t)
+# endif
+#endif
+
+#define FIX_HZ 1
+
+#ifdef FIX_HZ
+# ifndef _MSC_VER
+# include <sys/param.h>
+# endif
+# ifndef HZ
+# define HZ 100
+# endif
+#endif
+
+#ifdef HAVE_EVIL
+# include <Evil.h>
+#endif
+
+#ifdef HAVE_ESCAPE
+# include <Escape.h>
+#endif
+
+#ifdef HAVE_EXOTIC
+# include <Exotic.h>
+#endif
+
+/*
+ * On Windows, pipe() is implemented with sockets.
+ * Contrary to Linux, Windows uses different functions
+ * for sockets and fd's: write() is for fd's and send
+ * is for sockets. So I need to put some win32 code
+ * here. I can't think of a solution where the win32
+ * code is in Evil and not here.
+ */
+
+#ifdef _WIN32
+
+# include <winsock2.h>
+
+# define pipe_write(fd, buffer, size) send((fd), (char *)(buffer), size, 0)
+# define pipe_read(fd, buffer, size) recv((fd), (char *)(buffer), size, 0)
+# define pipe_close(fd) closesocket(fd)
+# define PIPE_FD_INVALID INVALID_SOCKET
+# define PIPE_FD_ERROR SOCKET_ERROR
+
+#else
+
+# include <unistd.h>
+# include <fcntl.h>
+
+# define pipe_write(fd, buffer, size) write((fd), buffer, size)
+# define pipe_read(fd, buffer, size) read((fd), buffer, size)
+# define pipe_close(fd) close(fd)
+# define PIPE_FD_INVALID -1
+# define PIPE_FD_ERROR -1
+
+#endif /* ! _WIN32 */
+
+#include "Ecore.h"
+#include "ecore_private.h"
+
+/* How of then we should retry to write to the pipe */
+#define ECORE_PIPE_WRITE_RETRY 6
+
+struct _Ecore_Pipe
+{
+ ECORE_MAGIC;
+ int fd_read;
+ int fd_write;
+ Ecore_Fd_Handler *fd_handler;
+ const void *data;
+ Ecore_Pipe_Cb handler;
+ unsigned int len;
+ int handling;
+ size_t already_read;
+ void *passed_data;
+ int message;
+ Eina_Bool delete_me : 1;
+};
+GENERIC_ALLOC_SIZE_DECLARE(Ecore_Pipe);
+
+static Eina_Bool _ecore_pipe_read(void *data,
+ Ecore_Fd_Handler *fd_handler);
+
+/**
+ * @addtogroup Ecore_Pipe_Group
+ *
+ * @{
+ */
+
+/**
+ * Create two file descriptors (sockets on Windows). Add
+ * a callback that will be called when the file descriptor that
+ * is listened receives data. An event is also put in the event
+ * queue when data is received.
+ *
+ * @param handler The handler called when data is received.
+ * @param data Data to pass to @p handler when it is called.
+ * @return A newly created Ecore_Pipe object if successful.
+ * @c NULL otherwise.
+ */
+EAPI Ecore_Pipe *
+ecore_pipe_add(Ecore_Pipe_Cb handler,
+ const void *data)
+{
+ Ecore_Pipe *p;
+
+ _ecore_lock();
+ p = _ecore_pipe_add(handler, data);
+ _ecore_unlock();
+
+ return p;
+}
+
+/**
+ * Free an Ecore_Pipe object created with ecore_pipe_add().
+ *
+ * @param p The Ecore_Pipe object to be freed.
+ * @return The pointer to the private data
+ */
+EAPI void *
+ecore_pipe_del(Ecore_Pipe *p)
+{
+ void *r;
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL);
+ _ecore_lock();
+ r = _ecore_pipe_del(p);
+ _ecore_unlock();
+ return r;
+}
+
+/**
+ * Close the read end of an Ecore_Pipe object created with ecore_pipe_add().
+ *
+ * @param p The Ecore_Pipe object.
+ */
+EAPI void
+ecore_pipe_read_close(Ecore_Pipe *p)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN;
+ _ecore_lock();
+ if (!ECORE_MAGIC_CHECK(p, ECORE_MAGIC_PIPE))
+ {
+ ECORE_MAGIC_FAIL(p, ECORE_MAGIC_PIPE, "ecore_pipe_read_close");
+ goto out;
+ }
+ if (p->fd_handler)
+ {
+ _ecore_main_fd_handler_del(p->fd_handler);
+ p->fd_handler = NULL;
+ }
+ if (p->fd_read != PIPE_FD_INVALID)
+ {
+ pipe_close(p->fd_read);
+ p->fd_read = PIPE_FD_INVALID;
+ }
+out:
+ _ecore_unlock();
+}
+
+/**
+ * Stop monitoring if necessary the pipe for reading. See ecore_pipe_thaw()
+ * for monitoring it again.
+ *
+ * @param p The Ecore_Pipe object.
+ * @since 1.1
+ */
+EAPI void
+ecore_pipe_freeze(Ecore_Pipe *p)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN;
+ _ecore_lock();
+ if (!ECORE_MAGIC_CHECK(p, ECORE_MAGIC_PIPE))
+ {
+ ECORE_MAGIC_FAIL(p, ECORE_MAGIC_PIPE, "ecore_pipe_read_freeze");
+ goto out;
+ }
+ if (p->fd_handler)
+ {
+ _ecore_main_fd_handler_del(p->fd_handler);
+ p->fd_handler = NULL;
+ }
+out:
+ _ecore_unlock();
+}
+
+/**
+ * Start monitoring again the pipe for reading. See ecore_pipe_freeze() for
+ * stopping the monitoring activity. This will not work if
+ * ecore_pipe_read_close() was previously called on the same pipe.
+ *
+ * @param p The Ecore_Pipe object.
+ * @since 1.1
+ */
+EAPI void
+ecore_pipe_thaw(Ecore_Pipe *p)
+{
+ EINA_MAIN_LOOP_CHECK_RETURN;
+ _ecore_lock();
+ if (!ECORE_MAGIC_CHECK(p, ECORE_MAGIC_PIPE))
+ {
+ ECORE_MAGIC_FAIL(p, ECORE_MAGIC_PIPE, "ecore_pipe_read_thaw");
+ goto out;
+ }
+ if (!p->fd_handler && p->fd_read != PIPE_FD_INVALID)
+ {
+ p->fd_handler = ecore_main_fd_handler_add(p->fd_read,
+ ECORE_FD_READ,
+ _ecore_pipe_read,
+ p,
+ NULL, NULL);
+ }
+out:
+ _ecore_unlock();
+}
+
+/**
+ * @brief Wait from another thread on the read side of a pipe.
+ *
+ * @param p The pipe to watch on.
+ * @param message_count The minimal number of message to wait before exiting.
+ * @param wait The amount of time in second to wait before exiting.
+ * @return the number of message catched during that wait call.
+ * @since 1.1
+ *
+ * Negative value for @p wait means infite wait.
+ */
+EAPI int
+ecore_pipe_wait(Ecore_Pipe *p,
+ int message_count,
+ double wait)
+{
+ int r;
+ _ecore_lock();
+ r = _ecore_pipe_wait(p, message_count, wait);
+ _ecore_unlock();
+ return r;
+}
+
+/**
+ * Close the write end of an Ecore_Pipe object created with ecore_pipe_add().
+ *
+ * @param p The Ecore_Pipe object.
+ */
+EAPI void
+ecore_pipe_write_close(Ecore_Pipe *p)
+{
+ _ecore_lock();
+ if (!ECORE_MAGIC_CHECK(p, ECORE_MAGIC_PIPE))
+ {
+ ECORE_MAGIC_FAIL(p, ECORE_MAGIC_PIPE, "ecore_pipe_write_close");
+ goto out;
+ }
+ if (p->fd_write != PIPE_FD_INVALID)
+ {
+ pipe_close(p->fd_write);
+ p->fd_write = PIPE_FD_INVALID;
+ }
+out:
+ _ecore_unlock();
+}
+
+/**
+ * Write on the file descriptor the data passed as parameter.
+ *
+ * @param p The Ecore_Pipe object.
+ * @param buffer The data to write into the pipe.
+ * @param nbytes The size of the @p buffer in bytes
+ * @return @c EINA_TRUE on a successful write, @c EINA_FALSE on error.
+ */
+EAPI Eina_Bool
+ecore_pipe_write(Ecore_Pipe *p,
+ const void *buffer,
+ unsigned int nbytes)
+{
+ ssize_t ret;
+ size_t already_written = 0;
+ int retry = ECORE_PIPE_WRITE_RETRY;
+ Eina_Bool ok = EINA_FALSE;
+
+ _ecore_lock();
+ if (!ECORE_MAGIC_CHECK(p, ECORE_MAGIC_PIPE))
+ {
+ ECORE_MAGIC_FAIL(p, ECORE_MAGIC_PIPE, "ecore_pipe_write");
+ goto out;
+ }
+
+ if (p->delete_me) goto out;
+
+ if (p->fd_write == PIPE_FD_INVALID) goto out;
+
+ /* First write the len into the pipe */
+ do
+ {
+ ret = pipe_write(p->fd_write, &nbytes, sizeof(nbytes));
+ if (ret == sizeof(nbytes))
+ {
+ retry = ECORE_PIPE_WRITE_RETRY;
+ break;
+ }
+ else if (ret > 0)
+ {
+ /* XXX What should we do here? */
+ ERR("The length of the data was not written complete"
+ " to the pipe");
+ goto out;
+ }
+ else if (ret == PIPE_FD_ERROR && errno == EPIPE)
+ {
+ pipe_close(p->fd_write);
+ p->fd_write = PIPE_FD_INVALID;
+ goto out;
+ }
+ else if (ret == PIPE_FD_ERROR && errno == EINTR)
+ /* try it again */
+ ;
+ else
+ {
+ ERR("An unhandled error (ret: %zd errno: %d)"
+ "occurred while writing to the pipe the length",
+ ret, errno);
+ }
+ }
+ while (retry--);
+
+ if (retry != ECORE_PIPE_WRITE_RETRY) goto out;
+
+ /* and now pass the data to the pipe */
+ do
+ {
+ ret = pipe_write(p->fd_write,
+ ((unsigned char *)buffer) + already_written,
+ nbytes - already_written);
+
+ if (ret == (ssize_t)(nbytes - already_written))
+ {
+ ok = EINA_TRUE;
+ goto out;
+ }
+ else if (ret >= 0)
+ {
+ already_written -= ret;
+ continue;
+ }
+ else if (ret == PIPE_FD_ERROR && errno == EPIPE)
+ {
+ pipe_close(p->fd_write);
+ p->fd_write = PIPE_FD_INVALID;
+ goto out;
+ }
+ else if (ret == PIPE_FD_ERROR && errno == EINTR)
+ /* try it again */
+ ;
+ else
+ {
+ ERR("An unhandled error (ret: %zd errno: %d)"
+ "occurred while writing to the pipe the length",
+ ret, errno);
+ }
+ }
+ while (retry--);
+
+out:
+ _ecore_unlock();
+ return ok;
+}
+
+/**
+ * @}
+ */
+
+/* Private functions */
+Ecore_Pipe *
+_ecore_pipe_add(Ecore_Pipe_Cb handler,
+ const void *data)
+{
+ Ecore_Pipe *p = NULL;
+ int fds[2];
+
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL);
+ if (!handler) return NULL;
+
+ p = ecore_pipe_calloc(1);
+ if (!p) return NULL;
+
+ if (pipe(fds))
+ {
+ ecore_pipe_mp_free(p);
+ return NULL;
+ }
+
+ ECORE_MAGIC_SET(p, ECORE_MAGIC_PIPE);
+ p->fd_read = fds[0];
+ p->fd_write = fds[1];
+ p->handler = handler;
+ p->data = data;
+
+ fcntl(p->fd_read, F_SETFL, O_NONBLOCK);
+ p->fd_handler = ecore_main_fd_handler_add(p->fd_read,
+ ECORE_FD_READ,
+ _ecore_pipe_read,
+ p,
+ NULL, NULL);
+
+ return p;
+}
+
+void *
+_ecore_pipe_del(Ecore_Pipe *p)
+{
+ void *data = NULL;
+
+ if (!ECORE_MAGIC_CHECK(p, ECORE_MAGIC_PIPE))
+ {
+ ECORE_MAGIC_FAIL(p, ECORE_MAGIC_PIPE, "ecore_pipe_del");
+ return NULL;
+ }
+ p->delete_me = EINA_TRUE;
+ if (p->handling > 0) return (void *)p->data;
+ if (p->fd_handler) _ecore_main_fd_handler_del(p->fd_handler);
+ if (p->fd_read != PIPE_FD_INVALID) pipe_close(p->fd_read);
+ if (p->fd_write != PIPE_FD_INVALID) pipe_close(p->fd_write);
+ data = (void *)p->data;
+ ecore_pipe_mp_free(p);
+ return data;
+}
+
+int
+_ecore_pipe_wait(Ecore_Pipe *p,
+ int message_count,
+ double wait)
+{
+ struct timeval tv, *t;
+ fd_set rset;
+ double end = 0.0;
+ double timeout;
+ int ret;
+ int total = 0;
+
+ EINA_MAIN_LOOP_CHECK_RETURN_VAL(-1);
+ if (p->fd_read == PIPE_FD_INVALID)
+ return -1;
+
+ FD_ZERO(&rset);
+ FD_SET(p->fd_read, &rset);
+
+ if (wait >= 0.0)
+ end = ecore_loop_time_get() + wait;
+ timeout = wait;
+
+ while (message_count > 0 && (timeout > 0.0 || wait <= 0.0))
+ {
+ if (wait >= 0.0)
+ {
+ /* finite() tests for NaN, too big, too small, and infinity. */
+ if ((!ECORE_FINITE(timeout)) || (timeout == 0.0))
+ {
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ }
+ else if (timeout > 0.0)
+ {
+ int sec, usec;
+#ifdef FIX_HZ
+ timeout += (0.5 / HZ);
+ sec = (int)timeout;
+ usec = (int)((timeout - (double)sec) * 1000000);
+#else
+ sec = (int)timeout;
+ usec = (int)((timeout - (double)sec) * 1000000);
+#endif
+ tv.tv_sec = sec;
+ tv.tv_usec = usec;
+ }
+ t = &tv;
+ }
+ else
+ {
+ t = NULL;
+ }
+
+ ret = main_loop_select(p->fd_read + 1, &rset, NULL, NULL, t);
+
+ if (ret > 0)
+ {
+ _ecore_pipe_read(p, NULL);
+ message_count -= p->message;
+ total += p->message;
+ p->message = 0;
+ }
+ else if (ret == 0)
+ {
+ break;
+ }
+ else if (errno != EINTR)
+ {
+ close(p->fd_read);
+ p->fd_read = PIPE_FD_INVALID;
+ break;
+ }
+
+ if (wait >= 0.0)
+ timeout = end - ecore_loop_time_get();
+ }
+
+ return total;
+}
+
+static void
+_ecore_pipe_unhandle(Ecore_Pipe *p)
+{
+ p->handling--;
+ if (p->delete_me)
+ {
+ _ecore_pipe_del(p);
+ }
+}
+
+static void
+_ecore_pipe_handler_call(Ecore_Pipe *p,
+ unsigned char *buf,
+ size_t len)
+{
+ void *data = (void*) p->data;
+ if (!p->delete_me)
+ {
+ _ecore_unlock();
+ p->handler(data, buf, len);
+ _ecore_lock();
+ }
+}
+
+static Eina_Bool
+_ecore_pipe_read(void *data,
+ Ecore_Fd_Handler *fd_handler EINA_UNUSED)
+{
+ Ecore_Pipe *p = (Ecore_Pipe *)data;
+ int i;
+
+ p->handling++;
+ for (i = 0; i < 16; i++)
+ {
+ ssize_t ret;
+
+ /* if we already have read some data we don't need to read the len
+ * but to finish the already started job
+ */
+ if (p->len == 0)
+ {
+ /* read the len of the passed data */
+ ret = pipe_read(p->fd_read, &p->len, sizeof(p->len));
+
+ /* catch the non error case first */
+ /* read amount ok - nothing more to do */
+ if (ret == sizeof(p->len))
+ ;
+ else if (ret > 0)
+ {
+ /* we got more data than we asked for - definite error */
+ ERR("Only read %i bytes from the pipe, although"
+ " we need to read %i bytes.",
+ (int)ret, (int)sizeof(p->len));
+ _ecore_pipe_unhandle(p);
+ return ECORE_CALLBACK_CANCEL;
+ }
+ else if (ret == 0)
+ {
+ /* we got no data */
+ if (i == 0)
+ {
+ /* no data on first try through means an error */
+ _ecore_pipe_handler_call(p, NULL, 0);
+ if (p->passed_data) free(p->passed_data);
+ p->passed_data = NULL;
+ p->already_read = 0;
+ p->len = 0;
+ p->message++;
+ pipe_close(p->fd_read);
+ p->fd_read = PIPE_FD_INVALID;
+ p->fd_handler = NULL;
+ _ecore_pipe_unhandle(p);
+ return ECORE_CALLBACK_CANCEL;
+ }
+ else
+ {
+ /* no data after first loop try is ok */
+ _ecore_pipe_unhandle(p);
+ return ECORE_CALLBACK_RENEW;
+ }
+ }
+#ifndef _WIN32
+ else if ((ret == PIPE_FD_ERROR) &&
+ ((errno == EINTR) || (errno == EAGAIN)))
+ {
+ return ECORE_CALLBACK_RENEW;
+ }
+ else
+ {
+ ERR("An unhandled error (ret: %i errno: %i [%s])"
+ "occurred while reading from the pipe the length",
+ (int)ret, errno, strerror(errno));
+ return ECORE_CALLBACK_RENEW;
+ }
+#else
+ else /* ret == PIPE_FD_ERROR is the only other case on Windows */
+ {
+ if (WSAGetLastError() != WSAEWOULDBLOCK)
+ {
+ _ecore_pipe_handler_call(p, NULL, 0);
+ if (p->passed_data) free(p->passed_data);
+ p->passed_data = NULL;
+ p->already_read = 0;
+ p->len = 0;
+ p->message++;
+ pipe_close(p->fd_read);
+ p->fd_read = PIPE_FD_INVALID;
+ p->fd_handler = NULL;
+ _ecore_pipe_unhandle(p);
+ return ECORE_CALLBACK_CANCEL;
+ }
+ }
+#endif
+ }
+
+ /* if somehow we got less than or equal to 0 we got an errnoneous
+ * messages so call callback with null and len we got. this case should
+ * never happen */
+ if (p->len == 0)
+ {
+ _ecore_pipe_handler_call(p, NULL, 0);
+ /* reset all values to 0 */
+ if (p->passed_data) free(p->passed_data);
+ p->passed_data = NULL;
+ p->already_read = 0;
+ p->len = 0;
+ p->message++;
+ _ecore_pipe_unhandle(p);
+ return ECORE_CALLBACK_RENEW;
+ }
+
+ /* we dont have a buffer to hold the data, so alloc it */
+ if (!p->passed_data)
+ {
+ p->passed_data = malloc(p->len);
+ /* alloc failed - error case */
+ if (!p->passed_data)
+ {
+ _ecore_pipe_handler_call(p, NULL, 0);
+ /* close the pipe */
+ p->already_read = 0;
+ p->len = 0;
+ p->message++;
+ pipe_close(p->fd_read);
+ p->fd_read = PIPE_FD_INVALID;
+ p->fd_handler = NULL;
+ _ecore_pipe_unhandle(p);
+ return ECORE_CALLBACK_CANCEL;
+ }
+ }
+
+ /* and read the passed data */
+ ret = pipe_read(p->fd_read,
+ ((unsigned char *)p->passed_data) + p->already_read,
+ p->len - p->already_read);
+
+ /* catch the non error case first */
+ /* if we read enough data to finish the message/buffer */
+ if (ret == (ssize_t)(p->len - p->already_read))
+ {
+ _ecore_pipe_handler_call(p, p->passed_data, p->len);
+ free(p->passed_data);
+ /* reset all values to 0 */
+ p->passed_data = NULL;
+ p->already_read = 0;
+ p->len = 0;
+ p->message++;
+ }
+ else if (ret > 0)
+ {
+ /* more data left to read */
+ p->already_read += ret;
+ _ecore_pipe_unhandle(p);
+ return ECORE_CALLBACK_RENEW;
+ }
+ else if (ret == 0)
+ {
+ /* 0 bytes to read - could be more to read next select wake up */
+ _ecore_pipe_unhandle(p);
+ return ECORE_CALLBACK_RENEW;
+ }
+#ifndef _WIN32
+ else if ((ret == PIPE_FD_ERROR) &&
+ ((errno == EINTR) || (errno == EAGAIN)))
+ {
+ _ecore_pipe_unhandle(p);
+ return ECORE_CALLBACK_RENEW;
+ }
+ else
+ {
+ ERR("An unhandled error (ret: %zd errno: %d)"
+ "occurred while reading from the pipe the data",
+ ret, errno);
+ _ecore_pipe_unhandle(p);
+ return ECORE_CALLBACK_RENEW;
+ }
+#else
+ else /* ret == PIPE_FD_ERROR is the only other case on Windows */
+ {
+ if (WSAGetLastError() != WSAEWOULDBLOCK)
+ {
+ _ecore_pipe_handler_call(p, NULL, 0);
+ if (p->passed_data) free(p->passed_data);
+ p->passed_data = NULL;
+ p->already_read = 0;
+ p->len = 0;
+ p->message++;
+ pipe_close(p->fd_read);
+ p->fd_read = PIPE_FD_INVALID;
+ p->fd_handler = NULL;
+ _ecore_pipe_unhandle(p);
+ return ECORE_CALLBACK_CANCEL;
+ }
+ else
+ break;
+ }
+#endif
+ }
+
+ _ecore_pipe_unhandle(p);
+ return ECORE_CALLBACK_RENEW;
+}
+
diff --git a/src/lib/ecore/ecore_poll.c b/src/lib/ecore/ecore_poll.c
new file mode 100644
index 0000000000..28be53857c
--- /dev/null
+++ b/src/lib/ecore/ecore_poll.c
@@ -0,0 +1,490 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#include <Eo.h>
+
+#include "Ecore.h"
+#include "ecore_private.h"
+
+#define MY_CLASS ECORE_POLLER_CLASS
+
+#define MY_CLASS_NAME "ecore_poller"
+
+EAPI Eo_Op ECORE_POLLER_BASE_ID = EO_NOOP;
+
+#define ECORE_POLLER_CHECK(obj) \
+ if (!eo_isa((obj), ECORE_POLLER_CLASS)) \
+ return
+
+struct _Ecore_Poller_Private_Data
+{
+ EINA_INLIST;
+ ECORE_MAGIC;
+ Ecore_Poller *obj;
+ int ibit;
+ unsigned char delete_me : 1;
+ Ecore_Task_Cb func;
+ void *data;
+};
+
+typedef struct _Ecore_Poller_Private_Data Ecore_Poller_Private_Data;
+
+static Ecore_Timer *timer = NULL;
+static int min_interval = -1;
+static int interval_incr = 0;
+static int at_tick = 0;
+static int just_added_poller = 0;
+static int poller_delete_count = 0;
+static int poller_walking = 0;
+static double poll_interval = 0.125;
+static double poll_cur_interval = 0.0;
+static double last_tick = 0.0;
+static Ecore_Poller_Private_Data *pollers[16] =
+{
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+static unsigned short poller_counters[16] =
+{
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static void _ecore_poller_next_tick_eval(void);
+static Eina_Bool _ecore_poller_cb_timer(void *data);
+
+static void
+_ecore_poller_next_tick_eval(void)
+{
+ int i;
+ double interval;
+
+ min_interval = -1;
+ for (i = 0; i < 15; i++)
+ {
+ if (pollers[i])
+ {
+ min_interval = i;
+ break;
+ }
+ }
+ if (min_interval < 0)
+ {
+ /* no pollers */
+ if (timer)
+ {
+ ecore_timer_del(timer);
+ timer = NULL;
+ }
+ return;
+ }
+ interval_incr = (1 << min_interval);
+ interval = interval_incr * poll_interval;
+ /* we are at the tick callback - so no need to do inter-tick adjustments
+ * so we can fasttrack this as t -= last_tick in theory is 0.0 (though
+ * in practice it will be a very very very small value. also the tick
+ * callback will adjust the timer interval at the end anyway */
+ if (at_tick)
+ {
+ if (!timer)
+ timer = ecore_timer_add(interval, _ecore_poller_cb_timer, NULL);
+ }
+ else
+ {
+ double t;
+
+ if (!timer)
+ timer = ecore_timer_add(interval, _ecore_poller_cb_timer, NULL);
+ else
+ {
+ t = ecore_time_get();
+ if (interval != poll_cur_interval)
+ {
+ t -= last_tick; /* time since we last ticked */
+ /* delete the timer and reset it to tick off in the new
+ * time interval. at the tick this will be adjusted */
+ ecore_timer_del(timer);
+ timer = ecore_timer_add(interval - t,
+ _ecore_poller_cb_timer, NULL);
+ }
+ }
+ }
+ poll_cur_interval = interval;
+}
+
+static Eina_Bool
+_ecore_poller_cb_timer(void *data EINA_UNUSED)
+{
+ int i;
+ Ecore_Poller_Private_Data *poller, *l;
+ int changes = 0;
+
+ at_tick++;
+ last_tick = ecore_time_get();