diff --git a/internal/desktop/drop.go b/internal/desktop/drop.go new file mode 100644 index 00000000..8bbbdbd4 --- /dev/null +++ b/internal/desktop/drop.go @@ -0,0 +1,53 @@ +package desktop + +import ( + "time" + + "demodesk/neko/internal/desktop/drop" +) + +const ( + DROP_DELAY = 100 * time.Millisecond +) + +func (manager *DesktopManagerCtx) DropFiles(x int, y int, files []string) bool { + mu.Lock() + defer mu.Unlock() + + drop.Emmiter.Clear() + + drop.Emmiter.Once("create", func(payload ...interface{}) { + manager.Move(0, 0) + }) + + drop.Emmiter.Once("cursor-enter", func(payload ...interface{}) { + manager.ButtonDown(1) + }) + + drop.Emmiter.Once("button-press", func(payload ...interface{}) { + manager.Move(x, y) + }) + + drop.Emmiter.Once("begin", func(payload ...interface{}) { + manager.Move(x, y) + time.Sleep(DROP_DELAY) + manager.Move(x, y) + time.Sleep(DROP_DELAY) + manager.ButtonUp(1) + }) + + finished := make(chan bool) + drop.Emmiter.Once("finish", func(payload ...interface{}) { + finished <- payload[0].(bool) + }) + + go drop.OpenWindow(files) + + select { + case succeeded := <- finished: + return succeeded + case <-time.After(1 * time.Second): + drop.CloseWindow(); + return false + } +} diff --git a/internal/desktop/drop/drop.c b/internal/desktop/drop/drop.c new file mode 100644 index 00000000..25568c07 --- /dev/null +++ b/internal/desktop/drop/drop.c @@ -0,0 +1,93 @@ +#include "drop.h" + +GtkWidget *drag_widget = NULL; + +static void dragDataGet( + GtkWidget *widget, + GdkDragContext *context, + GtkSelectionData *data, + guint target_type, + guint time, + gpointer user_data +) { + gchar **uris = (gchar **) user_data; + + if (target_type == DRAG_TARGET_TYPE_URI) { + gtk_selection_data_set_uris(data, uris); + return; + } + + if (target_type == DRAG_TARGET_TYPE_TEXT) { + gtk_selection_data_set_text(data, uris[0], -1); + return; + } +} + +static void dragEnd( + GtkWidget *widget, + GdkDragContext *context, + gpointer user_data +) { + gboolean succeeded = gdk_drag_drop_succeeded(context); + gtk_widget_destroy(widget); + goDragFinish(succeeded); + drag_widget = NULL; +} + +void dragWindowOpen(char **uris) { + if (drag_widget != NULL) dragWindowClose(); + + gtk_init(NULL, NULL); + + GtkWidget *widget = gtk_window_new(GTK_WINDOW_TOPLEVEL);; + GtkWindow *window = GTK_WINDOW(widget); + + gtk_window_move(window, 0, 0); + gtk_window_set_title(window, "Neko Drag & Drop Window"); + gtk_window_set_decorated(window, FALSE); + gtk_window_set_keep_above(window, TRUE); + gtk_window_set_default_size(window, 100, 100); + + GtkTargetList* target_list = gtk_target_list_new(NULL, 0); + gtk_target_list_add_uri_targets(target_list, DRAG_TARGET_TYPE_URI); + gtk_target_list_add_text_targets(target_list, DRAG_TARGET_TYPE_TEXT); + + gtk_drag_source_set(widget, GDK_BUTTON1_MASK, NULL, 0, GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_ASK); + gtk_drag_source_set_target_list(widget, target_list); + + g_signal_connect(widget, "map-event", G_CALLBACK(goDragCreate), NULL); + g_signal_connect(widget, "enter-notify-event", G_CALLBACK(goDragCursorEnter), NULL); + g_signal_connect(widget, "button-press-event", G_CALLBACK(goDragButtonPress), NULL); + g_signal_connect(widget, "drag-begin", G_CALLBACK(goDragBegin), NULL); + + g_signal_connect(widget, "drag-data-get", G_CALLBACK(dragDataGet), uris); + g_signal_connect(widget, "drag-end", G_CALLBACK(dragEnd), NULL); + g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL); + + gtk_widget_show_all(widget); + drag_widget = widget; + + gtk_main(); +} + +void dragWindowClose() { + gtk_widget_destroy(drag_widget); + drag_widget = NULL; +} + +char **dragUrisMake(int size) { + return calloc(size + 1, sizeof(char *)); +} + +void dragUrisSetFile(char **uris, char *file, int n) { + GFile *gfile = g_file_new_for_path(file); + uris[n] = g_file_get_uri(gfile); +} + +void dragUrisFree(char **uris, int size) { + for (int i = 0; i < size; i++) { + free(uris[i]); + } + + free(uris); +} diff --git a/internal/desktop/drop/drop.go b/internal/desktop/drop/drop.go new file mode 100644 index 00000000..dd06f993 --- /dev/null +++ b/internal/desktop/drop/drop.go @@ -0,0 +1,65 @@ +package drop + +/* +#cgo pkg-config: gtk+-3.0 + +#include "drop.h" +*/ +import "C" + +import ( + "sync" + + "github.com/kataras/go-events" +) + +var Emmiter events.EventEmmiter +var mu = sync.Mutex{} + +func init() { + Emmiter = events.New() +} + +func OpenWindow(files []string) { + mu.Lock() + defer mu.Unlock() + + size := C.int(len(files)) + urisUnsafe := C.dragUrisMake(size); + defer C.dragUrisFree(urisUnsafe, size) + + for i, file := range files { + C.dragUrisSetFile(urisUnsafe, C.CString(file), C.int(i)) + } + + C.dragWindowOpen(urisUnsafe) +} + +func CloseWindow() { + C.dragWindowClose() +} + +//export goDragCreate +func goDragCreate(widget *C.GtkWidget, event *C.GdkEvent, user_data C.gpointer) { + go Emmiter.Emit("create") +} + +//export goDragCursorEnter +func goDragCursorEnter(widget *C.GtkWidget, event *C.GdkEvent, user_data C.gpointer) { + go Emmiter.Emit("cursor-enter") +} + +//export goDragButtonPress +func goDragButtonPress(widget *C.GtkWidget, event *C.GdkEvent, user_data C.gpointer) { + go Emmiter.Emit("button-press") +} + +//export goDragBegin +func goDragBegin(widget *C.GtkWidget, context *C.GdkDragContext, user_data C.gpointer) { + go Emmiter.Emit("begin") +} + +//export goDragFinish +func goDragFinish(succeeded C.gboolean) { + go Emmiter.Emit("finish", bool(succeeded == C.int(1))) +} diff --git a/internal/desktop/drop/drop.h b/internal/desktop/drop/drop.h new file mode 100644 index 00000000..e18c99a2 --- /dev/null +++ b/internal/desktop/drop/drop.h @@ -0,0 +1,36 @@ +#pragma once + +#include + +enum { + DRAG_TARGET_TYPE_TEXT, + DRAG_TARGET_TYPE_URI +}; + +extern void goDragCreate(GtkWidget *widget, GdkEvent *event, gpointer user_data); +extern void goDragCursorEnter(GtkWidget *widget, GdkEvent *event, gpointer user_data); +extern void goDragButtonPress(GtkWidget *widget, GdkEvent *event, gpointer user_data); +extern void goDragBegin(GtkWidget *widget, GdkDragContext *context, gpointer user_data); +extern void goDragFinish(gboolean succeeded); + +static void dragDataGet( + GtkWidget *widget, + GdkDragContext *context, + GtkSelectionData *data, + guint target_type, + guint time, + gpointer user_data +); + +static void dragEnd( + GtkWidget *widget, + GdkDragContext *context, + gpointer user_data +); + +void dragWindowOpen(char **uris); +void dragWindowClose(); + +char **dragUrisMake(int size); +void dragUrisSetFile(char **uris, char *file, int n); +void dragUrisFree(char **uris, int size); diff --git a/internal/desktop/gtk.go b/internal/desktop/gtk.go deleted file mode 100644 index 5decbd2f..00000000 --- a/internal/desktop/gtk.go +++ /dev/null @@ -1,38 +0,0 @@ -package desktop - -import ( - "time" - - "demodesk/neko/internal/desktop/gtk" -) - -const ( - DELAY = 100 * time.Millisecond -) - -func (manager *DesktopManagerCtx) DropFiles(x int, y int, files []string) { - mu.Lock() - defer mu.Unlock() - - go gtk.DragWindow(files) - - // TODO: Find a bettter way. - for step := 1; step <= 6; step++ { - time.Sleep(DELAY) - - switch step { - case 1: - manager.Move(0, 0) - case 2: - manager.ButtonDown(1) - case 3: - fallthrough - case 4: - fallthrough - case 5: - manager.Move(x, y) - case 6: - manager.ButtonUp(1) - } - } -} diff --git a/internal/desktop/gtk/gtk.c b/internal/desktop/gtk/gtk.c deleted file mode 100644 index ffdb0ff1..00000000 --- a/internal/desktop/gtk/gtk.c +++ /dev/null @@ -1,103 +0,0 @@ -#include "gtk.h" - -static void drag_data_get( - GtkWidget *widget, - GdkDragContext *context, - GtkSelectionData *data, - guint target_type, - guint time, - gpointer user_data -) { - gchar **uris = (gchar **) user_data; - - if (target_type == TARGET_TYPE_URI) { - gtk_selection_data_set_uris(data, uris); - return; - } - - if (target_type == TARGET_TYPE_TEXT) { - gtk_selection_data_set_text(data, uris[0], -1); - return; - } -} - -static void drag_end( - GtkWidget *widget, - GdkDragContext *context, - gpointer user_data -) { - gboolean succeeded = gdk_drag_drop_succeeded(context); - GdkDragAction action = gdk_drag_context_get_selected_action(context); - - char* action_str; - switch (action) { - case GDK_ACTION_COPY: - action_str = "COPY"; break; - case GDK_ACTION_MOVE: - action_str = "MOVE"; break; - case GDK_ACTION_LINK: - action_str = "LINK"; break; - case GDK_ACTION_ASK: - action_str = "ASK"; break; - default: - action_str = malloc(sizeof(char) * 20); - snprintf(action_str, 20, "invalid (%d)", action); - break; - } - - fprintf(stderr, "Selected drop action: %s; Succeeded: %d\n", action_str, succeeded); - if (action_str[0] == 'i') { - free(action_str); - } - - gtk_widget_destroy(widget); -} - -void drag_window(char **uris) { - if (gtk_init_check(NULL, NULL) == FALSE) { - fprintf(stderr, "Unable to initialize GTK for drag and drop widget!\n"); - return; - } - - GtkWidget *widget = gtk_window_new(GTK_WINDOW_TOPLEVEL); - GtkWindow *window = GTK_WINDOW(widget); - - gtk_window_move(window, 0, 0); - gtk_window_set_title(window, "Neko Drag & Drop Window"); - gtk_window_set_decorated(window, FALSE); - gtk_window_set_keep_above(window, TRUE); - gtk_window_set_default_size(window, 0, 0); - - GtkTargetList* target_list = gtk_target_list_new(NULL, 0); - gtk_target_list_add_uri_targets(target_list, TARGET_TYPE_URI); - gtk_target_list_add_text_targets(target_list, TARGET_TYPE_TEXT); - - gtk_drag_source_set(widget, GDK_BUTTON1_MASK, NULL, 0, GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_ASK); - gtk_drag_source_set_target_list(widget, target_list); - - g_signal_connect(widget, "drag-data-get", G_CALLBACK(drag_data_get), uris); - g_signal_connect(widget, "drag-end", G_CALLBACK(drag_end), NULL); - g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL); - - fprintf(stderr, "Preparing to show drag and drop widget.\n"); - gtk_widget_show_all(widget); - gtk_main(); -} - -char **uris_make(int size) { - return calloc(size + 1, sizeof(char *)); -} - -void uris_set_file(char **uris, char *file, int n) { - GFile *gfile = g_file_new_for_path(file); - uris[n] = g_file_get_uri(gfile); -} - -void uris_free(char **uris, int size) { - int i; - for (i = 0; i < size; i++) { - free(uris[i]); - } - - free(uris); -} diff --git a/internal/desktop/gtk/gtk.go b/internal/desktop/gtk/gtk.go deleted file mode 100644 index 58d64dce..00000000 --- a/internal/desktop/gtk/gtk.go +++ /dev/null @@ -1,29 +0,0 @@ -package gtk - -/* -#cgo pkg-config: gtk+-3.0 - -#include "gtk.h" -*/ -import "C" - -import ( - "sync" -) - -var mu = sync.Mutex{} - -func DragWindow(files []string) { - mu.Lock() - defer mu.Unlock() - - size := C.int(len(files)) - urisUnsafe := C.uris_make(size); - defer C.uris_free(urisUnsafe, size) - - for i, file := range files { - C.uris_set_file(urisUnsafe, C.CString(file), C.int(i)) - } - - C.drag_window(urisUnsafe) -} diff --git a/internal/desktop/gtk/gtk.h b/internal/desktop/gtk/gtk.h deleted file mode 100644 index da2a06e9..00000000 --- a/internal/desktop/gtk/gtk.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include - -enum { - TARGET_TYPE_TEXT, - TARGET_TYPE_URI -}; - -static void drag_data_get( - GtkWidget *widget, - GdkDragContext *context, - GtkSelectionData *data, - guint target_type, - guint time, - gpointer user_data -); - -static void drag_end( - GtkWidget *widget, - GdkDragContext *context, - gpointer user_data -); - -void drag_window(char **uris); - -char **uris_make(int size); -void uris_set_file(char **uris, char *file, int n); -void uris_free(char **uris, int size); diff --git a/internal/types/desktop.go b/internal/types/desktop.go index afecec53..ec1af1d3 100644 --- a/internal/types/desktop.go +++ b/internal/types/desktop.go @@ -59,5 +59,5 @@ type DesktopManager interface { WriteClipboard(data string) // drop - DropFiles(x int, y int, files []string) + DropFiles(x int, y int, files []string) bool }