summaryrefslogtreecommitdiff
path: root/src/video/cocoa/cocoa_wnd.mm
diff options
context:
space:
mode:
Diffstat (limited to 'src/video/cocoa/cocoa_wnd.mm')
-rw-r--r--src/video/cocoa/cocoa_wnd.mm173
1 files changed, 173 insertions, 0 deletions
diff --git a/src/video/cocoa/cocoa_wnd.mm b/src/video/cocoa/cocoa_wnd.mm
index e9ea69705..2755b26ff 100644
--- a/src/video/cocoa/cocoa_wnd.mm
+++ b/src/video/cocoa/cocoa_wnd.mm
@@ -34,6 +34,10 @@
#include "../../window_gui.h"
+/* Table data for key mapping. */
+#include "cocoa_keys.h"
+
+
/**
* Important notice regarding all modifications!!!!!!!
* There are certain limitations because the file is objective C++.
@@ -47,6 +51,8 @@
NSString *OTTDMainLaunchGameEngine = @"ottdmain_launch_game_engine";
+bool _tab_is_down;
+
static bool _cocoa_video_dialog = false;
static OTTDMain *_ottd_main;
@@ -89,6 +95,31 @@ static const char *Utf8AdvanceByUtf16Units(const char *str, NSUInteger count)
return str;
}
+/**
+ * Convert a NSString to an UTF-32 encoded string.
+ * @param s String to convert.
+ * @return Vector of UTF-32 characters.
+ */
+static std::vector<WChar> NSStringToUTF32(NSString *s)
+{
+ std::vector<WChar> unicode_str;
+
+ unichar lead = 0;
+ for (NSUInteger i = 0; i < s.length; i++) {
+ unichar c = [ s characterAtIndex:i ];
+ if (Utf16IsLeadSurrogate(c)) {
+ lead = c;
+ continue;
+ } else if (Utf16IsTrailSurrogate(c)) {
+ if (lead != 0) unicode_str.push_back(Utf16DecodeSurrogate(lead, c));
+ } else {
+ unicode_str.push_back(c);
+ }
+ }
+
+ return unicode_str;
+}
+
/**
* The main class of the application, the application's delegate.
@@ -419,6 +450,7 @@ void CocoaDialog(const char *title, const char *message, const char *buttonLabel
@implementation OTTD_CocoaView {
float _current_magnification;
+ NSUInteger _current_mods;
}
/**
@@ -614,6 +646,147 @@ void CocoaDialog(const char *title, const char *message, const char *buttonLabel
}
+- (BOOL)internalHandleKeycode:(unsigned short)keycode unicode:(WChar)unicode pressed:(BOOL)down modifiers:(NSUInteger)modifiers
+{
+ switch (keycode) {
+ case QZ_UP: SB(_dirkeys, 1, 1, down); break;
+ case QZ_DOWN: SB(_dirkeys, 3, 1, down); break;
+ case QZ_LEFT: SB(_dirkeys, 0, 1, down); break;
+ case QZ_RIGHT: SB(_dirkeys, 2, 1, down); break;
+
+ case QZ_TAB: _tab_is_down = down; break;
+
+ case QZ_RETURN:
+ case QZ_f:
+ if (down && (modifiers & NSCommandKeyMask)) {
+ VideoDriver::GetInstance()->ToggleFullscreen(!_fullscreen);
+ }
+ break;
+
+ case QZ_v:
+ if (down && EditBoxInGlobalFocus() && (modifiers & (NSCommandKeyMask | NSControlKeyMask))) {
+ HandleKeypress(WKC_CTRL | 'V', unicode);
+ }
+ break;
+ case QZ_u:
+ if (down && EditBoxInGlobalFocus() && (modifiers & (NSCommandKeyMask | NSControlKeyMask))) {
+ HandleKeypress(WKC_CTRL | 'U', unicode);
+ }
+ break;
+ }
+
+ BOOL interpret_keys = YES;
+ if (down) {
+ /* Map keycode to OTTD code. */
+ auto vk = std::find_if(std::begin(_vk_mapping), std::end(_vk_mapping), [=](const VkMapping &m) { return m.vk_from == keycode; });
+ uint32 pressed_key = vk != std::end(_vk_mapping) ? vk->map_to : 0;
+
+ if (modifiers & NSShiftKeyMask) pressed_key |= WKC_SHIFT;
+ if (modifiers & NSControlKeyMask) pressed_key |= (_settings_client.gui.right_mouse_btn_emulation != RMBE_CONTROL ? WKC_CTRL : WKC_META);
+ if (modifiers & NSAlternateKeyMask) pressed_key |= WKC_ALT;
+ if (modifiers & NSCommandKeyMask) pressed_key |= (_settings_client.gui.right_mouse_btn_emulation != RMBE_CONTROL ? WKC_META : WKC_CTRL);
+
+ static bool console = false;
+
+ /* The second backquote may have a character, which we don't want to interpret. */
+ if (pressed_key == WKC_BACKQUOTE && (console || unicode == 0)) {
+ if (!console) {
+ /* Backquote is a dead key, require a double press for hotkey behaviour (i.e. console). */
+ console = true;
+ return YES;
+ } else {
+ /* Second backquote, don't interpret as text input. */
+ interpret_keys = NO;
+ }
+ }
+ console = false;
+
+ /* Don't handle normal characters if an edit box has the focus. */
+ if (!EditBoxInGlobalFocus() || IsInsideMM(pressed_key & ~WKC_SPECIAL_KEYS, WKC_F1, WKC_PAUSE + 1)) {
+ HandleKeypress(pressed_key, unicode);
+ }
+ DEBUG(driver, 2, "cocoa_v: QZ_KeyEvent: %x (%x), down, mapping: %x", keycode, unicode, pressed_key);
+ } else {
+ DEBUG(driver, 2, "cocoa_v: QZ_KeyEvent: %x (%x), up", keycode, unicode);
+ }
+
+ return interpret_keys;
+}
+
+- (void)keyDown:(NSEvent *)event
+{
+ /* Quit, hide and minimize */
+ switch (event.keyCode) {
+ case QZ_q:
+ case QZ_h:
+ case QZ_m:
+ if (event.modifierFlags & NSCommandKeyMask) {
+ [ self interpretKeyEvents:[ NSArray arrayWithObject:event ] ];
+ }
+ break;
+ }
+
+ /* Convert UTF-16 characters to UCS-4 chars. */
+ std::vector<WChar> unicode_str = NSStringToUTF32([ event characters ]);
+ if (unicode_str.empty()) unicode_str.push_back(0);
+
+ if (EditBoxInGlobalFocus()) {
+ if ([ self internalHandleKeycode:event.keyCode unicode:unicode_str[0] pressed:YES modifiers:event.modifierFlags ]) {
+ [ self interpretKeyEvents:[ NSArray arrayWithObject:event ] ];
+ }
+ } else {
+ [ self internalHandleKeycode:event.keyCode unicode:unicode_str[0] pressed:YES modifiers:event.modifierFlags ];
+ for (size_t i = 1; i < unicode_str.size(); i++) {
+ [ self internalHandleKeycode:0 unicode:unicode_str[i] pressed:YES modifiers:event.modifierFlags ];
+ }
+ }
+}
+
+- (void)keyUp:(NSEvent *)event
+{
+ /* Quit, hide and minimize */
+ switch (event.keyCode) {
+ case QZ_q:
+ case QZ_h:
+ case QZ_m:
+ if (event.modifierFlags & NSCommandKeyMask) {
+ [ self interpretKeyEvents:[ NSArray arrayWithObject:event ] ];
+ }
+ break;
+ }
+
+ /* Convert UTF-16 characters to UCS-4 chars. */
+ std::vector<WChar> unicode_str = NSStringToUTF32([ event characters ]);
+ if (unicode_str.empty()) unicode_str.push_back(0);
+
+ [ self internalHandleKeycode:event.keyCode unicode:unicode_str[0] pressed:NO modifiers:event.modifierFlags ];
+}
+
+- (void)flagsChanged:(NSEvent *)event
+{
+ const int mapping[] = { QZ_CAPSLOCK, QZ_LSHIFT, QZ_LCTRL, QZ_LALT, QZ_LMETA };
+
+ NSUInteger newMods = event.modifierFlags;
+ if (self->_current_mods == newMods) return;
+
+ /* Iterate through the bits, testing each against the current modifiers */
+ for (unsigned int i = 0, bit = NSAlphaShiftKeyMask; bit <= NSCommandKeyMask; bit <<= 1, ++i) {
+ unsigned int currentMask, newMask;
+
+ currentMask = self->_current_mods & bit;
+ newMask = newMods & bit;
+
+ if (currentMask && currentMask != newMask) { // modifier up event
+ [ self internalHandleKeycode:mapping[i] unicode:0 pressed:NO modifiers:newMods ];
+ } else if (newMask && currentMask != newMask) { // modifier down event
+ [ self internalHandleKeycode:mapping[i] unicode:0 pressed:YES modifiers:newMods ];
+ }
+ }
+
+ _current_mods = newMods;
+}
+
+
/** Insert the given text at the given range. */
- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange
{