panda3d/panda/src/tinydisplay/tinyOsxGraphicsWindow.mm
2009-02-25 23:08:10 +00:00

1791 lines
60 KiB
Plaintext

////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#include <Carbon/Carbon.h>
#include <Cocoa/Cocoa.h>
#include "tinyOsxGraphicsWindow.h"
#include "config_tinydisplay.h"
#include "tinyOsxGraphicsPipe.h"
#include "pStatTimer.h"
#include "keyboardButton.h"
#include "mouseButton.h"
#include "tinyGraphicsStateGuardian.h"
#include "throw_event.h"
#include "pnmImage.h"
#include "virtualFileSystem.h"
#include "config_util.h"
#include <ApplicationServices/ApplicationServices.h>
#include "pmutex.h"
//#include "mutexHolder.h"
////////////////////////////////////
\
Mutex & OSXGloablMutex()
{
static Mutex m("OSXWIN_Mutex");
return m;
}
struct work1
{
volatile bool work_done;
};
#define PANDA_CREATE_WINDOW 101
static void Post_Event_Wiait(unsigned short type, unsigned int data1 , unsigned int data2 , int target_window )
{
work1 w;
w.work_done = false;
NSEvent *ev = [NSEvent otherEventWithType:NSApplicationDefined
location:NSZeroPoint
modifierFlags:0
timestamp:0.0f
windowNumber:target_window
context:nil
subtype:type
data1:data1
data2:(int)&w];
[NSApp postEvent:ev atStart:NO];
while(!w.work_done)
usleep(10);
}
////////////////////////// Global Objects .....
TypeHandle TinyOsxGraphicsWindow::_type_handle;
TinyOsxGraphicsWindow * TinyOsxGraphicsWindow::FullScreenWindow = NULL;
#define USER_CONTAINER
#include <set>
#ifdef USER_CONTAINER
std::set< WindowRef > MyWindows;
void AddAWindow( WindowRef window)
{
MyWindows.insert(window);
}
bool checkmywindow(WindowRef window)
{
return MyWindows.find(window) != MyWindows.end();
}
#else
void AddAWindow( WindowRef window)
{
}
bool checkmywindow(WindowRef window)
{
return true;
}
#endif
////////////////////////////////////////////////////////////////////
// Function: GetCurrentOSxWindow
// Access: Static,
// Description: How to find the active window for events on osx..
//
////////////////////////////////////////////////////////////////////
TinyOsxGraphicsWindow* TinyOsxGraphicsWindow::GetCurrentOSxWindow(WindowRef window)
{
if(FullScreenWindow != NULL)
return FullScreenWindow;
if (NULL == window) // HID use this path
{
// Assume first we are a child window. If we cant find a window of that class, then we
// are standalone and can jsut grab the front window.
window = GetFrontWindowOfClass(kSimpleWindowClass, TRUE);
if (NULL == window)
window = FrontNonFloatingWindow();
}
if (window && checkmywindow(window))
//if (window )
{
return (TinyOsxGraphicsWindow *)GetWRefCon (window);
}
else
return NULL;
}
////////////////////////////////////////////////////////////////////
// Function: TinyOsxGraphicsWindow::event_handler
// Access: Public
// Description: The standard window event handler for non-fullscreen
// windows.
////////////////////////////////////////////////////////////////////
OSStatus TinyOsxGraphicsWindow::event_handler(EventHandlerCallRef myHandler, EventRef event)
{
OSStatus result = eventNotHandledErr;
UInt32 the_class = GetEventClass(event);
UInt32 kind = GetEventKind(event);
WindowRef window = NULL;
GetEventParameter(event, kEventParamDirectObject, typeWindowRef, NULL, sizeof(WindowRef), NULL, &window);
if (tinydisplay_cat.is_spam())
{
tinydisplay_cat.spam() << ClockObject::get_global_clock()->get_real_time() << " event_handler: " << (void *)this << ", " << window << ", " << the_class << ", " << kind << "\n";
}
switch (the_class)
{
case kEventClassMouse:
result = handleWindowMouseEvents (myHandler, event);
break;
case kEventClassWindow:
switch (kind) {
case kEventWindowCollapsing:
/*
Rect r;
GetWindowPortBounds (window, &r);
CompositeGLBufferIntoWindow( get_context(), &r, GetWindowPort (window));
UpdateCollapsedWindowDockTile (window);
*/
SystemSetWindowForground(false);
break;
case kEventWindowActivated: // called on click activation and initially
SystemSetWindowForground(true);
DoResize();
break;
case kEventWindowDeactivated:
SystemSetWindowForground(false);
break;
case kEventWindowClose: // called when window is being closed (close box)
// This is a message from the window manager indicating that
// the user has requested to close the window.
user_close_request();
result = noErr;
break;
case kEventWindowShown: // called on initial show (not on un-minimize)
if (window == FrontNonFloatingWindow ())
SetUserFocusWindow (window);
break;
case kEventWindowBoundsChanged: // called for resize and moves (drag)
DoResize();
break;
case kEventWindowZoomed:
break;
case kEventWindowCollapsed:
{
WindowProperties properties;
properties.set_minimized(true);
system_changed_properties(properties);
}
break;
case kEventWindowExpanded:
{
WindowProperties properties;
properties.set_minimized(false);
system_changed_properties(properties);
}
break;
}
break;
}
return result;
}
////////////////////////////////////////////////////////////////////
// Function: TinyOsxGraphicsWindow::user_close_request
// Access: Private
// Description: The user has requested to close the window, for
// instance with Cmd-W, or by clicking on the close
// button.
////////////////////////////////////////////////////////////////////
void TinyOsxGraphicsWindow::user_close_request()
{
string close_request_event = get_close_request_event();
if (!close_request_event.empty())
{
// In this case, the app has indicated a desire to intercept the request and process it directly.
throw_event(close_request_event);
}
else
{
// In this case, the default case, the app does not intend to service the request, so we do by closing the window.
close_window();
}
}
////////////////////////////////////////////////////////////////////
// Function: TinyOsxGraphicsWindow::SystemCloseWindow
// Access: private
// Description: The Windows is closed by a OS resource not by a internal request
//
////////////////////////////////////////////////////////////////////
void TinyOsxGraphicsWindow::SystemCloseWindow()
{
if (tinydisplay_cat.is_debug())
tinydisplay_cat.debug() << "System Closing Window \n";
ReleaseSystemResources();
};
////////////////////////////////////////////////////////////////////
// Function: windowEvtHndlr
// Access: file scope static
// Description: The C callback for Window Events ..
//
// We only hook this up for non fullscreen window... so we only
// handle system window events..
//
////////////////////////////////////////////////////////////////////
static pascal OSStatus windowEvtHndlr(EventHandlerCallRef myHandler, EventRef event, void *userData)
{
#pragma unused (userData)
// volatile().lock();
WindowRef window = NULL;
GetEventParameter(event, kEventParamDirectObject, typeWindowRef, NULL, sizeof(WindowRef), NULL, &window);
if (window != NULL)
{
TinyOsxGraphicsWindow *osx_win = TinyOsxGraphicsWindow::GetCurrentOSxWindow(window);
if (osx_win != (TinyOsxGraphicsWindow *)NULL)
{
//OSXGloablMutex().release();
return osx_win->event_handler(myHandler, event);
}
}
//OSXGloablMutex().release();
return eventNotHandledErr;
}
///////////////////////////////////////////////////////////////////
// Function: TinyOsxGraphicsWindow::DoResize
// Access:
// Description: The C callback for Window Events ..
//
// We only hook this up for none fullscreen window... so we only handle system window events..
//
////////////////////////////////////////////////////////////////////
void TinyOsxGraphicsWindow::DoResize(void)
{
tinydisplay_cat.info() << "In Resize....." << _properties << "\n";
// only in window mode .. not full screen
if(_osx_window != NULL && !_is_fullscreen && _properties.has_size())
{
Rect rectPort = {0,0,0,0};
CGRect viewRect = {{0.0f, 0.0f}, {0.0f, 0.0f}};
GetWindowPortBounds (_osx_window, &rectPort);
viewRect.size.width = (float) (rectPort.right - rectPort.left);
viewRect.size.height = (float) (rectPort.bottom - rectPort.top);
// tell panda
WindowProperties properties;
properties.set_size((int)viewRect.size.width,(int)viewRect.size.height);
properties.set_origin((int) rectPort.left,(int)rectPort.top);
system_changed_properties(properties);
ZB_resize(_frame_buffer, NULL, _properties.get_x_size(), _properties.get_y_size());
if (tinydisplay_cat.is_debug())
tinydisplay_cat.debug() << " Resizing Window " << viewRect.size.width << " " << viewRect.size.height << "\n";
}
};
///////////////////////////////////////////////////////////////////
// Function: appEvtHndlr
// Access:
// Description: The C callback for APlication Events..
//
// Hooked once for application
//
////////////////////////////////////////////////////////////////////
static pascal OSStatus appEvtHndlr (EventHandlerCallRef myHandler, EventRef event, void* userData)
{
#pragma unused (myHandler)
OSStatus result = eventNotHandledErr;
{
//OSXGloablMutex().lock();
TinyOsxGraphicsWindow *osx_win = NULL;
WindowRef window = NULL;
UInt32 the_class = GetEventClass (event);
UInt32 kind = GetEventKind (event);
GetEventParameter(event, kEventParamWindowRef, typeWindowRef, NULL, sizeof(WindowRef), NULL, (void*) &window);
osx_win = TinyOsxGraphicsWindow::GetCurrentOSxWindow(window);
if (osx_win == NULL)
{
//OSXGloablMutex().release();
return eventNotHandledErr;
}
switch (the_class)
{
case kEventClassTextInput:
if(kind == kEventTextInputUnicodeForKeyEvent)
osx_win->handleTextInput(myHandler, event);
//result = noErr;
//
// can not report handled .. the os will not sent the raw key strokes then
// if(osx_win->handleTextInput(myHandler, event) == noErr)
// result = noErr;
break;
case kEventClassKeyboard:
{
switch (kind) {
case kEventRawKeyRepeat:
case kEventRawKeyDown:
result = osx_win->handleKeyInput (myHandler, event, true);
break;
case kEventRawKeyUp:
result = osx_win->handleKeyInput (myHandler, event, false);
break;
case kEventRawKeyModifiersChanged:
{
UInt32 newModifiers;
OSStatus error = GetEventParameter(event, kEventParamKeyModifiers,typeUInt32, NULL,sizeof(UInt32), NULL, &newModifiers);
if(error == noErr)
{
osx_win->HandleModifireDeleta(newModifiers);
result = noErr;
}
}
break;
}
}
break;
case kEventClassMouse:
// tinydisplay_cat.info() << "Mouse movement handled by Application handler\n";
//if(TinyOsxGraphicsWindow::FullScreenWindow != NULL)
result = osx_win->handleWindowMouseEvents (myHandler, event);
//result = noErr;
break;
}
//OSXGloablMutex().release();
}
return result;
}
///////////////////////////////////////////////////////////////////
// Function: TinyOsxGraphicsWindow::handleTextInput
// Access:
// Description: Trap Unicode Input.
//
//
////////////////////////////////////////////////////////////////////
OSStatus TinyOsxGraphicsWindow::handleTextInput (EventHandlerCallRef myHandler, EventRef theTextEvent)
{
UniChar *text = NULL;
UInt32 actualSize = 0;
OSStatus ret = GetEventParameter (theTextEvent, kEventParamTextInputSendText, typeUnicodeText, NULL, 0, &actualSize, NULL);
if(ret != noErr)
return ret;
text = (UniChar*)NewPtr(actualSize);
if(text!= NULL)
{
ret = GetEventParameter (theTextEvent, kEventParamTextInputSendText,typeUnicodeText, NULL, actualSize, NULL, text);
if(ret != noErr)
return ret;
for(unsigned int x = 0; x < actualSize/sizeof(UniChar); ++x)
_input_devices[0].keystroke(text[x]);
DisposePtr((char *)text);
}
return ret;
}
///////////////////////////////////////////////////////////////////
// Function: TinyOsxGraphicsWindow::ReleaseSystemResources
// Access: private..
// Description: Clean up the OS level messes..
////////////////////////////////////////////////////////////////////
void TinyOsxGraphicsWindow::ReleaseSystemResources()
{
if (_is_fullscreen)
{
_is_fullscreen = false;
FullScreenWindow = NULL;
if (_originalMode != NULL)
CGDisplaySwitchToMode( kCGDirectMainDisplay, _originalMode );
CGDisplayRelease( kCGDirectMainDisplay );
_originalMode = NULL;
}
if(_osx_window != NULL)
{
SetWRefCon (_osx_window, (long int) NULL);
HideWindow (_osx_window);
DisposeWindow(_osx_window);
_osx_window = NULL;
}
if (_pending_icon != NULL) {
CGImageRelease(_pending_icon);
_pending_icon = NULL;
}
if (_current_icon != NULL) {
cerr << "release current icon\n";
CGImageRelease(_current_icon);
_current_icon = NULL;
}
WindowProperties properties;
properties.set_foreground(false);
properties.set_open(false);
properties.set_cursor_filename(Filename());
system_changed_properties(properties);
_is_fullscreen = false;
_osx_window = NULL;
}
static int id_seed = 100;
////////////////////////////////////////////////////////////////////
// Function: TinyOsxGraphicsWindow::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
TinyOsxGraphicsWindow::TinyOsxGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe,
const string &name,
const FrameBufferProperties &fb_prop,
const WindowProperties &win_prop,
int flags,
GraphicsStateGuardian *gsg,
GraphicsOutput *host) :
GraphicsWindow(engine, pipe, name, fb_prop, win_prop, flags, gsg, host),
_osx_window(NULL),
_is_fullscreen(false),
_pending_icon(NULL),
_current_icon(NULL),
_originalMode(NULL),
_ID(id_seed++)
{
GraphicsWindowInputDevice device =
GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard/mouse");
_input_devices.push_back(device);
_input_devices[0].set_pointer_in_window(0, 0);
_last_key_modifiers = 0;
_last_buttons = 0;
_cursor_hidden = false;
_display_hide_cursor = false;
_wheel_delta = 0;
_frame_buffer = NULL;
update_pixel_factor();
if (tinydisplay_cat.is_debug())
tinydisplay_cat.debug() << "TinyOsxGraphicsWindow::TinyOsxGraphicsWindow() -" <<_ID << "\n";
}
////////////////////////////////////////////////////////////////////
// Function: TinyOsxGraphicsWindow::Destructor
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
TinyOsxGraphicsWindow::~TinyOsxGraphicsWindow()
{
if (tinydisplay_cat.is_debug())
tinydisplay_cat.debug() << "TinyOsxGraphicsWindow::~TinyOsxGraphicsWindow() -" <<_ID << "\n";
// Make sure the window callback won't come back to this
// (destructed) object any more.
if (_osx_window) {
SetWRefCon (_osx_window, (long) NULL);
}
ReleaseSystemResources();
}
////////////////////////////////////////////////////////////////////
// Function: TinyOsxGraphicsWindow::set_icon_filename
// Access: Private
// Description: Called internally to load up an icon file that should
// be applied to the window. Returns true on success,
// false on failure.
////////////////////////////////////////////////////////////////////
bool TinyOsxGraphicsWindow::set_icon_filename(const Filename &icon_filename)
{
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
Filename icon_pathname = icon_filename;
if (!vfs->resolve_filename(icon_pathname, get_model_path())) {
// The filename doesn't exist along the search path.
if (icon_pathname.is_fully_qualified() && vfs->exists(icon_pathname)) {
// But it does exist locally, so accept it.
} else {
tinydisplay_cat.warning()
<< "Could not find icon filename " << icon_filename << "\n";
return false;
}
}
PNMImage pnmimage;
if (!pnmimage.read(icon_pathname))
{
tinydisplay_cat.warning()
<< "Could not read icon filename " << icon_pathname << "\n";
return false;
}
CGImageRef icon_image = TinyOsxGraphicsPipe::create_cg_image(pnmimage);
if (icon_image == NULL)
{
return false;
}
if (_pending_icon != NULL)
{
CGImageRelease(_pending_icon);
_pending_icon = NULL;
}
_pending_icon = icon_image;
return true;
}
////////////////////////////////////////////////////////////////////
// Function: TinyOsxGraphicsWindow::set_pointer_in_window
// Access: Private
// Description: Indicates the mouse pointer is seen within the
// window.
////////////////////////////////////////////////////////////////////
void TinyOsxGraphicsWindow::
set_pointer_in_window(int x, int y)
{
_input_devices[0].set_pointer_in_window(x, y);
if (_cursor_hidden != _display_hide_cursor) {
if (_cursor_hidden) {
CGDisplayHideCursor(kCGDirectMainDisplay);
_display_hide_cursor = true;
} else {
CGDisplayShowCursor(kCGDirectMainDisplay);
_display_hide_cursor = false;
}
}
}
////////////////////////////////////////////////////////////////////
// Function: TinyOsxGraphicsWindow::set_pointer_out_of_window
// Access: Private
// Description: Indicates the mouse pointer is no longer within the
// window.
////////////////////////////////////////////////////////////////////
void TinyOsxGraphicsWindow::
set_pointer_out_of_window() {
_input_devices[0].set_pointer_out_of_window();
if (_display_hide_cursor) {
CGDisplayShowCursor(kCGDirectMainDisplay);
_display_hide_cursor = false;
}
}
////////////////////////////////////////////////////////////////////
// Function: TinyOsxGraphicsWindow::begin_frame
// Access: Public, Virtual
// Description: This function will be called within the draw thread
// before beginning rendering for a given frame. It
// should do whatever setup is required, and return true
// if the frame should be rendered, or false if it
// should be skipped.
////////////////////////////////////////////////////////////////////
bool TinyOsxGraphicsWindow::begin_frame(FrameMode mode, Thread *current_thread)
{
PStatTimer timer(_make_current_pcollector);
begin_frame_spam(mode);
if (_gsg == (GraphicsStateGuardian *)NULL ||
(_osx_window == NULL && !_is_fullscreen))
{
// not powered up .. just abort..
return false;
}
// Now is a good time to apply the icon change that may have
// recently been requested. By this point, we should be able to get
// a handle to the dock context.
if (_pending_icon != NULL)
{
CGContextRef context = BeginCGContextForApplicationDockTile();
if (context != NULL)
{
SetApplicationDockTileImage(_pending_icon);
EndCGContextForApplicationDockTile(context);
if (_current_icon != NULL)
{
CGImageRelease(_current_icon);
_current_icon = NULL;
}
_current_icon = _pending_icon;
_pending_icon = NULL;
}
}
TinyGraphicsStateGuardian *tinygsg;
DCAST_INTO_R(tinygsg, _gsg, false);
tinygsg->_current_frame_buffer = _frame_buffer;
tinygsg->reset_if_new();
_gsg->set_current_properties(&get_fb_properties());
return _gsg->begin_frame(current_thread);
}
////////////////////////////////////////////////////////////////////
// Function: TinyOsxGraphicsWindow::end_frame
// Access: Public, Virtual
// Description: This function will be called within the draw thread
// after rendering is completed for a given frame. It
// should do whatever finalization is required.
////////////////////////////////////////////////////////////////////
void TinyOsxGraphicsWindow::end_frame(FrameMode mode, Thread *current_thread)
{
end_frame_spam(mode);
_gsg->end_frame(current_thread);
if (mode == FM_render) {
trigger_flip();
if (_one_shot) {
prepare_for_deletion();
}
clear_cube_map_selection();
}
}
////////////////////////////////////////////////////////////////////
// Function: TinyOsxGraphicsWindow::begin_flip
// Access: Public, Virtual
// Description: This function will be called within the draw thread
// after end_frame() has been called on all windows, to
// initiate the exchange of the front and back buffers.
//
// This should instruct the window to prepare for the
// flip at the next video sync, but it should not wait.
//
// We have the two separate functions, begin_flip() and
// end_flip(), to make it easier to flip all of the
// windows at the same time.
////////////////////////////////////////////////////////////////////
void TinyOsxGraphicsWindow::begin_flip()
{
if (_osx_window == NULL) {
return;
}
QDErr err;
// blit rendered framebuffer into window backing store
Rect src_rect = {0, 0, _frame_buffer->ysize, _frame_buffer->xsize};
Rect ddrc_rect = {0, 0, _frame_buffer->ysize, _frame_buffer->xsize};
if (get_pixel_factor() != 1.0) {
src_rect.right = get_fb_x_size();
src_rect.bottom = get_fb_y_size();
}
// create a GWorld containing our image
GWorldPtr pGWorld;
err = NewGWorldFromPtr(&pGWorld, k32BGRAPixelFormat, &src_rect, 0, 0, 0,
(char *)_frame_buffer->pbuf, _frame_buffer->linesize);
if (err != noErr) {
tinydisplay_cat.error()
<< " error in NewGWorldFromPtr, called from begin_flip()\n";
return;
}
GrafPtr out_port = GetWindowPort(_osx_window);
GrafPtr portSave = NULL;
Boolean portChanged = QDSwapPort(out_port, &portSave);
{
static PStatCollector b2("Wait:Flip:Begin:CopyBits");
PStatTimer t2(b2);
CopyBits(GetPortBitMapForCopyBits(pGWorld),
GetPortBitMapForCopyBits(out_port),
&src_rect, &ddrc_rect, srcCopy, 0);
}
if (portChanged) {
QDSwapPort(portSave, NULL);
}
DisposeGWorld(pGWorld);
}
////////////////////////////////////////////////////////////////////
// Function: TinyOsxGraphicsWindow::close_window
// Access: Protected, Virtual
// Description: Closes the window right now. Called from the window
// thread.
////////////////////////////////////////////////////////////////////
void TinyOsxGraphicsWindow::close_window()
{
SystemCloseWindow();
WindowProperties properties;
properties.set_open(false);
system_changed_properties(properties);
ReleaseSystemResources();
_gsg.clear();
_active = false;
GraphicsWindow::close_window();
}
//////////////////////////////////////////////////////////
// HACK ALLERT ************ Undocumented OSX calls...
// I can not find any other way to get the mouse focus to a window in OSX..
//
//extern "C" {
// struct CPSProcessSerNum
// {
// UInt32 lo;
// UInt32 hi;
// };
///
//extern OSErr CPSGetCurrentProcess(CPSProcessSerNum *psn);
//extern OSErr CPSEnableForegroundOperation( struct CPSProcessSerNum *psn);
//extern OSErr CPSSetProcessName ( struct CPSProcessSerNum *psn, char *processname);
//extern OSErr CPSSetFrontProcess( struct CPSProcessSerNum *psn);
//};
////////////////////////////////////////////////////////////////////
// Function: TinyOsxGraphicsWindow::open_window
// Access: Protected, Virtual
// Description: Opens the window right now. Called from the window
// thread. Returns true if the window is successfully
// opened, or false if there was a problem.
////////////////////////////////////////////////////////////////////
bool TinyOsxGraphicsWindow::open_window()
{
// GSG Creation/Initialization
TinyGraphicsStateGuardian *tinygsg;
if (_gsg == 0) {
// There is no old gsg. Create a new one.
tinygsg = new TinyGraphicsStateGuardian(_engine, _pipe, NULL);
_gsg = tinygsg;
} else {
DCAST_INTO_R(tinygsg, _gsg, false);
}
create_frame_buffer();
if (_frame_buffer == NULL) {
tinydisplay_cat.error()
<< "Could not create frame buffer.\n";
return false;
}
tinygsg->_current_frame_buffer = _frame_buffer;
tinygsg->reset_if_new();
if (!tinygsg->is_valid()) {
close_window();
return false;
}
WindowProperties req_properties = _properties;
//OSXGloablMutex().lock();
bool answer = OSOpenWindow(req_properties);
//OSXGloablMutex().release();
return answer;
}
bool TinyOsxGraphicsWindow::OSOpenWindow(WindowProperties &req_properties)
{
OSErr err = noErr;
if (_current_icon != NULL && _pending_icon == NULL)
{
// If we already have an icon specified, we'll need to reapply it
// when the window is succesfully created.
_pending_icon = _current_icon;
_current_icon = NULL;
}
static bool GlobalInits = false;
if (!GlobalInits)
{
//
// one time aplication inits.. to get a window open from a standalone aplication..
EventHandlerRef application_event_ref_ref1;
EventTypeSpec list1[] =
{
//{ kEventClassCommand, kEventProcessCommand },
//{ kEventClassCommand, kEventCommandUpdateStatus },
{ kEventClassMouse, kEventMouseDown },// handle trackball functionality globaly because there is only a single user
{ kEventClassMouse, kEventMouseUp },
{ kEventClassMouse, kEventMouseMoved },
{ kEventClassMouse, kEventMouseDragged },
{ kEventClassMouse, kEventMouseWheelMoved } ,
{ kEventClassKeyboard, kEventRawKeyDown },
{ kEventClassKeyboard, kEventRawKeyUp } ,
{ kEventClassKeyboard, kEventRawKeyRepeat },
{ kEventClassKeyboard, kEventRawKeyModifiersChanged } ,
{ kEventClassTextInput, kEventTextInputUnicodeForKeyEvent},
};
EventHandlerUPP gEvtHandler = NewEventHandlerUPP(appEvtHndlr);
err = InstallApplicationEventHandler (gEvtHandler, GetEventTypeCount (list1) , list1, this, &application_event_ref_ref1 );
GlobalInits = true;
ProcessSerialNumber psn = { 0, kCurrentProcess };
TransformProcessType(&psn, kProcessTransformToForegroundApplication);
SetFrontProcess(&psn);
}
if (req_properties.has_fullscreen() && req_properties.get_fullscreen())
{
tinydisplay_cat.info() << "Creating full screen\n";
// capture the main display
CGDisplayCapture( kCGDirectMainDisplay );
// if sized try and switch it..
if (req_properties.has_size())
{
_originalMode = CGDisplayCurrentMode( kCGDirectMainDisplay );
CFDictionaryRef newMode = CGDisplayBestModeForParameters( kCGDirectMainDisplay, 32, req_properties.get_x_size(), req_properties.get_y_size(), 0 );
if (newMode == NULL)
{
tinydisplay_cat.error()
<< "Invalid fullscreen size: " << req_properties.get_x_size()
<< ", " << req_properties.get_y_size()
<< "\n";
}
else
{
CGDisplaySwitchToMode( kCGDirectMainDisplay, newMode);
// Set our new window size according to the size we actually got.
SInt32 width, height;
CFNumberGetValue((CFNumberRef)CFDictionaryGetValue(newMode, kCGDisplayWidth), kCFNumberSInt32Type, &width);
CFNumberGetValue((CFNumberRef)CFDictionaryGetValue(newMode, kCGDisplayHeight), kCFNumberSInt32Type, &height);
_properties.set_size(width, height);
}
}
_properties.set_fullscreen(true);
_is_fullscreen =true;
FullScreenWindow = this;
req_properties.clear_fullscreen();
}
else
{
Rect r;
if (req_properties.has_origin())
{
r.top = req_properties.get_y_origin();
r.left =req_properties.get_x_origin();
}
else
{
r.top = 50;
r.left = 10;
}
if (req_properties.has_size())
{
r.right = r.left + req_properties.get_x_size();
r.bottom = r.top + req_properties.get_y_size();
}
else
{
r.right = r.left + 512;
r.bottom = r.top + 512;
}
if (req_properties.has_parent_window())
{
tinydisplay_cat.info() << "Creating child window\n";
CreateNewWindow(kSimpleWindowClass, kWindowNoAttributes, &r, &_osx_window);
AddAWindow(_osx_window);
_properties.set_fixed_size(true);
tinydisplay_cat.info() << "Child window created\n";
}
else
{
if (req_properties.has_undecorated() && req_properties.get_undecorated())
{ // create a unmovable .. no edge window..
tinydisplay_cat.info() << "Creating undecorated window\n";
CreateNewWindow(kDocumentWindowClass, kWindowStandardDocumentAttributes | kWindowNoTitleBarAttribute, &r, &_osx_window);
}
else
{ // create a window with crome and sizing and sucj
// In this case, we want to constrain the window to the
// available size.
Rect bounds;
GetAvailableWindowPositioningBounds(GetMainDevice(), &bounds);
r.left = max(r.left, bounds.left);
r.right = min(r.right, bounds.right);
r.top = max(r.top, bounds.top);
r.bottom = min(r.bottom, bounds.bottom);
tinydisplay_cat.info() << "Creating standard window\n";
CreateNewWindow(kDocumentWindowClass, kWindowStandardDocumentAttributes | kWindowStandardHandlerAttribute, &r, &_osx_window);
AddAWindow(_osx_window);
}
}
if (_osx_window)
{
EventHandlerUPP gWinEvtHandler; // window event handler
EventTypeSpec list[] =
{
{ kEventClassWindow, kEventWindowCollapsing },
{ kEventClassWindow, kEventWindowShown },
{ kEventClassWindow, kEventWindowActivated },
{ kEventClassWindow, kEventWindowDeactivated },
{ kEventClassWindow, kEventWindowClose },
{ kEventClassWindow, kEventWindowBoundsChanged },
{ kEventClassWindow, kEventWindowCollapsed },
{ kEventClassWindow, kEventWindowExpanded },
{ kEventClassWindow, kEventWindowZoomed },
{ kEventClassWindow, kEventWindowClosed },
};
SetWRefCon (_osx_window, (long) this); // point to the window record in the ref con of the window
gWinEvtHandler = NewEventHandlerUPP(windowEvtHndlr);
InstallWindowEventHandler(_osx_window, gWinEvtHandler, GetEventTypeCount(list), list, (void*)this, NULL); // add event handler
if(!req_properties.has_parent_window())
{
ShowWindow (_osx_window);
}
else
{
NSWindow* parentWindow = (NSWindow *)req_properties.get_parent_window();
// NSView* aView = [[parentWindow contentView] viewWithTag:378];
// NSRect aRect = [aView frame];
// NSPoint origin = [parentWindow convertBaseToScreen:aRect.origin];
// NSWindow* childWindow = [[NSWindow alloc] initWithWindowRef:_osx_window];
Post_Event_Wiait(PANDA_CREATE_WINDOW,(unsigned long) _osx_window,1,[parentWindow windowNumber]);
// [childWindow setFrameOrigin:origin];
// [childWindow setAcceptsMouseMovedEvents:YES];
// [childWindow setBackgroundColor:[NSColor blackColor]];
// this seems to block till the parent accepts the connection ?
// [parentWindow addChildWindow:childWindow ordered:NSWindowAbove];
// [childWindow orderFront:nil];
_properties.set_parent_window(req_properties.get_parent_window());
req_properties.clear_parent_window();
}
if (req_properties.has_fullscreen())
{
_properties.set_fullscreen(false);
req_properties.clear_fullscreen();
}
if (req_properties.has_undecorated())
{
_properties.set_undecorated(req_properties.get_undecorated());
req_properties.clear_undecorated();
}
}
// Now measure the size and placement of the window we
// actually ended up with.
Rect rectPort = {0,0,0,0};
GetWindowPortBounds (_osx_window, &rectPort);
_properties.set_size((int)(rectPort.right - rectPort.left),(int) (rectPort.bottom - rectPort.top));
req_properties.clear_size();
req_properties.clear_origin();
}
if (req_properties.has_icon_filename())
set_icon_filename(req_properties.get_icon_filename());
_properties.set_foreground(true);
_properties.set_minimized(false);
_properties.set_open(true);
if (_properties.has_size())
set_size_and_recalc(_properties.get_x_size(), _properties.get_y_size());
return (err == noErr);
}
////////////////////////////////////////////////////////////////////
// Function: TinyOsxGraphicsWindow::process_events()
// Access: virtual, protected
// Description: Required Event upcall . Used to dispatch Window and Aplication Events
// back into panda
//
////////////////////////////////////////////////////////////////////
void TinyOsxGraphicsWindow::process_events()
{
GraphicsWindow::process_events();
if (!osx_disable_event_loop)
{
EventRef theEvent;
EventTargetRef theTarget = GetEventDispatcherTarget();
if (!_properties.has_parent_window())
{
while (ReceiveNextEvent(0, NULL, kEventDurationNoWait, true, &theEvent)== noErr)
{
SendEventToEventTarget (theEvent, theTarget);
ReleaseEvent(theEvent);
}
}
}
};
////////////////////////////////////////////////////////////////////
// Function: TinyOsxGraphicsWindow::supports_pixel_zoom
// Access: Published, Virtual
// Description: Returns true if a call to set_pixel_zoom() will be
// respected, false if it will be ignored. If this
// returns false, then get_pixel_factor() will always
// return 1.0, regardless of what value you specify for
// set_pixel_zoom().
//
// This may return false if the underlying renderer
// doesn't support pixel zooming, or if you have called
// this on a DisplayRegion that doesn't have both
// set_clear_color() and set_clear_depth() enabled.
////////////////////////////////////////////////////////////////////
bool TinyOsxGraphicsWindow::
supports_pixel_zoom() const {
return true;
}
////////////////////////////////////////////////////////////////////
// Function: TinyOsxGraphicsWindow::handleKeyInput()
// Access: virtual, protected
// Description: Required Event upcall . Used to dispatch Window and Aplication Events
// back into panda
//
////////////////////////////////////////////////////////////////////
// key input handler
OSStatus TinyOsxGraphicsWindow::handleKeyInput (EventHandlerCallRef myHandler, EventRef event, Boolean keyDown) {
if (tinydisplay_cat.is_debug()) {
UInt32 keyCode;
GetEventParameter (event, kEventParamKeyCode, typeUInt32, NULL, sizeof(UInt32), NULL, &keyCode);
tinydisplay_cat.debug()
<< ClockObject::get_global_clock()->get_real_time()
<< " handleKeyInput: " << (void *)this << ", " << keyCode
<< ", " << (int)keyDown << "\n";
}
//CallNextEventHandler(myHandler, event);
// We don't check the result of the above function. In principle,
// this should return eventNotHandledErr if the key event is not
// handled by the OS, but in practice, testing this just seems to
// eat the Escape keypress meaninglessly. Keypresses like F11 that
// are already mapped in the desktop seem to not even come into this
// function in the first place.
UInt32 newModifiers = 0;
OSStatus error = GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &newModifiers);
if(error == noErr)
HandleModifireDeleta(newModifiers);
UInt32 keyCode;
GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL, sizeof(UInt32), NULL, &keyCode);
ButtonHandle button = OSX_TranslateKey(keyCode, event);
if (keyDown)
{
if ((newModifiers & cmdKey) != 0)
{
if (button == KeyboardButton::ascii_key("q") || button == KeyboardButton::ascii_key("w"))
{
// Command-Q or Command-W: quit the application or close the
// window, respectively. For now, we treat them both the
// same: close the window.
user_close_request();
}
}
SendKeyEvent(button, true);
}
else
{
SendKeyEvent(button, false);
}
return CallNextEventHandler(myHandler, event);
// return noErr;
}
////////////////////////////////////////////////////////////////////
// Function:
// Access:
// Description:
////////////////////////////////////////////////////////////////////
void TinyOsxGraphicsWindow::SystemSetWindowForground(bool forground)
{
WindowProperties properties;
properties.set_foreground(forground);
system_changed_properties(properties);
}
////////////////////////////////////////////////////////////////////
// Function:
// Access:
// Description:
////////////////////////////////////////////////////////////////////
void TinyOsxGraphicsWindow::SystemPointToLocalPoint(Point &qdGlobalPoint)
{
if(_osx_window != NULL)
{
GrafPtr savePort;
Boolean portChanged = QDSwapPort(GetWindowPort(_osx_window), &savePort);
GlobalToLocal(&qdGlobalPoint);
if (portChanged)
QDSwapPort(savePort, NULL);
}
}
////////////////////////////////////////////////////////////////////
// Function:
// Access:
// Description:
////////////////////////////////////////////////////////////////////
OSStatus TinyOsxGraphicsWindow::handleWindowMouseEvents (EventHandlerCallRef myHandler, EventRef event)
{
WindowRef window = NULL;
OSStatus result = eventNotHandledErr;
UInt32 kind = GetEventKind (event);
EventMouseButton button = 0;
Point qdGlobalPoint = {0, 0};
UInt32 modifiers = 0;
Rect rectPort;
SInt32 this_wheel_delta;
EventMouseWheelAxis wheelAxis;
// cerr <<" Start Mouse Event " << _ID << "\n";
// Mac OS X v10.1 and later
// should this be front window???
GetEventParameter(event, kEventParamWindowRef, typeWindowRef, NULL, sizeof(WindowRef), NULL, &window);
if(!_is_fullscreen && (window == NULL || window != _osx_window )) {
if (kind == kEventMouseMoved)
{
set_pointer_out_of_window();
}
return eventNotHandledErr;
}
GetWindowPortBounds (window, &rectPort);
// result = CallNextEventHandler(myHandler, event);
// if (eventNotHandledErr == result)
{ // only handle events not already handled (prevents wierd resize interaction)
switch (kind) {
// Whenever mouse button state changes, generate the
// appropriate Panda down/up events to represent the
// change.
case kEventMouseDown:
case kEventMouseUp:
{
GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers);
if (_properties.get_mouse_mode() == WindowProperties::M_relative)
{
GetEventParameter(event, kEventParamMouseDelta,typeQDPoint, NULL, sizeof(Point),NULL , (void*) &qdGlobalPoint);
MouseData currMouse = get_pointer(0);
qdGlobalPoint.h += currMouse.get_x();
qdGlobalPoint.v += currMouse.get_y();
}
else
{
GetEventParameter(event, kEventParamMouseLocation,typeQDPoint, NULL, sizeof(Point),NULL , (void*) &qdGlobalPoint);
SystemPointToLocalPoint(qdGlobalPoint);
}
set_pointer_in_window((int)qdGlobalPoint.h, (int)qdGlobalPoint.v);
UInt32 new_buttons = GetCurrentEventButtonState();
HandleButtonDelta(new_buttons);
}
result = noErr;
break;
case kEventMouseMoved:
case kEventMouseDragged:
if(_properties.get_mouse_mode()==WindowProperties::M_relative)
{
GetEventParameter(event, kEventParamMouseDelta,typeQDPoint, NULL, sizeof(Point),NULL , (void*) &qdGlobalPoint);
MouseData currMouse=get_pointer(0);
qdGlobalPoint.h+=currMouse.get_x();
qdGlobalPoint.v+=currMouse.get_y();
}
else
{
GetEventParameter(event, kEventParamMouseLocation,typeQDPoint, NULL, sizeof(Point),NULL , (void*) &qdGlobalPoint);
SystemPointToLocalPoint(qdGlobalPoint);
}
if (kind == kEventMouseMoved &&
(qdGlobalPoint.h < 0 || qdGlobalPoint.v < 0)) {
// Moving into the titlebar region.
set_pointer_out_of_window();
} else {
// Moving within the window itself (or dragging anywhere).
set_pointer_in_window((int)qdGlobalPoint.h, (int)qdGlobalPoint.v);
}
result = noErr;
break;
case kEventMouseWheelMoved:
GetEventParameter(event, kEventParamMouseWheelDelta, typeLongInteger, NULL, sizeof(this_wheel_delta), NULL, &this_wheel_delta);
GetEventParameter(event, kEventParamMouseWheelAxis, typeMouseWheelAxis, NULL, sizeof(wheelAxis), NULL, &wheelAxis );
GetEventParameter(event, kEventParamMouseLocation,typeQDPoint, NULL, sizeof(Point),NULL , (void*) &qdGlobalPoint);
SystemPointToLocalPoint(qdGlobalPoint);
if (wheelAxis == kEventMouseWheelAxisY)
{
set_pointer_in_window((int)qdGlobalPoint.h, (int)qdGlobalPoint.v);
_wheel_delta += this_wheel_delta;
SInt32 wheel_scale = osx_mouse_wheel_scale;
while (_wheel_delta > wheel_scale) {
_input_devices[0].button_down(MouseButton::wheel_up());
_input_devices[0].button_up(MouseButton::wheel_up());
_wheel_delta -= wheel_scale;
}
while (_wheel_delta < -wheel_scale) {
_input_devices[0].button_down(MouseButton::wheel_down());
_input_devices[0].button_up(MouseButton::wheel_down());
_wheel_delta += wheel_scale;
}
}
result = noErr;
break;
}
// result = noErr;
}
return result;
}
////////////////////////////////////////////////////////////////////
// Function: TinyOsxGraphicsWindow::OSX_TranslateKey
// Access: Private
// Description: MAC Key Codes to Panda Key Codes
////////////////////////////////////////////////////////////////////
ButtonHandle TinyOsxGraphicsWindow::OSX_TranslateKey(UInt32 key, EventRef event)
{
ButtonHandle nk = ButtonHandle::none();
switch ( key )
{
case 0: nk = KeyboardButton::ascii_key('a'); break;
case 11: nk = KeyboardButton::ascii_key('b'); break;
case 8: nk = KeyboardButton::ascii_key('c'); break;
case 2: nk = KeyboardButton::ascii_key('d'); break;
case 14: nk = KeyboardButton::ascii_key('e'); break;
case 3: nk = KeyboardButton::ascii_key('f'); break;
case 5: nk = KeyboardButton::ascii_key('g'); break;
case 4: nk = KeyboardButton::ascii_key('h'); break;
case 34: nk = KeyboardButton::ascii_key('i'); break;
case 38: nk = KeyboardButton::ascii_key('j'); break;
case 40: nk = KeyboardButton::ascii_key('k'); break;
case 37: nk = KeyboardButton::ascii_key('l'); break;
case 46: nk = KeyboardButton::ascii_key('m'); break;
case 45: nk = KeyboardButton::ascii_key('n'); break;
case 31: nk = KeyboardButton::ascii_key('o'); break;
case 35: nk = KeyboardButton::ascii_key('p'); break;
case 12: nk = KeyboardButton::ascii_key('q'); break;
case 15: nk = KeyboardButton::ascii_key('r'); break;
case 1: nk = KeyboardButton::ascii_key('s'); break;
case 17: nk = KeyboardButton::ascii_key('t'); break;
case 32: nk = KeyboardButton::ascii_key('u'); break;
case 9: nk = KeyboardButton::ascii_key('v'); break;
case 13: nk = KeyboardButton::ascii_key('w'); break;
case 7: nk = KeyboardButton::ascii_key('x'); break;
case 16: nk = KeyboardButton::ascii_key('y'); break;
case 6: nk = KeyboardButton::ascii_key('z'); break;
// top row numbers
case 29: nk = KeyboardButton::ascii_key('0'); break;
case 18: nk = KeyboardButton::ascii_key('1'); break;
case 19: nk = KeyboardButton::ascii_key('2'); break;
case 20: nk = KeyboardButton::ascii_key('3'); break;
case 21: nk = KeyboardButton::ascii_key('4'); break;
case 23: nk = KeyboardButton::ascii_key('5'); break;
case 22: nk = KeyboardButton::ascii_key('6'); break;
case 26: nk = KeyboardButton::ascii_key('7'); break;
case 28: nk = KeyboardButton::ascii_key('8'); break;
case 25: nk = KeyboardButton::ascii_key('9'); break;
// key pad ... do they really map to the top number in panda ?
case 82: nk = KeyboardButton::ascii_key('0'); break;
case 83: nk = KeyboardButton::ascii_key('1'); break;
case 84: nk = KeyboardButton::ascii_key('2'); break;
case 85: nk = KeyboardButton::ascii_key('3'); break;
case 86: nk = KeyboardButton::ascii_key('4'); break;
case 87: nk = KeyboardButton::ascii_key('5'); break;
case 88: nk = KeyboardButton::ascii_key('6'); break;
case 89: nk = KeyboardButton::ascii_key('7'); break;
case 91: nk = KeyboardButton::ascii_key('8'); break;
case 92: nk = KeyboardButton::ascii_key('9'); break;
// case 36: nk = KeyboardButton::ret(); break; // no return in panda ???
case 49: nk = KeyboardButton::space(); break;
case 51: nk = KeyboardButton::backspace(); break;
case 48: nk = KeyboardButton::tab(); break;
case 53: nk = KeyboardButton::escape(); break;
case 76: nk = KeyboardButton::enter(); break;
case 36: nk = KeyboardButton::enter(); break;
case 123: nk = KeyboardButton::left(); break;
case 124: nk = KeyboardButton::right(); break;
case 125: nk = KeyboardButton::down(); break;
case 126: nk = KeyboardButton::up(); break;
case 116: nk = KeyboardButton::page_up(); break;
case 121: nk = KeyboardButton::page_down(); break;
case 115: nk = KeyboardButton::home(); break;
case 119: nk = KeyboardButton::end(); break;
case 114: nk = KeyboardButton::help(); break;
case 117: nk = KeyboardButton::del(); break;
// case 71: nk = KeyboardButton::num_lock() break;
case 122: nk = KeyboardButton::f1(); break;
case 120: nk = KeyboardButton::f2(); break;
case 99: nk = KeyboardButton::f3(); break;
case 118: nk = KeyboardButton::f4(); break;
case 96: nk = KeyboardButton::f5(); break;
case 97: nk = KeyboardButton::f6(); break;
case 98: nk = KeyboardButton::f7(); break;
case 100: nk = KeyboardButton::f8(); break;
case 101: nk = KeyboardButton::f9(); break;
case 109: nk = KeyboardButton::f10(); break;
case 103: nk = KeyboardButton::f11(); break;
case 111: nk = KeyboardButton::f12(); break;
case 105: nk = KeyboardButton::f13(); break;
case 107: nk = KeyboardButton::f14(); break;
case 113: nk = KeyboardButton::f15(); break;
case 106: nk = KeyboardButton::f16(); break;
// shiftable chartablet
case 50: nk = KeyboardButton::ascii_key('`'); break;
case 27: nk = KeyboardButton::ascii_key('-'); break;
case 24: nk = KeyboardButton::ascii_key('='); break;
case 33: nk = KeyboardButton::ascii_key('['); break;
case 30: nk = KeyboardButton::ascii_key(']'); break;
case 42: nk = KeyboardButton::ascii_key('\\'); break;
case 41: nk = KeyboardButton::ascii_key(';'); break;
case 39: nk = KeyboardButton::ascii_key('\''); break;
case 43: nk = KeyboardButton::ascii_key(','); break;
case 47: nk = KeyboardButton::ascii_key('.'); break;
case 44: nk = KeyboardButton::ascii_key('/'); break;
default:
if (tinydisplay_cat.is_debug()) {
tinydisplay_cat.debug()
<< " Untranslated KeyCode: " << key
<< " (0x" << hex << key << dec << ")\n";
}
// not sure this is right .. but no mapping for keypad and such
// this at least does a best gess..
char charCode = 0;
if(GetEventParameter( event, kEventParamKeyMacCharCodes, typeChar, nil, sizeof( charCode ), nil, &charCode ) == noErr)
nk = KeyboardButton::ascii_key(charCode);
}
return nk;
}
////////////////////////////////////////////////////////////////////
// Function: TinyOsxGraphicsWindow::HandleModifireDeleta
// Access: Private
// Description: Used to emulate key events for the MAC key Modifiers..
////////////////////////////////////////////////////////////////////
void TinyOsxGraphicsWindow::HandleModifireDeleta(UInt32 newModifiers)
{
UInt32 changed = _last_key_modifiers ^ newModifiers;
if ((changed & (shiftKey | rightShiftKey)) != 0)
SendKeyEvent(KeyboardButton::shift(),(newModifiers & (shiftKey | rightShiftKey)) != 0) ;
if ((changed & (optionKey | rightOptionKey)) != 0)
SendKeyEvent(KeyboardButton::alt(),(newModifiers & (optionKey | rightOptionKey)) != 0);
if ((changed & (controlKey | rightControlKey)) != 0)
SendKeyEvent(KeyboardButton::control(),(newModifiers & (controlKey | rightControlKey)) != 0);
if ((changed & cmdKey) != 0)
SendKeyEvent(KeyboardButton::meta(),(newModifiers & cmdKey) != 0);
if ((changed & alphaLock) != 0)
SendKeyEvent(KeyboardButton::caps_lock(),(newModifiers & alphaLock) != 0);
// save current state
_last_key_modifiers = newModifiers;
};
////////////////////////////////////////////////////////////////////
// Function: TinyOsxGraphicsWindow::HandleButtonDelta
// Access: Private
// Description: Used to emulate buttons events/
////////////////////////////////////////////////////////////////////
void TinyOsxGraphicsWindow::
HandleButtonDelta(UInt32 new_buttons) {
UInt32 changed = _last_buttons ^ new_buttons;
if (changed & 0x01) {
if (new_buttons & 0x01) {
_input_devices[0].button_down(MouseButton::one());
} else {
_input_devices[0].button_up(MouseButton::one());
}
}
if (changed & 0x04) {
if (new_buttons & 0x04) {
_input_devices[0].button_down(MouseButton::two());
} else {
_input_devices[0].button_up(MouseButton::two());
}
}
if (changed & 0x02) {
if (new_buttons & 0x02) {
_input_devices[0].button_down(MouseButton::three());
} else {
_input_devices[0].button_up(MouseButton::three());
}
}
_last_buttons = new_buttons;
}
////////////////////////////////////////////////////////////////////
// Function: TinyOsxGraphicsWindow::move_pointer
// Access: Published, Virtual
// Description: Forces the pointer to the indicated position within
// the window, if possible.
//
// Returns true if successful, false on failure. This
// may fail if the mouse is not currently within the
// window, or if the API doesn't support this operation.
////////////////////////////////////////////////////////////////////
bool TinyOsxGraphicsWindow::move_pointer(int device, int x, int y)
{
if(_osx_window == NULL)
return false;
if (tinydisplay_cat.is_debug())
tinydisplay_cat.debug() << "move_pointer " << device <<" "<< x <<" "<< y <<"\n";
Point pt = {0, 0};
pt.h = x;
pt.v = y;
set_pointer_in_window(x, y);
if(_properties.get_mouse_mode()==WindowProperties::M_absolute)
{
LocalPointToSystemPoint(pt);
CGPoint newCursorPosition = {0, 0};
newCursorPosition.x = pt.h;
newCursorPosition.y = pt.v;
mouse_mode_relative();
CGWarpMouseCursorPosition(newCursorPosition);
mouse_mode_absolute();
}
return true;
};
bool TinyOsxGraphicsWindow::do_reshape_request(int x_origin, int y_origin, bool has_origin,int x_size, int y_size)
{
tinydisplay_cat.info() << "Do Reshape\n";
if (_properties.get_fullscreen()) {
return false;
}
if (_properties.has_parent_window())
{
if (has_origin)
{
NSWindow* parentWindow = (NSWindow *)_properties.get_parent_window();
NSRect parentFrame = [parentWindow frame];
MoveWindow(_osx_window, x_origin+parentFrame.origin.x, y_origin+parentFrame.origin.y, false);
}
}
else
{
// We sometimes get a bogus origin of (0, 0). As a special hack,
// treat this as a special case, and ignore it.
if (has_origin) {
if (x_origin != 0 || y_origin != 0) {
MoveWindow(_osx_window, x_origin, y_origin, false);
}
}
}
if (!_properties.get_undecorated())
{
// Constrain the window to the available desktop size.
Rect bounds;
GetAvailableWindowPositioningBounds(GetMainDevice(), &bounds);
x_size = min(x_size, bounds.right - bounds.left);
y_size = min(y_size, bounds.bottom - bounds.top);
}
SizeWindow(_osx_window, x_size, y_size, false);
system_changed_size(x_size, y_size);
ZB_resize(_frame_buffer, NULL, _properties.get_x_size(), _properties.get_y_size());
return true;
}
////////////////////////////////////////////////////////////////////
// Function: TinyOsxGraphicsWindow::set_properties_now
// Access: Public, Virtual
// Description: Applies the requested set of properties to the
// window, if possible, for instance to request a change
// in size or minimization status.
//
// The window properties are applied immediately, rather
// than waiting until the next frame. This implies that
// this method may *only* be called from within the
// window thread.
//
// The properties that have been applied are cleared
// from the structure by this function; so on return,
// whatever remains in the properties structure are
// those that were unchanged for some reason (probably
// because the underlying interface does not support
// changing that property on an open window).
////////////////////////////////////////////////////////////////////
void TinyOsxGraphicsWindow::set_properties_now(WindowProperties &properties)
{
if (tinydisplay_cat.is_debug())
{
tinydisplay_cat.debug()
<< "------------------------------------------------------\n";
tinydisplay_cat.debug()
<< "set_properties_now " << properties << "\n";
}
GraphicsWindow::set_properties_now(properties);
if (tinydisplay_cat.is_debug()) {
tinydisplay_cat.debug()
<< "set_properties_now After Base Class" << properties << "\n";
}
// for some changes .. a full rebuild is required for the OS layer Window.
// I think it is the crome atribute and full screen behaviour.
bool need_full_rebuild = false;
// if we are not full and transitioning to full
if (properties.has_fullscreen() &&
properties.get_fullscreen() != _properties.get_fullscreen()) {
need_full_rebuild = true;
}
// If we are fullscreen and requesting a size change
if (_properties.get_fullscreen() &&
(properties.has_size() &&
(properties.get_x_size() != _properties.get_x_size() ||
properties.get_y_size() != _properties.get_y_size()))) {
need_full_rebuild = true;
}
if (need_full_rebuild) {
// Logic here is .. take a union of the properties .. with the
// new allowed to overwrite the old states. and start a bootstrap
// of a new window ..
// get a copy of my properties..
WindowProperties req_properties(_properties);
ReleaseSystemResources();
req_properties.add_properties(properties);
OSOpenWindow(req_properties);
// Now we've handled all of the requested properties.
properties.clear();
}
if (properties.has_title()) {
_properties.set_title(properties.get_title());
if (_osx_window) {
SetWindowTitleWithCFString(_osx_window,
CFStringCreateWithCString(NULL,properties.get_title().c_str(),
kCFStringEncodingMacRoman));
}
properties.clear_title();
}
// An icon filename means to load up the icon and save it. We can't
// necessarily apply it immediately; it will get applied later, in
// the window event handler.
if (properties.has_icon_filename()) {
if (set_icon_filename(properties.get_icon_filename())) {
properties.clear_icon_filename();
}
}
// decorated .. if this changes it reqires a new window
if (properties.has_undecorated()) {
_properties.set_undecorated(properties.get_undecorated());
properties.clear_undecorated();
}
if (properties.has_cursor_hidden()) {
_properties.set_cursor_hidden(properties.get_cursor_hidden());
_cursor_hidden = properties.get_cursor_hidden();
if (_cursor_hidden && _input_devices[0].has_pointer()) {
if (!_display_hide_cursor) {
CGDisplayHideCursor(kCGDirectMainDisplay);
_display_hide_cursor = true;
}
} else {
if (_display_hide_cursor) {
CGDisplayShowCursor(kCGDirectMainDisplay);
_display_hide_cursor = false;
}
}
properties.clear_cursor_hidden();
}
if (tinydisplay_cat.is_debug()) {
tinydisplay_cat.debug()
<< "set_properties_now Out....." << _properties << "\n";
}
return;
}
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
void TinyOsxGraphicsWindow::LocalPointToSystemPoint(Point &qdLocalPoint)
{
if(_osx_window != NULL)
{
GrafPtr savePort;
Boolean portChanged = QDSwapPort(GetWindowPort(_osx_window), &savePort);
LocalToGlobal( &qdLocalPoint );
if (portChanged)
QDSwapPort(savePort, NULL);
}
}
////////////////////////////////////////////////////////////////////
// Function: TinyOsxGraphicsWindow::mouse_mode_relative
// Access: Protected, Virtual
// Description: detaches mouse. Only mouse delta from now on.
//
////////////////////////////////////////////////////////////////////
void TinyOsxGraphicsWindow::mouse_mode_relative()
{
CGAssociateMouseAndMouseCursorPosition(false);
}
////////////////////////////////////////////////////////////////////
// Function: TinyOsxGraphicsWindow::mouse_mode_absolute
// Access: Protected, Virtual
// Description: reattaches mouse to location
//
////////////////////////////////////////////////////////////////////
void TinyOsxGraphicsWindow::mouse_mode_absolute()
{
CGAssociateMouseAndMouseCursorPosition(true);
}
////////////////////////////////////////////////////////////////////
// Function: TinyOsxGraphicsWindow::create_frame_buffer
// Access: Private
// Description: Creates a suitable frame buffer for the current
// window size.
////////////////////////////////////////////////////////////////////
void TinyOsxGraphicsWindow::
create_frame_buffer() {
if (_frame_buffer != NULL) {
ZB_close(_frame_buffer);
_frame_buffer = NULL;
}
_frame_buffer = ZB_open(_properties.get_x_size(), _properties.get_y_size(), ZB_MODE_RGBA, 0, 0, 0, 0);
}