+#include "choose_dialog.h"
+#include "preferences-macosx.h"
+
+ChooseDialog::ChooseDialog(QWidget *parent) : QDialog(parent)
+{
+ setWindowTitle("RPCEmu - Choose Data Directory");
+
+ buttons_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+
+ // Create preamble label.
+ QString str = QString("Before using RPCEmu for the first time, you must select the directory
"
+ "that contains the folders and files required by the emulator, such as
"
+ "ROMs, hard drive images and the HostFS share.
"
+ "You can show this dialogue again by holding down the Command key
"
+ "whilst the application is loading.
");
+
+ preamble_label = new QLabel(str);
+
+ // Create choose label.
+ choose_label = new QLabel();
+ choose_label->setText("Please choose a directory below:");
+
+ // Create directory line edit.
+ directory_edit = new QLineEdit();
+ directory_edit->setMaxLength(511);
+ directory_edit->setReadOnly(true);
+
+ // Create directory button.
+ directory_button = new QPushButton("Select...", this);
+
+ // Create box for line edit and button.
+ directory_hbox = new QHBoxLayout();
+ directory_hbox->setSpacing(16);
+ directory_hbox->addWidget(directory_edit);
+ directory_hbox->addWidget(directory_button);
+
+ grid = new QGridLayout(this);
+ grid->addWidget(preamble_label, 0, 0);
+ grid->addWidget(choose_label, 1, 0);
+ grid->addLayout(directory_hbox, 2, 0);
+ grid->addWidget(buttons_box, 3, 0);
+
+ // Connect actions to widgets.
+ connect(directory_button, &QPushButton::pressed, this, &ChooseDialog::directory_button_pressed);
+
+ connect(buttons_box, &QDialogButtonBox::accepted, this, &QDialog::accept);
+ connect(buttons_box, &QDialogButtonBox::rejected, this, &QDialog::reject);
+
+ connect(this, &QDialog::accepted, this, &ChooseDialog::dialog_accepted);
+ connect(this, &QDialog::accepted, this, &ChooseDialog::dialog_rejected);
+
+ this->setFixedSize(this->sizeHint());
+}
+
+ChooseDialog::~ChooseDialog()
+{
+}
+
+void ChooseDialog::directory_button_pressed()
+{
+ QFileDialog folderDialog;
+ folderDialog.setWindowTitle("Choose Data Directory");
+ folderDialog.setFileMode(QFileDialog::Directory);
+
+ if (folderDialog.exec())
+ {
+ QStringList selection = folderDialog.selectedFiles();
+ QString folderName = selection.at(0);
+
+ directory_edit->setText(folderName);
+ }
+}
+
+void ChooseDialog::dialog_accepted()
+{
+ QString selectedFolder = directory_edit->text();
+ QByteArray ba = selectedFolder.toUtf8();
+
+ char *ptr = ba.data();
+ preferences_set_data_directory(ptr);
+}
+
+void ChooseDialog::dialog_rejected()
+{
+}
+
--- /dev/null 2018-11-18 19:57:31.000000000 +0000
+++ qt5/choose_dialog.h 2018-11-04 16:34:31.000000000 +0000
@@@@ -0,0 +1,64 @@@@
+/*
+ RPCEmu - An Acorn system emulator
+
+ Copyright (C) 2016-2017 Matthew Howkins
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef CHOOSE_DIALOG_H
+#define CHOOSE_DIALOG_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "rpc-qt5.h"
+#include "rpcemu.h"
+
+class ChooseDialog : public QDialog
+{
+
+ Q_OBJECT
+
+public:
+ ChooseDialog(QWidget *parent = 0);
+ virtual ~ChooseDialog();
+
+private slots:
+ void directory_button_pressed();
+
+ void dialog_accepted();
+ void dialog_rejected();
+
+private:
+
+ QLabel *preamble_label;
+ QLabel *choose_label;
+
+ QHBoxLayout *directory_hbox;
+ QLineEdit *directory_edit;
+ QPushButton *directory_button;
+
+ QDialogButtonBox *buttons_box;
+
+ QGridLayout *grid;
+
+};
+
+#endif
--- /dev/null 2018-11-18 19:56:31.000000000 +0000
+++ macosx/events-macosx.h 2018-11-02 19:57:29.000000000 +0000
@@@@ -0,0 +1,45 @@@@
+/*
+ RPCEmu - An Acorn system emulator
+
+ Copyright (C) 2017 Peter Howkins
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __EVENTS_MACOSX_H__
+#define __EVENTS_MACOSX_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+ nativeEventTypeModifiersChanged = 1
+} NativeEventType;
+
+typedef struct
+{
+ bool processed;
+ int eventType;
+ uint modifierMask;
+} NativeEvent;
+
+extern NativeEvent* handle_native_event(void *message);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __EVENTS_MACOSX_H__
--- /dev/null 2018-11-18 19:56:31.000000000 +0000
+++ macosx/events-macosx.m 2018-11-02 19:57:31.000000000 +0000
@@@@ -0,0 +1,56 @@@@
+/*
+ RPCEmu - An Acorn system emulator
+
+ Copyright (C) 2017 Matthew Howkins
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define UNUSED(x) (void)(x)
+
+#include
+#include
+
+#include
+#include
+#include
+
+#include "rpcemu.h"
+
+#include "events-macosx.h"
+
+
+NativeEvent* handle_native_event(void *message)
+{
+ // This extracts information from the Cocoa event and passes it back up the chain to C++.
+ NSEvent *event = (NSEvent *) message;
+
+ NativeEvent *result = (NativeEvent *) malloc(sizeof(NativeEvent));
+
+ // Only handle flags changed events, which are raised for modifier key changes.
+ if (event.type == NSEventTypeFlagsChanged)
+ {
+ result->eventType = nativeEventTypeModifiersChanged;
+ result->modifierMask = event.modifierFlags;
+ result->processed = 1;
+ }
+ else
+ {
+ result->processed = 0;
+ }
+
+ // Return zero if the event is not handled here.
+ return result;
+}
--- /dev/null 2018-11-18 19:56:31.000000000 +0000
+++ macosx/hid-macosx.h 2018-11-02 19:57:37.000000000 +0000
@@@@ -0,0 +1,34 @@@@
+///*
+// RPCEmu - An Acorn system emulator
+//
+// Copyright (C) 2017 Matthew Howkins
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+// */
+
+#ifndef __HID_MACOSX_H__
+#define __HID_MACOSX_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void init_hid_manager(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __HID_MACOSX_H__
--- /dev/null 2018-11-18 19:56:31.000000000 +0000
+++ macosx/hid-macosx.m 2018-11-02 19:57:40.000000000 +0000
@@@@ -0,0 +1,165 @@@@
+///*
+// RPCEmu - An Acorn system emulator
+//
+// Copyright (C) 2017 Matthew Howkins
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+// */
+
+#include
+#include
+
+#include
+#include
+#include
+
+#include "keyboard.h"
+#include "keyboard_macosx.h"
+
+#define UNUSED(x) (void)(x)
+
+static IOHIDManagerRef hidManager = NULL;
+
+CFDictionaryRef createHIDDeviceMatchingDictionary(uint32 usagePage, uint32 usage)
+{
+ CFMutableDictionaryRef dictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
+ if (dictionary)
+ {
+ CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usagePage);
+ if (number)
+ {
+ CFDictionarySetValue(dictionary, CFSTR(kIOHIDDeviceUsagePageKey), number);
+ CFRelease(number);
+
+ number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage);
+ if (number)
+ {
+ CFDictionarySetValue(dictionary, CFSTR(kIOHIDDeviceUsageKey), number);
+ CFRelease(number);
+
+ return dictionary;
+ }
+ }
+
+ CFRelease(dictionary);
+ }
+
+ return NULL;
+}
+
+void processHIDCallback(void *context, IOReturn result, void *sender, IOHIDValueRef value)
+{
+ UNUSED(result);
+ UNUSED(sender);
+
+ if (context != hidManager) return;
+
+ IOHIDElementRef element = IOHIDValueGetElement(value);
+ if (IOHIDElementGetUsagePage(element) != kHIDPage_KeyboardOrKeypad || IOHIDElementGetUsage(element) != kHIDUsage_KeyboardCapsLock) return;
+
+ CFIndex pressed = IOHIDValueGetIntegerValue(value);
+
+ uint8 scanCodes[] = { 0x58 };
+
+ if (pressed == 0)
+ {
+ keyboard_key_release(scanCodes);
+ }
+ else
+ {
+ keyboard_key_press(scanCodes);
+ }
+}
+
+const char *getCurrentKeyboardLayoutName()
+{
+ TISInputSourceRef currentSource = TISCopyCurrentKeyboardInputSource();
+ NSString *inputSource = (__bridge NSString *)(TISGetInputSourceProperty(currentSource, kTISPropertyInputSourceID));
+ NSUInteger lastIndex = [inputSource rangeOfString:@@"." options:NSBackwardsSearch].location;
+
+ NSString *layoutName = [inputSource substringFromIndex: lastIndex + 1];
+ lastIndex = [layoutName rangeOfString:@@" - "].location;
+
+ if (lastIndex != NSNotFound) layoutName = [layoutName substringToIndex: lastIndex];
+
+ return [layoutName UTF8String];
+}
+
+void terminate_hid_manager(void)
+{
+ if (!hidManager) return;
+
+ IOHIDManagerUnscheduleFromRunLoop(hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+ IOHIDManagerRegisterInputValueCallback(hidManager, NULL, NULL);
+ IOHIDManagerClose(hidManager, 0);
+
+ CFRelease(hidManager);
+
+ hidManager = NULL;
+}
+
+void init_hid_manager(void)
+{
+ const char *layoutName = getCurrentKeyboardLayoutName();
+ keyboard_configure_layout(layoutName);
+
+ hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
+ if (!hidManager) return;
+
+ CFDictionaryRef keyboard = NULL, keypad = NULL;
+ CFArrayRef matches = NULL;
+
+ keyboard = createHIDDeviceMatchingDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard);
+ if (!keyboard)
+ {
+ IOHIDManagerClose(hidManager, 0);
+ return;
+ }
+
+ keypad = createHIDDeviceMatchingDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Keypad);
+ if (!keypad)
+ {
+ CFRelease(keyboard);
+ IOHIDManagerClose(hidManager, 0);
+
+ return;
+ }
+
+ CFDictionaryRef matchesList[] = {keyboard, keypad};
+ matches = CFArrayCreate(kCFAllocatorDefault, (const void**) matchesList, 2, NULL);
+ if (!matches)
+ {
+ CFRelease(keypad);
+ CFRelease(keyboard);
+ IOHIDManagerClose(hidManager, 0);
+
+ return;
+ }
+
+ IOHIDManagerSetDeviceMatchingMultiple(hidManager, matches);
+ IOHIDManagerRegisterInputValueCallback(hidManager, processHIDCallback, hidManager);
+ IOHIDManagerScheduleWithRunLoop(hidManager, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
+ if (IOHIDManagerOpen(hidManager, kIOHIDOptionsTypeNone) != kIOReturnSuccess)
+ {
+ terminate_hid_manager();
+ }
+
+ CFRelease(matches);
+ CFRelease(keypad);
+ CFRelease(keyboard);
+}
+
+
--- /dev/null 2018-11-18 19:49:33.000000000 +0000
+++ hostfs-macosx.c 2018-11-04 16:50:44.000000000 +0000
@@@@ -0,0 +1,121 @@@@
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include "hostfs_internal.h"
+
+/**
+ * Convert ADFS time-stamped Load-Exec addresses to the equivalent time_t.
+ *
+ * @@param load RISC OS load address (assumed to be time-stamped)
+ * @@param exec RISC OS exec address (assumed to be time-stamped)
+ * @@return Time converted to time_t format
+ *
+ * Code adapted from fs/adfs/inode.c from Linux licensed under GPL2.
+ * Copyright (C) 1997-1999 Russell King
+ */
+static time_t
+hostfs_adfs2host_time(uint32_t load, uint32_t exec)
+{
+ uint32_t high = load << 24;
+ uint32_t low = exec;
+
+ high |= low >> 8;
+ low &= 0xff;
+
+ if (high < 0x3363996a) {
+ /* Too early */
+ return 0;
+ } else if (high >= 0x656e9969) {
+ /* Too late */
+ return 0x7ffffffd;
+ }
+
+ high -= 0x336e996a;
+ return (((high % 100) << 8) + low) / 100 + (high / 100 << 8);
+}
+
+/**
+ * Read information about an object.
+ *
+ * @@param host_pathname Full Host path to object
+ * @@param object_info Return object info (filled-in)
+ */
+void
+hostfs_read_object_info_platform(const char *host_pathname,
+ risc_os_object_info *object_info)
+{
+ struct stat info;
+ uint32_t low, high;
+
+ assert(host_pathname != NULL);
+ assert(object_info != NULL);
+
+ // Ignore DS_Store files.
+ if (strcasestr(host_pathname, ".DS_Store") != NULL)
+ {
+ object_info->type = OBJECT_TYPE_NOT_FOUND;
+ return;
+ }
+
+ if (stat(host_pathname, &info)) {
+ /* Error reading info about the object */
+ switch (errno) {
+ case ENOENT: /* Object not found */
+ case ENOTDIR: /* A path component is not a directory */
+ object_info->type = OBJECT_TYPE_NOT_FOUND;
+ break;
+
+ default:
+ /* Other error */
+ fprintf(stderr,
+ "hostfs_read_object_info_platform() could not stat() \'%s\': %s %d\n",
+ host_pathname, strerror(errno), errno);
+ object_info->type = OBJECT_TYPE_NOT_FOUND;
+ break;
+ }
+
+ return;
+ }
+
+ /* We were able to read about the object */
+ if (S_ISREG(info.st_mode)) {
+ object_info->type = OBJECT_TYPE_FILE;
+ } else if (S_ISDIR(info.st_mode)) {
+ object_info->type = OBJECT_TYPE_DIRECTORY;
+ } else {
+ /* Treat types other than file or directory as not found */
+ object_info->type = OBJECT_TYPE_NOT_FOUND;
+ return;
+ }
+
+ low = (uint32_t) ((info.st_mtime & 255) * 100);
+ high = (uint32_t) ((info.st_mtime / 256) * 100 + (low >> 8) + 0x336e996a);
+
+ /* If the file has filetype and timestamp, additional values will need to be filled in later */
+ object_info->load = (high >> 24);
+ object_info->exec = (low & 0xff) | (high << 8);
+
+ object_info->length = info.st_size;
+}
+
+/**
+ * Apply the timestamp to the supplied host object
+ *
+ * @@param host_path Full path to object (file or dir) in host format
+ * @@param load RISC OS load address (must contain time-stamp)
+ * @@param exec RISC OS exec address (must contain time-stamp)
+ */
+void
+hostfs_object_set_timestamp_platform(const char *host_path, uint32_t load, uint32_t exec)
+{
+ struct utimbuf t;
+
+ t.actime = t.modtime = hostfs_adfs2host_time(load, exec);
+ utime(host_path, &t);
+ /* TODO handle error in utime() */
+}
--- hostfs.c.orig 2018-11-02 19:15:19.000000000 +0000
+++ hostfs.c 2018-11-18 19:52:41.000000000 +0000
@@@@ -273,6 +273,9 @@@@
case '>':
*host_path++ = '^';
break;
+ case (char) 160:
+ *host_path++ = ' ';
+ break;
default:
*host_path++ = *path;
break;
@@@@ -539,7 +542,7 @@@@
while ((entry = readdir(d)) != NULL) {
char entry_path[PATH_MAX], ro_leaf[PATH_MAX];
- /* Ignore the current directory and it's parent */
+ /* Ignore the current directory and its parent */
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}
@@@@ -1650,7 +1653,7 @@@@
char entry_path[PATH_MAX], ro_leaf[PATH_MAX];
unsigned string_space;
- /* Ignore the current directory and it's parent */
+ /* Ignore the current directory and its parent */
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}
--- keyboard.c.orig 2018-11-02 19:15:19.000000000 +0000
+++ keyboard.c 2018-11-02 20:02:26.000000000 +0000
@@@@ -44,6 +44,10 @@@@
#include "arm.h"
#include "i8042.h"
+#ifdef __APPLE__
+#include "keyboard_macosx.h"
+#endif
+
/* Keyboard Commands */
#define KBD_CMD_ENABLE 0xf4
#define KBD_CMD_RESET 0xff
@@@@ -254,6 +258,11 @@@@
/* Mousehack reset */
mouse_hack.pointer = 0;
mouse_hack.cursor_linked = 1;
+
+#ifdef __APPLE__
+ keyboard_reset_modifiers(0);
+#endif
+
}
static uint8_t
--- /dev/null 2018-11-18 19:58:10.000000000 +0000
+++ qt5/keyboard_macosx.c 2018-11-02 20:44:35.000000000 +0000
@@@@ -0,0 +1,326 @@@@
+/*
+ RPCEmu - An Acorn system emulator
+
+ Copyright (C) 2017 Matthew Howkins
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "rpcemu.h"
+#include "keyboard.h"
+
+#include
+
+const int MAX_KEYBOARD_LAYOUTS = 20;
+
+typedef enum
+{
+ keyboardLayoutUndefined = 0,
+ keyboardLayoutBritish = 1,
+ keyboardLayoutFrench = 2
+} KeyboardLayoutType;
+
+static int keyboardType;
+
+typedef struct {
+ uint32_t virtual_key[MAX_KEYBOARD_LAYOUTS]; // Cocoa virtual keys
+ uint8_t set_2[8]; // PS/2 Set 2 make code
+} KeyMapInfo;
+
+// Mac virtual keys can be found in the following file:
+//
+// /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/Events.h.
+//
+// Key mappings are defined as follows:
+//
+// The first member is an array of virtual key codes. There will be at least three elements in the array for each key.
+//
+// The first element indicates whether or not there are different mappings for different keyboard layouts for this key code.
+// If the value is 0, each keyboard layout uses the same mapping. Where the value is 1, there are different mappings for different layouts.
+// An example of the former is "0" and of the latter, "Z" (in French, this is "Y").
+//
+// The second element in the array is the virtual key to use for the default language, British.
+// If additional, non-British languages are defined in the 'KeyboardLayoutType' enumeration (above) and in the
+// 'configureKeyboardLayout' function (below), virtual keys for these languages can be specified.
+// For example, on a French keyboard, 'Y' and 'Z' are transposed. Therefore, for each of the mappings for these keys,
+// two virtual keys are listed.
+//
+// The list of virtual key codes must be terminated with an 0xFFFF element.
+//
+// The second member is an array of PS/2 set 2 codes.
+
+static const KeyMapInfo key_map[] = {
+ { { 0, kVK_Escape, 0xFFFF }, { 0x76 } }, // Escape
+
+ { { 0, kVK_ISO_Section, 0xFFFF }, { 0x0e } }, // `
+ { { 0, kVK_ANSI_1, 0xFFFF}, { 0x16 } }, // 1
+ { { 0, kVK_ANSI_2, 0xFFFF }, { 0x1e } }, // 2
+ { { 0, kVK_ANSI_3, 0xFFFF}, { 0x26 } }, // 3
+ { { 0, kVK_ANSI_4, 0xFFFF }, { 0x25 } }, // 4
+ { { 0, kVK_ANSI_5, 0xFFFF }, { 0x2e } }, // 5
+ { { 0, kVK_ANSI_6, 0xFFFF }, { 0x36 } }, // 6
+ { { 0, kVK_ANSI_7, 0xFFFF }, { 0x3d } }, // 7
+ { { 0, kVK_ANSI_8, 0xFFFF }, { 0x3e } }, // 8
+ { { 0, kVK_ANSI_9, 0xFFFF }, { 0x46 } }, // 9
+ { { 0, kVK_ANSI_0, 0xFFFF }, { 0x45 } }, // 0
+ { { 0, kVK_ANSI_Minus, 0xFFFF }, { 0x4e } }, // -
+ { { 0, kVK_ANSI_Equal, 0xFFFF }, { 0x55 } }, // =
+ { { 0, kVK_Delete, 0xFFFF }, { 0x66 } }, // Backspace
+
+ { { 0, kVK_Tab, 0xFFFF }, { 0x0d } }, // Tab
+ { { 0, kVK_ANSI_Q, 0xFFFF }, { 0x15 } }, // Q
+ { { 0, kVK_ANSI_W, 0xFFFF }, { 0x1d } }, // W
+ { { 0, kVK_ANSI_E, 0xFFFF }, { 0x24 } }, // E
+ { { 0, kVK_ANSI_R, 0xFFFF }, { 0x2d } }, // R
+ { { 0, kVK_ANSI_T, 0xFFFF}, { 0x2c } }, // T
+ { { 1, kVK_ANSI_Y, kVK_ANSI_Z, 0xFFFF }, { 0x35 } }, // Y
+ { { 0, kVK_ANSI_U, 0xFFFF }, { 0x3c } }, // U
+ { { 0, kVK_ANSI_I, 0xFFFF }, { 0x43 } }, // I
+ { { 0, kVK_ANSI_O, 0xFFFF }, { 0x44 } }, // O
+ { { 0, kVK_ANSI_P, 0xFFFF }, { 0x4d } }, // P
+ { { 0, kVK_ANSI_LeftBracket, 0xFFFF }, { 0x54 } }, // [
+ { { 0, kVK_ANSI_RightBracket, 0xFFFF }, { 0x5b } }, // ]
+ { { 0, kVK_Return, 0xFFFF }, { 0x5a } }, // Return
+
+ { { 0, kVK_Control, 0xFFFF }, { 0x14 } }, // Left Ctrl
+ { { 0, kVK_ANSI_A, 0xFFFF }, { 0x1c } }, // A
+ { { 0, kVK_ANSI_S, 0xFFFF }, { 0x1b } }, // S
+ { { 0, kVK_ANSI_D, 0xFFFF }, { 0x23 } }, // D
+ { { 0, kVK_ANSI_F, 0xFFFF }, { 0x2b } }, // F
+ { { 0, kVK_ANSI_G, 0xFFFF }, { 0x34 } }, // G
+ { { 1, kVK_ANSI_H, 0xFFFF }, { 0x33 } }, // H
+ { { 0, kVK_ANSI_J, 0xFFFF }, { 0x3b } }, // J
+ { { 0, kVK_ANSI_K, 0xFFFF }, { 0x42 } }, // K
+ { { 0, kVK_ANSI_L, 0xFFFF }, { 0x4b } }, // L
+ { { 0, kVK_ANSI_Semicolon, 0xFFFF }, { 0x4c } }, // ;
+ { { 0, kVK_ANSI_Quote, 0xFFFF }, { 0x52 } }, // '
+ { { 0, kVK_ANSI_Backslash, 0xFFFF }, { 0x5d } }, // # (International only)
+
+ { { 0, kVK_ANSI_Grave, 0xFFFF }, { 0x61 } }, // `
+ { { 1, kVK_ANSI_Z, kVK_ANSI_Y, 0xFFFF }, { 0x1a } }, // Z
+ { { 0, kVK_ANSI_X, 0xFFFF }, { 0x22 } }, // X
+ { { 0, kVK_ANSI_C, 0xFFFF }, { 0x21 } }, // C
+ { { 0, kVK_ANSI_V, 0xFFFF }, { 0x2a } }, // V
+ { { 0, kVK_ANSI_B, 0xFFFF }, { 0x32 } }, // B
+ { { 0, kVK_ANSI_N, 0xFFFF }, { 0x31 } }, // N
+ { { 0, kVK_ANSI_M, 0xFFFF }, { 0x3a } }, // M
+ { { 0, kVK_ANSI_Comma, 0xFFFF }, { 0x41 } }, // ,
+ { { 0, kVK_ANSI_Period, 0xFFFF }, { 0x49 } }, // .
+ { { 0, kVK_ANSI_Slash, 0xFFFF }, { 0x4a } }, // /
+
+ { { 0, kVK_Space, 0xFFFF }, { 0x29 } }, // Space
+
+ { { 0, kVK_F1, 0xFFFF }, { 0x05 } }, // F1
+ { { 0, kVK_F2, 0xFFFF }, { 0x06 } }, // F2
+ { { 0, kVK_F3, 0xFFFF }, { 0x04 } }, // F3
+ { { 0, kVK_F4, 0xFFFF }, { 0x0c } }, // F4
+ { { 0, kVK_F5, 0xFFFF }, { 0x03 } }, // F5
+ { { 0, kVK_F6, 0xFFFF }, { 0x0b } }, // F6
+ { { 0, kVK_F7, 0xFFFF }, { 0x83 } }, // F7
+ { { 0, kVK_F8, 0xFFFF }, { 0x0a } }, // F8
+ { { 0, kVK_F9, 0xFFFF }, { 0x01 } }, // F9
+ { { 0, kVK_F10, 0xFFFF }, { 0x09 } }, // F10
+ { { 0, kVK_F11, 0xFFFF }, { 0x78 } }, // F11
+ { { 0, kVK_F12, 0xFFFF }, { 0x07 } }, // F12
+
+ { { 0, kVK_F13, 0xFFFF }, { 0xe0, 0x7c } }, // Print Screen/SysRq
+ { { 0, kVK_F14, 0xFFFF }, { 0x7e } }, // Scroll Lock
+ { { 0, kVK_F15, 0xFFFF }, { 0xe1, 0x14, 0x77, 0xe1, 0xf0, 0x14, 0xf0, 0x77 } }, // Break
+
+ { { 0, kVK_ANSI_KeypadClear, 0xFFFF }, { 0x77 } }, // Keypad Num Lock
+ { { 0, kVK_ANSI_KeypadDivide, 0xFFFF }, { 0xe0, 0x4a } }, // Keypad /
+ { { 0, kVK_ANSI_KeypadMultiply, 0xFFFF }, { 0x7c } }, // Keypad *
+ { { 0, kVK_ANSI_Keypad7, 0xFFFF }, { 0x6c } }, // Keypad 7
+ { { 0, kVK_ANSI_Keypad8, 0xFFFF }, { 0x75 } }, // Keypad 8
+ { { 0, kVK_ANSI_Keypad9, 0xFFFF }, { 0x7d } }, // Keypad 9
+ { { 0, kVK_ANSI_KeypadMinus, 0xFFFF }, { 0x7b } }, // Keypad -
+ { { 0, kVK_ANSI_Keypad4, 0xFFFF }, { 0x6b } }, // Keypad 4
+ { { 0, kVK_ANSI_Keypad5, 0xFFFF }, { 0x73 } }, // Keypad 5
+ { { 0, kVK_ANSI_Keypad6, 0xFFFF }, { 0x74 } }, // Keypad 6
+ { { 0, kVK_ANSI_KeypadPlus, 0xFFFF }, { 0x79 } }, // Keypad +
+ { { 0, kVK_ANSI_Keypad1, 0xFFFF }, { 0x69 } }, // Keypad 1
+ { { 0, kVK_ANSI_Keypad2, 0xFFFF }, { 0x72 } }, // Keypad 2
+ { { 0, kVK_ANSI_Keypad3, 0xFFFF }, { 0x7a } }, // Keypad 3
+ { { 0, kVK_ANSI_Keypad0, 0xFFFF }, { 0x70 } }, // Keypad 0
+ { { 0, kVK_ANSI_KeypadDecimal, 0xFFFF }, { 0x71 } }, // Keypad .
+ { { 0, kVK_ANSI_KeypadEnter, 0xFFFF }, { 0xe0, 0x5a } }, // Keypad Enter
+
+ { { 0, kVK_Function, 0xFFFF }, { 0xe0, 0x70 } }, // Insert
+ { { 0, kVK_ForwardDelete, 0xFFFF }, { 0xe0, 0x71 } }, // Delete
+ { { 0, kVK_Home, 0xFFFF }, { 0xe0, 0x6c } }, // Home
+ { { 0, kVK_End, 0xFFFF }, { 0xe0, 0x69 } }, // End
+ { { 0, kVK_UpArrow, 0xFFFF }, { 0xe0, 0x75 } }, // Up
+ { { 0, kVK_DownArrow, 0xFFFF }, { 0xe0, 0x72 } }, // Down
+ { { 0, kVK_LeftArrow, 0xFFFF }, { 0xe0, 0x6b } }, // Left
+ { { 0, kVK_RightArrow, 0xFFFF }, { 0xe0, 0x74 } }, // Right
+ { { 0, kVK_PageUp, 0xFFFF }, { 0xe0, 0x7d } }, // Page Up
+ { { 0, kVK_PageDown, 0xFFFF }, { 0xe0, 0x7a } }, // Page Down
+
+ { { 0, kVK_F16, 0xFFFF }, { 0xe0, 0x2f } }, // Application (Win Menu)
+
+ { { 0xFFFF }, { 0, 0 } },
+};
+
+typedef enum
+{
+ modifierKeyStateShift = 0,
+ modifierKeyStateControl = 1,
+ modifierKeyStateAlt = 2,
+ modifierKeyStateCapsLock = 3,
+ modifierKeyStateCommand = 4
+} ModifierKeyCode;
+
+typedef struct
+{
+ int keyState[5];
+} ModifierState;
+
+ModifierState modifierState;
+
+typedef struct {
+ uint32_t modifierMask;
+ int checkMask;
+ uint maskLeft;
+ uint maskRight;
+ uint8_t set_2_left[8];
+ uint8_t set_2_right[8];
+} ModifierMapInfo;
+
+// The following are from the "NSEventModifierFlagOption" enumeration.
+typedef enum
+{
+ nativeModifierFlagShift = (1 << 17),
+ nativeModifierFlagControl = (1<< 18),
+ nativeModifierFlagOption = (1 << 19),
+ nativeModifierFlagCommand = (1 << 20)
+} NativeModifierFlag;
+
+static const ModifierMapInfo modifier_map[] = {
+ {nativeModifierFlagShift, modifierKeyStateShift, 0x102, 0x104, {0x12}, {0x59} }, // Shift.
+ {nativeModifierFlagControl, modifierKeyStateControl, 0x101, 0x2100, {0x14}, {0xe0, 0x14} }, // Control.
+ {nativeModifierFlagOption, modifierKeyStateAlt, 0x120, 0x140, {0x11}, {0xe0, 0x11}}, // Alt.
+ {nativeModifierFlagCommand, modifierKeyStateCommand, 0x100108, 0x100110, {0xe0, 0x1f}, {0xe0, 0x27}}, // Command.
+ {0x1<<31, 0, 0, 0, {0}, {0} },
+};
+
+int get_virtual_key_index(size_t k)
+{
+ if (key_map[k].virtual_key[0] == 0) return 1;
+
+ for (int i = 1; i < MAX_KEYBOARD_LAYOUTS; i++)
+ {
+ if (key_map[k].virtual_key[i] == 0xFFFF) break;
+ if (i == keyboardType) return i;
+ }
+
+ return 0;
+}
+
+const uint8_t *
+keyboard_map_key(uint32_t native_scancode)
+{
+ size_t k;
+ int index;
+
+ for (k = 0; key_map[k].virtual_key[0] != 0xFFFF; k++) {
+ index = get_virtual_key_index(k);
+
+ if (key_map[k].virtual_key[index] == native_scancode) {
+ return key_map[k].set_2;
+ }
+ }
+ return NULL;
+}
+
+void keyboard_handle_modifier_keys(uint mask)
+{
+ size_t k;
+
+ for (k = 0; modifier_map[k].modifierMask != (1U << 31); k++)
+ {
+ int state = modifierState.keyState[modifier_map[k].checkMask];
+ uint modifierMask = modifier_map[k].modifierMask;
+
+ if ((mask & modifierMask) != 0)
+ {
+ if (modifier_map[k].maskLeft != 0xFFFFFFFF && (mask & modifier_map[k].maskLeft) == modifier_map[k].maskLeft && (state & 1) == 0)
+ {
+ state |= 1;
+ keyboard_key_press(modifier_map[k].set_2_left);
+ }
+ if (modifier_map[k].maskRight != 0xFFFFFFFF && (mask & modifier_map[k].maskRight) == modifier_map[k].maskRight && (state & 2) == 0)
+ {
+ state |= 2;
+ keyboard_key_press(modifier_map[k].set_2_right);
+ }
+ }
+ else if ((mask & modifierMask) ==0 && state != 0)
+ {
+ if (state & 1)
+ {
+ state &= ~1;
+ keyboard_key_release(modifier_map[k].set_2_left);
+ }
+ if (state & 2)
+ {
+ state &= ~2;
+ keyboard_key_release(modifier_map[k].set_2_right);
+ }
+ }
+
+ modifierState.keyState[modifier_map[k].checkMask] = state;
+ }
+}
+
+void keyboard_reset_modifiers(int sendReleaseEvent)
+{
+ size_t k;
+
+ for (k = 0; modifier_map[k].modifierMask != (1U << 31); k++)
+ {
+ int state = modifierState.keyState[modifier_map[k].checkMask];
+
+ if (sendReleaseEvent)
+ {
+ if (state & 1)
+ {
+ keyboard_key_release(modifier_map[k].set_2_left);
+ }
+ if (state & 2)
+ {
+ keyboard_key_release(modifier_map[k].set_2_right);
+ }
+ }
+
+ modifierState.keyState[modifier_map[k].checkMask] = 0;
+ }
+}
+
+void keyboard_configure_layout(const char *layoutName)
+{
+ if (!strcmp(layoutName, "British")) keyboardType = keyboardLayoutBritish;
+ else if (!strcasecmp(layoutName, "French")) keyboardType = keyboardLayoutFrench;
+ else keyboardType = keyboardLayoutUndefined;
+
+ if (keyboardType == keyboardLayoutUndefined)
+ {
+ fprintf(stderr, "Unsupported keyboard layout '%s' - reverting to 'British' (0).\n", layoutName);
+ keyboardType = keyboardLayoutBritish;
+ }
+ else
+ {
+ fprintf(stderr, "Using keyboard layout '%s' (%d).\n", layoutName, keyboardType);
+ }
+}
--- /dev/null 2018-11-18 19:58:10.000000000 +0000
+++ qt5/keyboard_macosx.h 2018-11-02 20:44:36.000000000 +0000
@@@@ -0,0 +1,39 @@@@
+/*
+ RPCEmu - An Acorn system emulator
+
+ Copyright (C) 2017 Matthew Howkins
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __KEYBOARD_MACOSX_H__
+#define __KEYBOARD_MACOSX_H__
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+extern void keyboard_handle_modifier_keys(uint32_t mask);
+extern void keyboard_reset_modifiers(int sendReleaseEvent);
+extern void keyboard_configure_layout(const char *layoutName);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
+
--- qt5/main_window.cpp.orig 2018-11-02 19:15:19.000000000 +0000
+++ qt5/main_window.cpp 2018-11-02 21:25:14.000000000 +0000
@@@@ -31,7 +31,11 @@@@
#if defined(Q_OS_WIN32)
#include "Windows.h"
-#endif /* Q_OS_WIN32 */
+#endif /* Q_OS_WIN32 */
+
+#if defined(Q_OS_MACOS)
+#include "macosx/events-macosx.h"
+#endif /* Q_OS_MACOS */
#include "rpcemu.h"
#include "keyboard.h"
@@@@ -423,6 +427,11 @@@@
// Clear the list of keys considered to be held in the host
held_keys.clear();
+
+#if defined(Q_OS_MACOS)
+ emit this->emulator.modifier_keys_reset_signal();
+#endif /* Q_OS_MACOS */
+
}
/**
@@@@ -531,7 +540,13 @@@@
// Regular case pass key press onto the emulator
if (!event->isAutoRepeat()) {
- native_keypress_event(event->nativeScanCode());
+
+#if defined(Q_OS_MACOS)
+ native_keypress_event(event->nativeVirtualKey(), event->nativeModifiers());
+#else
+ native_keypress_event(event->nativeScanCode(), event->nativeModifiers());
+#endif /* Q_OS_MACOS */
+
}
}
@@@@ -551,7 +566,13 @@@@
// Regular case pass key release onto the emulator
if (!event->isAutoRepeat()) {
- native_keyrelease_event(event->nativeScanCode());
+
+#if defined(Q_OS_MACOS)
+ native_keyrelease_event(event->nativeVirtualKey(), event->nativeModifiers());
+#else
+ native_release_event(event->nativeScanCode(), event->nativeModifiers());
+#endif /* Q_OS_MACOS */
+
}
}
@@@@ -559,10 +580,28 @@@@
* Called by us with native scan-code to forward key-press to the emulator
*
* @@param scan_code Native scan code of key
+ * @@param modifiers Native modifiers
*/
void
-MainWindow::native_keypress_event(unsigned scan_code)
+MainWindow::native_keypress_event(unsigned scan_code, unsigned modifiers)
{
+
+#if defined(Q_OS_MACOS)
+ if (!(scan_code == 0 && modifiers == 0))
+ {
+ // Check the key isn't already marked as held down (else ignore)
+ // (to deal with potentially inconsistent host messages)
+ bool found = (std::find(held_keys.begin(), held_keys.end(), scan_code) != held_keys.end());
+
+ if (!found) {
+ // Add the key to the list of held_keys, that will be released
+ // when the window loses the focus
+ held_keys.insert(held_keys.end(), scan_code);
+
+ emit this->emulator.key_press_signal(scan_code);
+ }
+ }
+#else
// Check the key isn't already marked as held down (else ignore)
// (to deal with potentially inconsistent host messages)
bool found = (std::find(held_keys.begin(), held_keys.end(), scan_code) != held_keys.end());
@@@@ -574,16 +613,39 @@@@
emit this->emulator.key_press_signal(scan_code);
}
+
+#endif /* Q_OS_MACOS */
}
/**
* Called by us with native scan-code to forward key-release to the emulator
*
* @@param scan_code Native scan code of key
+ * @@param modifiers Native modifiers
*/
void
-MainWindow::native_keyrelease_event(unsigned scan_code)
+MainWindow::native_keyrelease_event(unsigned scan_code, unsigned modifiers)
{
+
+#if defined(Q_OS_MACOS)
+
+ if (!(scan_code == 0 && modifiers == 0))
+ {
+ // Check the key is marked as held down (else ignore)
+ // (to deal with potentially inconsistent host messages)
+ bool found = (std::find(held_keys.begin(), held_keys.end(), scan_code) != held_keys.end());
+
+ if (found) {
+ // Remove the key from the list of held_keys, that will be released
+ // when the window loses the focus
+ held_keys.remove(scan_code);
+
+ emit this->emulator.key_release_signal(scan_code);
+ }
+ }
+
+#else
+
// Check the key is marked as held down (else ignore)
// (to deal with potentially inconsistent host messages)
bool found = (std::find(held_keys.begin(), held_keys.end(), scan_code) != held_keys.end());
@@@@ -595,6 +657,9 @@@@
emit this->emulator.key_release_signal(scan_code);
}
+
+#endif /* Q_OS_MACOS */
+
}
void
@@@@ -1480,3 +1545,38 @@@@
return false;
}
#endif // Q_OS_WIN32
+
+#if defined(Q_OS_MACOS)
+/**
+ * On OS X, handle additional events for modifier keys. The normal key press/release
+ * events do not differentiate between left and right.
+ *
+ * @@param eventType unused
+ * @@param message window event NSEvent data
+ * @@param result unused
+ * @@return bool of whether we've handled the event (true) or OS X/QT should deal with it (false)
+ */
+bool
+MainWindow::nativeEvent(const QByteArray &eventType, void *message, long *result)
+{
+ Q_UNUSED(eventType);
+ Q_UNUSED(result);
+
+ NativeEvent *event = handle_native_event(message);
+ if (!event->processed)
+ {
+ free(event);
+ return false;
+ }
+
+ if (event->eventType == nativeEventTypeModifiersChanged)
+ {
+ // Modifier key state has changed.
+ emit this->emulator.modifier_keys_changed_signal(event->modifierMask);
+ free(event);
+ }
+
+ return true;
+}
+
+#endif /* Q_OS_MACOS */
--- qt5/main_window.h.orig 2018-11-02 19:15:19.000000000 +0000
+++ qt5/main_window.h 2018-11-02 20:59:06.000000000 +0000
@@@@ -107,9 +107,9 @@@@
void closeEvent(QCloseEvent *event) Q_DECL_OVERRIDE;
void keyPressEvent(QKeyEvent *event) Q_DECL_OVERRIDE;
void keyReleaseEvent(QKeyEvent *event) Q_DECL_OVERRIDE;
-#if defined(Q_OS_WIN32)
+#if defined(Q_OS_WIN32) || defined(Q_OS_MACOS)
bool nativeEvent(const QByteArray &eventType, void *message, long *result) Q_DECL_OVERRIDE;
-#endif /* Q_OS_WIN32 */
+#endif /* Q_OS_WIN32 or Q_OS_MACOS */
private slots:
void menu_screenshot();
@@@@ -163,8 +163,8 @@@@
void cdrom_menu_selection_update(const QAction *cdrom_action);
- void native_keypress_event(unsigned scan_code);
- void native_keyrelease_event(unsigned scan_code);
+ void native_keypress_event(unsigned scan_code, unsigned modifiers);
+ void native_keyrelease_event(unsigned scan_code, unsigned modifiers);
void release_held_keys();
bool full_screen;
--- /dev/null 2018-11-18 19:56:31.000000000 +0000
+++ macosx/preferences-macosx.h 2018-11-04 16:43:54.000000000 +0000
@@@@ -0,0 +1,38 @@@@
+///*
+// RPCEmu - An Acorn system emulator
+//
+// Copyright (C) 2017 Matthew Howkins
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+// */
+
+#ifndef __PREFERENCES_MACOSX_H__
+#define __PREFERENCES_MACOSX_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void init_preferences(void);
+extern void preferences_set_data_directory(const char *path);
+extern const char *preferences_get_data_directory();
+
+extern bool promptForDataDirectory;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __PREFERENCES_MACOSX_H__
--- /dev/null 2018-11-18 19:57:31.000000000 +0000
+++ macosx/preferences-macosx.m 2018-11-04 16:47:58.000000000 +0000
@@@@ -0,0 +1,86 @@@@
+/*
+ RPCEmu - An Acorn system emulator
+
+ Copyright (C) 2017 Matthew Howkins
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define UNUSED(x) (void)(x)
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#include "rpcemu.h"
+
+bool promptForDataDirectory;
+NSString* const KeyDataDirectory = @@"DataDirectory";
+
+void init_preferences(void)
+{
+ NSMutableDictionary *defaultValues = [NSMutableDictionary dictionary];
+ [defaultValues setObject: @@"" forKey:KeyDataDirectory];
+
+ [[NSUserDefaults standardUserDefaults] registerDefaults: defaultValues];
+
+ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+
+ // Check to see if there is a proper path for the data directory.
+ // If not, prompt for one.
+ NSString *dataDirectory = [defaults stringForKey: KeyDataDirectory];
+ if (dataDirectory == nil || [dataDirectory length] == 0)
+ {
+ promptForDataDirectory = true;
+ }
+ else
+ {
+ const char *str = [dataDirectory UTF8String];
+
+ // Check the folder exists.
+ DIR *ptr = opendir(str);
+ if (ptr)
+ {
+ closedir(ptr);
+ rpcemu_set_datadir(str);
+
+ promptForDataDirectory = false;
+ }
+ else
+ {
+ promptForDataDirectory = true;
+ }
+ }
+}
+
+void preferences_set_data_directory(const char *path)
+{
+ NSString *dataDirectory = [NSString stringWithUTF8String: path];
+
+ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+ [defaults setObject:dataDirectory forKey:KeyDataDirectory];
+}
+
+const char* preferences_get_data_directory()
+{
+ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+ NSString *path = [defaults stringForKey: KeyDataDirectory];
+
+ return [path UTF8String];
+}
--- rpc-machdep.c.orig 2018-11-02 19:15:19.000000000 +0000
+++ rpc-machdep.c 2018-11-18 19:54:52.000000000 +0000
@@@@ -19,6 +19,9 @@@@
*/
#include
+#include
+#include
+#include
#include "rpcemu.h"
@@@@ -26,7 +29,39 @@@@
be, but currently this version is used by Linux, all the other autoconf
based builds and Windows. Only Mac OS X GUI version needs to override */
+#ifdef __APPLE__
+#include
+
+static char datadir[512] = "";
+
+int rpcemu_set_datadir(const char *path)
+{
+ int len = strlen(path);
+ if (len == 0) return 0;
+
+ if (path[len - 1] != '/')
+ {
+ snprintf(datadir, 512, "%s/", path);
+ }
+ else
+ {
+ strncpy(datadir, path, 512);
+ }
+
+ DIR *ptr = opendir(datadir);
+ if (ptr)
+ {
+ closedir(ptr);
+ return 1;
+ }
+
+ return 0;
+}
+
+#else
static char datadir[512] = "./";
+#endif
+
static char logpath[1024] = "";
/**
--- /dev/null 2018-11-18 19:54:25.000000000 +0000
+++ rpc-macosx.c 2018-11-02 20:04:55.000000000 +0000
@@@@ -0,0 +1,89 @@@@
+/*
+ RPCEmu - An Acorn system emulator
+
+ Copyright (C) 2005-2010 Sarah Walker
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include "rpcemu.h"
+#include "mem.h"
+#include "sound.h"
+#include "vidc20.h"
+
+
+
+
+/**
+ * Return disk space information about a file system.
+ *
+ * @@param path Pathname of object within file system
+ * @@param d Pointer to disk_info structure that will be filled in
+ * @@return On success 1 is returned, on error 0 is returned
+ */
+int
+path_disk_info(const char *path, disk_info *d)
+{
+ struct statvfs s;
+ int ret;
+
+ assert(path != NULL);
+ assert(d != NULL);
+
+ if ((ret = statvfs(path, &s)) != 0) {
+ return 0;
+ }
+
+ d->size = (uint64_t) s.f_blocks * (uint64_t) s.f_frsize;
+ d->free = (uint64_t) s.f_bavail * (uint64_t) s.f_frsize;
+
+ return 1;
+}
+
+/**
+ * Log details about the current Operating System version.
+ *
+ * This function should work on all Unix and Unix-like systems.
+ *
+ * Called during program start-up.
+ */
+void
+rpcemu_log_os(void)
+{
+ struct utsname u;
+
+ if (uname(&u) == -1) {
+ rpclog("OS: Could not determine: %s\n", strerror(errno));
+ return;
+ }
+
+ rpclog("OS: SysName = %s\n", u.sysname);
+ rpclog("OS: Release = %s\n", u.release);
+ rpclog("OS: Version = %s\n", u.version);
+ rpclog("OS: Machine = %s\n", u.machine);
+}
+
+
--- qt5/rpc-qt5.cpp.orig 2018-11-02 19:15:19.000000000 +0000
+++ qt5/rpc-qt5.cpp 2018-11-04 16:45:30.000000000 +0000
@@@@ -45,6 +45,15 @@@@
#include "ide.h"
#include "cdrom-iso.h"
+#if defined(Q_OS_MACOS)
+#include "choose_dialog.h"
+
+#include "macosx/preferences-macosx.h"
+#include "macosx/hid-macosx.h"
+
+#include "keyboard_macosx.h"
+#endif /* Q_OS_MACOS */
+
#if defined(Q_OS_WIN32)
#include "cdrom-ioctl.h"
@@@@ -396,6 +405,23 @@@@
} // extern "C"
+#if defined(Q_OS_MACOS)
+
+int rpcemu_choose_datadirectory()
+{
+ ChooseDialog dialog;
+ if (dialog.exec() == QDialog::Accepted)
+ {
+ const char *path = preferences_get_data_directory();
+
+ return rpcemu_set_datadir(path);
+ }
+
+ return 0;
+}
+
+#endif
+
/**
* Program entry point
*
@@@@ -415,6 +441,22 @@@@
// Add a program icon
QApplication::setWindowIcon(QIcon(":/rpcemu_icon.png"));
+#if defined(Q_OS_MACOS)
+
+ init_preferences();
+
+ // If there is not a data directory in the application preferences, prompt for one.
+ // This will also prompt if the "Command" key is held down while the application loads.
+ if (promptForDataDirectory || (QApplication::queryKeyboardModifiers() & Qt::ControlModifier) != 0)
+ {
+ if (!rpcemu_choose_datadirectory())
+ {
+ return(0);
+ }
+ }
+
+#endif
+
// start enough of the emulator system to allow
// the GUI to initialise (e.g. load the config to init
// the configure window)
@@@@ -435,7 +477,12 @@@@
QThread::connect(emulator, &Emulator::finished, emu_thread, &QThread::quit);
QThread::connect(emulator, &Emulator::finished, emulator, &Emulator::deleteLater);
QThread::connect(emu_thread, &QThread::finished, emu_thread, &QThread::deleteLater);
-
+
+#if defined(Q_OS_MACOS)
+ // Initialise HID manager for Caps Lock key events.
+ init_hid_manager();
+#endif /* Q_OS_MACOS */
+
// Create Main Window
MainWindow main_window(*emulator);
pMainWin = &main_window;
@@@@ -470,6 +517,15 @@@@
connect(this, &Emulator::key_release_signal,
this, &Emulator::key_release);
+
+#if defined(Q_OS_MACOS)
+
+ // Modifier keys on a Mac must be handled separately, as there is no way of telling
+ // left or right from the key press and key release events due to a lack of scan codes.
+ connect(this, &Emulator::modifier_keys_changed_signal, this, &Emulator::modifier_keys_changed);
+ connect(this, &Emulator::modifier_keys_reset_signal, this, &Emulator::modifier_keys_reset);
+
+#endif /* Q_OS_MACOS */
connect(this, &Emulator::mouse_move_signal, this, &Emulator::mouse_move);
connect(this, &Emulator::mouse_move_relative_signal, this, &Emulator::mouse_move_relative);
@@@@ -615,6 +671,27 @@@@
keyboard_key_release(scan_codes);
}
+#if defined(Q_OS_MACOS)
+
+/**
+ * Modifier keys changed
+ * @@param mask The modifier key mask from the original NSEvent
+ */
+void Emulator::modifier_keys_changed(unsigned mask)
+{
+ keyboard_handle_modifier_keys(mask);
+}
+
+/**
+ * Modifier keys reset
+ */
+void Emulator::modifier_keys_reset()
+{
+ keyboard_reset_modifiers(true);
+}
+
+#endif /* Q_OS_MACOS */
+
/**
* Mouse has moved in absolute position (mousehack mode)
*
--- qt5/rpc-qt5.h.orig 2018-11-02 19:15:19.000000000 +0000
+++ qt5/rpc-qt5.h 2018-11-02 21:03:37.000000000 +0000
@@@@ -49,7 +49,12 @@@@
void key_press_signal(unsigned scan_code);
void key_release_signal(unsigned scan_code);
-
+
+#if defined(Q_OS_MACOS)
+ void modifier_keys_changed_signal(unsigned mask);
+ void modifier_keys_reset_signal();
+#endif /* Q_OS_MACOS */
+
void mouse_move_signal(int x, int y);
void mouse_move_relative_signal(int dx, int dy);
void mouse_press_signal(int buttons);
@@@@ -76,6 +81,11 @@@@
void key_press(unsigned scan_code);
void key_release(unsigned scan_code);
+
+#if defined(Q_OS_MACOS)
+ void modifier_keys_changed(unsigned mask);
+ void modifier_keys_reset();
+#endif /* Q_OS_MACOS */
void mouse_move(int x, int y);
void mouse_move_relative(int dx, int dy);
--- rpcemu.h.orig 2018-11-02 19:15:19.000000000 +0000
+++ rpcemu.h 2018-11-04 16:46:58.000000000 +0000
@@@@ -170,6 +170,11 @@@@
/* These functions can optionally be overridden by a platform. If not
needed to be overridden, there is a generic version in rpc-machdep.c */
+
+#ifdef __APPLE__
+extern int rpcemu_set_datadir(const char *path);
+#endif
+
extern const char *rpcemu_get_datadir(void);
extern const char *rpcemu_get_log_path(void);
--- ../../original/src/qt5/rpcemu.pro 2018-11-02 19:15:19.000000000 +0000
+++ qt5/rpcemu.pro 2018-11-18 20:15:49.000000000 +0000
@@@@ -6,6 +6,9 @@@@
QT += core widgets gui multimedia
INCLUDEPATH += ../
+macx {
+ INCLUDEPATH += ../macosx
+}
HEADERS = ../superio.h \
../cdrom-iso.h \
@@@@ -85,10 +88,31 @@@@
network_dialog.h
}
-unix {
- SOURCES += keyboard_x.c \
- ../hostfs-unix.c \
- ../rpc-linux.c
+!macx {
+ unix {
+ SOURCES += keyboard_x.c \
+ ../hostfs-unix.c \
+ ../rpc-linux.c
+ }
+}
+
+macx
+{
+ SOURCES += keyboard_macosx.c \
+ ../hostfs-macosx.c \
+ ../rpc-macosx.c \
+ ../macosx/hid-macosx.m \
+ ../macosx/events-macosx.m \
+ ../macosx/preferences-macosx.m \
+ choose_dialog.cpp
+
+ HEADERS += keyboard_macosx.h \
+ ../macosx/hid-macosx.h \
+ ../macosx/events-macosx.h \
+ ../macosx/preferences-macosx.h \
+ choose_dialog.h
+
+ ICON = ../macosx/rpcemu.icns
}
# Place exes in top level directory
@@@@ -133,4 +157,12 @@@@
TARGET = $$join(TARGET, , , -debug)
}
-LIBS +=
+!macx {
+ LIBS +=
+}
+
+macx {
+ LIBS += -framework coreFoundation -framework IOKit -framework Foundation -framework Carbon
+
+ QMAKE_INFO_PLIST = ../macosx/Info.plist
+}
--- qt5/settings.cpp.orig 2018-11-02 19:15:19.000000000 +0000
+++ qt5/settings.cpp 2018-11-02 20:42:30.000000000 +0000
@@@@ -41,8 +41,10 @@@@
QByteArray ba;
snprintf(filename, sizeof(filename), "%srpc.cfg", rpcemu_get_datadir());
+
+ rpclog("Loading configuration from '%s'.\n", filename);
- QSettings settings("rpc.cfg", QSettings::IniFormat);
+ QSettings settings(filename, QSettings::IniFormat);
/* Copy the contents of the configfile to the log */
QStringList keys = settings.childKeys();
@@@@ -188,8 +190,10 @@@@
QString sText;
snprintf(filename, sizeof(filename), "%srpc.cfg", rpcemu_get_datadir());
+
+ rpclog("Saving configuration to '%s'.\n", filename);
- QSettings settings("rpc.cfg", QSettings::IniFormat);
+ QSettings settings(filename, QSettings::IniFormat);
char s[256];
@
1.1
log
@emulators/rpcemu: Add a patchset that adds macOS support
This makes the keyboard work, among others.
@
text
@@