// C++ Headers
#include <cstdio>
#include <map>
// Windows Headers
#ifdef _WIN32
#define Rectangle WinRectangle
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
#include <windows.h>
#undef Rectangle
#endif
// Project Headers
#include "Window.h"
#include "MemoryMapping.h"
//#define GL_EXPERIMENTS
#ifdef GL_EXPERIMENTS
#include "GL/ImmediateMode.h"
#include "GL/Program.h"
#endif
#if _WIN32
BEGIN_NAMESPACE
int g_windowCount = 0;
Map<HWND,Window*> g_windowMap;
//static const wchar_t* g_windowClass = L"WindowClass";
struct BackBuffer
{
HBITMAP m_hbitmap;
PixelBuffer m_target;
};
struct WindowData
{
HWND m_hWnd;
HDC m_handle;
HGLRC m_glContext;
BackBuffer m_backBuffer;
Point m_mousePos;
MouseButtons m_mouseButtons;
Size m_size;
bool m_active;
int m_nextTimerId;
};
static void InitializeBackBuffer(BackBuffer* a_backBuffer, int a_w, int a_h, int a_depth)
{
#ifdef GL_EXPERIMENTS
int newW = nextPowerOfTwo(a_w); // Needed for textures with OpenGL
int newH = nextPowerOfTwo(a_h); // Needed for textures with OpenGL
#else
int newW = a_w;
int newH = a_h;
#endif
BITMAPINFO info = { { sizeof(BITMAPINFOHEADER), newW, -newH, 1, WORD(a_depth), BI_RGB, 0 } };
a_backBuffer->m_target.m_width = a_w;
a_backBuffer->m_target.m_strideBytes = newW * sizeof(int);
a_backBuffer->m_target.m_height = a_h;
a_backBuffer->m_target.m_format = (PixelFormat)a_depth;
a_backBuffer->m_hbitmap = CreateDIBSection(0, &info, DIB_RGB_COLORS, (void**)&(a_backBuffer->m_target.m_pixels), 0, 0);
}
static void DestroyBackBuffer(BackBuffer* a_backBuffer)
{
DeleteObject(a_backBuffer->m_target.m_pixels);
DeleteObject(a_backBuffer->m_hbitmap);
}
#ifdef GL_EXPERIMENTS
void Window::initialize(HWND a_hwnd)
{
AppInit(a_hwnd, targetBuffer());
m_data->m_glContext = wglGetCurrentContext();
}
void Window::paintGL(HWND a_hwnd)
{
/*
Rectangle rectangle = { {0, 0}, {window->width(), window->height()} };
Event ev;
ev.m_type = Event::ET_PaintEvent;
ev.m_paintEvent.m_rectangle = rectangle;
window->event(ev);
*/
//wglMakeCurrent(glhdc, m_data->m_glContext);
AppPaint(targetBuffer());
//wglMakeCurrent(NULL, NULL);
}
void Window::destroy(HWND a_hwnd)
{
AppDestroy(a_hwnd);
}
#else
void Window::initialize(HWND a_hwnd)
{
}
void Window::paintGL(HWND a_hwnd)
{
paintUpdate();
}
void Window::destroy(HWND a_hwnd)
{
}
void AppContruct()
{
}
void AppMouseUpdate(int a_x, int a_y, int a_buttonMask, MouseButtons a_mouseButton, MouseState a_mouseState)
{
}
void AppWheelUpdate(int)
{
}
void glDrawText(int x, int y, const char* text, uint32_t a_color, uint32_t a_dropColor, int32_t a_fontSize, bool a_invertColors)
{
}
#endif
#define WINDOW_TRACE(...)
LRESULT CALLBACK MainWndProc(
HWND hwnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam) // second message parameter
{
Window* window = g_windowMap[hwnd];
if (!window)
{
if (uMsg == WM_CREATE && lParam)
{
WINDOW_TRACE("WM_CREATE (during CreateWindow)");
window = (Window*)((CREATESTRUCT*)lParam)->lpCreateParams;
window->initialize(hwnd);
}
//if (!window)
{
// When new windows are added, these messages are seen
// straight away inside the the call to CreateWindowW():
// WM_GETMINMAXINFO // 36
// WM_NCCREATE // 129
// WM_NCCALCSIZE // 131
// WM_CREATE // 1
//printf("window not found!\n");
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
//printf("window found!\n");
int mouseCase = 0;
bool keyState = false;
MouseButtons mbLUT[] = { MB_None, MB_Left, MB_Middle, MB_Right };
SizeOptions sizeOpts;
switch (uMsg)
{
/*
case WM_ACTIVATE:
if (wParam == WA_INACTIVE)
ReleaseCapture();
else
SetCapture(hwnd);
return 1;
*/
case WM_TIMER:
WINDOW_TRACE("WM_TIMER");
window->timerUpdate((TimerId)wParam);
return 0;
case WM_CREATE:
WINDOW_TRACE("WM_CREATE");
// Initialize the window.
window->initialize(hwnd);
return 0;
case WM_PAINT:
WINDOW_TRACE("WM_PAINT");
// Paint the window's client area.
window->paintGL(hwnd);
WINDOW_TRACE("WM_PAINT end");
return 0;
case WM_SIZING:
WINDOW_TRACE("WM_SIZING");
window->sizeOptions(sizeOpts);
{
int constrainWidth = 0, constrainHeight = 0;
if (((RECT *)lParam)->right - ((RECT *)lParam)->left > sizeOpts.m_maximum.m_width)
constrainWidth = sizeOpts.m_maximum.m_width;
if (((RECT *)lParam)->right - ((RECT *)lParam)->left < sizeOpts.m_minimum.m_width)
constrainWidth = sizeOpts.m_minimum.m_width;
if (((RECT *)lParam)->bottom - ((RECT *)lParam)->top > sizeOpts.m_maximum.m_height)
constrainHeight = sizeOpts.m_maximum.m_height;
if (((RECT *)lParam)->bottom - ((RECT *)lParam)->top < sizeOpts.m_minimum.m_height)
constrainHeight = sizeOpts.m_minimum.m_height;
if (constrainWidth)
{
if (wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_LEFT || wParam == WMSZ_TOPLEFT)
((RECT *)lParam)->left = ((RECT *)lParam)->right - constrainWidth - 1;
else
((RECT *)lParam)->right = ((RECT *)lParam)->left + constrainWidth + 1;
}
if (constrainHeight)
{
if (wParam == WMSZ_TOPLEFT || wParam == WMSZ_TOP || wParam == WMSZ_TOPRIGHT)
((RECT *)lParam)->top = ((RECT *)lParam)->bottom - constrainHeight - 1;
else
((RECT *)lParam)->bottom = ((RECT *)lParam)->top + constrainHeight + 1;
}
}
return 0;
case WM_SIZE:
WINDOW_TRACE("WM_SIZE %i %i", LOWORD(lParam) + 1, HIWORD(lParam) + 1);
printf("WM_SIZE %i %i", LOWORD(lParam) + 1, HIWORD(lParam) + 1);
window->destroy(hwnd);
window->resize(LOWORD(lParam) + 1, HIWORD(lParam) + 1);
// Set the size and position of the window.
window->initialize(hwnd); // Recreate the GL context
return 0;
case WM_RBUTTONDBLCLK: mouseCase++;
case WM_RBUTTONDOWN: mouseCase++;
case WM_RBUTTONUP: mouseCase++;
case WM_MBUTTONDBLCLK: mouseCase++;
case WM_MBUTTONDOWN: mouseCase++;
case WM_MBUTTONUP: mouseCase++;
case WM_LBUTTONDBLCLK: mouseCase++;
case WM_LBUTTONDOWN: mouseCase++;
case WM_LBUTTONUP: mouseCase+=3;
case WM_MOUSEMOVE:
AppMouseUpdate(LOWORD(lParam), HIWORD(lParam), (int)wParam, mbLUT[mouseCase / 3], (MouseState)(mouseCase % 3));
window->mouseUpdate(LOWORD(lParam), HIWORD(lParam), (int)wParam, mbLUT[mouseCase / 3], (MouseState)(mouseCase % 3));
return 0;
case WM_MOUSEWHEEL:
AppWheelUpdate((short)HIWORD(wParam));
window->wheelUpdate((short)HIWORD(wParam));
window->paintGL(hwnd);
return 0;
case WM_KEYDOWN:
keyState = true;
case WM_KEYUP:
window->keyUpdate((int)wParam, keyState);
return 0;
case WM_DESTROY:
WINDOW_TRACE("WM_DESTROY");
window->deactivate();
// Clean up window-specific data objects.
window->destroy(hwnd);
if ( !g_windowCount )
PostQuitMessage(1);
return 0;
case WM_SETCURSOR:
SetCursor(LoadCursor(NULL, IDC_ARROW));
return 1;
//
// Process other messages.
//
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return -1;
}
Window::Window(const char* a_title, bool a_fullscreen)
: Widget(a_title, true)
{
AppContruct();
g_windowCount++;
m_data = new WindowData;
m_data->m_handle = NULL;
m_data->m_mouseButtons = MB_None;
m_data->m_mousePos.m_x = 0;
m_data->m_mousePos.m_y = 0;
m_data->m_nextTimerId = 1;
InitializeBackBuffer(&m_data->m_backBuffer, 1, 1, 32);
/*
if ( a_fullscreen )
{
//m_data->m_hWnd = CreateWindowEx( 0, L"STATIC", L"", WS_POPUP, 0, 0, m_data->w, m_data->h, NULL, NULL, m_data->hInstance, NULL);
}
else
*/
{
m_data->m_hWnd = CreateWindowW(L"MainWClass", String(a_title).toWString().c_str(), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT , NULL, NULL, GetModuleHandle(0), this);
}
g_windowMap[m_data->m_hWnd] = this;
//g_windowMap.insert(m_data->m_hWnd, this); // this doesn't work when using std::map, somehow different to above line
ShowWindow(m_data->m_hWnd, SW_SHOW);
UpdateWindow(m_data->m_hWnd);
m_data->m_active = true;
SetCursor(LoadCursor(NULL, IDC_ARROW));
}
Window::~Window()
{
if (m_data->m_active)
{
//DestroyWindow(m_data->m_hWnd);
deactivate();
}
delete m_data;
}
void Window::deactivate()
{
if (m_data->m_active)
{
m_data->m_active = false;
g_windowMap.erase(m_data->m_hWnd);
DestroyWindow(m_data->m_hWnd);
g_windowCount--;
DestroyBackBuffer(&m_data->m_backBuffer);
DeleteDC(m_data->m_handle);
}
}
void Window::sizeOptions(SizeOptions& a_sizeOptions)
{
Widget::sizeOptions(a_sizeOptions);
RECT wRect, cRect;
GetWindowRect(m_data->m_hWnd, &wRect);
GetClientRect(m_data->m_hWnd, &cRect);
int windowBorderHeight = wRect.bottom - cRect.bottom + cRect.top - wRect.top;
int windowBorderWidth = wRect.right - cRect.right + cRect.left - wRect.left;
a_sizeOptions.m_minimum.m_height += windowBorderHeight;
a_sizeOptions.m_preferred.m_height += windowBorderHeight;
a_sizeOptions.m_maximum.m_height += windowBorderHeight;
a_sizeOptions.m_minimum.m_width += windowBorderWidth;
a_sizeOptions.m_preferred.m_width += windowBorderWidth;
a_sizeOptions.m_maximum.m_width += windowBorderWidth;
}
TimerId Window::startTimer(int a_milliSeconds)
{
return (TimerId)SetTimer(m_data->m_hWnd, m_data->m_nextTimerId++, a_milliSeconds, 0);
}
void Window::killTimer(TimerId a_timerId)
{
KillTimer(m_data->m_hWnd, (UINT_PTR)a_timerId);
}
void Window::timerUpdate(TimerId a_timerId)
{
Event ev;
ev.m_type = Event::ET_TimerEvent;
ev.m_timerEvent.m_timerId = a_timerId;
event(ev);
}
void Window::mouseUpdate(int a_x, int a_y, int a_buttonMask, MouseButtons a_mouseButton, MouseState a_mouseState)
{
Event ev;
ev.m_type = Event::ET_MouseEvent;
ev.m_mouseEvent.m_oldPosition = m_data->m_mousePos;
ev.m_mouseEvent.m_oldButtons = m_data->m_mouseButtons;
/*
POINT mousePos;
GetCursorPos(&mousePos);
m_data->m_mouseX = mousePos.x;
m_data->m_mouseY = mousePos.y;
*/
m_data->m_mousePos.m_x = a_x;
m_data->m_mousePos.m_y = a_y;
if ( a_mouseState == MS_Up )
m_data->m_mouseButtons = MouseButtons((int)m_data->m_mouseButtons & ~(int)a_mouseButton);
else
m_data->m_mouseButtons |= a_mouseButton;
ev.m_mouseEvent.m_position = m_data->m_mousePos;
ev.m_mouseEvent.m_buttons = m_data->m_mouseButtons;
event(ev);
}
void Window::wheelUpdate(int a_wheelVal)
{
Event ev;
ev.m_type = Event::ET_WheelEvent;
ev.m_wheelEvent.m_degreesRotation = a_wheelVal;
event(ev);
}
void Window::keyUpdate(int a_keyCode, bool a_keyDown)
{
Event ev;
ev.m_type = Event::ET_KeyEvent;
ev.m_keyEvent.m_key = (Key)a_keyCode; // TODO
ev.m_keyEvent.m_state = (a_keyDown) ? KS_Pressed : KS_Released;
ev.m_keyEvent.m_modifiers = M_None; // TODO
event(ev);
}
void Window::resize(int a_newW, int a_newH)
{
Event ev;
ev.m_type = Event::ET_SizeEvent;
ev.m_sizeEvent.m_old = m_data->m_size;
RECT rect;
GetClientRect(m_data->m_hWnd, &rect);
m_data->m_size.m_width = rect.right - rect.left + 1;
m_data->m_size.m_height = rect.bottom - rect.top + 1;
if ( a_newW != m_data->m_size.m_width || a_newH != m_data->m_size.m_height )
printf("resizing to %i %i %i %i\n", a_newW, a_newH, m_data->m_size.m_width, m_data->m_size.m_height);
//if (m_data->m_backBuffer)
printf("destorying back buffer\n");
DestroyBackBuffer(&m_data->m_backBuffer);
printf("creating new back buffer\n");
InitializeBackBuffer(&m_data->m_backBuffer, m_data->m_size.m_width, m_data->m_size.m_height, 32);
if (!m_data->m_backBuffer.m_target.m_pixels)
{
printf("error making back buffer\n");
m_data->m_backBuffer.m_target.m_width = 0;
m_data->m_backBuffer.m_target.m_height = 0;
}
printf("created new back buffer\n");
//m_data->lastTick = clock();
HDC hdc = GetDC(m_data->m_hWnd);
DeleteDC(m_data->m_handle);
m_data->m_handle = CreateCompatibleDC(hdc);
SelectObject(m_data->m_handle, m_data->m_backBuffer.m_hbitmap);
ReleaseDC(m_data->m_hWnd, hdc);
//sizeEvent(a_newW, a_newH);
updateLayout(); // perhaps this could be let to cascade through the recursive SizeEvent processing?
ev.m_sizeEvent.m_new = m_data->m_size;
event(ev);
}
/*
// Game loop style update
void Window::doUpdate()
{
printf("updating\n");
SHORT space = GetAsyncKeyState(VK_SPACE);
SHORT left = GetAsyncKeyState(VK_LBUTTON);
SHORT right = GetAsyncKeyState(VK_RBUTTON);
SHORT middle = GetAsyncKeyState(VK_MBUTTON);
int keyState = ((space) ? 8 : 0) | ((left) ? 4 : 0) | ((right) ? 2 : 0) | ((middle) ? 1 : 0);
}
*/
void Window::paintUpdate()
{
RECT rect;
GetUpdateRect(m_data->m_hWnd, &rect, false);
Rectangle a_rectangle = { { { rect.left, rect.top } },
{ { rect.right - rect.left + 1, rect.bottom - rect.top + 1 } } };
/*
update(rectangle);
printf("paintUpdate end\n");
*/
Event ev;
ev.m_type = Event::ET_PaintEvent;
ev.m_paintEvent.m_rectangle = a_rectangle;
// TODO: when inside an update, clip the painting to the update rect.
// Clipping can be achieved by fiddling with the paintTarget
// This is clipping within our portion of the drawing code, not the win32 api part
// This sends out paintEvents
event(ev);
//paintEvent(&m_data->m_backBuffer.m_target, m_data->m_mouseX, m_data->m_mouseY, keyState);
// Using BeginPaint will make the drawing portion with the win32 APIs clipped
HDC hdc = GetDC(m_data->m_hWnd);
//StretchBlt(hdc, 0, 0, m_data->m_backBuffer.m_target.m_width, m_data->m_backBuffer.m_target.m_height, m_data->m_handle, 0, 0, 640, 480, SRCCOPY);
BitBlt(hdc, 0, 0, m_data->m_backBuffer.m_target.m_width, m_data->m_backBuffer.m_target.m_height, m_data->m_handle, 0, 0, SRCCOPY);
ReleaseDC(m_data->m_hWnd, hdc);
// Also using BeginPaint/EndPaint avoid the need to call ValidateRect
// but using BeginPaint/EndPaint and InvalidateRect may make the
// code end up less refactored
RECT r = { a_rectangle.m_x, a_rectangle.m_y,
a_rectangle.m_x + a_rectangle.m_width - 1, a_rectangle.m_y + a_rectangle.m_height - 1 };
ValidateRect(m_data->m_hWnd, &r);
}
void Window::update(Rectangle& a_rectangle)
{
RECT r = { a_rectangle.m_x, a_rectangle.m_y,
a_rectangle.m_x + a_rectangle.m_width - 1, a_rectangle.m_y + a_rectangle.m_height - 1 };
InvalidateRect(m_data->m_hWnd, &r, false);
/*
printf("update\n");
Event ev;
ev.m_type = Event::ET_PaintEvent;
ev.m_paintEvent.m_rectangle = a_rectangle;
// TODO: when inside an update, clip the painting to the update rect.
// Clipping can be achieved by fiddling with the paintTarget
// This is clipping within our portion of the drawing code, not the win32 api part
// This sends out paintEvents
printf("update2\n");
event(ev);
printf("update3\n");
//paintEvent(&m_data->m_backBuffer.m_target, m_data->m_mouseX, m_data->m_mouseY, keyState);
// Using BeginPaint will make the drawing portion with the win32 APIs clipped
HDC hdc = GetDC(m_data->m_hWnd);
//StretchBlt(hdc, 0, 0, m_data->m_backBuffer.m_target.m_width, m_data->m_backBuffer.m_target.m_height, m_data->m_handle, 0, 0, 640, 480, SRCCOPY);
BitBlt(hdc, 0, 0, m_data->m_backBuffer.m_target.m_width, m_data->m_backBuffer.m_target.m_height, m_data->m_handle, 0, 0, SRCCOPY);
ReleaseDC(m_data->m_hWnd, hdc);
// Also using BeginPaint/EndPaint avoid the need to call ValidateRect
// but using BeginPaint/EndPaint and InvalidateRect may make the
// code end up less refactored
printf("update4\n");
RECT r = { a_rectangle.m_x, a_rectangle.m_y,
a_rectangle.m_x + a_rectangle.m_width - 1, a_rectangle.m_y + a_rectangle.m_height - 1 };
ValidateRect(m_data->m_hWnd, &r);
*/
}
PixelBuffer& Window::targetBuffer()
{
return m_data->m_backBuffer.m_target;
}
END_NAMESPACE
#else
#define GL_SILENCE_DEPRECATION
#include "ObjectiveC.h"
#include <ApplicationServices/ApplicationServices.h>
#include <CoreVideo/CoreVideo.h>
#include <OpenGL/gl.h>
#include <pthread.h>
#include <unistd.h>
#ifndef _NDEBUG
//#define DEBUG_LOG printf
#define DEBUG_LOG(...)
#else
#define DEBUG_LOG(...)
#endif
extern id NSApp;
BEGIN_NAMESPACE
struct WindowData
{
ObjectiveC::Object m_nativeHandle = ObjectiveC::Object{(id)nullptr};
ObjectiveC::Object m_view = ObjectiveC::Object{(id)nullptr};
Point m_mousePos = (Point){ 0, 0 };
MouseButtons m_mouseButtons = MB_None;
Size m_size = (Size){ 1, 1 };
bool m_active = true;
TimerId m_nextTimerId = 1;
bool m_invalidated = false;
int m_texState = 0; // 0 - not-created, 1 - re-create, 2 - valid
GLuint m_texId = 0;
PixelBuffer m_backBuffer;
CVDisplayLinkRef m_displayLink; // display link for managing rendering thread
std::map<TimerId,id> m_activeTimers;
};
struct NSPoint { double x, y; };
struct NSSize { double width, height; };
struct NSRect { double x, y, width, height; };
struct NSEvent;
typedef uint32_t unichar;
/*
void WindowOnDraw(id self, SEL _cmd, NSRect rect);
void WindowOnResize(id self, SEL _cmd, NSSize newSize);
void WindowOnMouseEvent(id self, SEL _cmd, NSEvent* event);
void ProcessTimerEvent(id self, SEL _cmd, void* timerEvent);
*/
void PrintClassOfId(id self)
{
printf("class: %s\n", class_getName(object_getClass(self)));
}
void WindowOnReshape(id self, SEL _cmd)
{
ObjectiveC::AutoReleasePool autoReleasePool;
Window* winPtr = ObjectiveC::Object(self).get<Window*>("WindowPtr");
if (winPtr)
winPtr->reshape();
}
bool WindowAcceptsKeyInput(id self, SEL _cmd)
{
DEBUG_LOG("WindowAcceptsKeyInput called\n");
return true;
}
void WindowOnKeyInput(id self, SEL _cmd)
{
DEBUG_LOG("WindowOnKeyInput called\n");
}
void WindowOnDraw(id self, SEL _cmd, NSRect rect)
{
DEBUG_LOG("WindowOnDraw called\n");
ObjectiveC::AutoReleasePool autoReleasePool;
Window* winPtr = ObjectiveC::Object(self).get<Window*>("WindowPtr");
if (winPtr)
winPtr->paintUpdate();
}
void WindowOnResize(id self, SEL _cmd, NSSize newSize)
{
ObjectiveC::AutoReleasePool autoReleasePool;
Window* winPtr = ObjectiveC::Object(self).get<Window*>("WindowPtr");
if (winPtr)
winPtr->resize(int(newSize.width), int(newSize.height));
}
void WindowOnKeyEvent(id self, SEL _cmd, NSEvent* event)
{
ObjectiveC::AutoReleasePool autoReleasePool;
Window* winPtr = ObjectiveC::Object(self).get<Window*>("WindowPtr");
unichar ch = ObjectiveC::Object((id)event)["charactersIgnoringModifiers"]()["characterAtIndex:"].call<unichar>(0);
printf("got char: %i\n", ch);
if (winPtr)
{
winPtr->keyUpdate(ch, 1);// keyUpdate(int(pnt.x), h-int(pnt.y), 0, MB_Left, (buttons & 1) ? MS_Down : MS_Up);
winPtr->keyUpdate(ch, 0);// keyUpdate(int(pnt.x), h-int(pnt.y), 0, MB_Left, (buttons & 1) ? MS_Down : MS_Up);
}
}
void WindowOnMouseEvent(id self, SEL _cmd, NSEvent* event)
{
ObjectiveC::AutoReleasePool autoReleasePool;
Window* winPtr = ObjectiveC::Object(self).get<Window*>("WindowPtr");
NSPoint pnt = ObjectiveC::Object((id)event)["locationInWindow"].call<NSPoint>();
static unsigned lastButtons = 0;
unsigned buttons = ObjectiveC::Object(ObjectiveC::classCache("NSEvent"))["pressedMouseButtons"].call<unsigned>();
if (winPtr)
{
int h = winPtr->targetBuffer().m_height;
#if USE_RETINA
if (c_useRetina)
{
h /= 2;//c_retinaScale;
//h >>= 1;
}
#endif
if ((buttons & 1) || (lastButtons & 1))
{
winPtr->mouseUpdate(int(pnt.x), h-int(pnt.y), 0, MB_Left, (buttons & 1) ? MS_Down : MS_Up);
}
if ((buttons & 2) || (lastButtons & 2))
{
winPtr->mouseUpdate(int(pnt.x), h-int(pnt.y), 0, MB_Right, (buttons & 2) ? MS_Down : MS_Up);
}
if ( buttons == 0 )
{
winPtr->mouseUpdate(int(pnt.x), h-int(pnt.y), 0, MB_Left, MS_Up);
}
}
lastButtons = buttons;
}
extern bool s_started;
CVReturn OnCanRepaint(CVDisplayLinkRef displayLink,
const CVTimeStamp *inNow, const CVTimeStamp *inOutputTime,
CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *callbackData)
{
//if (!s_started)
// return 0;
DEBUG_LOG("OnCanRepaint called\n");
Window* winPtr = (Window*)callbackData;
winPtr->repaint();
//ObjectiveC::Object* winPtr = (ObjectiveC::Object*)callbackData;
//(*winPtr)["drawRect:"]();
return 0;
}
void UpdateMouseTrackingArea(id self, SEL _cmd)
{
ObjectiveC::AutoReleasePool autoReleasePool;
ObjectiveC::Object obj(self);
Window* winPtr = obj.get<Window*>("WindowPtr");
NSRect bounds{ 0, 0, (double)winPtr->width(), (double)winPtr->height() };
ObjectiveC::Object area = ObjectiveC::Object("NSTrackingArea")["alloc"]();
area["initWithRect:options:owner:userInfo:"](bounds, 0x82, self, nil);
obj["addTrackingArea:"](area.m_object);
}
void ProcessTimerEvent(id self, SEL _cmd, id timer)
{
ObjectiveC::AutoReleasePool autoReleasePool;
Window* winPtr = ObjectiveC::Object(self).get<Window*>("WindowPtr");
TimerId timerId = ObjectiveC::Object(timer)["userInfo"]().get<TimerId>("TimerId");
if (winPtr)
{
winPtr->timerUpdate(timerId);
}
}
// TODO: Need a nice way to wrap this
void RegisterWindowClass()
{
Class WindowClass;
WindowClass = objc_allocateClassPair((Class)objc_getClass("NSWindow"), "Window", 0);
class_addMethod(WindowClass, sel_getUid("canBecomeKeyWindow"), (IMP)WindowAcceptsKeyInput, "v@:");
class_addMethod(WindowClass, sel_getUid("canBecomeMainWindow"), (IMP)WindowAcceptsKeyInput, "v@:");
class_addMethod(WindowClass, sel_getUid("keyDown:"), (IMP)WindowOnKeyEvent, "v@:");
class_addIvar(WindowClass, "WindowPtr", sizeof(Window*), sizeof(Window*), "Window*");
objc_registerClassPair(WindowClass);
Class PanelClass;
PanelClass = objc_allocateClassPair((Class)objc_getClass("NSPanel"), "Panel", 0);
class_addMethod(PanelClass, sel_getUid("canBecomeKeyWindow"), (IMP)WindowAcceptsKeyInput, "v@:");
class_addMethod(PanelClass, sel_getUid("keyDown:"), (IMP)WindowOnKeyEvent, "v@:");
class_addIvar(PanelClass, "WindowPtr", sizeof(Window*), sizeof(Window*), "Window*");
objc_registerClassPair(PanelClass);
Class TimerClass;
TimerClass = objc_allocateClassPair((Class)objc_getClass("NSObject"), "Timer", 0);
class_addIvar(TimerClass, "TimerId", sizeof(TimerId), sizeof(TimerId), "TimerId");
objc_registerClassPair(TimerClass);
Class ViewClass;
#if 1 // USE_OPENGL
ViewClass = objc_allocateClassPair((Class)objc_getClass("NSOpenGLView"), "View", 0);
class_addMethod(ViewClass, sel_getUid("reshape:"), (IMP)WindowOnReshape, "v@:");
#else
ViewClass = objc_allocateClassPair((Class)objc_getClass("NSView"), "View", 0);
#endif
class_addMethod(ViewClass, sel_getUid("drawRect:"), (IMP)WindowOnDraw, "v@:");
class_addMethod(ViewClass, sel_getUid("setFrameSize:"), (IMP)WindowOnResize, "v@:");
class_addMethod(ViewClass, sel_getUid("keyDown:"), (IMP)WindowOnKeyEvent, "v@:");
class_addMethod(ViewClass, sel_getUid("mouseDown:"), (IMP)WindowOnMouseEvent, "v@:");
class_addMethod(ViewClass, sel_getUid("mouseDragged:"), (IMP)WindowOnMouseEvent, "v@:");
class_addMethod(ViewClass, sel_getUid("mouseUp:"), (IMP)WindowOnMouseEvent, "v@:");
class_addMethod(ViewClass, sel_getUid("mouseMoved:"), (IMP)WindowOnMouseEvent, "v@:");
class_addMethod(ViewClass, sel_getUid("rightMouseDown:"), (IMP)WindowOnMouseEvent, "v@:");
class_addMethod(ViewClass, sel_getUid("rightMouseDragged:"), (IMP)WindowOnMouseEvent, "v@:");
class_addMethod(ViewClass, sel_getUid("rightMouseUp:"), (IMP)WindowOnMouseEvent, "v@:");
class_addMethod(ViewClass, sel_getUid("rightMouseMoved:"), (IMP)WindowOnMouseEvent, "v@:");
class_addMethod(ViewClass, sel_getUid("updateTrackingAreas"), (IMP)UpdateMouseTrackingArea, "v@:");
class_addMethod(ViewClass, sel_getUid("processTimerEvent:"), (IMP)ProcessTimerEvent, "v@:");
class_addIvar(ViewClass, "WindowPtr", sizeof(Window*), sizeof(Window*), "Window*");
objc_registerClassPair(ViewClass);
}
static void InitializeBackBuffer(PixelBuffer& a_backBuffer, int a_w, int a_h, int a_depth)
{
a_backBuffer.m_width = a_w;
a_backBuffer.m_strideBytes = a_w * sizeof(int);
a_backBuffer.m_height = a_h;
a_backBuffer.m_format = (PixelFormat)a_depth;
a_backBuffer.m_isRetina = false;
#if USE_RETINA
if (c_useRetina)
{
int w = a_w * 2;//c_retinaScale;
int h = a_h * 2;//c_retinaScale;
a_backBuffer.m_pixels = new uint32_t[w * h];
a_backBuffer.m_strideBytes = w * sizeof(uint32_t);
a_backBuffer.m_width = w;
a_backBuffer.m_height = h;
a_backBuffer.m_isRetina = true;
}
else
{
a_backBuffer.m_pixels = new uint32_t[a_w * a_h];
}
#else
a_backBuffer.m_pixels = new uint32_t[a_w * a_h];
#endif
}
static void DestroyBackBuffer(PixelBuffer& a_backBuffer)
{
delete[] a_backBuffer.m_pixels;
}
/*
namespace ObjC
{
// Wrapper idea - could do this for main ObjC classes
class NSWindow
{
public:
NSWindow(double w, double h, unsigned windowFlags)
{
obj = ObjectiveC::Object("NSWindow")["alloc"]()["initWithContentRect:styleMask:backing:defer:"]((NSRect){ 0, 0, w, h }, windowFlags, 2, false);
}
inline void setContentView(id view) { obj["setContentView:"](view); }
inline void becomeFirstResponder() { obj["becomeFirstResponder"](); }
inline void makeKeyAndOrderFront() { obj["makeKeyAndOrderFront:"](obj.m_object); }
private:
ObjectiveC::Object obj;
};
} // namespace ObjC
*/
ObjectiveC::Object createView(Window* winPtr, double w, double h, ObjectiveC::Object window)
{
typedef uint32_t NSOpenGLPixelFormatAttribute;
const NSOpenGLPixelFormatAttribute NSOpenGLPFADoubleBuffer = 5;
const NSOpenGLPixelFormatAttribute NSOpenGLPFAColorSize = 8;
const NSOpenGLPixelFormatAttribute NSOpenGLPFAAlphaSize = 11;
const NSOpenGLPixelFormatAttribute NSOpenGLPFADepthSize = 12;
const NSOpenGLPixelFormatAttribute NSOpenGLPFAStencilSize = 13;
const NSOpenGLPixelFormatAttribute NSOpenGLPFAWindow = 80;
NSOpenGLPixelFormatAttribute pixelFormatAttrs[] =
{
NSOpenGLPFAWindow,
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAColorSize, 24,
NSOpenGLPFAAlphaSize, 8,
NSOpenGLPFADepthSize, 24,
NSOpenGLPFAStencilSize, 8,
0
};
ObjectiveC::Object format = ObjectiveC::Object("NSOpenGLPixelFormat")["alloc"]()["initWithAttributes:"](pixelFormatAttrs);
ObjectiveC::Object view = ObjectiveC::Object("View")["alloc"]()["initWithFrame:pixelFormat:"]((NSRect){ 0, 0, w, h }, format.m_object);
view.set("WindowPtr", winPtr);
window["setContentView:"](view.m_object);
#if USE_RETINA
if (c_useRetina)
{
// Make use of retina display higher resolution
// - TODO: need to make some major code changes to take advantage of this
// - need to work out the scale factor if this is supported
// - then need to make a buffer with the larger size and adjust drawing to scale to this
// - may need to change APIs to make things more screen/device
// independent - eg not based on pixel positions/sizes but on physical sizes / DPI
view["setWantsBestResolutionOpenGLSurface:"].call<void>(YES);
}
else
{
view["setWantsBestResolutionOpenGLSurface:"].call<void>(NO);
}
#endif
return view;
}
Window::Window(const char* a_title, bool a_fullscreen, WindowFlags a_flags)
: Widget(a_title, true)
{
ObjectiveC::AutoReleasePool autoReleasePool;
// TODO: this shouldn't need to have to match the size the window becomes
double w = 1, h = 1; // initialize as 1x1 in size
m_data = new WindowData;
InitializeBackBuffer(m_data->m_backBuffer, w, h, 32);
ObjectiveC::Object win((id)0);
// NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask | NSMiniaturizableWindowMask | NSWindowStyleMaskFullSizeContentView
unsigned windowFlags = (1<<0) | (1<<1) | (1<<2) | (1<<3);// | (1<<15);
if (a_flags == WF_NoTitle)
{
windowFlags = (1<<0) | (1<<4) | (1<<15);
win = ObjectiveC::Object("Panel")["alloc"]()["initWithContentRect:styleMask:backing:defer:"]((NSRect){ 0, -h, w, h }, windowFlags, 2, false);
win["setTitlebarAppearsTransparent:"](YES);
}
else
{
win = ObjectiveC::Object("Window")["alloc"]()["initWithContentRect:styleMask:backing:defer:"]((NSRect){ 0, -h, w, h }, windowFlags, 2, false);
win["setTitle:"](ObjectiveC::Object("NSString")["stringWithUTF8String:"]("Hello World"));
}
m_data->m_nativeHandle = win;
// Setting the window title:
win.set("WindowPtr", this);
m_data->m_view = createView(this, w, h, win);
// win["becomeFirstResponder"]();
//win["makeFirstResponder:"](m_data->m_view);
//win["makeFirstResponder:"](win.m_object);
win["makeKeyAndOrderFront:"](win.m_object);
win["makeKeyWindow"]();
//win["makeMainWindow"]();
//m_data->m_nativeHandle["orderFrontRegardless"]();
//m_data->m_nativeHandle["orderBack:"](m_data->m_nativeHandle.m_object);
//m_data->m_nativeHandle["orderFront:"](m_data->m_nativeHandle.m_object);
//m_data->m_nativeHandle["makeKeyWindow"]();
//m_data->m_nativeHandle["makeKeyAndOrderFront:"](m_data->m_nativeHandle.m_object);
// Set up a callback for when we can repaint again (as far as possible - gameloop style mode)
CVReturn error = CVDisplayLinkCreateWithActiveCGDisplays(&m_data->m_displayLink);
error = error || CVDisplayLinkSetOutputCallback(m_data->m_displayLink, OnCanRepaint, this);
//error = error || CVDisplayLinkSetOutputCallback(m_data->m_displayLink, OnCanRepaint, &m_data->m_view);
error = error || CVDisplayLinkStart(m_data->m_displayLink);
if (error)
{
DEBUG_LOG("DisplayLink set up had error: %d", error);
m_data->m_displayLink = NULL;
}
//ObjectiveC::Object app(NSApp);
//app["setMainWindow:"](win);
// Periodic update timer - TODO: probably don't need, but works as a way to test setting up and using timers
//ObjectiveC::Object(ObjectiveC::classCache("NSTimer"))["scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:"](
// 2.0, m_data->m_view.m_object, ObjectiveC::methodCache("processTimerEvent:"), nil, YES);
//startTimer(2000.0f);
}
Window::~Window()
{
CVDisplayLinkStop(m_data->m_displayLink);
CVDisplayLinkRelease(m_data->m_displayLink);
m_data->m_displayLink = NULL;
if (m_data->m_active)
{
deactivate();
}
delete m_data;
}
void Window::deactivate()
{
if (m_data->m_active)
{
m_data->m_nativeHandle["close"]();
m_data->m_active = false;
// DestroyWindow(m_data->m_hWnd);
DestroyBackBuffer(m_data->m_backBuffer);
}
}
void Window::sizeOptions(SizeOptions& a_sizeOptions)
{
Widget::sizeOptions(a_sizeOptions);
//RECT wRect, cRect;
//GetWindowRect(m_data->m_hWnd, &wRect);
//GetClientRect(m_data->m_hWnd, &cRect);
int windowBorderHeight = 40; // wRect.bottom - cRect.bottom + cRect.top - wRect.top;
int windowBorderWidth = 2; // wRect.right - cRect.right + cRect.left - wRect.left;
a_sizeOptions.m_minimum.m_height += windowBorderHeight;
a_sizeOptions.m_preferred.m_height += windowBorderHeight;
a_sizeOptions.m_maximum.m_height += windowBorderHeight;
a_sizeOptions.m_minimum.m_width += windowBorderWidth;
a_sizeOptions.m_preferred.m_width += windowBorderWidth;
a_sizeOptions.m_maximum.m_width += windowBorderWidth;
}
TimerId Window::startTimer(int a_milliSeconds)
{
m_data->m_nextTimerId++;
ObjectiveC::Object timer = ObjectiveC::Object("Timer")["alloc"]();
timer.set("TimerId", m_data->m_nextTimerId);
id timerId =
ObjectiveC::Object(ObjectiveC::classCache("NSTimer"))["scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:"](
a_milliSeconds / 1000.0, m_data->m_view.m_object, ObjectiveC::methodCache("processTimerEvent:"), timer.m_object, YES).m_object;
m_data->m_activeTimers[m_data->m_nextTimerId] = timerId;
return (TimerId)m_data->m_nextTimerId;
}
void Window::killTimer(TimerId a_timerId)
{
ObjectiveC::Object timer = m_data->m_activeTimers[a_timerId];
timer["invalidate"]();
m_data->m_activeTimers.erase(a_timerId);
// TODO: release?
}
void Window::timerUpdate(TimerId a_timerId)
{
Event ev;
ev.m_type = Event::ET_TimerEvent;
ev.m_timerEvent.m_timerId = a_timerId;
event(ev);
}
void Window::mouseUpdate(int a_x, int a_y, int a_buttonMask, MouseButtons a_mouseButton, MouseState a_mouseState)
{
Event ev;
ev.m_type = Event::ET_MouseEvent;
ev.m_mouseEvent.m_oldPosition = m_data->m_mousePos;
ev.m_mouseEvent.m_oldButtons = m_data->m_mouseButtons;
/*
POINT mousePos;
GetCursorPos(&mousePos);
m_data->m_mouseX = mousePos.x;
m_data->m_mouseY = mousePos.y;
*/
m_data->m_mousePos.m_x = a_x;
m_data->m_mousePos.m_y = a_y;
if ( a_mouseState == MS_Up )
m_data->m_mouseButtons = MouseButtons((int)m_data->m_mouseButtons & ~(int)a_mouseButton);
else
m_data->m_mouseButtons |= a_mouseButton;
ev.m_mouseEvent.m_position = m_data->m_mousePos;
ev.m_mouseEvent.m_buttons = m_data->m_mouseButtons;
event(ev);
}
void Window::wheelUpdate(int a_wheelVal)
{
Event ev;
ev.m_type = Event::ET_WheelEvent;
ev.m_wheelEvent.m_degreesRotation = a_wheelVal;
event(ev);
}
void Window::keyUpdate(int a_keyCode, bool a_keyDown)
{
Event ev;
ev.m_type = Event::ET_KeyEvent;
ev.m_keyEvent.m_key = (Key)a_keyCode; // TODO
ev.m_keyEvent.m_state = (a_keyDown) ? KS_Pressed : KS_Released;
ev.m_keyEvent.m_modifiers = M_None; // TODO
event(ev);
}
void Window::setNewSize(int a_newW, int a_newH)
{
ObjectiveC::AutoReleasePool autoReleasePool;
if ( a_newW != m_data->m_size.m_width || a_newH != m_data->m_size.m_height )
{
m_data->m_nativeHandle["setContentSize:"]((NSSize){double(a_newW), double(a_newH)});
}
}
Rectangle Window::worldGeometry()
{
ObjectiveC::AutoReleasePool autoReleasePool;
Class objectsClass = object_getClass(m_data->m_nativeHandle.m_object);
objc_property_t property = class_getProperty(objectsClass, "frame");
//DEBUG_LOG("%s %s\n", property_getName(property), property_getAttributes(property));
printf("%s %s\n", property_getName(property), property_getAttributes(property));
/*
*/
/*
//Class objectsClass = (Class)objc_getClass("NSView");
Protocol* proto = objc_getProtocol("NSWindow");
unsigned outCount, i;
objc_property_t *properties = protocol_copyPropertyList(proto, &outCount);
//objc_property_t *properties = class_copyPropertyList(objectsClass, &outCount);
//DEBUG_LOG("cls: %s count: %i\n", class_getName(objectsClass), outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
DEBUG_LOG("%s %s\n", property_getName(property), property_getAttributes(property));
}
*/
//[propertyName cStringUsingEncoding:NSASCIIStringEnCoding] );
SEL getter;
const char* getterName = property_copyAttributeValue(property, "G");
//printf("getter: %s\n", getterName);
if (getterName==NULL)
{
//getter = sel_getUid( "frame");
//getter = sel_registerName("frame");
//getter = NSSelectorFromString("frame");// propertyName );
}
else
{
getter = sel_getUid( getterName );
}
//ObjectiveC::Method md(m_data->m_nativeHandle.m_object, getter);
//NSRect r = md.call<NSRect>();
//id value = objc_msgSend(m_data->m_nativeHandle, getter);
//ObjectiveC::Object f = ObjectiveC::Object("NSString")["stringWithUTF8String:"]("frame");
//NSRect r = m_data->m_nativeHandle["valueForKey:"].call<NSRect>(f);
//NSRect r = m_data->m_nativeHandle["valueForKey:"].call<NSRect>("frame");
// id object = [customObject valueForKey:[NSString stringWithUTF8String:propertyName]];
// NSRect r = m_data->m_nativeHandle.get<NSRect>("getFrame");
NSSize r = m_data->m_nativeHandle.get<NSSize>("contentSize");
//NSRect r;
//NSRect r = m_data->m_view.get<NSRect>("getFrame");
//if (pr)
//r = *pr;
//NSRect r = m_data->m_nativeHandle["frame"].call<NSRect>();
NSPoint origin{ 0, 0 };
NSPoint p = m_data->m_nativeHandle["convertBaseToScreen:"].call<NSPoint>( origin );
Rectangle rect;
rect.m_x = 10;//r.x;
rect.m_y = 500;//r.y;
rect.m_width = r.width;
rect.m_height = r.height;
rect.m_x = p.x;
rect.m_y = p.y;
//DEBUG_LOG("Got frame rect: %i,%i, %i x %i\n", rect.m_x, rect.m_y,
printf("Got frame rect: %i,%i, %i x %i\n", rect.m_x, rect.m_y,
rect.m_width, rect.m_height);
return rect;
}
void Window::setPosition(int a_x, int a_y)
{
ObjectiveC::AutoReleasePool autoReleasePool;
m_data->m_nativeHandle["setFrameTopLeftPoint:"]((NSPoint){double(a_x), double(a_y)});
}
void Window::reshape()
{
DEBUG_LOG("reshape called\n");
}
void Window::resize(int a_newW, int a_newH)
{
ObjectiveC::AutoReleasePool autoReleasePool;
Event ev;
ev.m_type = Event::ET_SizeEvent;
ev.m_sizeEvent.m_old = m_data->m_size;
if ( a_newW != m_data->m_size.m_width || a_newH != m_data->m_size.m_height )
{
//DEBUG_LOG("resizing to %i %i %i %i\n", a_newW, a_newH, m_data->m_size.m_width, m_data->m_size.m_height);
m_data->m_size.m_width = a_newW;
m_data->m_size.m_height = a_newH;
// Re-create the view
m_data->m_texState = 1; // 1 = re-create
m_data->m_view = createView(this, double(a_newW), double(a_newH), m_data->m_nativeHandle);
DEBUG_LOG("destorying back buffer\n");
DestroyBackBuffer(m_data->m_backBuffer);
DEBUG_LOG("creating new back buffer\n");
InitializeBackBuffer(m_data->m_backBuffer, a_newW, a_newH, 32);
if (!m_data->m_backBuffer.m_pixels)
{
DEBUG_LOG("error making back buffer\n");
m_data->m_backBuffer.m_width = 0;
m_data->m_backBuffer.m_height = 0;
}
DEBUG_LOG("created new back buffer\n");
updateLayout(); // perhaps this could be let to cascade through the recursive SizeEvent processing?
ev.m_sizeEvent.m_new = m_data->m_size;
event(ev);
m_data->m_invalidated = false;
repaint();
//paintUpdate();
}
}
void Window::paintUpdate()
{
DEBUG_LOG("paintUpdate called\n");
PixelBuffer& target = targetBuffer();
int width = target.m_width;
int height = target.m_height;
uint32_t* pixels = target.m_pixels;
Rectangle a_rectangle = { { { 0, 0 } }, { { width, height } } };
// if (!s_started)
// return;
if (width < 3 || height < 3)
return;
if (!pixels)
{
DEBUG_LOG("paintUpdate called with null pixels\n");
return;
}
Event ev;
ev.m_type = Event::ET_PaintEvent;
ev.m_paintEvent.m_rectangle = a_rectangle;
event(ev);
#if 1 // USE_OPENGL
ObjectiveC::Object ctx = m_data->m_view["openGLContext"]();
if (!ctx.m_object)
return;
ctx["makeCurrentContext"]();
# if USE_GL_DRAW_PIXELS
// One way to do it, less lines of code, but less efficient
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
// Flip upside-down - TODO: perhaps can instead change the coordinate system so bottom-left is 0,0
uint32_t* tmpBuf = new uint32_t[width*height];
for (int j = 0; j < height; j++)
for (int i = 0; i < width; i++)
tmpBuf[j*width + i] = pixels[(height-1-j) * width + i];
glDrawPixels(width, height, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, tmpBuf);
delete[] tmpBuf;
# else
// Can remove this if doing only opaque drawing
glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Only allow GL painting on the first child of a window, usually the root widget which had a window created for it
std::list<Widget*> child = children<Widget>();
if (child.size() == 1)
child.front()->glPaint();
// Allow drawing over the top
glEnable(GL_BLEND);
//glBlendFunc(GL_DST_COLOR, GL_ZERO);
//glBlendFunc(GL_SRC_ALPHA, GL_ZERO);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_TEXTURE_2D);
// Texture way to do it. Probably more efficient
if (m_data->m_texState != 2)
{
if (m_data->m_texState == 1)
{
glDeleteTextures(1, &m_data->m_texId);
}
if (m_data->m_texState != 2)
{
glGenTextures(1, &m_data->m_texId);
}
glBindTexture(GL_TEXTURE_2D, m_data->m_texId);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
m_data->m_texState = 2;
}
// else
{
glViewport(0, 0, width, height);
glBindTexture(GL_TEXTURE_2D, m_data->m_texId);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_BGRA,
GL_UNSIGNED_INT_8_8_8_8_REV, pixels);
glActiveTexture(GL_TEXTURE0);
glBegin(GL_TRIANGLES);
glTexCoord2f( 0.0f, 2.0f );
glVertex3f( -1.0f, -3.0f, 0.0f );
glTexCoord2f( 0.0f, 0.0f );
glVertex3f( -1.0f, 1.0f, 0.0f );
glTexCoord2f( 2.0f, 0.0f );
glVertex3f( 3.0f, 1.0f, 0.0f );
glEnd();
}
/*
// Only allow GL painting on the first child of a window, usually the root widget which had a window created for it
std::list<Widget*> child = children<Widget>();
if (child.size() == 1)
child.front()->glPaint();
*/
# endif
glFlush();
ctx["flushBuffer"]();
#else
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef bitmapContext = CGBitmapContextCreate(pixels, width, height, 8, sizeof(uint32_t)*width, colorSpace, kCGImageAlphaNoneSkipLast);
CFRelease(colorSpace);
CGImageRef imageRef = CGBitmapContextCreateImage(bitmapContext);
// graphicsPort is deprecated and since 10.10, docs say the new API is CGContext
CGContextRef ctx = (CGContextRef)ObjectiveC::Object(ObjectiveC::classCache("NSGraphicsContext"))["currentContext"]()["graphicsPort"]().m_object;
CGContextDrawImage(ctx, CGRectMake(0, 0, width, height), imageRef);
CGImageRelease(imageRef);
#endif
m_data->m_invalidated = false;
}
void Window::update(Rectangle& a_rectangle)
{
DEBUG_LOG("window update\n");
static int retryCount = 0;
if (!m_data->m_invalidated || retryCount > 100)
{
DEBUG_LOG("invalidating\n");
ObjectiveC::AutoReleasePool autoReleasePool;
//m_data->m_view["setNeedsDisplay"]();
m_data->m_view["setNeedsDisplayInRect:"](
(NSRect){ double(a_rectangle.m_x), double(a_rectangle.m_y),
double(a_rectangle.m_width), double(a_rectangle.m_height) });
m_data->m_invalidated = true;
}
else
{
retryCount++;
}
}
PixelBuffer& Window::targetBuffer()
{
return m_data->m_backBuffer;
}
END_NAMESPACE
#endif