require "sigc++-2.0";
if_arch "!windows"
{
- require "xlib";
+ if_arch "!darwin"
+ {
+ require "xlib";
+ };
};
if_arch "windows"
{
library "gdi32";
};
};
+ if_arch "darwin"
+ {
+ build_info
+ {
+ library "CoreFoundation.framework";
+ library "AppKit.framework";
+ };
+ };
feature "devil" "Include DevIL support for loading image files";
if_feature "devil"
if_arch "!windows"
{
- feature "xrandr" "Include support for video mode switching with XRandR";
- if_feature "xrandr"
+ if_arch "!darwin"
{
- build_info
+ feature "xrandr" "Include support for video mode switching with XRandR";
+ if_feature "xrandr"
{
- library "Xrandr";
+ build_info
+ {
+ library "Xrandr";
+ };
};
};
};
overlay "wgl";
};
};
+ if_arch "darwin"
+ {
+ overlay "cocoa";
+ };
if_arch "!windows"
{
- overlay "x11";
- if_feature "opengl"
+ if_arch "!darwin"
{
- overlay "glx";
+ overlay "x11";
+ if_feature "opengl"
+ {
+ overlay "glx";
+ };
};
};
install true;
--- /dev/null
+#ifndef MSP_GRAPHICS_COCOADISPLAY_H_
+#define MSP_GRAPHICS_COCOADISPLAY_H_
+
+#include "cocoaevent.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _CocoaDisplay;
+typedef struct _CocoaDisplay CocoaDisplay;
+
+CocoaDisplay *create_display();
+void destroy_display(CocoaDisplay *);
+void queue_event(CocoaDisplay *, CocoaEvent *);
+bool get_event(CocoaDisplay *, CocoaEvent *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+#import <AppKit/NSApplication.h>
+#import <Foundation/NSRunLoop.h>
+#include "cocoadisplay.h"
+
+void convert_event(NSEvent *, CocoaEvent *);
+
+struct _CocoaDisplay
+{
+ NSApplication *app;
+ CFMutableArrayRef event_queue;
+};
+
+const void *copy_event(CFAllocatorRef, const void *);
+void free_event(CFAllocatorRef, const void *);
+
+CocoaDisplay *create_display()
+{
+ CocoaDisplay *display = (CocoaDisplay *)malloc(sizeof(CocoaDisplay));
+ display->app = [NSApplication sharedApplication];
+ // Since OS X 10.6
+ //[display->app setActivationPolicy:NSApplicationActivationPolicyRegular];
+ [display->app finishLaunching];
+
+ CFArrayCallBacks callbacks;
+ callbacks.version = 0;
+ callbacks.retain = copy_event;
+ callbacks.release = free_event;
+ callbacks.copyDescription = NULL;
+ callbacks.equal = NULL;
+ display->event_queue = CFArrayCreateMutable(NULL, 0, &callbacks);
+
+ return display;
+}
+
+void destroy_display(CocoaDisplay *display)
+{
+ CFRelease(display->event_queue);
+ free(display);
+}
+
+void queue_event(CocoaDisplay *display, CocoaEvent *event)
+{
+ CFArrayAppendValue(display->event_queue, event);
+}
+
+bool get_event(CocoaDisplay *display, CocoaEvent *buf)
+{
+ if(CFArrayGetCount(display->event_queue))
+ {
+ *buf = *(const CocoaEvent *)CFArrayGetValueAtIndex(display->event_queue, 0);
+ CFArrayRemoveValueAtIndex(display->event_queue, 0);
+ return true;
+ }
+
+ NSEvent *event = [display->app nextEventMatchingMask:NSAnyEventMask untilDate:nil inMode:NSDefaultRunLoopMode dequeue:YES];
+ if(event)
+ {
+ [display->app sendEvent:event];
+ convert_event(event, buf);
+ return true;
+ }
+
+ return false;
+}
+
+const void *copy_event(CFAllocatorRef allocator, const void *event)
+{
+ void *copy = CFAllocatorAllocate(allocator, sizeof(CocoaEvent), 0);
+ memcpy(copy, event, sizeof(CocoaEvent));
+ return copy;
+}
+
+void free_event(CFAllocatorRef allocator, const void *event)
+{
+ CFAllocatorDeallocate(allocator, (void *)event);
+}
--- /dev/null
+#ifndef MSP_GRAPHICS_COCOAEVENT_H_
+#define MSP_GRAPHICS_COCOAEVENT_H_
+
+#include "cocoawindow.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum
+{
+ LEFT_MOUSE_DOWN = 1,
+ LEFT_MOUSE_UP = 2,
+ RIGHT_MOUSE_DOWN = 3,
+ RIGHT_MOUSE_UP = 4,
+ MOUSE_MOVED = 5,
+ KEY_DOWN = 10,
+ KEY_UP = 11,
+ SCROLL_WHEEL = 22,
+ OTHER_MOUSE_DOWN = 25,
+ OTHER_MOUSE_UP = 26,
+ WINDOW_CLOSED = 1000
+} CocoaEventType;
+
+typedef struct
+{
+ CocoaEventType type;
+ CocoaWindow *window;
+} CocoaAnyEvent;
+
+typedef struct
+{
+ CocoaEventType type;
+ CocoaWindow *window;
+ unsigned button;
+ bool state;
+} CocoaButtonEvent;
+
+typedef struct
+{
+ CocoaEventType type;
+ CocoaWindow *window;
+ float x;
+ float y;
+} CocoaMotionEvent;
+
+typedef struct
+{
+ CocoaEventType type;
+ CocoaWindow *window;
+ unsigned key;
+ bool state;
+ int character;
+} CocoaKeyEvent;
+
+typedef union
+{
+ CocoaEventType type;
+ CocoaAnyEvent any;
+ CocoaButtonEvent button;
+ CocoaMotionEvent motion;
+ CocoaKeyEvent key;
+} CocoaEvent;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+#import <AppKit/NSEvent.h>
+#import <AppKit/NSWindow.h>
+#import <Foundation/NSString.h>
+#include "cocoaevent.h"
+
+NSWindow *get_native_window(CocoaWindow *);
+CocoaWindow *lookup_window(NSWindow *);
+
+CocoaWindow *last_window = 0;
+
+void convert_event(NSEvent *ev, CocoaEvent *cev)
+{
+ NSEventType type = [ev type];
+ cev->any.type = (CocoaEventType)type;
+
+ NSWindow *window = [ev window];
+ if(window)
+ {
+ cev->any.window = lookup_window([ev window]);
+ last_window = cev->any.window;
+ }
+
+ if(type==NSLeftMouseDown || type==NSLeftMouseUp)
+ {
+ cev->button.button = 1;
+ cev->button.state = (type==NSLeftMouseDown);
+ }
+ else if(type==NSRightMouseDown || type==NSRightMouseUp)
+ {
+ cev->button.button = 3;
+ cev->button.state = (type==NSRightMouseDown);
+ }
+ else if(type==NSOtherMouseDown || type==NSOtherMouseUp)
+ {
+ cev->button.button = [ev buttonNumber];
+ cev->button.state = (type==NSOtherMouseDown);
+ }
+ else if(type==NSMouseMoved)
+ {
+ NSPoint pt = [ev locationInWindow];
+ if(!window && last_window)
+ {
+ /* If the pointer leaves the window, any subsequent MouseMoved events
+ are reported to a null window until the window is clicked. */
+ window = get_native_window(last_window);
+ pt = [window convertScreenToBase:pt];
+ cev->any.window = last_window;
+ }
+ cev->motion.x = pt.x;
+ cev->motion.y = pt.y;
+ }
+ else if(type==NSKeyDown)
+ {
+ NSString *chars = [[ev charactersIgnoringModifiers] uppercaseString];
+ if([chars length]==1)
+ cev->key.key = [chars characterAtIndex:0];
+
+ chars = [ev characters];
+ if([chars length]==1)
+ cev->key.character = [chars characterAtIndex:0];
+ }
+}
--- /dev/null
+#ifndef MSP_GRAPHICS_COCOAWINDOW_H_
+#define MSP_GRAPHICS_COCOAWINDOW_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _CocoaDisplay;
+
+struct _CocoaWindow;
+typedef struct _CocoaWindow CocoaWindow;
+
+CocoaWindow *create_window(struct _CocoaDisplay *, unsigned, unsigned, bool, bool);
+void destroy_window(CocoaWindow *);
+void set_window_title(CocoaWindow *, const char *);
+void set_window_size(CocoaWindow *, unsigned, unsigned);
+void show_window(CocoaWindow *);
+void hide_window(CocoaWindow *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+#import <AppKit/NSWindow.h>
+#import <CoreFoundation/CFArray.h>
+#include "cocoadisplay.h"
+#include "cocoawindow.h"
+
+@interface WindowDelegate: NSObject <NSWindowDelegate>
+
+- (void)windowWillClose:(NSNotification *)notification;
+
+@end
+
+struct _CocoaWindow
+{
+ CocoaDisplay *display;
+ __strong NSWindow *window;
+ NSUInteger style;
+};
+
+CFMutableDictionaryRef windows = NULL;
+CFMutableArrayRef event_queue = NULL;
+
+CocoaWindow *create_window(CocoaDisplay *display, unsigned width, unsigned height, bool fullscreen, bool resizable)
+{
+ NSRect rect = NSMakeRect(0, 0, width, height);
+ NSUInteger style = NSTitledWindowMask|NSClosableWindowMask;
+ if(resizable)
+ style |= NSResizableWindowMask;
+ NSWindow *window = [NSWindow alloc];
+ window = [window initWithContentRect:rect styleMask:style backing:NSBackingStoreBuffered defer:NO];
+ [window setAcceptsMouseMovedEvents:YES];
+
+ (void)fullscreen;
+
+ CocoaWindow *wrapper = (CocoaWindow *)malloc(sizeof(CocoaWindow));
+ wrapper->display = display;
+ wrapper->window = window;
+ wrapper->style = style;
+
+ [window setDelegate:[[WindowDelegate alloc] init]];
+
+ if(!windows)
+ windows = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
+ CFDictionaryAddValue(windows, window, wrapper);
+
+ return wrapper;
+}
+
+void destroy_window(CocoaWindow *window)
+{
+ CFDictionaryRemoveValue(windows, window->window);
+ if(!CFDictionaryGetCount(windows))
+ {
+ CFRelease(windows);
+ windows = NULL;
+ }
+
+ [window->window release];
+ free(window);
+}
+
+NSWindow *get_native_window(CocoaWindow *window)
+{
+ return window->window;
+}
+
+CocoaWindow *lookup_window(NSWindow *window)
+{
+ return (CocoaWindow *)CFDictionaryGetValue(windows, window);
+}
+
+void set_window_title(CocoaWindow *window, const char *title)
+{
+ [window->window setTitle:[NSString stringWithCString:title encoding:NSUTF8StringEncoding]];
+}
+
+void set_window_size(CocoaWindow *window, unsigned width, unsigned height)
+{
+ NSRect rect = NSMakeRect(0, 0, width, height);
+ rect = [NSWindow frameRectForContentRect:rect styleMask:window->style];
+ [window->window setFrame:rect display:YES];
+}
+
+void show_window(CocoaWindow *window)
+{
+ [window->window makeKeyAndOrderFront:nil];
+ [NSApp activateIgnoringOtherApps:NO];
+}
+
+void hide_window(CocoaWindow *window)
+{
+ (void)window;
+}
+
+@implementation WindowDelegate
+
+- (void)windowWillClose:(NSNotification *)notification
+{
+ CocoaWindow *window = lookup_window((NSWindow *)[notification object]);
+ if(window)
+ {
+ CocoaEvent event;
+ event.any.type = WINDOW_CLOSED;
+ event.any.window = window;
+ queue_event(window->display, &event);
+ }
+}
+
+@end
--- /dev/null
+#include <stdexcept>
+#include "cocoadisplay.h"
+#include "display.h"
+#include "display_private.h"
+
+using namespace std;
+
+namespace Msp {
+namespace Graphics {
+
+Display::Display(const std::string &):
+ priv(new Private)
+{
+ priv->display = create_display();
+}
+
+Display::~Display()
+{
+ destroy_display(priv->display);
+ delete priv;
+}
+
+void Display::set_mode(const VideoMode &, bool)
+{
+ throw runtime_error("video mode switching not supported");
+}
+
+bool Display::process_events()
+{
+ Window::Event event;
+ if(!get_event(priv->display, &event.cevent))
+ return false;
+
+ map<WindowHandle, Window *>::iterator i = priv->windows.find(event.cevent.any.window);
+ if(i!=priv->windows.end())
+ i->second->event(event);
+
+ return true;
+}
+
+void Display::check_error()
+{
+}
+
+} // namespace Graphics
+} // namespace Msp
--- /dev/null
+#ifndef MSP_GRAPHICS_DISPLAY_PLATFORM_H_
+#define MSP_GRAPHICS_DISPLAY_PLATFORM_H_
+
+#include "cocoadisplay.h"
+
+namespace Msp {
+namespace Graphics {
+
+typedef CocoaDisplay *DisplayHandle;
+typedef int MonitorHandle;
+typedef int ModeHandle;
+
+} // namespace Graphics
+} // namespace Msp
+
+#endif
--- /dev/null
+#include "cocoadisplay.h"
+#include "cocoawindow.h"
+#include "display.h"
+#include "display_private.h"
+#include "window.h"
+#include "window_private.h"
+
+namespace Msp {
+namespace Graphics {
+
+void Window::platform_init()
+{
+ CocoaDisplay *dpy = display.get_private().display;
+ priv->window = create_window(dpy, options.width, options.height, options.fullscreen, options.resizable);
+}
+
+void Window::platform_cleanup()
+{
+ destroy_window(priv->window);
+}
+
+void Window::set_title(const std::string &title)
+{
+ set_window_title(priv->window, title.c_str());
+}
+
+void Window::platform_reconfigure(bool /*fullscreen_changed*/)
+{
+ set_window_size(priv->window, options.width, options.height);
+}
+
+void Window::show_cursor(bool)
+{
+}
+
+void Window::warp_pointer(int, int)
+{
+}
+
+void Window::platform_show()
+{
+ show_window(priv->window);
+}
+
+void Window::platform_hide()
+{
+ hide_window(priv->window);
+}
+
+bool Window::event(const Event &ev)
+{
+ switch(ev.cevent.type)
+ {
+ case LEFT_MOUSE_DOWN:
+ case LEFT_MOUSE_UP:
+ case RIGHT_MOUSE_DOWN:
+ case RIGHT_MOUSE_UP:
+ case KEY_DOWN:
+ case KEY_UP:
+ case OTHER_MOUSE_DOWN:
+ case OTHER_MOUSE_UP:
+ signal_input_event.emit(ev);
+ break;
+ case MOUSE_MOVED:
+ if(ev.cevent.motion.x>=0 && ev.cevent.motion.x<options.width && ev.cevent.motion.y>=0 && ev.cevent.motion.y<options.height)
+ signal_input_event.emit(ev);
+ break;
+ case WINDOW_CLOSED:
+ signal_close.emit();
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace Graphics
+} // namespace Msp
--- /dev/null
+#ifndef MSP_GRAPHICS_WINDOW_PLATFORM_H_
+#define MSP_GRAPHICS_WINDOW_PLATFORM_H_
+
+#include "cocoaevent.h"
+#include "cocoawindow.h"
+
+namespace Msp {
+namespace Graphics {
+
+struct PlatformWindowPrivate
+{
+};
+
+struct PlatformEvent
+{
+ CocoaEvent cevent;
+};
+
+typedef CocoaWindow *WindowHandle;
+
+} // namespace Graphics
+} // namespace Msp
+
+#endif
--- /dev/null
+#include <msp/graphics/window_private.h>
+#include "keyboard.h"
+#include "keys_private.h"
+
+using namespace std;
+
+namespace Msp {
+namespace Input {
+
+string Keyboard::get_button_name(unsigned btn) const
+{
+ return Device::get_button_name(btn);
+}
+
+void Keyboard::input_event(const Graphics::Window::Event &event)
+{
+ switch(event.cevent.type)
+ {
+ case KEY_DOWN:
+ case KEY_UP:
+ if(unsigned key = key_from_sys(event.cevent.key.key))
+ set_button_state(key, event.cevent.type==KEY_DOWN, true);
+ if(event.cevent.type==KEY_DOWN && event.cevent.key.character>0)
+ signal_character.emit(event.cevent.key.character);
+ break;
+ default:;
+ }
+}
+
+} // namespace Inpus
+} // namespace Msp
--- /dev/null
+#include "keys.h"
+
+namespace Msp {
+namespace Input {
+
+unsigned sys_keymap[N_KEYS_]=
+{
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 8, 9, 10, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 27, 0, 0, 0, 0,
+
+ ' ', 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 0, 0, 0, 0, 0, 0,
+
+ 0, 'A', 'B', 'C', 'D', 'E', 'F', 'G',
+ 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
+ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
+ 'X', 'Y', 'Z', 0, 0, 0, 0, 0,
+
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+
+ 0xF702, 0xF703, 0xF700, 0xF701, 0, 0, 0, 0,
+ 0xF729, 0xF72B, 0xF72C, 0xF72D, 0xF727, 0xF728, 0, 0,
+ 0, 0xF704, 0xF705, 0xF706, 0xF707, 0xF708, 0xF709, 0xF70A,
+ 0xF70B, 0xF70C, 0xF70D, 0xF70E, 0xF70F, 0, 0, 0,
+
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0xF72F, 0xF739, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+
+ 0xF730, 0xF72E, 0xF735, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+} // namespace Input
+} // namespace Msp
--- /dev/null
+#include <msp/graphics/window_private.h>
+#include "mouse.h"
+
+namespace Msp {
+namespace Input {
+
+void Mouse::input_event(const Graphics::Window::Event &event)
+{
+ switch(event.cevent.type)
+ {
+ case LEFT_MOUSE_DOWN:
+ case LEFT_MOUSE_UP:
+ case RIGHT_MOUSE_DOWN:
+ case RIGHT_MOUSE_UP:
+ case OTHER_MOUSE_DOWN:
+ case OTHER_MOUSE_UP:
+ set_button_state(event.cevent.button.button, event.cevent.button.state, true);
+ break;
+ case MOUSE_MOVED:
+ set_axis_value(0, event.cevent.motion.x*2.0/window.get_width()-1.0, true);
+ set_axis_value(1, event.cevent.motion.y*2.0/window.get_height()-1.0, true);
+ break;
+ default:;
+ }
+}
+
+} // namespace Input
+} // namespace Msp