/*
* This source file is part of RmlUi, the HTML/CSS Interface Middleware
*
* For the latest information, see http://github.com/mikke89/RmlUi
*
* Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
* Copyright (c) 2019 The RmlUi Team, and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#include "Shell.h"
#include
#include "ShellFileInterface.h"
#include "macosx/InputMacOSX.h"
#include
#include
#include
#include
#include
static const EventTypeSpec INPUT_EVENTS[] = {
{ kEventClassKeyboard, kEventRawKeyDown },
{ kEventClassKeyboard, kEventRawKeyUp },
{ kEventClassKeyboard, kEventRawKeyModifiersChanged },
{ kEventClassMouse, kEventMouseDown },
{ kEventClassMouse, kEventMouseUp },
{ kEventClassMouse, kEventMouseMoved },
{ kEventClassMouse, kEventMouseDragged },
{ kEventClassMouse, kEventMouseWheelMoved },
};
static const EventTypeSpec WINDOW_EVENTS[] = {
{ kEventClassWindow, kEventWindowClose },
{ kEventClassWindow, kEventWindowBoundsChanged },
};
static WindowRef window;
static timeval start_time;
static Rml::Core::String clipboard_text;
static std::unique_ptr file_interface;
static void IdleTimerCallback(EventLoopTimerRef timer, EventLoopIdleTimerMessage inState, void* p);
static OSStatus EventHandler(EventHandlerCallRef next_handler, EventRef event, void* p);
bool Shell::Initialise()
{
gettimeofday(&start_time, nullptr);
InputMacOSX::Initialise();
Rml::Core::String root = FindSamplesRoot();
file_interface = std::make_unique(root);
Rml::Core::SetFileInterface(file_interface.get());
return true;
}
void Shell::Shutdown()
{
file_interface.reset();
}
Rml::Core::String Shell::FindSamplesRoot()
{
Rml::Core::String path = "../../Samples/";
// Find the location of the executable.
CFBundleRef bundle = CFBundleGetMainBundle();
CFURLRef executable_url = CFBundleCopyExecutableURL(bundle);
CFStringRef executable_posix_file_name = CFURLCopyFileSystemPath(executable_url, kCFURLPOSIXPathStyle);
CFIndex max_length = CFStringGetMaximumSizeOfFileSystemRepresentation(executable_posix_file_name);
char* executable_file_name = new char[max_length];
if (!CFStringGetFileSystemRepresentation(executable_posix_file_name, executable_file_name, max_length))
executable_file_name[0] = 0;
Rml::Core::String executable_path = Rml::Core::String(executable_file_name);
executable_path = executable_path.substr(0, executable_path.rfind("/") + 1);
delete[] executable_file_name;
CFRelease(executable_posix_file_name);
CFRelease(executable_url);
return (executable_path + "../../../" + path);
}
static ShellRenderInterfaceExtensions *shell_renderer;
bool Shell::OpenWindow(const char* name, ShellRenderInterfaceExtensions *_shell_renderer, unsigned int width, unsigned int height, bool allow_resize)
{
shell_renderer = _shell_renderer;
Rect content_bounds = { 60, 20, 60 + height, 20 + width };
OSStatus result = CreateNewWindow(kDocumentWindowClass,
(allow_resize ? (kWindowStandardDocumentAttributes | kWindowLiveResizeAttribute) :
kWindowCloseBoxAttribute) | kWindowStandardHandlerAttribute,
&content_bounds,
&window);
if (result != noErr)
return false;
CFStringRef window_title = CFStringCreateWithCString(nullptr, name, kCFStringEncodingUTF8);
if (result != noErr)
return false;
result = SetWindowTitleWithCFString(window, window_title);
if (result != noErr)
{
CFRelease(window_title);
return false;
}
CFRelease(window_title);
ShowWindow(window);
if(shell_renderer != nullptr) {
shell_renderer->AttachToNative(window);
}
return true;
}
void Shell::CloseWindow()
{
if(shell_renderer) {
shell_renderer->DetachFromNative();
}
// Close the window.
HideWindow(window);
ReleaseWindow(window);
}
void Shell::EventLoop(ShellIdleFunction idle_function)
{
OSStatus error;
error = InstallApplicationEventHandler(NewEventHandlerUPP(InputMacOSX::EventHandler),
GetEventTypeCount(INPUT_EVENTS),
INPUT_EVENTS,
nullptr,
nullptr);
if (error != noErr)
DisplayError("Unable to install handler for input events, error: %d.", error);
error = InstallWindowEventHandler(window,
NewEventHandlerUPP(EventHandler),
GetEventTypeCount(WINDOW_EVENTS),
WINDOW_EVENTS,
nullptr,
nullptr);
if (error != noErr)
DisplayError("Unable to install handler for window events, error: %d.", error);
EventLoopTimerRef timer;
error = InstallEventLoopIdleTimer(GetMainEventLoop(), // inEventLoop
0, // inFireDelay
5 * kEventDurationMillisecond, // inInterval (200 Hz)
NewEventLoopIdleTimerUPP(IdleTimerCallback), // inTimerProc
(void*) idle_function, // inTimerData,
&timer // outTimer
);
if (error != noErr)
DisplayError("Unable to install Carbon event loop timer, error: %d.", error);
RunApplicationEventLoop();
}
void Shell::RequestExit()
{
EventRef event;
OSStatus result = CreateEvent(nullptr, // default allocator
kEventClassApplication,
kEventAppQuit,
0,
kEventAttributeNone,
&event);
if (result == noErr)
PostEventToQueue(GetMainEventQueue(), event, kEventPriorityStandard);
}
void Shell::DisplayError(const char* fmt, ...)
{
const int buffer_size = 1024;
char buffer[buffer_size];
va_list argument_list;
// Print the message to the buffer.
va_start(argument_list, fmt);
int len = vsnprintf(buffer, buffer_size - 2, fmt, argument_list);
if (len < 0 || len > buffer_size - 2)
{
len = buffer_size - 2;
}
buffer[len] = '\n';
buffer[len + 1] = '\0';
va_end(argument_list);
fprintf(stderr, "%s", buffer);
}
void Shell::Log(const char* fmt, ...)
{
const int buffer_size = 1024;
char buffer[buffer_size];
va_list argument_list;
// Print the message to the buffer.
va_start(argument_list, fmt);
int len = vsnprintf(buffer, buffer_size - 2, fmt, argument_list);
if ( len < 0 || len > buffer_size - 2 )
{
len = buffer_size - 2;
}
buffer[len] = '\n';
buffer[len + 1] = '\0';
va_end(argument_list);
printf("%s", buffer);
}
double Shell::GetElapsedTime()
{
struct timeval now;
gettimeofday(&now, nullptr);
double sec = now.tv_sec - start_time.tv_sec;
double usec = now.tv_usec;
double result = sec + (usec / 1000000.0);
return result;
}
void Shell::SetMouseCursor(const Rml::Core::String& cursor_name)
{
// Not implemented
}
void Shell::SetClipboardText(const Rml::Core::String& text)
{
// Todo: interface with system clipboard
clipboard_text = text;
}
void Shell::GetClipboardText(Rml::Core::String& text)
{
// Todo: interface with system clipboard
text = clipboard_text;
}
static void IdleTimerCallback(EventLoopTimerRef timer, EventLoopIdleTimerMessage inState, void* p)
{
Shell::ShellIdleFunction function = (Shell::ShellIdleFunction) p;
function();
}
static OSStatus EventHandler(EventHandlerCallRef next_handler, EventRef event, void* p)
{
switch (GetEventClass(event))
{
case kEventClassWindow:
{
switch (GetEventKind(event))
{
case kEventWindowClose:
Shell::RequestExit();
break;
case kEventWindowBoundsChanged:
// Window resized, update the rmlui context
UInt32 attributes;
GetEventParameter(event, kEventParamAttributes, typeUInt32, nullptr, sizeof(UInt32), nullptr, &attributes);
if(attributes & kWindowBoundsChangeSizeChanged)
{
Rect bounds;
GetEventParameter(event, kEventParamCurrentBounds, typeQDRectangle, nullptr, sizeof(Rect), nullptr, &bounds);
UInt32 width = bounds.right - bounds.left;
UInt32 height = bounds.bottom - bounds.top;
shell_renderer->SetViewport(width, height);
}
break;
}
}
break;
}
// InputMacOSX::ProcessCarbonEvent(event);
return CallNextEventHandler(next_handler, event);
}