Path: blob/master/platform/macos/display_server_macos_base.mm
10277 views
/**************************************************************************/ /* display_server_macos_base.mm */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /**************************************************************************/ /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ /* "Software"), to deal in the Software without restriction, including */ /* without limitation the rights to use, copy, modify, merge, publish, */ /* distribute, sublicense, and/or sell copies of the Software, and to */ /* permit persons to whom the Software is furnished to do so, subject to */ /* the following conditions: */ /* */ /* The above copyright notice and this permission notice shall be */ /* included in all copies or substantial portions of the Software. */ /* */ /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ #import "display_server_macos_base.h" #import "key_mapping_macos.h" #import "tts_macos.h" #include "core/config/project_settings.h" #include "drivers/png/png_driver_common.h" #import <Carbon/Carbon.h> void DisplayServerMacOSBase::clipboard_set(const String &p_text) { _THREAD_SAFE_METHOD_ NSString *copiedString = [NSString stringWithUTF8String:p_text.utf8().get_data()]; NSArray *copiedStringArray = [NSArray arrayWithObject:copiedString]; NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; [pasteboard clearContents]; [pasteboard writeObjects:copiedStringArray]; } String DisplayServerMacOSBase::clipboard_get() const { _THREAD_SAFE_METHOD_ NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; NSArray *classArray = [NSArray arrayWithObject:[NSString class]]; NSDictionary *options = [NSDictionary dictionary]; BOOL ok = [pasteboard canReadObjectForClasses:classArray options:options]; if (!ok) { return ""; } NSArray *objectsToPaste = [pasteboard readObjectsForClasses:classArray options:options]; NSString *string = [objectsToPaste objectAtIndex:0]; String ret; ret.append_utf8([string UTF8String]); return ret; } Ref<Image> DisplayServerMacOSBase::clipboard_get_image() const { Ref<Image> image; NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; NSString *result = [pasteboard availableTypeFromArray:[NSArray arrayWithObjects:NSPasteboardTypeTIFF, NSPasteboardTypePNG, nil]]; if (!result) { return image; } NSData *data = [pasteboard dataForType:result]; if (!data) { return image; } NSBitmapImageRep *bitmap = [NSBitmapImageRep imageRepWithData:data]; NSData *pngData = [bitmap representationUsingType:NSBitmapImageFileTypePNG properties:@{}]; image.instantiate(); PNGDriverCommon::png_to_image((const uint8_t *)pngData.bytes, pngData.length, false, image); return image; } bool DisplayServerMacOSBase::clipboard_has() const { NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; NSArray *classArray = [NSArray arrayWithObject:[NSString class]]; NSDictionary *options = [NSDictionary dictionary]; return [pasteboard canReadObjectForClasses:classArray options:options]; } bool DisplayServerMacOSBase::clipboard_has_image() const { NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; NSString *result = [pasteboard availableTypeFromArray:[NSArray arrayWithObjects:NSPasteboardTypeTIFF, NSPasteboardTypePNG, nil]]; return result; } void DisplayServerMacOSBase::initialize_tts() const { const_cast<DisplayServerMacOSBase *>(this)->tts = [[TTS_MacOS alloc] init]; } bool DisplayServerMacOSBase::tts_is_speaking() const { if (unlikely(!tts)) { initialize_tts(); } ERR_FAIL_NULL_V(tts, false); return [tts isSpeaking]; } bool DisplayServerMacOSBase::tts_is_paused() const { if (unlikely(!tts)) { initialize_tts(); } ERR_FAIL_NULL_V(tts, false); return [tts isPaused]; } TypedArray<Dictionary> DisplayServerMacOSBase::tts_get_voices() const { if (unlikely(!tts)) { initialize_tts(); } ERR_FAIL_NULL_V(tts, TypedArray<Dictionary>()); return [tts getVoices]; } void DisplayServerMacOSBase::tts_speak(const String &p_text, const String &p_voice, int p_volume, float p_pitch, float p_rate, int p_utterance_id, bool p_interrupt) { if (unlikely(!tts)) { initialize_tts(); } ERR_FAIL_NULL(tts); [tts speak:p_text voice:p_voice volume:p_volume pitch:p_pitch rate:p_rate utterance_id:p_utterance_id interrupt:p_interrupt]; } void DisplayServerMacOSBase::tts_pause() { if (unlikely(!tts)) { initialize_tts(); } ERR_FAIL_NULL(tts); [tts pauseSpeaking]; } void DisplayServerMacOSBase::tts_resume() { if (unlikely(!tts)) { initialize_tts(); } ERR_FAIL_NULL(tts); [tts resumeSpeaking]; } void DisplayServerMacOSBase::tts_stop() { if (unlikely(!tts)) { initialize_tts(); } ERR_FAIL_NULL(tts); [tts stopSpeaking]; } void DisplayServerMacOSBase::_update_keyboard_layouts() const { kbd_layouts.clear(); current_layout = 0; TISInputSourceRef cur_source = TISCopyCurrentKeyboardInputSource(); NSString *cur_name = (__bridge NSString *)TISGetInputSourceProperty(cur_source, kTISPropertyLocalizedName); CFRelease(cur_source); // Enum IME layouts. NSDictionary *filter_ime = @{ (NSString *)kTISPropertyInputSourceType : (NSString *)kTISTypeKeyboardInputMode }; NSArray *list_ime = (__bridge NSArray *)TISCreateInputSourceList((__bridge CFDictionaryRef)filter_ime, false); for (NSUInteger i = 0; i < [list_ime count]; i++) { LayoutInfo ly; NSString *name = (__bridge NSString *)TISGetInputSourceProperty((__bridge TISInputSourceRef)[list_ime objectAtIndex:i], kTISPropertyLocalizedName); ly.name.append_utf8([name UTF8String]); NSArray *langs = (__bridge NSArray *)TISGetInputSourceProperty((__bridge TISInputSourceRef)[list_ime objectAtIndex:i], kTISPropertyInputSourceLanguages); ly.code.append_utf8([(NSString *)[langs objectAtIndex:0] UTF8String]); kbd_layouts.push_back(ly); if ([name isEqualToString:cur_name]) { current_layout = kbd_layouts.size() - 1; } } // Enum plain keyboard layouts. NSDictionary *filter_kbd = @{ (NSString *)kTISPropertyInputSourceType : (NSString *)kTISTypeKeyboardLayout }; NSArray *list_kbd = (__bridge NSArray *)TISCreateInputSourceList((__bridge CFDictionaryRef)filter_kbd, false); for (NSUInteger i = 0; i < [list_kbd count]; i++) { LayoutInfo ly; NSString *name = (__bridge NSString *)TISGetInputSourceProperty((__bridge TISInputSourceRef)[list_kbd objectAtIndex:i], kTISPropertyLocalizedName); ly.name.append_utf8([name UTF8String]); NSArray *langs = (__bridge NSArray *)TISGetInputSourceProperty((__bridge TISInputSourceRef)[list_kbd objectAtIndex:i], kTISPropertyInputSourceLanguages); ly.code.append_utf8([(NSString *)[langs objectAtIndex:0] UTF8String]); kbd_layouts.push_back(ly); if ([name isEqualToString:cur_name]) { current_layout = kbd_layouts.size() - 1; } } keyboard_layout_dirty = false; } void DisplayServerMacOSBase::_keyboard_layout_changed(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef user_info) { DisplayServerMacOSBase *ds = (DisplayServerMacOSBase *)DisplayServer::get_singleton(); if (ds) { ds->keyboard_layout_dirty = true; } } int DisplayServerMacOSBase::keyboard_get_layout_count() const { if (keyboard_layout_dirty) { _update_keyboard_layouts(); } return kbd_layouts.size(); } void DisplayServerMacOSBase::keyboard_set_current_layout(int p_index) { if (keyboard_layout_dirty) { _update_keyboard_layouts(); } ERR_FAIL_INDEX(p_index, kbd_layouts.size()); NSString *cur_name = [NSString stringWithUTF8String:kbd_layouts[p_index].name.utf8().get_data()]; NSDictionary *filter_kbd = @{ (NSString *)kTISPropertyInputSourceType : (NSString *)kTISTypeKeyboardLayout }; NSArray *list_kbd = (__bridge NSArray *)TISCreateInputSourceList((__bridge CFDictionaryRef)filter_kbd, false); for (NSUInteger i = 0; i < [list_kbd count]; i++) { NSString *name = (__bridge NSString *)TISGetInputSourceProperty((__bridge TISInputSourceRef)[list_kbd objectAtIndex:i], kTISPropertyLocalizedName); if ([name isEqualToString:cur_name]) { TISSelectInputSource((__bridge TISInputSourceRef)[list_kbd objectAtIndex:i]); break; } } NSDictionary *filter_ime = @{ (NSString *)kTISPropertyInputSourceType : (NSString *)kTISTypeKeyboardInputMode }; NSArray *list_ime = (__bridge NSArray *)TISCreateInputSourceList((__bridge CFDictionaryRef)filter_ime, false); for (NSUInteger i = 0; i < [list_ime count]; i++) { NSString *name = (__bridge NSString *)TISGetInputSourceProperty((__bridge TISInputSourceRef)[list_ime objectAtIndex:i], kTISPropertyLocalizedName); if ([name isEqualToString:cur_name]) { TISSelectInputSource((__bridge TISInputSourceRef)[list_ime objectAtIndex:i]); break; } } } int DisplayServerMacOSBase::keyboard_get_current_layout() const { if (keyboard_layout_dirty) { _update_keyboard_layouts(); } return current_layout; } String DisplayServerMacOSBase::keyboard_get_layout_language(int p_index) const { if (keyboard_layout_dirty) { _update_keyboard_layouts(); } ERR_FAIL_INDEX_V(p_index, kbd_layouts.size(), ""); return kbd_layouts[p_index].code; } String DisplayServerMacOSBase::keyboard_get_layout_name(int p_index) const { if (keyboard_layout_dirty) { _update_keyboard_layouts(); } ERR_FAIL_INDEX_V(p_index, kbd_layouts.size(), ""); return kbd_layouts[p_index].name; } Key DisplayServerMacOSBase::keyboard_get_keycode_from_physical(Key p_keycode) const { if (p_keycode == Key::PAUSE || p_keycode == Key::NONE) { return p_keycode; } Key modifiers = p_keycode & KeyModifierMask::MODIFIER_MASK; Key keycode_no_mod = p_keycode & KeyModifierMask::CODE_MASK; unsigned int macos_keycode = KeyMappingMacOS::unmap_key(keycode_no_mod); return (Key)(KeyMappingMacOS::remap_key(macos_keycode, 0, false) | modifiers); } Key DisplayServerMacOSBase::keyboard_get_label_from_physical(Key p_keycode) const { if (p_keycode == Key::PAUSE || p_keycode == Key::NONE) { return p_keycode; } Key modifiers = p_keycode & KeyModifierMask::MODIFIER_MASK; Key keycode_no_mod = p_keycode & KeyModifierMask::CODE_MASK; unsigned int macos_keycode = KeyMappingMacOS::unmap_key(keycode_no_mod); return (Key)(KeyMappingMacOS::remap_key(macos_keycode, 0, true) | modifiers); } void DisplayServerMacOSBase::show_emoji_and_symbol_picker() const { [[NSApplication sharedApplication] orderFrontCharacterPalette:nil]; } DisplayServerMacOSBase::DisplayServerMacOSBase() { // Init TTS bool tts_enabled = GLOBAL_GET("audio/general/text_to_speech"); if (tts_enabled) { initialize_tts(); } // Register to be notified on keyboard layout changes. CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(), nullptr, _keyboard_layout_changed, kTISNotifySelectedKeyboardInputSourceChanged, nullptr, CFNotificationSuspensionBehaviorDeliverImmediately); } DisplayServerMacOSBase::~DisplayServerMacOSBase() { CFNotificationCenterRemoveObserver(CFNotificationCenterGetDistributedCenter(), nullptr, kTISNotifySelectedKeyboardInputSourceChanged, nullptr); }