From dfc6bd2687092b83d33f9b04d21e9275295e807f Mon Sep 17 00:00:00 2001 From: Marc-Aurel Zent Date: Sat, 17 Jun 2023 21:31:15 +0200 Subject: [PATCH] Fix OSX backend threading issues --- backends/imgui_impl_osx.mm | 97 ++++++++++++++++++++++++++------------ 1 file changed, 68 insertions(+), 29 deletions(-) diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index 97c3e8f78eb2..6303dfcf24d9 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -25,6 +25,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2023-06-19: Fixed threading issues with io.SetClipboardTextFn(), io.GetClipboardTextFn() and io.SetPlatformImeDataFn(). // 2023-04-09: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_Pen. // 2023-02-01: Fixed scroll wheel scaling for devices emitting events with hasPreciseScrollingDeltas==false (e.g. non-Apple mices). // 2022-11-02: Fixed mouse coordinates before clicking the host window. @@ -412,30 +413,57 @@ bool ImGui_ImplOSX_Init(NSView* view) // Note that imgui.cpp also include default OSX clipboard handlers which can be enabled // by adding '#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS' in imconfig.h and adding '-framework ApplicationServices' to your linker command-line. // Since we are already in ObjC land here, it is easy for us to add a clipboard handler using the NSPasteboard api. + // The events below may be blocking until dispatched when not invoked from the main thread (usually in the realm of 0.6 to 2ms). See #6527. io.SetClipboardTextFn = [](void*, const char* str) -> void { - NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; - [pasteboard declareTypes:[NSArray arrayWithObject:NSPasteboardTypeString] owner:nil]; - [pasteboard setString:[NSString stringWithUTF8String:str] forType:NSPasteboardTypeString]; + void (*setClipboardText)(const char*) = [](const char* str) -> void + { + NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; + [pasteboard declareTypes:[NSArray arrayWithObject:NSPasteboardTypeString] owner:nil]; + [pasteboard setString:[NSString stringWithUTF8String:str] forType:NSPasteboardTypeString]; + }; + + if ([NSThread isMainThread]) + return setClipboardText(str); + + dispatch_sync(dispatch_get_main_queue(), + ^{ + setClipboardText(str); + }); }; io.GetClipboardTextFn = [](void*) -> const char* { - NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; - NSString* available = [pasteboard availableTypeFromArray: [NSArray arrayWithObject:NSPasteboardTypeString]]; - if (![available isEqualToString:NSPasteboardTypeString]) - return nullptr; - - NSString* string = [pasteboard stringForType:NSPasteboardTypeString]; - if (string == nil) - return nullptr; - - const char* string_c = (const char*)[string UTF8String]; - size_t string_len = strlen(string_c); - static ImVector s_clipboard; - s_clipboard.resize((int)string_len + 1); - strcpy(s_clipboard.Data, string_c); - return s_clipboard.Data; + const char* (*getClipboardText)(void) = [](void) -> const char* + { + NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; + NSString* available = [pasteboard availableTypeFromArray: [NSArray arrayWithObject:NSPasteboardTypeString]]; + if (![available isEqualToString:NSPasteboardTypeString]) + return nullptr; + + NSString* string = [pasteboard stringForType:NSPasteboardTypeString]; + if (string == nil) + return nullptr; + + const char* string_c = (const char*)[string UTF8String]; + size_t string_len = strlen(string_c); + static ImVector s_clipboard; + s_clipboard.resize((int)string_len + 1); + strcpy(s_clipboard.Data, string_c); + return s_clipboard.Data; + }; + + if ([NSThread isMainThread]) + return getClipboardText(); + + __block const char* clipboardText; + + dispatch_sync(dispatch_get_main_queue(), + ^{ + clipboardText = getClipboardText(); + }); + + return clipboardText; }; [[NSNotificationCenter defaultCenter] addObserver:bd->Observer @@ -456,18 +484,29 @@ bool ImGui_ImplOSX_Init(NSView* view) io.SetPlatformImeDataFn = [](ImGuiViewport* viewport, ImGuiPlatformImeData* data) -> void { - ImGui_ImplOSX_Data* bd = ImGui_ImplOSX_GetBackendData(); - if (data->WantVisible) - { - [bd->InputContext activate]; - } - else + void (*setPlatformImeData)(ImGuiPlatformImeData*) = [](ImGuiPlatformImeData* data) -> void { - [bd->InputContext discardMarkedText]; - [bd->InputContext invalidateCharacterCoordinates]; - [bd->InputContext deactivate]; - } - [bd->KeyEventResponder setImePosX:data->InputPos.x imePosY:data->InputPos.y + data->InputLineHeight]; + ImGui_ImplOSX_Data* bd = ImGui_ImplOSX_GetBackendData(); + if (data->WantVisible) + { + [bd->InputContext activate]; + } + else + { + [bd->InputContext discardMarkedText]; + [bd->InputContext invalidateCharacterCoordinates]; + [bd->InputContext deactivate]; + } + [bd->KeyEventResponder setImePosX:data->InputPos.x imePosY:data->InputPos.y + data->InputLineHeight]; + }; + + if ([NSThread isMainThread]) + return setPlatformImeData(data); + + dispatch_sync(dispatch_get_main_queue(), + ^{ + setPlatformImeData(data); + }); }; return true;