pkgsrc-Changes archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
CVS commit: pkgsrc/emulators/rpcemu
Module Name: pkgsrc
Committed By: js
Date: Sun Nov 1 20:18:30 UTC 2020
Modified Files:
pkgsrc/emulators/rpcemu: Makefile
Added Files:
pkgsrc/emulators/rpcemu/files: Info.plist
rpcemu-0.9.3-mac-patch-v1.patch
Removed Files:
pkgsrc/emulators/rpcemu/files: rpcemu-0.9.1-mac-v1.patch
Log Message:
emulators/rpcemu: Update to a newer patchset for macOS support
To generate a diff of this commit:
cvs rdiff -u -r1.2 -r1.3 pkgsrc/emulators/rpcemu/Makefile
cvs rdiff -u -r0 -r1.1 pkgsrc/emulators/rpcemu/files/Info.plist \
pkgsrc/emulators/rpcemu/files/rpcemu-0.9.3-mac-patch-v1.patch
cvs rdiff -u -r1.1 -r0 \
pkgsrc/emulators/rpcemu/files/rpcemu-0.9.1-mac-v1.patch
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: pkgsrc/emulators/rpcemu/Makefile
diff -u pkgsrc/emulators/rpcemu/Makefile:1.2 pkgsrc/emulators/rpcemu/Makefile:1.3
--- pkgsrc/emulators/rpcemu/Makefile:1.2 Sat Oct 24 16:53:41 2020
+++ pkgsrc/emulators/rpcemu/Makefile Sun Nov 1 20:18:30 2020
@@ -1,7 +1,7 @@
-# $NetBSD: Makefile,v 1.2 2020/10/24 16:53:41 js Exp $
+# $NetBSD: Makefile,v 1.3 2020/11/01 20:18:30 js Exp $
DISTNAME= rpcemu-0.9.3
-PKGREVISION= 1
+PKGREVISION= 2
CATEGORIES= emulators
MASTER_SITES= http://www.marutan.net/rpcemu/cgi/download.php?sFName=${PKGVERSION_NOREV}/
@@ -18,7 +18,9 @@ INSTALLATION_DIRS+= bin
post-patch:
${RUN} cd ${WRKSRC}/src && \
- ${PATCH} -p0 <${FILESDIR}/rpcemu-0.9.1-mac-v1.patch
+ ${PATCH} -p1 <${FILESDIR}/rpcemu-0.9.3-mac-patch-v1.patch
+ ${RUN} ${MKDIR} ${WRKSRC}/src/macosx
+ ${RUN} cp ${FILESDIR}/Info.plist ${WRKSRC}/src/macosx/Info.plist
do-configure:
cd ${WRKSRC} && ${QTDIR}/bin/qmake src/qt5/rpcemu.pro
Added files:
Index: pkgsrc/emulators/rpcemu/files/Info.plist
diff -u /dev/null pkgsrc/emulators/rpcemu/files/Info.plist:1.1
--- /dev/null Sun Nov 1 20:18:30 2020
+++ pkgsrc/emulators/rpcemu/files/Info.plist Sun Nov 1 20:18:30 2020
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleGetInfoString</key>
+ <string>Created by Qt/QMake</string>
+ <key>CFBundleIconFile</key>
+ <string>${ASSETCATALOG_COMPILER_APPICON_NAME}</string>
+ <key>CFBundleIdentifier</key>
+ <string>org.marutan.rpcemu</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleSignature</key>
+ <string>${QMAKE_PKGINFO_TYPEINFO}</string>
+ <key>LSMinimumSystemVersion</key>
+ <string>${MACOSX_DEPLOYMENT_TARGET}</string>
+ <key>NOTE</key>
+ <string>This file was generated by Qt/QMake.</string>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
+ <key>NSSupportsAutomaticGraphicsSwitching</key>
+ <true/>
+</dict>
+</plist>
Index: pkgsrc/emulators/rpcemu/files/rpcemu-0.9.3-mac-patch-v1.patch
diff -u /dev/null pkgsrc/emulators/rpcemu/files/rpcemu-0.9.3-mac-patch-v1.patch:1.1
--- /dev/null Sun Nov 1 20:18:30 2020
+++ pkgsrc/emulators/rpcemu/files/rpcemu-0.9.3-mac-patch-v1.patch Sun Nov 1 20:18:30 2020
@@ -0,0 +1,2264 @@
+From http://www.riscos.info/pipermail/rpcemu/2020-May/002887.html
+
+--- original/ArmDynarec.c 2020-05-06 20:19:23.000000000 +0100
++++ src/ArmDynarec.c 2020-05-07 21:14:11.000000000 +0100
+@@ -580,12 +580,39 @@
+ {
+ const long page_size = sysconf(_SC_PAGESIZE);
+ const long page_mask = ~(page_size - 1);
+- void *start;
++ void *start, *addr;
+ long end;
++ int mmap_flags = 0;
+
+ start = (void *) ((long) ptr & page_mask);
+ end = ((long) ptr + len + page_size - 1) & page_mask;
+ len = (size_t) (end - (long) start);
++
++#if __APPLE__
++ // More up-to-date versions of OS X require "mmap" to be called prior to "mprotect".
++ // Certain versions also require the MAP_JIT flag as well.
++ // Try without first, and if that fails, add the flag in.
++ mmap_flags = MAP_PRIVATE | MAP_ANON | MAP_FIXED;
++
++ addr = mmap(NULL, page_size, PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANON, -1, 0);
++ if (addr == MAP_FAILED)
++ {
++ mmap_flags |= MAP_JIT;
++ }
++ else
++ {
++ munmap(addr, page_size);
++ }
++
++ addr = mmap(start, len, PROT_READ | PROT_WRITE | PROT_EXEC, mmap_flags, -1, 0);
++
++ if (addr == MAP_FAILED)
++ {
++ perror("mmap");
++ exit(1);
++ }
++
++#endif
+
+ if (mprotect(start, len, PROT_READ | PROT_WRITE | PROT_EXEC) != 0) {
+ perror("mprotect");
+--- /dev/null 2020-05-14 17:35:51.000000000 +0100
++++ src/qt5/choose_dialog.cpp 2020-05-07 21:05:20.000000000 +0100
+@@ -0,0 +1,107 @@
++/*
++ 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.
++ */
++
++#include <QFileDialog>
++#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("<p>Before using RPCEmu for the first time, you must select the directory <br/>"
++ "that contains the folders and files required by the emulator, such as <br>"
++ "ROMs, hard drive images and the HostFS share.</p>"
++ "<p>You can show this dialogue again by holding down the Command key <br/>"
++ "whilst the application is loading.</p>");
++
++ 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 2020-05-14 17:35:51.000000000 +0100
++++ src/qt5/choose_dialog.h 2020-05-07 21:05:20.000000000 +0100
+@@ -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 <QDialog>
++#include <QDialogButtonBox>
++#include <QGridLayout>
++#include <QLabel>
++#include <QLineEdit>
++#include <QPushButton>
++
++#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 2020-05-14 17:35:51.000000000 +0100
++++ src/macosx/events-macosx.h 2020-05-07 21:05:20.000000000 +0100
+@@ -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 2020-05-14 17:35:51.000000000 +0100
++++ src/macosx/events-macosx.m 2020-05-07 21:05:20.000000000 +0100
+@@ -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 <stddef.h>
++#include <stdint.h>
++
++#include <Cocoa/Cocoa.h>
++#include <Carbon/Carbon.h>
++#include <IOKit/hid/IOHIDLib.h>
++
++#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 2020-05-14 17:35:51.000000000 +0100
++++ src/macosx/hid-macosx.h 2020-05-07 21:05:20.000000000 +0100
+@@ -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 2020-05-14 17:35:51.000000000 +0100
++++ src/macosx/hid-macosx.m 2020-05-07 21:05:20.000000000 +0100
+@@ -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 <stddef.h>
++#include <stdint.h>
++
++#include <Cocoa/Cocoa.h>
++#include <Carbon/Carbon.h>
++#include <IOKit/hid/IOHIDLib.h>
++
++#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 2020-05-14 17:35:51.000000000 +0100
++++ src/hostfs-macosx.c 2020-05-07 21:05:20.000000000 +0100
+@@ -0,0 +1,121 @@
++#include <assert.h>
++#include <errno.h>
++#include <stdio.h>
++#include <string.h>
++
++#include <utime.h>
++#include <sys/stat.h>
++
++#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() */
++}
+--- original/hostfs.c 2020-05-06 20:19:23.000000000 +0100
++++ src/hostfs.c 2020-05-07 21:05:20.000000000 +0100
+@@ -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;
+ }
+--- original/iomd.c 2020-05-06 20:19:23.000000000 +0100
++++ src/iomd.c 2020-05-07 21:05:20.000000000 +0100
+@@ -840,19 +840,28 @@
+ }
+ /* Middle */
+ if (mouse_buttons & 4) {
++
++#ifdef __APPLE__
++ temp |= 0x20;
++#else
+ if (config.mousetwobutton) {
+ temp |= 0x10; // bit 4
+ } else {
+ temp |= 0x20; // bit 5
+ }
++#endif
+ }
+ /* Right */
+ if (mouse_buttons & 2) {
++#ifdef __APPLE__
++ temp |= 0x10;
++#else
+ if (config.mousetwobutton) {
+ temp |= 0x20; // bit 5
+ } else {
+ temp |= 0x10; // bit 4
+ }
++#endif
+ }
+
+ /* bit 0 contains the monitor id bit, 0 for VGA, 1 for TV type monitors.
+--- original/keyboard.c 2020-05-06 20:19:23.000000000 +0100
++++ src/keyboard.c 2020-05-07 21:05:20.000000000 +0100
+@@ -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,10 @@
+ /* Mousehack reset */
+ mouse_hack.pointer = 0;
+ mouse_hack.cursor_linked = 1;
++
++#ifdef __APPLE__
++ keyboard_reset_modifiers(0);
++#endif
+ }
+
+ static uint8_t
+@@ -731,6 +739,7 @@
+ {
+ uint8_t tmp;
+
++#ifndef __APPLE__
+ if (config.mousetwobutton) {
+ /* To help people with only two buttons on their mouse,
+ swap the behaviour of middle and right buttons */
+@@ -740,6 +749,7 @@
+
+ mouseb = mousel | (mousem << 1) | (mouser << 2);
+ }
++#endif
+
+ tmp = (mouseb & 7) | 8;
+
+@@ -1192,6 +1202,15 @@
+ if (mouse.buttons & 1) {
+ buttons |= 4; /* Left button */
+ }
++
++#ifdef __APPLE__
++ if (mouse.buttons & 2) {
++ buttons |= 1; /* Right button */
++ }
++ if (mouse.buttons & 4) {
++ buttons |= 2; /* Middle button */
++ }
++#else
+ if (config.mousetwobutton) {
+ /* To help people with only two buttons on their mouse, swap
+ the behaviour of middle and right buttons */
+@@ -1209,6 +1228,8 @@
+ buttons |= 2; /* Middle button */
+ }
+ }
++#endif
++
+ arm.reg[2] = buttons;
+
+ arm.reg[3] = 0; /* R3 = time of button change */
+@@ -1258,4 +1279,4 @@
+ mouse_osunits_to_host(osx, osy, &x, &y);
+
+ rpcemu_move_host_mouse(x, y);
+-}
+\ No newline at end of file
++}
+--- /dev/null 2020-05-14 17:35:51.000000000 +0100
++++ src/qt5/keyboard_macosx.c 2020-05-07 21:05:20.000000000 +0100
+@@ -0,0 +1,351 @@
++/*
++ 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 <Carbon/Carbon.h>
++
++#define UNUSED(x) (void)(x)
++
++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];
++ int simulateMenuButton;
++} 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}, 0 }, // Shift.
++ {nativeModifierFlagControl, modifierKeyStateControl, 0x101, 0x2100, {0x14}, {0xe0, 0x14}, 0}, // Control.
++ {nativeModifierFlagOption, modifierKeyStateAlt, 0x120, 0x140, {0x11}, {0xe0, 0x11}, 0}, // Alt.
++ {nativeModifierFlagCommand, modifierKeyStateCommand, 0x100108, 0x100110, {0xe0, 0x1f}, {0xe0, 0x27}, 1}, // Command.
++ {0x1<<31, 0, 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].simulateMenuButton && config.mousetwobutton && state == 0)
++ {
++ state = 3;
++ mouse_mouse_press(4);
++ }
++ else
++ {
++ if ((mask & modifier_map[k].maskLeft) == modifier_map[k].maskLeft && (state & 1) == 0)
++ {
++ state |= 1;
++ keyboard_key_press(modifier_map[k].set_2_left);
++ }
++
++ if ((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 (config.mousetwobutton && modifier_map[k].simulateMenuButton)
++ {
++ state = 0;
++ mouse_mouse_release(4);
++ }
++ else
++ {
++ 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);
++ }
++}
++
++int keyboard_check_special_keys()
++{
++ return (modifierState.keyState[modifierKeyStateControl] != 0 && modifierState.keyState[modifierKeyStateCommand] != 0);
++}
+--- /dev/null 2020-05-14 17:35:51.000000000 +0100
++++ src/qt5/keyboard_macosx.h 2020-05-07 21:05:20.000000000 +0100
+@@ -0,0 +1,40 @@
++/*
++ 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 <stdint.h>
++
++#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);
++extern int keyboard_check_special_keys();
++
++#ifdef __cplusplus
++}
++#endif /* __cplusplus */
++
++#endif
++
+--- original/qt5/main_window.cpp 2020-05-06 20:19:23.000000000 +0100
++++ src/qt5/main_window.cpp 2020-05-07 21:14:11.000000000 +0100
+@@ -31,7 +31,12 @@
+
+ #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"
++#include "keyboard_macosx.h"
++#endif /* Q_OS_MACOS */
+
+ #include "rpcemu.h"
+ #include "keyboard.h"
+@@ -66,6 +71,9 @@
+ void
+ MainDisplay::mouseMoveEvent(QMouseEvent *event)
+ {
++ // Ignore mouse events if the application is terminating.
++ if (quited) return;
++
+ if((!pconfig_copy->mousehackon && mouse_captured) || full_screen) {
+ QPoint middle;
+
+@@ -90,6 +98,9 @@
+ void
+ MainDisplay::mousePressEvent(QMouseEvent *event)
+ {
++ // Ignore mouse events if the application is terminating.
++ if (quited) return;
++
+ // Handle turning on mouse capture in capture mouse mode
+ if(!pconfig_copy->mousehackon) {
+ if(!mouse_captured) {
+@@ -110,6 +121,9 @@
+ void
+ MainDisplay::mouseReleaseEvent(QMouseEvent *event)
+ {
++ // Ignore mouse events if the application is terminating.
++ if (quited) return;
++
+ if (event->button() & 7) {
+ emit this->emulator.mouse_release_signal(event->button() & 7);
+ }
+@@ -443,6 +457,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 */
++
+ }
+
+ /**
+@@ -505,53 +524,18 @@
+ }
+
+ // Special case, check for Ctrl-End, our multi purpose do clever things key
+- if((Qt::Key_End == event->key()) && (event->modifiers() & Qt::ControlModifier)) {
+- if(full_screen) {
+- // Change Full Screen -> Windowed
+-
+- display->set_full_screen(false);
+-
+- int host_xsize, host_ysize;
+- display->get_host_size(host_xsize, host_ysize);
+- display->setFixedSize(host_xsize, host_ysize);
+-
+- menuBar()->setVisible(true);
+- this->showNormal();
+- this->setFixedSize(this->sizeHint());
+-
+- full_screen = false;
+-
+- // Request redraw of display
+- display->update();
+-
+- // If we were in mousehack mode before entering fullscreen
+- // return to it now
+- if(reenable_mousehack) {
+- emit this->emulator.mouse_hack_signal();
+- }
+- reenable_mousehack = false;
+-
+- // If we were in mouse capture mode before entering fullscreen
+- // and we hadn't captured the mouse, display the host cursor now
+- if(!config_copy.mousehackon && !mouse_captured) {
+- this->display->setCursor(Qt::ArrowCursor);
+- }
+-
+- return;
+- } else if(!pconfig_copy->mousehackon && mouse_captured) {
+- // Turn off mouse capture
+- mouse_captured = 0;
+-
+- // show pointer in mouse capture mode when it's not been captured
+- this->display->setCursor(Qt::ArrowCursor);
+-
+- return;
+- }
++ if((Qt::Key_End == event->key()) && (event->modifiers() & Qt::ControlModifier))
++ {
++ processMagicKeys();
+ }
+
+ // 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 */
+ }
+ }
+
+@@ -571,7 +555,11 @@
+
+ // 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_keyrelease_event(event->nativeScanCode(), event->nativeModifiers());
++#endif /* Q_OS_MACOS */
+ }
+ }
+
+@@ -581,8 +569,25 @@
+ * @param scan_code Native scan code of key
+ */
+ 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());
+@@ -592,8 +597,9 @@
+ // when the window loses the focus
+ held_keys.insert(held_keys.end(), scan_code);
+
+- emit this->emulator.key_press_signal(scan_code);
++
+ }
++#endif
+ }
+
+ /**
+@@ -602,8 +608,26 @@
+ * @param scan_code Native scan code of key
+ */
+ 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());
+@@ -615,6 +639,7 @@
+
+ emit this->emulator.key_release_signal(scan_code);
+ }
++#endif
+ }
+
+ void
+@@ -1312,7 +1337,13 @@
+
+ if(!pconfig_copy->mousehackon) {
+ if(mouse_captured) {
++
++#if defined(Q_OS_MACOS)
++ capture_text = " Press CTRL-COMMAND to release mouse";
++#else
+ capture_text = " Press CTRL-END to release mouse";
++#endif
++
+ } else {
+ capture_text = " Click to capture mouse";
+ }
+@@ -1415,6 +1446,52 @@
+ }
+ }
+
++void
++MainWindow::processMagicKeys()
++{
++ if(full_screen) {
++ // Change Full Screen -> Windowed
++
++ display->set_full_screen(false);
++
++ int host_xsize, host_ysize;
++ display->get_host_size(host_xsize, host_ysize);
++ display->setFixedSize(host_xsize, host_ysize);
++
++ menuBar()->setVisible(true);
++ this->showNormal();
++ this->setFixedSize(this->sizeHint());
++
++ full_screen = false;
++
++ // Request redraw of display
++ display->update();
++
++ // If we were in mousehack mode before entering fullscreen
++ // return to it now
++ if(reenable_mousehack) {
++ emit this->emulator.mouse_hack_signal();
++ }
++ reenable_mousehack = false;
++
++ // If we were in mouse capture mode before entering fullscreen
++ // and we hadn't captured the mouse, display the host cursor now
++ if(!config_copy.mousehackon && !mouse_captured) {
++ this->display->setCursor(Qt::ArrowCursor);
++ }
++
++ return;
++ } else if(!pconfig_copy->mousehackon && mouse_captured) {
++ // Turn off mouse capture
++ mouse_captured = 0;
++
++ // show pointer in mouse capture mode when it's not been captured
++ this->display->setCursor(Qt::ArrowCursor);
++
++ return;
++ }
++}
++
+ #if defined(Q_OS_WIN32)
+ /**
+ * windows pre event handler used by us to modify some default behaviour
+@@ -1473,3 +1550,46 @@
+ 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);
++
++ if (keyboard_check_special_keys())
++ {
++ // Magic key combination to release mouse capture.
++ processMagicKeys();
++ }
++
++ free(event);
++ }
++
++ return true;
++}
++
++#endif /* Q_OS_MACOS */
++
+--- original/qt5/main_window.h 2020-05-06 20:19:23.000000000 +0100
++++ src/qt5/main_window.h 2020-05-07 21:14:11.000000000 +0100
+@@ -109,7 +109,7 @@
+ 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 */
+
+@@ -165,9 +165,11 @@
+
+ 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();
++
++ void processMagicKeys();
+
+ bool full_screen;
+ bool reenable_mousehack; ///< Did we disable mousehack entering fullscreen and have to reenable it on leaving fullscreen?
+--- /dev/null 2020-05-14 17:35:51.000000000 +0100
++++ src/macosx/network-macosx.c 2020-05-07 21:05:20.000000000 +0100
+@@ -0,0 +1,97 @@
++/*
++ 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.
++ */
++
++/* RPCemu networking */
++
++#include <assert.h>
++#include <ctype.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <fcntl.h>
++#include <unistd.h>
++#include <sys/time.h>
++#include <sys/types.h>
++#include <sys/ioctl.h>
++#include <errno.h>
++#include <string.h>
++#include <sys/socket.h>
++#include <net/if.h>
++#include <netinet/in.h>
++#include <arpa/inet.h>
++#include <pwd.h>
++#include <grp.h>
++#include <signal.h>
++
++#include "rpcemu.h"
++#include "mem.h"
++#include "podules.h"
++#include "network.h"
++
++int
++network_plt_init(void)
++{
++ // Do nothing on a Mac, as TUN/TAP is not supported.
++ return 0;
++}
++
++/**
++ * Shutdown any running network components.
++ *
++ * Called on program shutdown and program reset after
++ * configuration has changed.
++ */
++void
++network_plt_reset(void)
++{
++ // Do nothing on a Mac, as TUN/TAP is not supported.
++}
++
++uint32_t
++network_plt_rx(uint32_t errbuf, uint32_t mbuf, uint32_t rxhdr, uint32_t *dataavail)
++{
++ NOT_USED(errbuf);
++ NOT_USED(mbuf);
++ NOT_USED(rxhdr);
++ NOT_USED(dataavail);
++
++ // Do nothing on a Mac, as TUN/TAP is not supported.
++ return 0;
++}
++
++uint32_t
++network_plt_tx(uint32_t errbuf, uint32_t mbufs, uint32_t dest, uint32_t src, uint32_t frametype)
++{
++ NOT_USED(errbuf);
++ NOT_USED(mbufs);
++ NOT_USED(dest);
++ NOT_USED(src);
++ NOT_USED(frametype);
++
++ // Do nothing on a Mac, as TUN/TAP is not supported.
++ return 0;
++}
++
++void
++network_plt_setirqstatus(uint32_t address)
++{
++ NOT_USED(address);
++
++ // Do nothing on a Mac, as TUN/TAP is not supported.
++}
+--- original/network.c 2020-05-06 20:19:23.000000000 +0100
++++ src/network.c 2020-05-07 21:14:11.000000000 +0100
+@@ -80,8 +80,13 @@
+ filebase = chunkbase + (8 * 2) + 4; // required size for two entries
+ poduleromsize = filebase + ((sizeof(description) + 3) & ~3u); // Word align description string
+
+- // Add on size for driver module if it can be opened successfully
+- f = fopen("netroms/EtherRPCEm,ffa", "rb");
++ char filename[512];
++ snprintf(filename,sizeof(filename), "%snetroms/EtherRPCEm,ffa", rpcemu_get_datadir());
++
++ rpclog("network_rom_init: Attempting to load Ethernet ROM from '%s'\n", filename);
++
++ // Add on size for driver module if it can be opened successfully
++ f = fopen(filename, "rb");
+ if (f != NULL) {
+ long len;
+
+@@ -124,6 +129,8 @@
+ if (len == module_file_size) { // Load was OK
+ len = (len + 3) & ~3u;
+ makechunk(0x81, filebase, (uint32_t) len); // 0x81 = RISC OS, ROM
++
++ rpclog("network_rom_init: Successfuly loaded 'EtherRPCEm,ffa' into podulerom\n");
+ }
+ }
+ }
+--- /dev/null 2020-05-14 17:35:51.000000000 +0100
++++ src/macosx/preferences-macosx.h 2020-05-07 21:05:20.000000000 +0100
+@@ -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 2020-05-14 17:35:51.000000000 +0100
++++ src/macosx/preferences-macosx.m 2020-05-07 21:05:20.000000000 +0100
+@@ -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 <stddef.h>
++#include <stdint.h>
++#include <dirent.h>
++
++#include <Cocoa/Cocoa.h>
++#include <Carbon/Carbon.h>
++#include <IOKit/hid/IOHIDLib.h>
++
++#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];
++}
+--- original/rpc-machdep.c 2020-05-06 20:19:23.000000000 +0100
++++ src/rpc-machdep.c 2020-05-07 21:05:20.000000000 +0100
+@@ -26,7 +26,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 <dirent.h>
++
++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 2020-05-14 17:35:51.000000000 +0100
++++ src/rpc-macosx.c 2020-05-07 21:05:20.000000000 +0100
+@@ -0,0 +1,87 @@
++/*
++ 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 <assert.h>
++#include <errno.h>
++#include <signal.h>
++#include <string.h>
++
++#include <pthread.h>
++#include <sys/statvfs.h>
++#include <sys/types.h>
++#include <sys/utsname.h>
++#include <sys/wait.h>
++
++#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);
++}
+--- original/qt5/rpc-qt5.cpp 2020-05-06 20:19:23.000000000 +0100
++++ src/qt5/rpc-qt5.cpp 2020-05-08 14:36:13.000000000 +0100
+@@ -47,6 +47,15 @@
+ #include "network.h"
+ #include "network-nat.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"
+
+@@ -398,6 +407,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
+ *
+@@ -416,6 +442,20 @@
+
+ // 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::ShiftModifier) != 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
+@@ -438,6 +478,11 @@
+ 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 the HID manager for CAPS LOCK key events.
++ init_hid_manager();
++#endif
+
+ // Create Main Window
+ MainWindow main_window(*emulator);
+@@ -473,6 +518,14 @@
+
+ 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);
+@@ -630,6 +683,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)
+ *
+--- original/qt5/rpc-qt5.h 2020-05-06 20:19:23.000000000 +0100
++++ src/qt5/rpc-qt5.h 2020-05-07 21:14:11.000000000 +0100
+@@ -49,6 +49,11 @@
+ 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);
+@@ -78,6 +83,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);
+--- original/rpcemu.h 2020-05-06 20:19:23.000000000 +0100
++++ src/rpcemu.h 2020-05-07 21:14:11.000000000 +0100
+@@ -72,7 +72,7 @@
+ /* Note that networking is currently supported on Mac OS X with the Cocoa GUI
+ version but not with the Allegro GUI. */
+ #if defined __linux || defined __linux__ || defined WIN32 || defined _WIN32 || \
+- defined RPCEMU_COCOA_GUI
++ defined RPCEMU_COCOA_GUI || __APPLE__
+ #define RPCEMU_NETWORKING
+ #endif
+
+@@ -169,6 +169,10 @@
+ extern uint32_t inscount;
+ extern int cyccount;
+
++#ifdef __APPLE__
++extern int rpcemu_set_datadir(const char *path);
++#endif
++
+ /* These functions can optionally be overridden by a platform. If not
+ needed to be overridden, there is a generic version in rpc-machdep.c */
+ extern const char *rpcemu_get_datadir(void);
+--- original/qt5/rpcemu.pro 2020-05-06 20:19:23.000000000 +0100
++++ src/qt5/rpcemu.pro 2020-05-14 17:33:58.000000000 +0100
+@@ -6,6 +6,10 @@
+ QT += core widgets gui multimedia
+ INCLUDEPATH += ../
+
++macx {
++ INCLUDEPATH += ../macosx
++}
++
+ # This ensures that using switch with enum requires every value to be handled
+ QMAKE_CFLAGS += -Werror=switch
+ QMAKE_CXXFLAGS += -Werror=switch
+@@ -61,7 +65,7 @@
+ plt_sound.cpp
+
+ # NAT Networking
+-linux | win32 {
++linux | win32 | macx {
+ HEADERS += ../network-nat.h
+ SOURCES += ../network-nat.c
+
+@@ -141,10 +145,38 @@
+ 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 += ../network.c \
++ network_dialog.cpp \
++ keyboard_macosx.c \
++ ../hostfs-macosx.c \
++ ../rpc-macosx.c \
++ ../macosx/hid-macosx.m \
++ ../macosx/events-macosx.m \
++ ../macosx/preferences-macosx.m \
++ ../macosx/network-macosx.c \
++ ../macosx/system-macosx.m \
++ choose_dialog.cpp
++
++ HEADERS += ../network.h \
++ network_dialog.h \
++ keyboard_macosx.h \
++ ../macosx/hid-macosx.h \
++ ../macosx/events-macosx.h \
++ ../macosx/preferences-macosx.h \
++ ../macosx/system-macosx.h \
++ choose_dialog.h
++
++ ICON = ../macosx/rpcemu.icns
++
+ }
+
+ # Place exes in top level directory
+@@ -189,4 +221,13 @@
+ TARGET = $$join(TARGET, , , -debug)
+ }
+
+-LIBS +=
++!macx {
++ LIBS +=
++}
++
++macx {
++ LIBS += -framework coreFoundation -framework IOKit -framework Foundation -framework Carbon
++
++ QMAKE_INFO_PLIST = ../macosx/Info.plist
++}
++
+--- original/qt5/settings.cpp 2020-05-06 20:19:23.000000000 +0100
++++ src/qt5/settings.cpp 2020-05-07 21:14:11.000000000 +0100
+@@ -42,7 +42,7 @@
+
+ snprintf(filename, sizeof(filename), "%srpc.cfg", rpcemu_get_datadir());
+
+- QSettings settings("rpc.cfg", QSettings::IniFormat);
++ QSettings settings(filename, QSettings::IniFormat);
+
+ /* Copy the contents of the configfile to the log */
+ QStringList keys = settings.childKeys();
+@@ -199,7 +199,7 @@
+
+ snprintf(filename, sizeof(filename), "%srpc.cfg", rpcemu_get_datadir());
+
+- QSettings settings("rpc.cfg", QSettings::IniFormat);
++ QSettings settings(filename, QSettings::IniFormat);
+
+ char s[256];
+
+--- /dev/null 2020-05-14 17:35:51.000000000 +0100
++++ src/macosx/system-macosx.h 2020-05-07 21:05:20.000000000 +0100
+@@ -0,0 +1,34 @@
++/*
++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 __SYSTEM_MACOSX_H__
++#define __SYSTEM_MACOSX_H__
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++extern unsigned int get_macosx_version(void);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif
+--- /dev/null 2020-05-14 17:35:51.000000000 +0100
++++ src/macosx/system-macosx.m 2020-05-07 21:05:20.000000000 +0100
+@@ -0,0 +1,35 @@
++/*
++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 <stddef.h>
++#include <stdint.h>
++
++#include <Cocoa/Cocoa.h>
++#include <Carbon/Carbon.h>
++
++unsigned int get_macosx_version(void)
++{
++ NSOperatingSystemVersion version;
++
++ version = [[NSProcessInfo processInfo] operatingSystemVersion];
++
++ return (version.majorVersion << 16) | (version.minorVersion << 8) | (version.patchVersion);
++}
++
Home |
Main Index |
Thread Index |
Old Index