From 9d615463f419d2ee28cd811a05c6500c3a3575c7 Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Mon, 2 Oct 2023 20:47:04 +1100 Subject: [PATCH 1/5] [linux] support clipboard --- v3/pkg/application/clipboard.go | 6 +++-- v3/pkg/application/clipboard_linux.go | 36 +++++++++++++++++++++------ v3/pkg/application/linux_cgo.go | 2 +- v3/pkg/application/mainthread.go | 12 +++++++++ 4 files changed, 45 insertions(+), 11 deletions(-) diff --git a/v3/pkg/application/clipboard.go b/v3/pkg/application/clipboard.go index 8a651e43..f21b597e 100644 --- a/v3/pkg/application/clipboard.go +++ b/v3/pkg/application/clipboard.go @@ -16,9 +16,11 @@ func newClipboard() *Clipboard { } func (c *Clipboard) SetText(text string) bool { - return c.impl.setText(text) + return InvokeSyncWithResult(func() bool { + return c.impl.setText(text) + }) } func (c *Clipboard) Text() (string, bool) { - return c.impl.text() + return InvokeSyncWithResultAndOther(c.impl.text) } diff --git a/v3/pkg/application/clipboard_linux.go b/v3/pkg/application/clipboard_linux.go index ef7b2560..2ecdbfa8 100644 --- a/v3/pkg/application/clipboard_linux.go +++ b/v3/pkg/application/clipboard_linux.go @@ -2,8 +2,29 @@ package application +/* +#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0 + +#include "gtk/gtk.h" +#include "webkit2/webkit2.h" + +static gchar* getClipboardText() { + GtkClipboard *clip = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); + return gtk_clipboard_wait_for_text(clip); +} + +static void setClipboardText(gchar* text) { + GtkClipboard *clip = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); + gtk_clipboard_set_text(clip, text, -1); + + clip = gtk_clipboard_get(GDK_SELECTION_PRIMARY); + gtk_clipboard_set_text(clip, text, -1); +} +*/ +import "C" import ( "sync" + "unsafe" ) var clipboardLock sync.RWMutex @@ -13,19 +34,18 @@ type linuxClipboard struct{} func (m linuxClipboard) setText(text string) bool { clipboardLock.Lock() defer clipboardLock.Unlock() - // cText := C.CString(text) - // success := C.setClipboardText(cText) - // C.free(unsafe.Pointer(cText)) - success := false - return bool(success) + cText := C.CString(text) + C.setClipboardText(cText) + C.free(unsafe.Pointer(cText)) + return true } func (m linuxClipboard) text() (string, bool) { clipboardLock.RLock() defer clipboardLock.RUnlock() - // clipboardText := C.getClipboardText() - // result := C.GoString(clipboardText) - return "", false + clipboardText := C.getClipboardText() + result := C.GoString(clipboardText) + return result, true } func newClipboardImpl() *linuxClipboard { diff --git a/v3/pkg/application/linux_cgo.go b/v3/pkg/application/linux_cgo.go index 97b66fdc..dce953cc 100644 --- a/v3/pkg/application/linux_cgo.go +++ b/v3/pkg/application/linux_cgo.go @@ -119,7 +119,7 @@ static void* gtkFileChooserDialogNew(char* title, GtkWindow* window, GtkFileChoo GTK_RESPONSE_CANCEL, acceptLabel, GTK_RESPONSE_ACCEPT, - 0); + NULL); } typedef struct Screen { diff --git a/v3/pkg/application/mainthread.go b/v3/pkg/application/mainthread.go index d7b0af37..b76663aa 100644 --- a/v3/pkg/application/mainthread.go +++ b/v3/pkg/application/mainthread.go @@ -67,6 +67,18 @@ func InvokeSyncWithResultAndError[T any](fn func() (T, error)) (res T, err error return res, err } +func InvokeSyncWithResultAndOther[T any, U any](fn func() (T, U)) (res T, other U) { + var wg sync.WaitGroup + wg.Add(1) + globalApplication.dispatchOnMainThread(func() { + defer processPanicHandlerRecover() + res, other = fn() + wg.Done() + }) + wg.Wait() + return res, other +} + func InvokeAsync(fn func()) { globalApplication.dispatchOnMainThread(func() { defer processPanicHandlerRecover() From 7c98ee329a8226ad47768303e4d5b42c2927203b Mon Sep 17 00:00:00 2001 From: Travis McLane Date: Mon, 2 Oct 2023 11:03:37 -0500 Subject: [PATCH 2/5] [v3] move linux clipboard logic to `linux_cgo` --- v3/pkg/application/clipboard_linux.go | 29 ++------------------------- v3/pkg/application/linux_cgo.go | 17 ++++++++++++++++ 2 files changed, 19 insertions(+), 27 deletions(-) diff --git a/v3/pkg/application/clipboard_linux.go b/v3/pkg/application/clipboard_linux.go index 2ecdbfa8..1c662cd6 100644 --- a/v3/pkg/application/clipboard_linux.go +++ b/v3/pkg/application/clipboard_linux.go @@ -2,29 +2,8 @@ package application -/* -#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0 - -#include "gtk/gtk.h" -#include "webkit2/webkit2.h" - -static gchar* getClipboardText() { - GtkClipboard *clip = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); - return gtk_clipboard_wait_for_text(clip); -} - -static void setClipboardText(gchar* text) { - GtkClipboard *clip = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); - gtk_clipboard_set_text(clip, text, -1); - - clip = gtk_clipboard_get(GDK_SELECTION_PRIMARY); - gtk_clipboard_set_text(clip, text, -1); -} -*/ -import "C" import ( "sync" - "unsafe" ) var clipboardLock sync.RWMutex @@ -34,18 +13,14 @@ type linuxClipboard struct{} func (m linuxClipboard) setText(text string) bool { clipboardLock.Lock() defer clipboardLock.Unlock() - cText := C.CString(text) - C.setClipboardText(cText) - C.free(unsafe.Pointer(cText)) + clipboardSet(text) return true } func (m linuxClipboard) text() (string, bool) { clipboardLock.RLock() defer clipboardLock.RUnlock() - clipboardText := C.getClipboardText() - result := C.GoString(clipboardText) - return result, true + return clipboardGet(), true } func newClipboardImpl() *linuxClipboard { diff --git a/v3/pkg/application/linux_cgo.go b/v3/pkg/application/linux_cgo.go index dce953cc..65838ae5 100644 --- a/v3/pkg/application/linux_cgo.go +++ b/v3/pkg/application/linux_cgo.go @@ -273,6 +273,23 @@ func showAllWindows(application pointer) { } } +// Clipboard +func clipboardGet() string { + clip := C.gtk_clipboard_get(C.GDK_SELECTION_CLIPBOARD) + text := C.gtk_clipboard_wait_for_text(clip) + return C.GoString(text) +} + +func clipboardSet(text string) { + cText := C.CString(text) + clip := C.gtk_clipboard_get(C.GDK_SELECTION_CLIPBOARD) + C.gtk_clipboard_set_text(clip, cText, -1) + + clip = C.gtk_clipboard_get(C.GDK_SELECTION_PRIMARY) + C.gtk_clipboard_set_text(clip, cText, -1) + C.free(unsafe.Pointer(cText)) +} + // Menu func menuAddSeparator(menu *Menu) { C.gtk_menu_shell_append( From 757a4383e677ee2becba23fdfc4a8358dcd7e4c7 Mon Sep 17 00:00:00 2001 From: Travis McLane Date: Fri, 29 Sep 2023 23:54:57 -0500 Subject: [PATCH 3/5] [v3] send dialog results over channels --- v3/pkg/application/dialogs.go | 32 +++++++++++++---- v3/pkg/application/dialogs_darwin.go | 38 ++++++++++----------- v3/pkg/application/dialogs_linux.go | 4 +-- v3/pkg/application/dialogs_windows.go | 23 ++++++++++--- v3/pkg/application/linux_cgo.go | 49 ++++++++++++++------------- 5 files changed, 88 insertions(+), 58 deletions(-) diff --git a/v3/pkg/application/dialogs.go b/v3/pkg/application/dialogs.go index dfc9c922..bd54059f 100644 --- a/v3/pkg/application/dialogs.go +++ b/v3/pkg/application/dialogs.go @@ -1,6 +1,7 @@ package application import ( + "fmt" "strings" "sync" ) @@ -159,7 +160,7 @@ func (d *MessageDialog) SetMessage(message string) *MessageDialog { } type openFileDialogImpl interface { - show() ([]string, error) + show() (chan string, error) } type FileFilter struct { @@ -265,10 +266,11 @@ func (d *OpenFileDialogStruct) PromptForSingleSelection() (string, error) { if d.impl == nil { d.impl = newOpenFileDialogImpl(d) } - selection, err := InvokeSyncWithResultAndError(d.impl.show) + var result string - if len(selection) > 0 { - result = selection[0] + selections, err := InvokeSyncWithResultAndError(d.impl.show) + if err == nil { + result = <-selections } return result, err @@ -289,7 +291,17 @@ func (d *OpenFileDialogStruct) PromptForMultipleSelection() ([]string, error) { if d.impl == nil { d.impl = newOpenFileDialogImpl(d) } - return InvokeSyncWithResultAndError(d.impl.show) + + selections, err := InvokeSyncWithResultAndError(d.impl.show) + + var result []string + fmt.Println("Waiting for results:") + for filename := range selections { + fmt.Println(filename) + result = append(result, filename) + } + + return result, err } func (d *OpenFileDialogStruct) SetMessage(message string) *OpenFileDialogStruct { @@ -385,7 +397,7 @@ type SaveFileDialogStruct struct { } type saveFileDialogImpl interface { - show() (string, error) + show() (chan string, error) } func (d *SaveFileDialogStruct) SetOptions(options *SaveFileDialogOptions) { @@ -448,7 +460,13 @@ func (d *SaveFileDialogStruct) PromptForSingleSelection() (string, error) { if d.impl == nil { d.impl = newSaveFileDialogImpl(d) } - return InvokeSyncWithResultAndError(d.impl.show) + + var result string + selections, err := InvokeSyncWithResultAndError(d.impl.show) + if err == nil { + result = <-selections + } + return result, err } func (d *SaveFileDialogStruct) SetButtonText(text string) *SaveFileDialogStruct { diff --git a/v3/pkg/application/dialogs_darwin.go b/v3/pkg/application/dialogs_darwin.go index b3753092..bdd7aa28 100644 --- a/v3/pkg/application/dialogs_darwin.go +++ b/v3/pkg/application/dialogs_darwin.go @@ -18,7 +18,7 @@ extern void saveFileDialogCallback(uint id, char* path); static void showAboutBox(char* title, char *message, void *icon, int length) { // run on main thread - dispatch_async(dispatch_get_main_queue(), ^{ + // dispatch_async(dispatch_get_main_queue(), ^{ NSAlert *alert = [[NSAlert alloc] init]; if (title != NULL) { [alert setMessageText:[NSString stringWithUTF8String:title]]; @@ -34,7 +34,7 @@ static void showAboutBox(char* title, char *message, void *icon, int length) { } [alert setAlertStyle:NSAlertStyleInformational]; [alert runModal]; - }); + // }); } @@ -163,7 +163,7 @@ static void showOpenFileDialog(unsigned int dialogID, void *window) { // run on main thread - dispatch_async(dispatch_get_main_queue(), ^{ + dispatch_async(dispatch_get_main_queue(), ^{ NSOpenPanel *panel = [NSOpenPanel openPanel]; @@ -229,7 +229,7 @@ static void showOpenFileDialog(unsigned int dialogID, processOpenFileDialogResults(panel, result, dialogID); }]; } - }); + }); } static void showSaveFileDialog(unsigned int dialogID, @@ -246,7 +246,7 @@ static void showSaveFileDialog(unsigned int dialogID, void *window) { // run on main thread - dispatch_async(dispatch_get_main_queue(), ^{ + dispatch_async(dispatch_get_main_queue(), ^{ NSSavePanel *panel = [NSSavePanel savePanel]; if (message != NULL) { @@ -280,8 +280,8 @@ static void showSaveFileDialog(unsigned int dialogID, [panel beginSheetModalForWindow:(__bridge NSWindow *)window completionHandler:^(NSInteger result) { const char *path = NULL; if (result == NSModalResponseOK) { - NSURL *url = [panel URL]; - const char *path = [[url path] UTF8String]; + NSURL *url = [panel URL]; + path = [[url path] UTF8String]; } saveFileDialogCallback(dialogID, (char *)path); }]; @@ -290,12 +290,12 @@ static void showSaveFileDialog(unsigned int dialogID, const char *path = NULL; if (result == NSModalResponseOK) { NSURL *url = [panel URL]; - const char *path = [[url path] UTF8String]; + path = [[url path] UTF8String]; } saveFileDialogCallback(dialogID, (char *)path); }]; } - }); + }); } */ @@ -321,7 +321,9 @@ func (m *macosApp) showAboutDialog(title string, message string, icon []byte) { if icon != nil { iconData = unsafe.Pointer(&icon[0]) } - C.showAboutBox(C.CString(title), C.CString(message), iconData, C.int(len(icon))) + InvokeAsync(func() { + C.showAboutBox(C.CString(title), C.CString(message), iconData, C.int(len(icon))) + }) } type macosDialog struct { @@ -331,7 +333,7 @@ type macosDialog struct { } func (m *macosDialog) show() { - globalApplication.dispatchOnMainThread(func() { + InvokeAsync(func() { // Mac can only have 4 Buttons on a dialog if len(m.dialog.Buttons) > 4 { @@ -419,7 +421,7 @@ func toCString(s string) *C.char { return C.CString(s) } -func (m *macosOpenFileDialog) show() ([]string, error) { +func (m *macosOpenFileDialog) show() (chan string, error) { openFileResponses[m.dialog.id] = make(chan string) nsWindow := unsafe.Pointer(nil) if m.dialog.window != nil { @@ -445,7 +447,6 @@ func (m *macosOpenFileDialog) show() ([]string, error) { } filterPatterns = strings.Join(allPatterns, ";") } - C.showOpenFileDialog(C.uint(m.dialog.id), C.bool(m.dialog.canChooseFiles), C.bool(m.dialog.canChooseDirectories), @@ -462,11 +463,8 @@ func (m *macosOpenFileDialog) show() ([]string, error) { toCString(m.dialog.directory), toCString(m.dialog.buttonText), nsWindow) - var result []string - for filename := range openFileResponses[m.dialog.id] { - result = append(result, filename) - } - return result, nil + + return openFileResponses[m.dialog.id], nil } //export openFileDialogCallback @@ -504,7 +502,7 @@ func newSaveFileDialogImpl(d *SaveFileDialogStruct) *macosSaveFileDialog { } } -func (m *macosSaveFileDialog) show() (string, error) { +func (m *macosSaveFileDialog) show() (chan string, error) { saveFileResponses[m.dialog.id] = make(chan string) nsWindow := unsafe.Pointer(nil) if m.dialog.window != nil { @@ -524,7 +522,7 @@ func (m *macosSaveFileDialog) show() (string, error) { toCString(m.dialog.buttonText), toCString(m.dialog.filename), nsWindow) - return <-saveFileResponses[m.dialog.id], nil + return saveFileResponses[m.dialog.id], nil } //export saveFileDialogCallback diff --git a/v3/pkg/application/dialogs_linux.go b/v3/pkg/application/dialogs_linux.go index b6473454..4943766f 100644 --- a/v3/pkg/application/dialogs_linux.go +++ b/v3/pkg/application/dialogs_linux.go @@ -53,7 +53,7 @@ func newOpenFileDialogImpl(d *OpenFileDialogStruct) *linuxOpenFileDialog { } } -func (m *linuxOpenFileDialog) show() ([]string, error) { +func (m *linuxOpenFileDialog) show() (chan string, error) { return runOpenFileDialog(m.dialog) } @@ -67,6 +67,6 @@ func newSaveFileDialogImpl(d *SaveFileDialogStruct) *linuxSaveFileDialog { } } -func (m *linuxSaveFileDialog) show() (string, error) { +func (m *linuxSaveFileDialog) show() (chan string, error) { return runSaveFileDialog(m.dialog) } diff --git a/v3/pkg/application/dialogs_windows.go b/v3/pkg/application/dialogs_windows.go index 93baf6a7..f7716754 100644 --- a/v3/pkg/application/dialogs_windows.go +++ b/v3/pkg/application/dialogs_windows.go @@ -91,7 +91,7 @@ func getDefaultFolder(folder string) (string, error) { return filepath.Abs(folder) } -func (m *windowOpenFileDialog) show() ([]string, error) { +func (m *windowOpenFileDialog) show() (chan string, error) { defaultFolder, err := getDefaultFolder(m.dialog.directory) if err != nil { @@ -133,7 +133,14 @@ func (m *windowOpenFileDialog) show() ([]string, error) { result = []string{temp.(string)} } - return result, nil + files := make(chan string) + go func() { + for _, file := range result { + files <- file + } + close(files) + }() + return files, nil } type windowSaveFileDialog struct { @@ -146,10 +153,12 @@ func newSaveFileDialogImpl(d *SaveFileDialogStruct) *windowSaveFileDialog { } } -func (m *windowSaveFileDialog) show() (string, error) { +func (m *windowSaveFileDialog) show() (chan string, error) { + files := make(chan string) defaultFolder, err := getDefaultFolder(m.dialog.directory) if err != nil { - return "", err + close(files) + return files, err } config := cfd.DialogConfig{ @@ -164,7 +173,11 @@ func (m *windowSaveFileDialog) show() (string, error) { func() (cfd.Dialog, error) { return cfd.NewSaveFileDialog(config) }, false) - return result.(string), nil + go func() { + files <- result.(string) + close(files) + }() + return files, err } func calculateMessageDialogFlags(options MessageDialogOptions) uint32 { diff --git a/v3/pkg/application/linux_cgo.go b/v3/pkg/application/linux_cgo.go index 65838ae5..dcdffaae 100644 --- a/v3/pkg/application/linux_cgo.go +++ b/v3/pkg/application/linux_cgo.go @@ -973,7 +973,7 @@ func messageDialogCB(button C.int) { } -func runChooserDialog(window pointer, allowMultiple, createFolders, showHidden bool, currentFolder, title string, action int, acceptLabel string, filters []FileFilter) ([]string, error) { +func runChooserDialog(window pointer, allowMultiple, createFolders, showHidden bool, currentFolder, title string, action int, acceptLabel string, filters []FileFilter) (chan string, error) { titleStr := C.CString(title) defer C.free(unsafe.Pointer(titleStr)) cancelStr := C.CString("_Cancel") @@ -1036,27 +1036,32 @@ func runChooserDialog(window pointer, allowMultiple, createFolders, showHidden b return string(bytes) } - response := C.gtk_dialog_run((*C.GtkDialog)(fc)) - selections := []string{} - if response == C.GTK_RESPONSE_ACCEPT { - filenames := C.gtk_file_chooser_get_filenames((*C.GtkFileChooser)(fc)) - iter := filenames - count := 0 - for { - selections = append(selections, buildStringAndFree(C.gpointer(iter.data))) - iter = iter.next - if iter == nil || count == 1024 { - break + selections := make(chan string) + // run this on the gtk thread + InvokeAsync(func() { + go func() { + response := C.gtk_dialog_run((*C.GtkDialog)(fc)) + if response == C.GTK_RESPONSE_ACCEPT { + filenames := C.gtk_file_chooser_get_filenames((*C.GtkFileChooser)(fc)) + iter := filenames + count := 0 + for { + selections <- buildStringAndFree(C.gpointer(iter.data)) + iter = iter.next + if iter == nil || count == 1024 { + break + } + count++ + } + close(selections) + C.gtk_widget_destroy((*C.GtkWidget)(unsafe.Pointer(fc))) } - count++ - } - } - - defer C.gtk_widget_destroy((*C.GtkWidget)(unsafe.Pointer(fc))) + }() + }) return selections, nil } -func runOpenFileDialog(dialog *OpenFileDialogStruct) ([]string, error) { +func runOpenFileDialog(dialog *OpenFileDialogStruct) (chan string, error) { const GtkFileChooserActionOpen = C.GTK_FILE_CHOOSER_ACTION_OPEN window := nilPointer @@ -1145,7 +1150,7 @@ func runQuestionDialog(parent pointer, options *MessageDialog) int { return int(C.gtk_dialog_run((*C.GtkDialog)(unsafe.Pointer(dialog)))) } -func runSaveFileDialog(dialog *SaveFileDialogStruct) (string, error) { +func runSaveFileDialog(dialog *SaveFileDialogStruct) (chan string, error) { window := nilPointer buttonText := dialog.buttonText if buttonText == "" { @@ -1162,11 +1167,7 @@ func runSaveFileDialog(dialog *SaveFileDialogStruct) (string, error) { buttonText, dialog.filters) - if err != nil || len(results) == 0 { - return "", err - } - - return results[0], nil + return results, err } // systray From dc8cbcf41047e5690e688922cd3c058f9428626b Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Tue, 3 Oct 2023 08:33:58 +1100 Subject: [PATCH 4/5] [darwin] Refactor events into mac specific files --- .../application/application_darwin_delegate.m | 2 +- v3/pkg/application/webview_window_darwin.m | 20 +-- .../application/webview_window_darwin_drag.m | 2 +- v3/pkg/events/events_darwin.go | 4 +- v3/pkg/events/events_darwin.h | 136 ++++++++++++++++++ 5 files changed, 140 insertions(+), 24 deletions(-) create mode 100644 v3/pkg/events/events_darwin.h diff --git a/v3/pkg/application/application_darwin_delegate.m b/v3/pkg/application/application_darwin_delegate.m index 855b0309..c4a360bd 100644 --- a/v3/pkg/application/application_darwin_delegate.m +++ b/v3/pkg/application/application_darwin_delegate.m @@ -1,6 +1,6 @@ //go:build darwin #import "application_darwin_delegate.h" -#import "../events/events.h" +#import "../events/events_darwin.h" extern bool hasListeners(unsigned int); @implementation AppDelegate - (void)dealloc diff --git a/v3/pkg/application/webview_window_darwin.m b/v3/pkg/application/webview_window_darwin.m index fb4523a4..b2ee37e0 100644 --- a/v3/pkg/application/webview_window_darwin.m +++ b/v3/pkg/application/webview_window_darwin.m @@ -2,7 +2,7 @@ #import #import #import "webview_window_darwin.h" -#import "../events/events.h" +#import "../events/events_darwin.h" extern void processMessage(unsigned int, const char*); extern void processURLRequest(unsigned int, void *); extern void processWindowKeyDownEvent(unsigned int, const char*); @@ -17,14 +17,10 @@ extern bool hasListeners(unsigned int); [self setMovableByWindowBackground:YES]; return self; } - - (void)keyDown:(NSEvent *)event { - NSUInteger modifierFlags = event.modifierFlags; - // Create an array to hold the modifier strings NSMutableArray *modifierStrings = [NSMutableArray array]; - // Check for modifier flags and add corresponding strings to the array if (modifierFlags & NSEventModifierFlagShift) { [modifierStrings addObject:@"shift"]; @@ -38,29 +34,23 @@ extern bool hasListeners(unsigned int); if (modifierFlags & NSEventModifierFlagCommand) { [modifierStrings addObject:@"cmd"]; } - NSString *keyString = [self keyStringFromEvent:event]; if (keyString.length > 0) { [modifierStrings addObject:keyString]; } - // Combine the modifier strings with the key character NSString *keyEventString = [modifierStrings componentsJoinedByString:@"+"]; - const char* utf8String = [keyEventString UTF8String]; WebviewWindowDelegate *delegate = (WebviewWindowDelegate*)self.delegate; processWindowKeyDownEvent(delegate.windowId, utf8String); } - - (NSString *)keyStringFromEvent:(NSEvent *)event { - // Get the pressed key // Check for special keys like escape and tab NSString *characters = [event characters]; if (characters.length == 0) { return @""; } - if ([characters isEqualToString:@"\r"]) { return @"enter"; } @@ -90,7 +80,6 @@ extern bool hasListeners(unsigned int); if ([characters isEqualToString:@"\x0C"]) { return @"clear"; } - switch ([event keyCode]) { // Function keys case 122: return @"f1"; @@ -113,7 +102,6 @@ extern bool hasListeners(unsigned int); case 79: return @"f18"; case 80: return @"f19"; case 90: return @"f20"; - // Letter keys case 0: return @"a"; case 11: return @"b"; @@ -141,7 +129,6 @@ extern bool hasListeners(unsigned int); case 7: return @"x"; case 16: return @"y"; case 6: return @"z"; - // Number keys case 29: return @"0"; case 18: return @"1"; @@ -153,7 +140,6 @@ extern bool hasListeners(unsigned int); case 26: return @"7"; case 28: return @"8"; case 25: return @"9"; - // Other special keys case 51: return @"delete"; case 117: return @"forward delete"; @@ -164,7 +150,6 @@ extern bool hasListeners(unsigned int); case 48: return @"tab"; case 53: return @"escape"; case 49: return @"space"; - // Punctuation and other keys (for a standard US layout) case 33: return @"["; case 30: return @"]"; @@ -177,12 +162,9 @@ extern bool hasListeners(unsigned int); case 24: return @"="; case 50: return @"`"; case 42: return @"\\"; - default: return @""; } } - - - (BOOL)canBecomeKeyWindow { return YES; } diff --git a/v3/pkg/application/webview_window_darwin_drag.m b/v3/pkg/application/webview_window_darwin_drag.m index 345c5ee3..9a8e9c57 100644 --- a/v3/pkg/application/webview_window_darwin_drag.m +++ b/v3/pkg/application/webview_window_darwin_drag.m @@ -4,7 +4,7 @@ #import #import "webview_window_darwin_drag.h" -#import "../events/events.h" +#import "../events/events_darwin.h" extern void processDragItems(unsigned int windowId, char** arr, int length); diff --git a/v3/pkg/events/events_darwin.go b/v3/pkg/events/events_darwin.go index 12a7c3af..d6e978ab 100644 --- a/v3/pkg/events/events_darwin.go +++ b/v3/pkg/events/events_darwin.go @@ -6,12 +6,10 @@ package events #cgo CFLAGS: -x objective-c #cgo LDFLAGS: -framework Cocoa -mmacosx-version-min=10.13 -#include "events.h" +#include "events_darwin.h" #include #include -#include "events.h" - bool hasListener[MAX_EVENTS] = {false}; void registerListener(unsigned int event) { diff --git a/v3/pkg/events/events_darwin.h b/v3/pkg/events/events_darwin.h new file mode 100644 index 00000000..f3bb1bbe --- /dev/null +++ b/v3/pkg/events/events_darwin.h @@ -0,0 +1,136 @@ +//go:build darwin + +#ifndef _events_darwin_h +#define _events_darwin_h + +extern void processApplicationEvent(unsigned int, void* data); +extern void processWindowEvent(unsigned int, unsigned int); + +#define EventApplicationDidBecomeActive 1024 +#define EventApplicationDidChangeBackingProperties 1025 +#define EventApplicationDidChangeEffectiveAppearance 1026 +#define EventApplicationDidChangeIcon 1027 +#define EventApplicationDidChangeOcclusionState 1028 +#define EventApplicationDidChangeScreenParameters 1029 +#define EventApplicationDidChangeStatusBarFrame 1030 +#define EventApplicationDidChangeStatusBarOrientation 1031 +#define EventApplicationDidFinishLaunching 1032 +#define EventApplicationDidHide 1033 +#define EventApplicationDidResignActiveNotification 1034 +#define EventApplicationDidUnhide 1035 +#define EventApplicationDidUpdate 1036 +#define EventApplicationWillBecomeActive 1037 +#define EventApplicationWillFinishLaunching 1038 +#define EventApplicationWillHide 1039 +#define EventApplicationWillResignActive 1040 +#define EventApplicationWillTerminate 1041 +#define EventApplicationWillUnhide 1042 +#define EventApplicationWillUpdate 1043 +#define EventApplicationDidChangeTheme 1044 +#define EventWindowDidBecomeKey 1045 +#define EventWindowDidBecomeMain 1046 +#define EventWindowDidBeginSheet 1047 +#define EventWindowDidChangeAlpha 1048 +#define EventWindowDidChangeBackingLocation 1049 +#define EventWindowDidChangeBackingProperties 1050 +#define EventWindowDidChangeCollectionBehavior 1051 +#define EventWindowDidChangeEffectiveAppearance 1052 +#define EventWindowDidChangeOcclusionState 1053 +#define EventWindowDidChangeOrderingMode 1054 +#define EventWindowDidChangeScreen 1055 +#define EventWindowDidChangeScreenParameters 1056 +#define EventWindowDidChangeScreenProfile 1057 +#define EventWindowDidChangeScreenSpace 1058 +#define EventWindowDidChangeScreenSpaceProperties 1059 +#define EventWindowDidChangeSharingType 1060 +#define EventWindowDidChangeSpace 1061 +#define EventWindowDidChangeSpaceOrderingMode 1062 +#define EventWindowDidChangeTitle 1063 +#define EventWindowDidChangeToolbar 1064 +#define EventWindowDidChangeVisibility 1065 +#define EventWindowDidDeminiaturize 1066 +#define EventWindowDidEndSheet 1067 +#define EventWindowDidEnterFullScreen 1068 +#define EventWindowDidEnterVersionBrowser 1069 +#define EventWindowDidExitFullScreen 1070 +#define EventWindowDidExitVersionBrowser 1071 +#define EventWindowDidExpose 1072 +#define EventWindowDidFocus 1073 +#define EventWindowDidMiniaturize 1074 +#define EventWindowDidMove 1075 +#define EventWindowDidOrderOffScreen 1076 +#define EventWindowDidOrderOnScreen 1077 +#define EventWindowDidResignKey 1078 +#define EventWindowDidResignMain 1079 +#define EventWindowDidResize 1080 +#define EventWindowDidUpdate 1081 +#define EventWindowDidUpdateAlpha 1082 +#define EventWindowDidUpdateCollectionBehavior 1083 +#define EventWindowDidUpdateCollectionProperties 1084 +#define EventWindowDidUpdateShadow 1085 +#define EventWindowDidUpdateTitle 1086 +#define EventWindowDidUpdateToolbar 1087 +#define EventWindowDidUpdateVisibility 1088 +#define EventWindowShouldClose 1089 +#define EventWindowWillBecomeKey 1090 +#define EventWindowWillBecomeMain 1091 +#define EventWindowWillBeginSheet 1092 +#define EventWindowWillChangeOrderingMode 1093 +#define EventWindowWillClose 1094 +#define EventWindowWillDeminiaturize 1095 +#define EventWindowWillEnterFullScreen 1096 +#define EventWindowWillEnterVersionBrowser 1097 +#define EventWindowWillExitFullScreen 1098 +#define EventWindowWillExitVersionBrowser 1099 +#define EventWindowWillFocus 1100 +#define EventWindowWillMiniaturize 1101 +#define EventWindowWillMove 1102 +#define EventWindowWillOrderOffScreen 1103 +#define EventWindowWillOrderOnScreen 1104 +#define EventWindowWillResignMain 1105 +#define EventWindowWillResize 1106 +#define EventWindowWillUnfocus 1107 +#define EventWindowWillUpdate 1108 +#define EventWindowWillUpdateAlpha 1109 +#define EventWindowWillUpdateCollectionBehavior 1110 +#define EventWindowWillUpdateCollectionProperties 1111 +#define EventWindowWillUpdateShadow 1112 +#define EventWindowWillUpdateTitle 1113 +#define EventWindowWillUpdateToolbar 1114 +#define EventWindowWillUpdateVisibility 1115 +#define EventWindowWillUseStandardFrame 1116 +#define EventMenuWillOpen 1117 +#define EventMenuDidOpen 1118 +#define EventMenuDidClose 1119 +#define EventMenuWillSendAction 1120 +#define EventMenuDidSendAction 1121 +#define EventMenuWillHighlightItem 1122 +#define EventMenuDidHighlightItem 1123 +#define EventMenuWillDisplayItem 1124 +#define EventMenuDidDisplayItem 1125 +#define EventMenuWillAddItem 1126 +#define EventMenuDidAddItem 1127 +#define EventMenuWillRemoveItem 1128 +#define EventMenuDidRemoveItem 1129 +#define EventMenuWillBeginTracking 1130 +#define EventMenuDidBeginTracking 1131 +#define EventMenuWillEndTracking 1132 +#define EventMenuDidEndTracking 1133 +#define EventMenuWillUpdate 1134 +#define EventMenuDidUpdate 1135 +#define EventMenuWillPopUp 1136 +#define EventMenuDidPopUp 1137 +#define EventMenuWillSendActionToItem 1138 +#define EventMenuDidSendActionToItem 1139 +#define EventWebViewDidStartProvisionalNavigation 1140 +#define EventWebViewDidReceiveServerRedirectForProvisionalNavigation 1141 +#define EventWebViewDidFinishNavigation 1142 +#define EventWebViewDidCommitNavigation 1143 +#define EventWindowFileDraggingEntered 1144 +#define EventWindowFileDraggingPerformed 1145 +#define EventWindowFileDraggingExited 1146 + +#define MAX_EVENTS 124 + + +#endif \ No newline at end of file From 8ddd29d285c42d15731e68c039331db19cfb3d84 Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Tue, 3 Oct 2023 08:37:11 +1100 Subject: [PATCH 5/5] [linux] Implement events --- .../runtime/desktop/api/event_types.js | 3 + v3/pkg/application/application_linux.go | 13 ++- v3/pkg/application/events_common_linux.go | 20 +++++ v3/pkg/events/events.go | 88 +++++++++++-------- v3/pkg/events/events.txt | 1 + v3/pkg/events/events_linux.go | 21 +++++ v3/pkg/events/events_linux.h | 14 +++ v3/tasks/events/generate.go | 79 ++++++++++++++--- 8 files changed, 183 insertions(+), 56 deletions(-) create mode 100644 v3/pkg/application/events_common_linux.go create mode 100644 v3/pkg/events/events_linux.go create mode 100644 v3/pkg/events/events_linux.h diff --git a/v3/internal/runtime/desktop/api/event_types.js b/v3/internal/runtime/desktop/api/event_types.js index e2a7857e..f130789a 100644 --- a/v3/internal/runtime/desktop/api/event_types.js +++ b/v3/internal/runtime/desktop/api/event_types.js @@ -147,6 +147,9 @@ export const EventTypes = { WindowFileDraggingPerformed: "mac:WindowFileDraggingPerformed", WindowFileDraggingExited: "mac:WindowFileDraggingExited", }, + Linux: { + SystemThemeChanged: "linux:SystemThemeChanged", +}, Common: { ApplicationStarted: "common:ApplicationStarted", WindowMaximise: "common:WindowMaximise", diff --git a/v3/pkg/application/application_linux.go b/v3/pkg/application/application_linux.go index 27c04a89..bf64a9d2 100644 --- a/v3/pkg/application/application_linux.go +++ b/v3/pkg/application/application_linux.go @@ -3,13 +3,10 @@ package application import ( - "fmt" "log" "os" "strings" "sync" - - "github.com/wailsapp/wails/v3/pkg/events" ) func init() { @@ -104,10 +101,12 @@ func (m *linuxApp) run() error { // Add a hook to the ApplicationDidFinishLaunching event // FIXME: add Wails specific events - i.e. Shouldn't platform specific ones be translated to Wails events? - m.parent.On(events.Mac.ApplicationDidFinishLaunching, func(evt *Event) { - // Do we need to do anything now? - fmt.Println("events.Mac.ApplicationDidFinishLaunching received!") - }) + //m.parent.On(events.Mac.ApplicationDidFinishLaunching, func(evt *Event) { + // // Do we need to do anything now? + // fmt.Println("events.Mac.ApplicationDidFinishLaunching received!") + //}) + + m.setupCommonEvents() return appRun(m.application) } diff --git a/v3/pkg/application/events_common_linux.go b/v3/pkg/application/events_common_linux.go new file mode 100644 index 00000000..bafe8379 --- /dev/null +++ b/v3/pkg/application/events_common_linux.go @@ -0,0 +1,20 @@ +//go:build linux + +package application + +import "github.com/wailsapp/wails/v3/pkg/events" + +var commonApplicationEventMap = map[events.ApplicationEventType]events.ApplicationEventType{ + events.Linux.SystemThemeChanged: events.Common.ThemeChanged, +} + +func (m *linuxApp) setupCommonEvents() { + for sourceEvent, targetEvent := range commonApplicationEventMap { + sourceEvent := sourceEvent + targetEvent := targetEvent + m.parent.On(sourceEvent, func(event *Event) { + event.Id = uint(targetEvent) + applicationEvents <- event + }) + } +} diff --git a/v3/pkg/events/events.go b/v3/pkg/events/events.go index fd0134f7..d674ea0e 100644 --- a/v3/pkg/events/events.go +++ b/v3/pkg/events/events.go @@ -33,25 +33,25 @@ type commonEvents struct { func newCommonEvents() commonEvents { return commonEvents{ - ApplicationStarted: 1167, - WindowMaximise: 1168, - WindowUnMaximise: 1169, - WindowFullscreen: 1170, - WindowUnFullscreen: 1171, - WindowRestore: 1172, - WindowMinimise: 1173, - WindowUnMinimise: 1174, - WindowClosing: 1175, - WindowZoom: 1176, - WindowZoomIn: 1177, - WindowZoomOut: 1178, - WindowZoomReset: 1179, - WindowFocus: 1180, - WindowLostFocus: 1181, - WindowShow: 1182, - WindowHide: 1183, - WindowDPIChanged: 1184, - ThemeChanged: 1185, + ApplicationStarted: 1168, + WindowMaximise: 1169, + WindowUnMaximise: 1170, + WindowFullscreen: 1171, + WindowUnFullscreen: 1172, + WindowRestore: 1173, + WindowMinimise: 1174, + WindowUnMinimise: 1175, + WindowClosing: 1176, + WindowZoom: 1177, + WindowZoomIn: 1178, + WindowZoomOut: 1179, + WindowZoomReset: 1180, + WindowFocus: 1181, + WindowLostFocus: 1182, + WindowShow: 1183, + WindowHide: 1184, + WindowDPIChanged: 1185, + ThemeChanged: 1186, } } @@ -311,6 +311,18 @@ func newMacEvents() macEvents { } } +var Linux = newLinuxEvents() + +type linuxEvents struct { + SystemThemeChanged ApplicationEventType +} + +func newLinuxEvents() linuxEvents { + return linuxEvents{ + SystemThemeChanged: 1167, + } +} + var Windows = newWindowsEvents() type windowsEvents struct { @@ -509,23 +521,23 @@ var eventToJS = map[uint]string{ 1164: "windows:WindowClose", 1165: "windows:WindowSetFocus", 1166: "windows:WindowKillFocus", - 1167: "common:ApplicationStarted", - 1168: "common:WindowMaximise", - 1169: "common:WindowUnMaximise", - 1170: "common:WindowFullscreen", - 1171: "common:WindowUnFullscreen", - 1172: "common:WindowRestore", - 1173: "common:WindowMinimise", - 1174: "common:WindowUnMinimise", - 1175: "common:WindowClosing", - 1176: "common:WindowZoom", - 1177: "common:WindowZoomIn", - 1178: "common:WindowZoomOut", - 1179: "common:WindowZoomReset", - 1180: "common:WindowFocus", - 1181: "common:WindowLostFocus", - 1182: "common:WindowShow", - 1183: "common:WindowHide", - 1184: "common:WindowDPIChanged", - 1185: "common:ThemeChanged", + 1168: "common:ApplicationStarted", + 1169: "common:WindowMaximise", + 1170: "common:WindowUnMaximise", + 1171: "common:WindowFullscreen", + 1172: "common:WindowUnFullscreen", + 1173: "common:WindowRestore", + 1174: "common:WindowMinimise", + 1175: "common:WindowUnMinimise", + 1176: "common:WindowClosing", + 1177: "common:WindowZoom", + 1178: "common:WindowZoomIn", + 1179: "common:WindowZoomOut", + 1180: "common:WindowZoomReset", + 1181: "common:WindowFocus", + 1182: "common:WindowLostFocus", + 1183: "common:WindowShow", + 1184: "common:WindowHide", + 1185: "common:WindowDPIChanged", + 1186: "common:ThemeChanged", } diff --git a/v3/pkg/events/events.txt b/v3/pkg/events/events.txt index 288714ac..2f724e7d 100644 --- a/v3/pkg/events/events.txt +++ b/v3/pkg/events/events.txt @@ -141,6 +141,7 @@ windows:WindowUnMinimise windows:WindowClose windows:WindowSetFocus windows:WindowKillFocus +linux:SystemThemeChanged common:ApplicationStarted common:WindowMaximise common:WindowUnMaximise diff --git a/v3/pkg/events/events_linux.go b/v3/pkg/events/events_linux.go new file mode 100644 index 00000000..a7bc3b1f --- /dev/null +++ b/v3/pkg/events/events_linux.go @@ -0,0 +1,21 @@ +//go:build linux + +package events + +/* +#include "events_linux.h" +#include +#include + +bool hasListener[MAX_EVENTS] = {false}; + +void registerListener(unsigned int event) { + hasListener[event] = true; +} + +bool hasListeners(unsigned int event) { + return hasListener[event]; +} + +*/ +import "C" diff --git a/v3/pkg/events/events_linux.h b/v3/pkg/events/events_linux.h new file mode 100644 index 00000000..58aa558b --- /dev/null +++ b/v3/pkg/events/events_linux.h @@ -0,0 +1,14 @@ +//go:build linux + +#ifndef _events_linux_h +#define _events_linux_h + +extern void processApplicationEvent(unsigned int, void* data); +extern void processWindowEvent(unsigned int, unsigned int); + +#define EventSystemThemeChanged 1167 + +#define MAX_EVENTS 2 + + +#endif \ No newline at end of file diff --git a/v3/tasks/events/generate.go b/v3/tasks/events/generate.go index 970f8b3a..82ba932a 100644 --- a/v3/tasks/events/generate.go +++ b/v3/tasks/events/generate.go @@ -36,6 +36,16 @@ func newMacEvents() macEvents { $$MACEVENTSVALUES } } +var Linux = newLinuxEvents() + +type linuxEvents struct { +$$LINUXEVENTSDECL} + +func newLinuxEvents() linuxEvents { + return linuxEvents{ +$$LINUXEVENTSVALUES } +} + var Windows = newWindowsEvents() type windowsEvents struct { @@ -55,15 +65,27 @@ $$EVENTTOJS} ` -var eventsH = `//go:build darwin +var eventsDarwinH = `//go:build darwin -#ifndef _events_h -#define _events_h +#ifndef _events_darwin_h +#define _events_darwin_h extern void processApplicationEvent(unsigned int, void* data); extern void processWindowEvent(unsigned int, unsigned int); -$$CHEADEREVENTS +$$CDARWINHEADEREVENTS + +#endif` + +var eventsLinuxH = `//go:build linux + +#ifndef _events_linux_h +#define _events_linux_h + +extern void processApplicationEvent(unsigned int, void* data); +extern void processWindowEvent(unsigned int, unsigned int); + +$$CLINUXHEADEREVENTS #endif` @@ -73,6 +95,8 @@ export const EventTypes = { $$WINDOWSJSEVENTS }, Mac: { $$MACJSEVENTS }, + Linux: { +$$LINUXJSEVENTS}, Common: { $$COMMONJSEVENTS }, }; @@ -87,7 +111,7 @@ func main() { macEventsDecl := bytes.NewBufferString("") macEventsValues := bytes.NewBufferString("") - cHeaderEvents := bytes.NewBufferString("") + cDarwinHeaderEvents := bytes.NewBufferString("") windowDelegateEvents := bytes.NewBufferString("") applicationDelegateEvents := bytes.NewBufferString("") webviewDelegateEvents := bytes.NewBufferString("") @@ -104,8 +128,14 @@ func main() { eventToJS := bytes.NewBufferString("") + linuxEventsDecl := bytes.NewBufferString("") + linuxEventsValues := bytes.NewBufferString("") + linuxJSEvents := bytes.NewBufferString("") + cLinuxHeaderEvents := bytes.NewBufferString("") + var id int var maxMacEvents int + var maxLinuxEvents int var line []byte // Loop over each line in the file for id, line = range bytes.Split(eventNames, []byte{'\n'}) { @@ -135,6 +165,22 @@ func main() { // Add to buffer switch platform { + case "linux": + eventType := "ApplicationEventType" + if strings.HasPrefix(event, "Window") { + eventType = "WindowEventType" + } + if strings.HasPrefix(event, "WebView") { + eventType = "WindowEventType" + } + cLinuxHeaderEvents.WriteString("#define Event" + eventTitle + " " + strconv.Itoa(id) + "\n") + linuxEventsDecl.WriteString("\t" + eventTitle + " " + eventType + "\n") + linuxEventsValues.WriteString("\t\t" + event + ": " + strconv.Itoa(id) + ",\n") + linuxJSEvents.WriteString("\t\t" + event + ": \"" + strings.TrimSpace(string(line)) + "\",\n") + maxLinuxEvents++ + if ignoreEvent { + continue + } case "mac": eventType := "ApplicationEventType" if strings.HasPrefix(event, "Window") { @@ -146,9 +192,9 @@ func main() { macEventsDecl.WriteString("\t" + eventTitle + " " + eventType + "\n") macEventsValues.WriteString("\t\t" + event + ": " + strconv.Itoa(id) + ",\n") macJSEvents.WriteString("\t\t" + event + ": \"" + strings.TrimSpace(string(line)) + "\",\n") - cHeaderEvents.WriteString("#define Event" + eventTitle + " " + strconv.Itoa(id) + "\n") + cDarwinHeaderEvents.WriteString("#define Event" + eventTitle + " " + strconv.Itoa(id) + "\n") eventToJS.WriteString("\t" + strconv.Itoa(id) + ": \"" + strings.TrimSpace(string(line)) + "\",\n") - maxMacEvents = id + maxMacEvents++ if ignoreEvent { continue } @@ -210,10 +256,13 @@ func main() { } } - cHeaderEvents.WriteString("\n#define MAX_EVENTS " + strconv.Itoa(maxMacEvents+1) + "\n") + cLinuxHeaderEvents.WriteString("\n#define MAX_EVENTS " + strconv.Itoa(maxLinuxEvents+1) + "\n") + cDarwinHeaderEvents.WriteString("\n#define MAX_EVENTS " + strconv.Itoa(maxMacEvents+1) + "\n") // Save the eventsGo template substituting the values and decls templateToWrite := strings.ReplaceAll(eventsGo, "$$MACEVENTSDECL", macEventsDecl.String()) + templateToWrite = strings.ReplaceAll(templateToWrite, "$$LINUXEVENTSDECL", linuxEventsDecl.String()) + templateToWrite = strings.ReplaceAll(templateToWrite, "$$LINUXEVENTSVALUES", linuxEventsValues.String()) templateToWrite = strings.ReplaceAll(templateToWrite, "$$MACEVENTSVALUES", macEventsValues.String()) templateToWrite = strings.ReplaceAll(templateToWrite, "$$WINDOWSEVENTSDECL", windowsEventsDecl.String()) templateToWrite = strings.ReplaceAll(templateToWrite, "$$WINDOWSEVENTSVALUES", windowsEventsValues.String()) @@ -228,15 +277,23 @@ func main() { // Save the eventsJS template substituting the values and decls templateToWrite = strings.ReplaceAll(eventsJS, "$$MACJSEVENTS", macJSEvents.String()) templateToWrite = strings.ReplaceAll(templateToWrite, "$$WINDOWSJSEVENTS", windowsJSEvents.String()) + templateToWrite = strings.ReplaceAll(templateToWrite, "$$LINUXJSEVENTS", linuxJSEvents.String()) templateToWrite = strings.ReplaceAll(templateToWrite, "$$COMMONJSEVENTS", commonJSEvents.String()) err = os.WriteFile("../../internal/runtime/desktop/api/event_types.js", []byte(templateToWrite), 0644) if err != nil { panic(err) } - // Save the eventsH template substituting the values and decls - templateToWrite = strings.ReplaceAll(eventsH, "$$CHEADEREVENTS", cHeaderEvents.String()) - err = os.WriteFile("../../pkg/events/events.h", []byte(templateToWrite), 0644) + // Save the eventsDarwinH template substituting the values and decls + templateToWrite = strings.ReplaceAll(eventsDarwinH, "$$CDARWINHEADEREVENTS", cDarwinHeaderEvents.String()) + err = os.WriteFile("../../pkg/events/events_darwin.h", []byte(templateToWrite), 0644) + if err != nil { + panic(err) + } + + // Save the eventsDarwinH template substituting the values and decls + templateToWrite = strings.ReplaceAll(eventsLinuxH, "$$CLINUXHEADEREVENTS", cLinuxHeaderEvents.String()) + err = os.WriteFile("../../pkg/events/events_linux.h", []byte(templateToWrite), 0644) if err != nil { panic(err) }