#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define win_w 800 #define win_h 800 #define ECORE_FD ipc_socket[0] #define WEBKIT_FD ipc_socket[1] #define LOCK(name) pthread_mutex_lock(&name->lock) #define UNLOCK(name) pthread_mutex_unlock(&name->lock) typedef enum { EVAS_OBJECT_WEBKIT_RESIZE, EVAS_OBJECT_WEBKIT_EXPOSE, EVAS_OBJECT_WEBKIT_TITLE_CHANGED, EVAS_OBJECT_WEBKIT_CLEANUP, EVAS_OBJECT_WEBKIT_EMBED_CREATE, EVAS_OBJECT_WEBKIT_EMBED_DESTROY, EVAS_OBJECT_WEBKIT_EMBED_RESIZE, EVAS_OBJECT_WEBKIT_EMBED_OPEN_URL, EVAS_OBJECT_WEBKIT_EMBED_MOTION_NOTIFY, EVAS_OBJECT_WEBKIT_EMBED_BUTTON_PRESS, EVAS_OBJECT_WEBKIT_EMBED_BUTTON_RELEASE, EVAS_OBJECT_WEBKIT_EMBED_KEY_PRESS, EVAS_OBJECT_WEBKIT_EMBED_KEY_RELEASE, EVAS_OBJECT_WEBKIT_EMBED_ENTER_NOTIFY, EVAS_OBJECT_WEBKIT_EMBED_LEAVE_NOTIFY } Evas_WebKit_EventType; typedef struct { Evas_WebKit_EventType type; // XXX use union! int w, h; // window size int x, y; // mouse position char *title; } Evas_WebKit_Event; typedef struct { Evas_WebKit_Event event; time_t timestamp; char *id; } Evas_WebKit_Message; typedef struct _Evas_WebKit_Private { char *id; char *buffer; int buffer_size; int h, w; pthread_mutex_t lock; time_t timestamp; } Evas_WebKit_Private; Ecore_Evas *ee = NULL; Evas *evas = NULL; cairo_surface_t *surface = NULL; cairo_t *source_context = NULL; static int ipc_socket[2]; static Ecore_Hash *obj_mapping; static Ecore_Hash *private_data_mapping; static Ecore_Hash *webview_mapping; static GtkWidget* web_test; static void webkit_web_view_resize(WebKitWebView *web_view, Evas_WebKit_Private *data, int w, int h) { GtkAllocation alloc = {0, 0, w, h}; cairo_surface_destroy(surface); gtk_widget_size_allocate(GTK_WIDGET(web_view), &alloc); surface = cairo_surface_create_similar(cairo_get_target(source_context), CAIRO_CONTENT_COLOR_ALPHA, w, h); data->w = w; data->h = h; } static void webkit_web_view_clear(WebKitWebView *web_view, Evas_WebKit_Private *data) { int w, h; w = cairo_image_surface_get_width(surface); h = cairo_image_surface_get_height(surface); cairo_surface_destroy(surface); surface = cairo_surface_create_similar(cairo_get_target(source_context), CAIRO_CONTENT_COLOR_ALPHA, w, h); data->w = w; data->h = h; } static void webkit_web_view_paint(WebKitWebView *web_view, Evas_WebKit_Private *data, int x, int y, int w, int h) { WebKitWebFrame *web_frame; cairo_t *cr; cr = cairo_create(surface); cairo_rectangle (cr, x, y, w, h); web_frame = webkit_web_view_get_main_frame(web_view); webkit_web_frame_show(web_frame, cr); cairo_destroy(cr); data->buffer = (char *) cairo_image_surface_get_data(surface); } static void read_message(int ipc_fd, char **id, Evas_WebKit_Event *event, time_t *timestamp) { Evas_WebKit_Message msg; read(ipc_fd, &msg, sizeof(Evas_WebKit_Message)); *event = msg.event; *id = msg.id; *timestamp = msg.timestamp; } static void send_message(int ipc_fd, char *id, Evas_WebKit_Event event, time_t timestamp) { Evas_WebKit_Message msg; msg.event = event; msg.id = id; msg.timestamp = timestamp; write(ipc_fd, &msg, sizeof(Evas_WebKit_Message)); } static void title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer user_data) { Evas_WebKit_Private *data; Evas_WebKit_Event event; data = (Evas_WebKit_Private *) user_data; event.type = EVAS_OBJECT_WEBKIT_TITLE_CHANGED; event.title = strdup(title); send_message(WEBKIT_FD, data->id, event, data->timestamp); } static void progress_change_cb (WebKitWebView *web_view, gint progress, gpointer user_data) { Evas_WebKit_Private *data; Evas_WebKit_Event event; data = (Evas_WebKit_Private *) user_data; webkit_web_view_clear(web_view, data); webkit_web_view_paint(web_view, data, 0, 0, data->w, data->h); printf("Load progress %d\n", progress); event.type = EVAS_OBJECT_WEBKIT_EXPOSE; send_message(WEBKIT_FD, data->id, event, data->timestamp); } static int _evas_webkit_dispatch(void *user_data, Ecore_Fd_Handler *fd_handler) { Evas_Object *obj; Evas_WebKit_Private *data; Evas_WebKit_Event event; char *id; time_t timestamp; (void) user_data; (void) fd_handler; read_message(ECORE_FD, &id, &event, ×tamp); data = ecore_hash_get(private_data_mapping, id); printf("HERE"); // optimization //if (data->timestamp != timestamp) // return 1; obj = ecore_hash_get(obj_mapping, (void *) id); LOCK(data); switch (event.type) { case EVAS_OBJECT_WEBKIT_RESIZE: evas_object_image_pixels_dirty_set(obj, 1); evas_object_image_fill_set(obj, 0, 0, data->w, data->h); evas_object_image_size_set(obj, data->w, data->h); evas_object_image_data_copy_set(obj, data->buffer); evas_object_image_pixels_dirty_set(obj, 0); break; case EVAS_OBJECT_WEBKIT_EXPOSE: evas_object_image_pixels_dirty_set(obj, 1); evas_object_image_fill_set(obj, 0, 0, data->w, data->h); evas_object_image_size_set(obj, data->w, data->h); evas_object_image_data_copy_set(obj, data->buffer); evas_object_image_pixels_dirty_set(obj, 0); break; case EVAS_OBJECT_WEBKIT_TITLE_CHANGED: ecore_evas_title_set(ee, event.title); free(event.title); break; case EVAS_OBJECT_WEBKIT_CLEANUP: printf("CLEANUP"); ecore_hash_remove(private_data_mapping, id); ecore_hash_remove(obj_mapping, id); //UNLOCK(data); //free(data); //return 1; default: break; } UNLOCK(data); return 1; } gboolean webkit_dispatch(GIOChannel *source, GIOCondition condition, gpointer user_data) { WebKitWebView *web_view; Evas_WebKit_Private *data; Evas_WebKit_Event event; char *id; time_t timestamp; read_message(WEBKIT_FD, &id, &event, ×tamp); data = ecore_hash_get(private_data_mapping, (void *) id); LOCK(data); switch (event.type) { case EVAS_OBJECT_WEBKIT_EMBED_CREATE: printf("Create GtkWebView with id=%s\n", id); web_test = webkit_web_canvas_new(0, 0); web_view = webkit_web_canvas_get_web_view(WEBKIT_WEB_CANVAS(web_test)); g_signal_connect (G_OBJECT (web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), data); g_signal_connect (G_OBJECT (web_view), "title-changed", G_CALLBACK (title_change_cb), data); webkit_web_view_open(web_view, "http://www.enlightenment.org"); ecore_hash_set(webview_mapping, id, web_view); break; case EVAS_OBJECT_WEBKIT_EMBED_DESTROY: printf("Remove GtkWebView with id=%s\n", id); ecore_hash_remove(webview_mapping, id); event.type = EVAS_OBJECT_WEBKIT_CLEANUP; send_message(WEBKIT_FD, id, event, data->timestamp); break; case EVAS_OBJECT_WEBKIT_EMBED_RESIZE: printf("Resize GtkWebView (%d, %d) with id=%s - ", event.w, event.h, id); web_view = ecore_hash_get(webview_mapping, (void *) id); webkit_web_view_resize(web_view, data, event.w, event.h); webkit_web_view_paint(web_view, data, 0, 0, event.w, event.h); event.type = EVAS_OBJECT_WEBKIT_RESIZE; send_message(WEBKIT_FD, id, event, data->timestamp); break; case EVAS_OBJECT_WEBKIT_EMBED_MOTION_NOTIFY: web_view = ecore_hash_get(webview_mapping, (void *) id); webkit_web_view_clear(web_view, data); webkit_web_view_paint(web_view, data, 0, 0, data->w, data->h); event.type = EVAS_OBJECT_WEBKIT_EXPOSE; send_message(WEBKIT_FD, id, event, data->timestamp); break; case EVAS_OBJECT_WEBKIT_EMBED_BUTTON_PRESS: break; case EVAS_OBJECT_WEBKIT_EMBED_BUTTON_RELEASE: break; case EVAS_OBJECT_WEBKIT_EMBED_KEY_PRESS: break; case EVAS_OBJECT_WEBKIT_EMBED_KEY_RELEASE: break; case EVAS_OBJECT_WEBKIT_EMBED_ENTER_NOTIFY: break; case EVAS_OBJECT_WEBKIT_EMBED_LEAVE_NOTIFY: break; default: break; } UNLOCK(data); return TRUE; } void * webkit_thread(void *data) { int watch; GMainLoop *webkit_loop; GIOChannel *io_channel; webkit_loop = g_main_loop_new(NULL, TRUE); gtk_init(NULL, NULL); webview_mapping = ecore_hash_new(ecore_direct_hash, ecore_direct_compare); io_channel = g_io_channel_unix_new(WEBKIT_FD); watch = g_io_add_watch(io_channel, G_IO_IN, webkit_dispatch, NULL); g_main_loop_run(webkit_loop); pthread_exit(NULL); ecore_hash_destroy(webview_mapping); } static pthread_t thread; int evas_webkit_init() { if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, ipc_socket) < 0) return 1; obj_mapping = ecore_hash_new(ecore_direct_hash, ecore_direct_compare); private_data_mapping = ecore_hash_new(ecore_direct_hash, ecore_direct_compare); surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 100, 100); source_context = cairo_create(surface); pthread_create(&thread, NULL, webkit_thread, NULL); return 0; } void evas_webkit_shutdown() { pthread_join(thread, NULL); ecore_hash_destroy(private_data_mapping); ecore_hash_destroy(obj_mapping); } void on_evas_object_webkit_resize(void *user_data, Evas *e, Evas_Object *obj, void *event_info) { Evas_WebKit_Private *data; Evas_WebKit_Event event; int x, y, w, h; char *id; id = evas_object_data_get(obj, "object-id"); data = ecore_hash_get(private_data_mapping, id); evas_object_geometry_get(obj, &x, &y, &w, &h); evas_object_image_pixels_dirty_set(obj, 1); LOCK(data); data->timestamp = time(NULL); event.type = EVAS_OBJECT_WEBKIT_EMBED_RESIZE; event.w = w; event.h = h; //printf("signal resize (%d, %d, %d, %d) - ", y, x, w, h); send_message(ECORE_FD, id, event, data->timestamp); UNLOCK(data); } void on_evas_object_webkit_mouse_move(void *user_data, Evas *e, Evas_Object *obj, void *event_info) { Evas_WebKit_Private *data; Evas_WebKit_Event event; char *id; id = evas_object_data_get(obj, "object-id"); data = ecore_hash_get(private_data_mapping, id); LOCK(data); data->timestamp = time(NULL); event.type = EVAS_OBJECT_WEBKIT_EMBED_MOTION_NOTIFY; // XXX get from event_info event.x = 0; event.y = 0; printf("signal mouse (%d, %d) \n", data->w, data->h); send_message(ECORE_FD, id, event, data->timestamp); UNLOCK(data); } void on_evas_object_webkit_mouse_down(void *data, Evas *e, Evas_Object *obj, void *event_info) { char *id; id = evas_object_data_get(obj, "object-id"); //send_message(ECORE_FD, id, EVAS_OBJECT_WEBKIT_EMBED_BUTTON_PRESS); } void on_evas_object_webkit_mouse_up(void *data, Evas *e, Evas_Object *obj, void *event_info) { char *id; id = evas_object_data_get(obj, "object-id"); //send_message(ECORE_FD, id, EVAS_OBJECT_WEBKIT_EMBED_BUTTON_RELEASE); } void on_evas_object_webkit_key_down(void *data, Evas *e, Evas_Object *obj, void *event_info) { char *id; id = evas_object_data_get(obj, "object-id"); //send_message(ECORE_FD, id, EVAS_OBJECT_WEBKIT_EMBED_KEY_PRESS); } void on_evas_object_webkit_key_up(void *data, Evas *e, Evas_Object *obj, void *event_info) { char *id; id = evas_object_data_get(obj, "object-id"); //send_message(ECORE_FD, id, EVAS_OBJECT_WEBKIT_EMBED_KEY_RELEASE); } void on_evas_object_webkit_mouse_in(void *data, Evas *e, Evas_Object *obj, void *event_info) { char *id; id = evas_object_data_get(obj, "object-id"); //send_message(ECORE_FD, id, EVAS_OBJECT_WEBKIT_EMBED_ENTER_NOTIFY); } void on_evas_object_webkit_mouse_out(void *data, Evas *e, Evas_Object *obj, void *event_info) { char *id; id = evas_object_data_get(obj, "object-id"); //send_message(ECORE_FD, id, EVAS_OBJECT_WEBKIT_EMBED_LEAVE_NOTIFY); } void on_evas_object_webkit_del(void *data, Evas *e, Evas_Object *obj, void *event_info) { Evas_WebKit_Event event; char *id; evas_object_event_callback_del(obj, EVAS_CALLBACK_DEL, on_evas_object_webkit_del); evas_object_event_callback_del(obj, EVAS_CALLBACK_RESIZE, on_evas_object_webkit_resize); evas_object_event_callback_del(obj, EVAS_CALLBACK_MOUSE_MOVE, on_evas_object_webkit_mouse_move); evas_object_event_callback_del(obj, EVAS_CALLBACK_MOUSE_DOWN, on_evas_object_webkit_mouse_down); evas_object_event_callback_del(obj, EVAS_CALLBACK_MOUSE_UP, on_evas_object_webkit_mouse_up); evas_object_event_callback_del(obj, EVAS_CALLBACK_KEY_DOWN, on_evas_object_webkit_key_down); evas_object_event_callback_del(obj, EVAS_CALLBACK_KEY_UP, on_evas_object_webkit_key_up); evas_object_event_callback_del(obj, EVAS_CALLBACK_MOUSE_IN, on_evas_object_webkit_mouse_in); evas_object_event_callback_del(obj, EVAS_CALLBACK_MOUSE_OUT, on_evas_object_webkit_mouse_out); id = evas_object_data_get(obj, "object-id"); event.type = EVAS_OBJECT_WEBKIT_EMBED_DESTROY; send_message(ECORE_FD, id, event, time(NULL)); } Evas_Object * evas_object_webkit_add(Ecore_Evas *ee) { Evas_WebKit_Event event; Evas_Object *obj = NULL; char *id; char tmp[100]; obj = evas_object_image_add(ecore_evas_get(ee)); evas_object_image_colorspace_set(obj, EVAS_COLORSPACE_ARGB8888); Evas_WebKit_Private *private_data = malloc(sizeof(Evas_WebKit_Private)); memset(private_data, 0, sizeof(Evas_WebKit_Private)); pthread_mutex_init(&private_data->lock, NULL); sprintf(tmp, "%d", ecore_hash_count(obj_mapping)); id = strdup(tmp); ecore_hash_set(obj_mapping, id, obj); ecore_hash_set(private_data_mapping, id, private_data); private_data->id = id; evas_object_data_set(obj, "object-id", id); event.type = EVAS_OBJECT_WEBKIT_EMBED_CREATE; send_message(ECORE_FD, id, event, time(NULL)); evas_object_event_callback_add(obj, EVAS_CALLBACK_DEL, on_evas_object_webkit_del, NULL); evas_object_event_callback_add(obj, EVAS_CALLBACK_RESIZE, on_evas_object_webkit_resize, NULL); evas_object_event_callback_add(obj, EVAS_CALLBACK_MOUSE_MOVE, on_evas_object_webkit_mouse_move, NULL); evas_object_event_callback_add(obj, EVAS_CALLBACK_MOUSE_DOWN, on_evas_object_webkit_mouse_down, NULL); evas_object_event_callback_add(obj, EVAS_CALLBACK_MOUSE_UP, on_evas_object_webkit_mouse_up, NULL); evas_object_event_callback_add(obj, EVAS_CALLBACK_KEY_DOWN, on_evas_object_webkit_key_down, NULL); evas_object_event_callback_add(obj, EVAS_CALLBACK_KEY_UP, on_evas_object_webkit_key_up, NULL); evas_object_event_callback_add(obj, EVAS_CALLBACK_MOUSE_IN, on_evas_object_webkit_mouse_in, NULL); evas_object_event_callback_add(obj, EVAS_CALLBACK_MOUSE_OUT, on_evas_object_webkit_mouse_out, NULL); ecore_main_fd_handler_add(ECORE_FD, ECORE_FD_READ, _evas_webkit_dispatch, NULL, NULL, NULL); return obj; } /* -------- APPLICATION -------- */ static void on_ecore_evas_resize(Ecore_Evas *ee) { Evas_Object *obj = NULL; int x, y, w, h; ecore_evas_geometry_get(ee, &x, &y, &w, &h); obj = evas_object_name_find(ecore_evas_get(ee), "webkit"); if (obj) { evas_object_move(obj, 0, 0); evas_object_resize(obj, w, h); } } static void on_ecore_evas_delete_request(Ecore_Evas *ee) { (void) ee; ecore_main_loop_quit(); } static int on_ecore_signal_exit(void *data, int type, void *event) { (void) data; (void) type; (void) event; ecore_main_loop_quit(); return 0; } int main(int argc, const char **argv) { Evas_Object *webkit = NULL; int ret = EXIT_FAILURE; if (!ecore_init()) { fprintf(stderr, "Unable to init Ecore, bailing out.\n"); goto out; } ecore_app_args_set(argc, argv); ecore_evas_init(); if (evas_webkit_init()) { fprintf(stderr, "Unable to open IPC channel.\n"); goto out; } ee = ecore_evas_software_x11_new(NULL, 0, 0, 0, win_w, win_h); if (!ee) goto out_ecore; evas = ecore_evas_get(ee); ecore_event_handler_add(ECORE_EVENT_SIGNAL_EXIT, on_ecore_signal_exit, ee); ecore_evas_title_set(ee, "WebKit on Evas Test"); ecore_evas_name_class_set(ee, "EvasWebKitTest", "EvasWebKitTest"); ecore_evas_callback_delete_request_set(ee, on_ecore_evas_delete_request); ecore_evas_callback_resize_set(ee, on_ecore_evas_resize); webkit = evas_object_webkit_add(ee); evas_object_name_set(webkit, "webkit"); evas_object_show(webkit); on_ecore_evas_resize(ee); ecore_evas_show(ee); ecore_main_loop_begin(); evas_object_del(webkit); evas_webkit_shutdown(); ecore_evas_shutdown(); ret = EXIT_SUCCESS; out_ecore: ecore_shutdown(); out: return ret; }