c7bc44d5ad
That year should bring the long-awaited OpenGL ES 3.0 compatible renderer with state-of-the-art rendering techniques tuned to work as low as middle end handheld devices - without compromising with the possibilities given for higher end desktop games of course. Great times ahead for the Godot community and the gamers that will play our games!
2447 lines
55 KiB
C++
2447 lines
55 KiB
C++
/*************************************************************************/
|
|
/* os_windows.cpp */
|
|
/*************************************************************************/
|
|
/* This file is part of: */
|
|
/* GODOT ENGINE */
|
|
/* http://www.godotengine.org */
|
|
/*************************************************************************/
|
|
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
|
|
/* */
|
|
/* 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 "drivers/gles2/rasterizer_gles2.h"
|
|
|
|
#include "os_windows.h"
|
|
#include "drivers/unix/memory_pool_static_malloc.h"
|
|
#include "os/memory_pool_dynamic_static.h"
|
|
#include "drivers/windows/thread_windows.h"
|
|
#include "drivers/windows/semaphore_windows.h"
|
|
#include "drivers/windows/mutex_windows.h"
|
|
#include "main/main.h"
|
|
#include "drivers/windows/file_access_windows.h"
|
|
#include "drivers/windows/dir_access_windows.h"
|
|
|
|
|
|
#include "servers/visual/visual_server_raster.h"
|
|
#include "servers/audio/audio_server_sw.h"
|
|
#include "servers/visual/visual_server_wrap_mt.h"
|
|
|
|
#include "tcp_server_winsock.h"
|
|
#include "packet_peer_udp_winsock.h"
|
|
#include "stream_peer_winsock.h"
|
|
#include "lang_table.h"
|
|
#include "os/memory_pool_dynamic_prealloc.h"
|
|
#include "globals.h"
|
|
#include "io/marshalls.h"
|
|
#include "joystick.h"
|
|
|
|
#include "shlobj.h"
|
|
#include <regstr.h>
|
|
#include <process.h>
|
|
|
|
static const WORD MAX_CONSOLE_LINES = 1500;
|
|
|
|
extern "C" {
|
|
#ifdef _MSC_VER
|
|
_declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
|
|
#else
|
|
__attribute__((visibility("default"))) DWORD NvOptimusEnablement = 0x00000001;
|
|
#endif
|
|
}
|
|
|
|
#ifndef WM_MOUSEHWHEEL
|
|
#define WM_MOUSEHWHEEL 0x020e
|
|
#endif
|
|
|
|
//#define STDOUT_FILE
|
|
|
|
extern HINSTANCE godot_hinstance;
|
|
|
|
void RedirectIOToConsole() {
|
|
|
|
int hConHandle;
|
|
|
|
intptr_t lStdHandle;
|
|
|
|
CONSOLE_SCREEN_BUFFER_INFO coninfo;
|
|
|
|
FILE *fp;
|
|
|
|
// allocate a console for this app
|
|
|
|
AllocConsole();
|
|
|
|
// set the screen buffer to be big enough to let us scroll text
|
|
|
|
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE),
|
|
|
|
&coninfo);
|
|
|
|
coninfo.dwSize.Y = MAX_CONSOLE_LINES;
|
|
|
|
SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE),
|
|
|
|
coninfo.dwSize);
|
|
|
|
// redirect unbuffered STDOUT to the console
|
|
|
|
lStdHandle = (intptr_t)GetStdHandle(STD_OUTPUT_HANDLE);
|
|
|
|
hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
|
|
|
|
fp = _fdopen( hConHandle, "w" );
|
|
|
|
*stdout = *fp;
|
|
|
|
setvbuf( stdout, NULL, _IONBF, 0 );
|
|
|
|
// redirect unbuffered STDIN to the console
|
|
|
|
lStdHandle = (intptr_t)GetStdHandle(STD_INPUT_HANDLE);
|
|
|
|
hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
|
|
|
|
fp = _fdopen( hConHandle, "r" );
|
|
|
|
*stdin = *fp;
|
|
|
|
setvbuf( stdin, NULL, _IONBF, 0 );
|
|
|
|
// redirect unbuffered STDERR to the console
|
|
|
|
lStdHandle = (intptr_t)GetStdHandle(STD_ERROR_HANDLE);
|
|
|
|
hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
|
|
|
|
fp = _fdopen( hConHandle, "w" );
|
|
|
|
*stderr = *fp;
|
|
|
|
setvbuf( stderr, NULL, _IONBF, 0 );
|
|
|
|
// make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog
|
|
|
|
// point to console as well
|
|
}
|
|
|
|
int OS_Windows::get_video_driver_count() const {
|
|
|
|
return 1;
|
|
}
|
|
const char * OS_Windows::get_video_driver_name(int p_driver) const {
|
|
|
|
return "GLES2";
|
|
}
|
|
|
|
OS::VideoMode OS_Windows::get_default_video_mode() const {
|
|
|
|
return VideoMode(1024,600,false);
|
|
}
|
|
|
|
int OS_Windows::get_audio_driver_count() const {
|
|
|
|
return AudioDriverManagerSW::get_driver_count();
|
|
}
|
|
const char * OS_Windows::get_audio_driver_name(int p_driver) const {
|
|
|
|
AudioDriverSW* driver = AudioDriverManagerSW::get_driver(p_driver);
|
|
ERR_FAIL_COND_V( !driver, "" );
|
|
return AudioDriverManagerSW::get_driver(p_driver)->get_name();
|
|
}
|
|
|
|
static MemoryPoolStatic *mempool_static=NULL;
|
|
static MemoryPoolDynamic *mempool_dynamic=NULL;
|
|
|
|
void OS_Windows::initialize_core() {
|
|
|
|
|
|
last_button_state=0;
|
|
|
|
//RedirectIOToConsole();
|
|
maximized=false;
|
|
minimized=false;
|
|
borderless=false;
|
|
|
|
ThreadWindows::make_default();
|
|
SemaphoreWindows::make_default();
|
|
MutexWindows::make_default();
|
|
|
|
FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_RESOURCES);
|
|
FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_USERDATA);
|
|
FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_FILESYSTEM);
|
|
//FileAccessBufferedFA<FileAccessWindows>::make_default();
|
|
DirAccess::make_default<DirAccessWindows>(DirAccess::ACCESS_RESOURCES);
|
|
DirAccess::make_default<DirAccessWindows>(DirAccess::ACCESS_USERDATA);
|
|
DirAccess::make_default<DirAccessWindows>(DirAccess::ACCESS_FILESYSTEM);
|
|
|
|
TCPServerWinsock::make_default();
|
|
StreamPeerWinsock::make_default();
|
|
PacketPeerUDPWinsock::make_default();
|
|
|
|
mempool_static = new MemoryPoolStaticMalloc;
|
|
#if 1
|
|
mempool_dynamic = memnew( MemoryPoolDynamicStatic );
|
|
#else
|
|
#define DYNPOOL_SIZE 4*1024*1024
|
|
void * buffer = malloc( DYNPOOL_SIZE );
|
|
mempool_dynamic = memnew( MemoryPoolDynamicPrealloc(buffer,DYNPOOL_SIZE) );
|
|
|
|
#endif
|
|
|
|
// We need to know how often the clock is updated
|
|
if( !QueryPerformanceFrequency((LARGE_INTEGER *)&ticks_per_second) )
|
|
ticks_per_second = 1000;
|
|
// If timeAtGameStart is 0 then we get the time since
|
|
// the start of the computer when we call GetGameTime()
|
|
ticks_start = 0;
|
|
ticks_start = get_ticks_usec();
|
|
|
|
process_map = memnew((Map<ProcessID, ProcessInfo>));
|
|
|
|
IP_Unix::make_default();
|
|
|
|
cursor_shape=CURSOR_ARROW;
|
|
|
|
|
|
}
|
|
|
|
bool OS_Windows::can_draw() const {
|
|
|
|
return !minimized;
|
|
};
|
|
|
|
#define MI_WP_SIGNATURE 0xFF515700
|
|
#define SIGNATURE_MASK 0xFFFFFF00
|
|
#define IsPenEvent(dw) (((dw) & SIGNATURE_MASK) == MI_WP_SIGNATURE)
|
|
|
|
|
|
void OS_Windows::_touch_event(bool p_pressed, int p_x, int p_y, int idx) {
|
|
|
|
InputEvent event;
|
|
event.type = InputEvent::SCREEN_TOUCH;
|
|
event.ID=++last_id;
|
|
event.screen_touch.index = idx;
|
|
|
|
event.screen_touch.pressed = p_pressed;
|
|
|
|
event.screen_touch.x=p_x;
|
|
event.screen_touch.y=p_y;
|
|
|
|
if (main_loop) {
|
|
input->parse_input_event(event);
|
|
}
|
|
};
|
|
|
|
void OS_Windows::_drag_event(int p_x, int p_y, int idx) {
|
|
|
|
InputEvent event;
|
|
event.type = InputEvent::SCREEN_DRAG;
|
|
event.ID=++last_id;
|
|
event.screen_drag.index = idx;
|
|
|
|
event.screen_drag.x=p_x;
|
|
event.screen_drag.y=p_y;
|
|
|
|
if (main_loop)
|
|
input->parse_input_event(event);
|
|
};
|
|
|
|
LRESULT OS_Windows::WndProc(HWND hWnd,UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
|
|
|
|
|
switch (uMsg) // Check For Windows Messages
|
|
{
|
|
case WM_ACTIVATE: // Watch For Window Activate Message
|
|
{
|
|
minimized = HIWORD(wParam) != 0;
|
|
if (!main_loop) {
|
|
return 0;
|
|
};
|
|
if (LOWORD(wParam) == WA_ACTIVE || LOWORD(wParam) == WA_CLICKACTIVE) {
|
|
|
|
main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN);
|
|
alt_mem=false;
|
|
control_mem=false;
|
|
shift_mem=false;
|
|
if (mouse_mode==MOUSE_MODE_CAPTURED) {
|
|
RECT clipRect;
|
|
GetClientRect(hWnd, &clipRect);
|
|
ClientToScreen(hWnd, (POINT*) &clipRect.left);
|
|
ClientToScreen(hWnd, (POINT*) &clipRect.right);
|
|
ClipCursor(&clipRect);
|
|
SetCapture(hWnd);
|
|
|
|
}
|
|
} else {
|
|
main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT);
|
|
alt_mem=false;
|
|
|
|
};
|
|
|
|
return 0; // Return To The Message Loop
|
|
}
|
|
|
|
case WM_PAINT:
|
|
|
|
Main::force_redraw();
|
|
break;
|
|
|
|
case WM_SYSCOMMAND: // Intercept System Commands
|
|
{
|
|
switch (wParam) // Check System Calls
|
|
{
|
|
case SC_SCREENSAVE: // Screensaver Trying To Start?
|
|
case SC_MONITORPOWER: // Monitor Trying To Enter Powersave?
|
|
return 0; // Prevent From Happening
|
|
case SC_KEYMENU:
|
|
if ((lParam>>16)<=0)
|
|
return 0;
|
|
}
|
|
break; // Exit
|
|
}
|
|
|
|
case WM_CLOSE: // Did We Receive A Close Message?
|
|
{
|
|
if (main_loop)
|
|
main_loop->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST);
|
|
//force_quit=true;
|
|
return 0; // Jump Back
|
|
}
|
|
case WM_MOUSELEAVE: {
|
|
|
|
old_invalid=true;
|
|
outside=true;
|
|
if (main_loop && mouse_mode!=MOUSE_MODE_CAPTURED)
|
|
main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_EXIT);
|
|
if (input)
|
|
input->set_mouse_in_window(false);
|
|
|
|
} break;
|
|
case WM_MOUSEMOVE: {
|
|
|
|
if (outside) {
|
|
//mouse enter
|
|
|
|
if (main_loop && mouse_mode!=MOUSE_MODE_CAPTURED)
|
|
main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_ENTER);
|
|
if (input)
|
|
input->set_mouse_in_window(true);
|
|
|
|
CursorShape c=cursor_shape;
|
|
cursor_shape=CURSOR_MAX;
|
|
set_cursor_shape(c);
|
|
outside=false;
|
|
|
|
//Once-Off notification, must call again....
|
|
TRACKMOUSEEVENT tme;
|
|
tme.cbSize=sizeof(TRACKMOUSEEVENT);
|
|
tme.dwFlags=TME_LEAVE;
|
|
tme.hwndTrack=hWnd;
|
|
tme.dwHoverTime=HOVER_DEFAULT;
|
|
TrackMouseEvent(&tme);
|
|
|
|
}
|
|
|
|
/*
|
|
LPARAM extra = GetMessageExtraInfo();
|
|
if (IsPenEvent(extra)) {
|
|
|
|
int idx = extra & 0x7f;
|
|
_drag_event(idx, uMsg, wParam, lParam);
|
|
if (idx != 0) {
|
|
return 0;
|
|
};
|
|
// fallthrough for mouse event
|
|
};
|
|
*/
|
|
|
|
|
|
InputEvent event;
|
|
event.type=InputEvent::MOUSE_MOTION;
|
|
event.ID=++last_id;
|
|
InputEventMouseMotion &mm=event.mouse_motion;
|
|
|
|
mm.mod.control=(wParam&MK_CONTROL)!=0;
|
|
mm.mod.shift=(wParam&MK_SHIFT)!=0;
|
|
mm.mod.alt=alt_mem;
|
|
|
|
mm.button_mask|=(wParam&MK_LBUTTON)?(1<<0):0;
|
|
mm.button_mask|=(wParam&MK_RBUTTON)?(1<<1):0;
|
|
mm.button_mask|=(wParam&MK_MBUTTON)?(1<<2):0;
|
|
last_button_state=mm.button_mask;
|
|
/*mm.button_mask|=(wParam&MK_XBUTTON1)?(1<<5):0;
|
|
mm.button_mask|=(wParam&MK_XBUTTON2)?(1<<6):0;*/
|
|
mm.x=GET_X_LPARAM(lParam);
|
|
mm.y=GET_Y_LPARAM(lParam);
|
|
|
|
if (mouse_mode==MOUSE_MODE_CAPTURED) {
|
|
|
|
Point2i c(video_mode.width/2,video_mode.height/2);
|
|
if (Point2i(mm.x,mm.y)==c) {
|
|
center=c;
|
|
return 0;
|
|
}
|
|
|
|
Point2i ncenter(mm.x,mm.y);
|
|
mm.x = old_x + (mm.x-center.x);
|
|
mm.y = old_y + (mm.y-center.y);
|
|
center=ncenter;
|
|
POINT pos = { (int) c.x, (int) c.y };
|
|
ClientToScreen(hWnd, &pos);
|
|
SetCursorPos(pos.x, pos.y);
|
|
|
|
}
|
|
|
|
input->set_mouse_pos(Point2(mm.x,mm.y));
|
|
mm.speed_x=input->get_mouse_speed().x;
|
|
mm.speed_y=input->get_mouse_speed().y;
|
|
|
|
if (old_invalid) {
|
|
|
|
old_x=mm.x;
|
|
old_y=mm.y;
|
|
old_invalid=false;
|
|
}
|
|
|
|
mm.relative_x=mm.x-old_x;
|
|
mm.relative_y=mm.y-old_y;
|
|
old_x=mm.x;
|
|
old_y=mm.y;
|
|
if (main_loop)
|
|
input->parse_input_event(event);
|
|
|
|
|
|
|
|
} break;
|
|
case WM_LBUTTONDOWN:
|
|
case WM_LBUTTONUP:
|
|
case WM_MBUTTONDOWN:
|
|
case WM_MBUTTONUP:
|
|
case WM_RBUTTONDOWN:
|
|
case WM_RBUTTONUP:
|
|
case WM_MOUSEWHEEL:
|
|
case WM_MOUSEHWHEEL:
|
|
case WM_LBUTTONDBLCLK:
|
|
case WM_RBUTTONDBLCLK:
|
|
/*case WM_XBUTTONDOWN:
|
|
case WM_XBUTTONUP: */{
|
|
|
|
/*
|
|
LPARAM extra = GetMessageExtraInfo();
|
|
if (IsPenEvent(extra)) {
|
|
|
|
int idx = extra & 0x7f;
|
|
_touch_event(idx, uMsg, wParam, lParam);
|
|
if (idx != 0) {
|
|
return 0;
|
|
};
|
|
// fallthrough for mouse event
|
|
};
|
|
*/
|
|
|
|
InputEvent event;
|
|
event.type=InputEvent::MOUSE_BUTTON;
|
|
event.ID=++last_id;
|
|
InputEventMouseButton &mb=event.mouse_button;
|
|
|
|
switch (uMsg) {
|
|
case WM_LBUTTONDOWN: {
|
|
mb.pressed=true;
|
|
mb.button_index=1;
|
|
} break;
|
|
case WM_LBUTTONUP: {
|
|
mb.pressed=false;
|
|
mb.button_index=1;
|
|
} break;
|
|
case WM_MBUTTONDOWN: {
|
|
mb.pressed=true;
|
|
mb.button_index=3;
|
|
|
|
} break;
|
|
case WM_MBUTTONUP: {
|
|
mb.pressed=false;
|
|
mb.button_index=3;
|
|
} break;
|
|
case WM_RBUTTONDOWN: {
|
|
mb.pressed=true;
|
|
mb.button_index=2;
|
|
} break;
|
|
case WM_RBUTTONUP: {
|
|
mb.pressed=false;
|
|
mb.button_index=2;
|
|
} break;
|
|
case WM_LBUTTONDBLCLK: {
|
|
|
|
mb.pressed=true;
|
|
mb.button_index=1;
|
|
mb.doubleclick = true;
|
|
} break;
|
|
case WM_RBUTTONDBLCLK: {
|
|
|
|
mb.pressed=true;
|
|
mb.button_index=2;
|
|
mb.doubleclick = true;
|
|
} break;
|
|
case WM_MOUSEWHEEL: {
|
|
|
|
mb.pressed=true;
|
|
int motion = (short)HIWORD(wParam);
|
|
if (!motion)
|
|
return 0;
|
|
|
|
|
|
if (motion>0)
|
|
mb.button_index= BUTTON_WHEEL_UP;
|
|
else
|
|
mb.button_index= BUTTON_WHEEL_DOWN;
|
|
|
|
|
|
} break;
|
|
case WM_MOUSEHWHEEL: {
|
|
|
|
mb.pressed = true;
|
|
int motion = (short)HIWORD(wParam);
|
|
if (!motion)
|
|
return 0;
|
|
|
|
if (motion<0)
|
|
mb.button_index = BUTTON_WHEEL_LEFT;
|
|
else
|
|
mb.button_index = BUTTON_WHEEL_RIGHT;
|
|
} break;
|
|
/*
|
|
case WM_XBUTTONDOWN: {
|
|
mb.pressed=true;
|
|
mb.button_index=(HIWORD(wParam)==XBUTTON1)?6:7;
|
|
} break;
|
|
case WM_XBUTTONUP:
|
|
mb.pressed=true;
|
|
mb.button_index=(HIWORD(wParam)==XBUTTON1)?6:7;
|
|
} break;*/
|
|
default: { return 0; }
|
|
}
|
|
|
|
|
|
mb.mod.control=(wParam&MK_CONTROL)!=0;
|
|
mb.mod.shift=(wParam&MK_SHIFT)!=0;
|
|
mb.mod.alt=alt_mem;
|
|
//mb.mod.alt=(wParam&MK_MENU)!=0;
|
|
mb.button_mask|=(wParam&MK_LBUTTON)?(1<<0):0;
|
|
mb.button_mask|=(wParam&MK_RBUTTON)?(1<<1):0;
|
|
mb.button_mask|=(wParam&MK_MBUTTON)?(1<<2):0;
|
|
|
|
last_button_state=mb.button_mask;
|
|
/*
|
|
mb.button_mask|=(wParam&MK_XBUTTON1)?(1<<5):0;
|
|
mb.button_mask|=(wParam&MK_XBUTTON2)?(1<<6):0;*/
|
|
mb.x=GET_X_LPARAM(lParam);
|
|
mb.y=GET_Y_LPARAM(lParam);
|
|
|
|
if (mouse_mode==MOUSE_MODE_CAPTURED) {
|
|
|
|
mb.x=old_x;
|
|
mb.y=old_y;
|
|
}
|
|
|
|
mb.global_x=mb.x;
|
|
mb.global_y=mb.y;
|
|
|
|
|
|
if (uMsg != WM_MOUSEWHEEL) {
|
|
if (mb.pressed) {
|
|
|
|
if (++pressrc>0)
|
|
SetCapture(hWnd);
|
|
} else {
|
|
|
|
|
|
if (--pressrc<=0) {
|
|
ReleaseCapture();
|
|
pressrc=0;
|
|
}
|
|
|
|
}
|
|
} else if (mouse_mode!=MOUSE_MODE_CAPTURED) {
|
|
// for reasons unknown to mankind, wheel comes in screen cordinates
|
|
POINT coords;
|
|
coords.x = mb.x;
|
|
coords.y = mb.y;
|
|
|
|
ScreenToClient(hWnd, &coords);
|
|
|
|
mb.x = coords.x;
|
|
mb.y = coords.y;
|
|
}
|
|
|
|
if (main_loop) {
|
|
input->parse_input_event(event);
|
|
if (mb.pressed && mb.button_index>3) {
|
|
//send release for mouse wheel
|
|
mb.pressed=false;
|
|
event.ID=++last_id;
|
|
input->parse_input_event(event);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
} break;
|
|
|
|
case WM_SIZE: {
|
|
int window_w = LOWORD(lParam);
|
|
int window_h = HIWORD(lParam);
|
|
if (window_w > 0 && window_h > 0) {
|
|
video_mode.width = window_w;
|
|
video_mode.height = window_h;
|
|
}
|
|
//return 0; // Jump Back
|
|
} break;
|
|
|
|
case WM_ENTERSIZEMOVE: {
|
|
move_timer_id = SetTimer(hWnd, 1, USER_TIMER_MINIMUM,(TIMERPROC) NULL);
|
|
} break;
|
|
case WM_EXITSIZEMOVE: {
|
|
KillTimer(hWnd, move_timer_id);
|
|
} break;
|
|
case WM_TIMER: {
|
|
if (wParam == move_timer_id) {
|
|
process_key_events();
|
|
Main::iteration();
|
|
}
|
|
} break;
|
|
|
|
case WM_SYSKEYDOWN:
|
|
case WM_SYSKEYUP:
|
|
case WM_KEYUP:
|
|
case WM_KEYDOWN: {
|
|
|
|
|
|
if (wParam==VK_SHIFT)
|
|
shift_mem=uMsg==WM_KEYDOWN;
|
|
if (wParam==VK_CONTROL)
|
|
control_mem=uMsg==WM_KEYDOWN;
|
|
if (wParam==VK_MENU) {
|
|
alt_mem=(uMsg==WM_KEYDOWN || uMsg==WM_SYSKEYDOWN);
|
|
if (lParam&(1<<24))
|
|
gr_mem=alt_mem;
|
|
}
|
|
|
|
//if (wParam==VK_WIN) TODO wtf is this?
|
|
// meta_mem=uMsg==WM_KEYDOWN;
|
|
|
|
|
|
} //fallthrough
|
|
case WM_CHAR: {
|
|
|
|
ERR_BREAK(key_event_pos >= KEY_EVENT_BUFFER_SIZE);
|
|
|
|
// Make sure we don't include modifiers for the modifier key itself.
|
|
KeyEvent ke;
|
|
ke.mod_state.shift= (wParam != VK_SHIFT) ? shift_mem : false;
|
|
ke.mod_state.alt= (! (wParam == VK_MENU && (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN))) ? alt_mem : false;
|
|
ke.mod_state.control= (wParam != VK_CONTROL) ? control_mem : false;
|
|
ke.mod_state.meta=meta_mem;
|
|
ke.uMsg=uMsg;
|
|
|
|
if (ke.uMsg==WM_SYSKEYDOWN)
|
|
ke.uMsg=WM_KEYDOWN;
|
|
if (ke.uMsg==WM_SYSKEYUP)
|
|
ke.uMsg=WM_KEYUP;
|
|
|
|
|
|
/*if (ke.uMsg==WM_KEYDOWN && alt_mem && uMsg!=WM_SYSKEYDOWN) {
|
|
//altgr hack for intl keyboards, not sure how good it is
|
|
//windows is weeeeird
|
|
ke.mod_state.alt=false;
|
|
ke.mod_state.control=false;
|
|
print_line("")
|
|
}*/
|
|
|
|
|
|
ke.wParam=wParam;
|
|
ke.lParam=lParam;
|
|
key_event_buffer[key_event_pos++]=ke;
|
|
|
|
} break;
|
|
case WM_INPUTLANGCHANGEREQUEST: {
|
|
|
|
print_line("input lang change");
|
|
} break;
|
|
|
|
#if WINVER >= 0x0700 // for windows 7
|
|
case WM_TOUCH: {
|
|
|
|
BOOL bHandled = FALSE;
|
|
UINT cInputs = LOWORD(wParam);
|
|
PTOUCHINPUT pInputs = memnew_arr(TOUCHINPUT, cInputs);
|
|
if (pInputs){
|
|
if (GetTouchInputInfo((HTOUCHINPUT)lParam, cInputs, pInputs, sizeof(TOUCHINPUT))){
|
|
for (UINT i=0; i < cInputs; i++){
|
|
TOUCHINPUT ti = pInputs[i];
|
|
//do something with each touch input entry
|
|
if (ti.dwFlags & TOUCHEVENTF_MOVE) {
|
|
|
|
_drag_event(ti.x / 100, ti.y / 100, ti.dwID);
|
|
} else if (ti.dwFlags & (TOUCHEVENTF_UP | TOUCHEVENTF_DOWN)) {
|
|
|
|
_touch_event(ti.dwFlags & TOUCHEVENTF_DOWN != 0, ti.x / 100, ti.y / 100, ti.dwID);
|
|
};
|
|
}
|
|
bHandled = TRUE;
|
|
}else{
|
|
/* handle the error here */
|
|
}
|
|
memdelete_arr(pInputs);
|
|
}else{
|
|
/* handle the error here, probably out of memory */
|
|
}
|
|
if (bHandled) {
|
|
CloseTouchInputHandle((HTOUCHINPUT)lParam);
|
|
return 0;
|
|
};
|
|
|
|
} break;
|
|
|
|
#endif
|
|
case WM_DEVICECHANGE: {
|
|
|
|
joystick->probe_joysticks();
|
|
} break;
|
|
case WM_SETCURSOR: {
|
|
|
|
if(LOWORD(lParam) == HTCLIENT) {
|
|
if(mouse_mode == MOUSE_MODE_HIDDEN || mouse_mode == MOUSE_MODE_CAPTURED) {
|
|
//Hide the cursor
|
|
if(hCursor == NULL)
|
|
hCursor = SetCursor(NULL);
|
|
else
|
|
SetCursor(NULL);
|
|
}
|
|
else {
|
|
if(hCursor != NULL) {
|
|
SetCursor(hCursor);
|
|
hCursor = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
} break;
|
|
case WM_DROPFILES: {
|
|
|
|
HDROP hDropInfo = NULL;
|
|
hDropInfo = (HDROP) wParam;
|
|
const int buffsize=4096;
|
|
wchar_t buf[buffsize];
|
|
|
|
int fcount = DragQueryFileW(hDropInfo, 0xFFFFFFFF,NULL,0);
|
|
|
|
Vector<String> files;
|
|
|
|
for(int i=0;i<fcount;i++) {
|
|
|
|
DragQueryFileW(hDropInfo, i, buf, buffsize);
|
|
String file=buf;
|
|
files.push_back(file);
|
|
}
|
|
|
|
if (files.size() && main_loop) {
|
|
main_loop->drop_files(files,0);
|
|
}
|
|
|
|
|
|
} break;
|
|
|
|
|
|
|
|
default: {
|
|
|
|
if (user_proc) {
|
|
|
|
return CallWindowProcW(user_proc, hWnd, uMsg, wParam, lParam);
|
|
};
|
|
};
|
|
}
|
|
|
|
return DefWindowProcW(hWnd,uMsg,wParam,lParam);
|
|
|
|
}
|
|
|
|
LRESULT CALLBACK WndProc(HWND hWnd,UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
|
|
|
OS_Windows *os_win = static_cast<OS_Windows*>(OS::get_singleton());
|
|
if (os_win)
|
|
return os_win->WndProc(hWnd,uMsg,wParam,lParam);
|
|
else
|
|
return DefWindowProcW(hWnd,uMsg,wParam,lParam);
|
|
|
|
}
|
|
|
|
|
|
|
|
void OS_Windows::process_key_events() {
|
|
|
|
for(int i=0;i<key_event_pos;i++) {
|
|
|
|
KeyEvent &ke = key_event_buffer[i];
|
|
switch(ke.uMsg) {
|
|
|
|
case WM_CHAR: {
|
|
if ((i==0 && ke.uMsg==WM_CHAR) || (i>0 && key_event_buffer[i-1].uMsg==WM_CHAR))
|
|
{
|
|
InputEvent event;
|
|
event.type=InputEvent::KEY;
|
|
event.ID=++last_id;
|
|
InputEventKey &k=event.key;
|
|
|
|
|
|
k.mod=ke.mod_state;
|
|
k.pressed=true;
|
|
k.scancode=KeyMappingWindows::get_keysym(ke.wParam);
|
|
k.unicode=ke.wParam;
|
|
if (k.unicode && gr_mem) {
|
|
k.mod.alt=false;
|
|
k.mod.control=false;
|
|
}
|
|
|
|
if (k.unicode<32)
|
|
k.unicode=0;
|
|
|
|
input->parse_input_event(event);
|
|
}
|
|
|
|
//do nothing
|
|
} break;
|
|
case WM_KEYUP:
|
|
case WM_KEYDOWN: {
|
|
|
|
|
|
InputEvent event;
|
|
event.type=InputEvent::KEY;
|
|
event.ID=++last_id;
|
|
InputEventKey &k=event.key;
|
|
|
|
|
|
k.mod=ke.mod_state;
|
|
k.pressed=(ke.uMsg==WM_KEYDOWN);
|
|
|
|
k.scancode=KeyMappingWindows::get_keysym(ke.wParam);
|
|
if (i+1 < key_event_pos && key_event_buffer[i+1].uMsg==WM_CHAR)
|
|
k.unicode=key_event_buffer[i+1].wParam;
|
|
if (k.unicode && gr_mem) {
|
|
k.mod.alt=false;
|
|
k.mod.control=false;
|
|
}
|
|
|
|
if (k.unicode<32)
|
|
k.unicode=0;
|
|
|
|
|
|
|
|
k.echo=(ke.uMsg==WM_KEYDOWN && (ke.lParam&(1<<30)));
|
|
|
|
input->parse_input_event(event);
|
|
|
|
|
|
} break;
|
|
}
|
|
}
|
|
|
|
key_event_pos=0;
|
|
}
|
|
|
|
enum _MonitorDpiType
|
|
{
|
|
MDT_Effective_DPI = 0,
|
|
MDT_Angular_DPI = 1,
|
|
MDT_Raw_DPI = 2,
|
|
MDT_Default = MDT_Effective_DPI
|
|
};
|
|
|
|
|
|
static int QueryDpiForMonitor(HMONITOR hmon, _MonitorDpiType dpiType= MDT_Default)
|
|
{
|
|
|
|
|
|
int dpiX = 96, dpiY = 96;
|
|
|
|
static HMODULE Shcore = NULL;
|
|
typedef HRESULT (WINAPI* GetDPIForMonitor_t)(HMONITOR hmonitor, _MonitorDpiType dpiType, UINT *dpiX, UINT *dpiY);
|
|
static GetDPIForMonitor_t getDPIForMonitor = NULL;
|
|
|
|
if (Shcore == NULL)
|
|
{
|
|
Shcore = LoadLibraryW(L"Shcore.dll");
|
|
getDPIForMonitor = Shcore ? (GetDPIForMonitor_t)GetProcAddress(Shcore, "GetDpiForMonitor") : NULL;
|
|
|
|
if ((Shcore == NULL) || (getDPIForMonitor == NULL))
|
|
{
|
|
if (Shcore)
|
|
FreeLibrary(Shcore);
|
|
Shcore = (HMODULE)INVALID_HANDLE_VALUE;
|
|
}
|
|
}
|
|
|
|
UINT x = 0, y = 0;
|
|
HRESULT hr = E_FAIL;
|
|
bool bSet = false;
|
|
if (hmon && (Shcore != (HMODULE)INVALID_HANDLE_VALUE))
|
|
{
|
|
hr = getDPIForMonitor(hmon, dpiType/*MDT_Effective_DPI*/, &x, &y);
|
|
if (SUCCEEDED(hr) && (x > 0) && (y > 0))
|
|
{
|
|
|
|
dpiX = (int)x;
|
|
dpiY = (int)y;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
static int overallX = 0, overallY = 0;
|
|
if (overallX <= 0 || overallY <= 0)
|
|
{
|
|
HDC hdc = GetDC(NULL);
|
|
if (hdc)
|
|
{
|
|
overallX = GetDeviceCaps(hdc, LOGPIXELSX);
|
|
overallY = GetDeviceCaps(hdc, LOGPIXELSY);
|
|
ReleaseDC(NULL, hdc);
|
|
}
|
|
}
|
|
if (overallX > 0 && overallY > 0)
|
|
{
|
|
dpiX = overallX; dpiY = overallY;
|
|
}
|
|
}
|
|
|
|
|
|
return (dpiX+dpiY)/2;
|
|
}
|
|
|
|
|
|
BOOL CALLBACK OS_Windows::MonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
|
|
OS_Windows *self=(OS_Windows*)OS::get_singleton();
|
|
MonitorInfo minfo;
|
|
minfo.hMonitor=hMonitor;
|
|
minfo.hdcMonitor=hdcMonitor;
|
|
minfo.rect.pos.x=lprcMonitor->left;
|
|
minfo.rect.pos.y=lprcMonitor->top;
|
|
minfo.rect.size.x=lprcMonitor->right - lprcMonitor->left;
|
|
minfo.rect.size.y=lprcMonitor->bottom - lprcMonitor->top;
|
|
|
|
minfo.dpi = QueryDpiForMonitor(hMonitor);
|
|
|
|
self->monitor_info.push_back(minfo);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void OS_Windows::initialize(const VideoMode& p_desired,int p_video_driver,int p_audio_driver) {
|
|
|
|
|
|
|
|
main_loop=NULL;
|
|
outside=true;
|
|
|
|
WNDCLASSEXW wc;
|
|
|
|
video_mode=p_desired;
|
|
//printf("**************** desired %s, mode %s\n", p_desired.fullscreen?"true":"false", video_mode.fullscreen?"true":"false");
|
|
RECT WindowRect;
|
|
|
|
WindowRect.left=0;
|
|
WindowRect.right=video_mode.width;
|
|
WindowRect.top=0;
|
|
WindowRect.bottom=video_mode.height;
|
|
|
|
memset(&wc,0,sizeof(WNDCLASSEXW));
|
|
wc.cbSize=sizeof(WNDCLASSEXW);
|
|
wc.style= CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS;
|
|
wc.lpfnWndProc = (WNDPROC)::WndProc;
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra= 0;
|
|
//wc.hInstance = hInstance;
|
|
wc.hInstance = godot_hinstance ? godot_hinstance : GetModuleHandle(NULL);
|
|
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
|
|
wc.hCursor = NULL;//LoadCursor(NULL, IDC_ARROW);
|
|
wc.hbrBackground = NULL;
|
|
wc.lpszMenuName = NULL;
|
|
wc.lpszClassName = L"Engine";
|
|
|
|
if (!RegisterClassExW(&wc)) {
|
|
MessageBox(NULL,"Failed To Register The Window Class.","ERROR",MB_OK|MB_ICONEXCLAMATION);
|
|
return; // Return
|
|
}
|
|
|
|
|
|
EnumDisplayMonitors(NULL,NULL,MonitorEnumProc,0);
|
|
|
|
print_line("DETECTED MONITORS: "+itos(monitor_info.size()));
|
|
pre_fs_valid=true;
|
|
if (video_mode.fullscreen) {
|
|
|
|
DEVMODE current;
|
|
memset(¤t,0,sizeof(current));
|
|
EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, ¤t);
|
|
|
|
WindowRect.right = current.dmPelsWidth;
|
|
WindowRect.bottom = current.dmPelsHeight;
|
|
|
|
/* DEVMODE dmScreenSettings;
|
|
memset(&dmScreenSettings,0,sizeof(dmScreenSettings));
|
|
dmScreenSettings.dmSize=sizeof(dmScreenSettings);
|
|
dmScreenSettings.dmPelsWidth = video_mode.width;
|
|
dmScreenSettings.dmPelsHeight = video_mode.height;
|
|
dmScreenSettings.dmBitsPerPel = current.dmBitsPerPel;
|
|
dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;
|
|
|
|
LONG err = ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN);
|
|
if (err!=DISP_CHANGE_SUCCESSFUL) {
|
|
|
|
video_mode.fullscreen=false;
|
|
}*/
|
|
pre_fs_valid=false;
|
|
}
|
|
|
|
DWORD dwExStyle;
|
|
DWORD dwStyle;
|
|
|
|
if (video_mode.fullscreen||video_mode.borderless_window) {
|
|
|
|
dwExStyle=WS_EX_APPWINDOW;
|
|
dwStyle=WS_POPUP;
|
|
|
|
} else {
|
|
dwExStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
|
|
dwStyle=WS_OVERLAPPEDWINDOW;
|
|
if (!video_mode.resizable) {
|
|
dwStyle &= ~WS_THICKFRAME;
|
|
dwStyle &= ~WS_MAXIMIZEBOX;
|
|
}
|
|
}
|
|
|
|
AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle);
|
|
|
|
|
|
char* windowid = getenv("GODOT_WINDOWID");
|
|
if (windowid) {
|
|
|
|
// strtoull on mingw
|
|
#ifdef MINGW_ENABLED
|
|
hWnd = (HWND)strtoull(windowid, NULL, 0);
|
|
#else
|
|
hWnd = (HWND)_strtoui64(windowid, NULL, 0);
|
|
#endif
|
|
SetLastError(0);
|
|
user_proc = (WNDPROC)GetWindowLongPtr(hWnd, GWLP_WNDPROC);
|
|
SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)(WNDPROC)::WndProc);
|
|
DWORD le = GetLastError();
|
|
if (user_proc == 0 && le != 0) {
|
|
|
|
printf("Error setting WNDPROC: %li\n", le);
|
|
};
|
|
LONG_PTR proc = GetWindowLongPtr(hWnd, GWLP_WNDPROC);
|
|
|
|
RECT rect;
|
|
if (!GetClientRect(hWnd, &rect)) {
|
|
MessageBoxW(NULL,L"Window Creation Error.",L"ERROR",MB_OK|MB_ICONEXCLAMATION);
|
|
return; // Return FALSE
|
|
};
|
|
video_mode.width = rect.right;
|
|
video_mode.height = rect.bottom;
|
|
video_mode.fullscreen = false;
|
|
} else {
|
|
|
|
if (!(hWnd=CreateWindowExW(dwExStyle,L"Engine",L"", dwStyle|WS_CLIPSIBLINGS|WS_CLIPCHILDREN, (GetSystemMetrics(SM_CXSCREEN)-WindowRect.right)/2, (GetSystemMetrics(SM_CYSCREEN)-WindowRect.bottom)/2, WindowRect.right-WindowRect.left,WindowRect.bottom-WindowRect.top, NULL,NULL, hInstance,NULL))) {
|
|
MessageBoxW(NULL,L"Window Creation Error.",L"ERROR",MB_OK|MB_ICONEXCLAMATION);
|
|
return; // Return FALSE
|
|
}
|
|
|
|
|
|
};
|
|
|
|
#if defined(OPENGL_ENABLED) || defined(GLES2_ENABLED) || defined(LEGACYGL_ENABLED)
|
|
gl_context = memnew( ContextGL_Win(hWnd,false) );
|
|
gl_context->initialize();
|
|
rasterizer = memnew( RasterizerGLES2 );
|
|
#else
|
|
#ifdef DX9_ENABLED
|
|
rasterizer = memnew( RasterizerDX9(hWnd) );
|
|
#endif
|
|
#endif
|
|
|
|
visual_server = memnew( VisualServerRaster(rasterizer) );
|
|
if (get_render_thread_mode()!=RENDER_THREAD_UNSAFE) {
|
|
|
|
visual_server =memnew(VisualServerWrapMT(visual_server,get_render_thread_mode()==RENDER_SEPARATE_THREAD));
|
|
}
|
|
|
|
//
|
|
physics_server = memnew( PhysicsServerSW );
|
|
physics_server->init();
|
|
|
|
physics_2d_server = Physics2DServerWrapMT::init_server<Physics2DServerSW>();
|
|
physics_2d_server->init();
|
|
|
|
if (!is_no_window_mode_enabled()) {
|
|
ShowWindow(hWnd,SW_SHOW); // Show The Window
|
|
SetForegroundWindow(hWnd); // Slightly Higher Priority
|
|
SetFocus(hWnd); // Sets Keyboard Focus To
|
|
}
|
|
|
|
/*
|
|
DEVMODE dmScreenSettings; // Device Mode
|
|
memset(&dmScreenSettings,0,sizeof(dmScreenSettings)); // Makes Sure Memory's Cleared
|
|
dmScreenSettings.dmSize=sizeof(dmScreenSettings); // Size Of The Devmode Structure
|
|
dmScreenSettings.dmPelsWidth = width; // Selected Screen Width
|
|
dmScreenSettings.dmPelsHeight = height; // Selected Screen Height
|
|
dmScreenSettings.dmBitsPerPel = bits; // Selected Bits Per Pixel
|
|
dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;
|
|
if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)
|
|
|
|
|
|
|
|
|
|
*/
|
|
visual_server->init();
|
|
|
|
input = memnew( InputDefault );
|
|
joystick = memnew (joystick_windows(input, &hWnd));
|
|
|
|
AudioDriverManagerSW::get_driver(p_audio_driver)->set_singleton();
|
|
|
|
if (AudioDriverManagerSW::get_driver(p_audio_driver)->init()!=OK) {
|
|
|
|
ERR_PRINT("Initializing audio failed.");
|
|
}
|
|
|
|
sample_manager = memnew( SampleManagerMallocSW );
|
|
audio_server = memnew( AudioServerSW(sample_manager) );
|
|
|
|
audio_server->init();
|
|
|
|
spatial_sound_server = memnew( SpatialSoundServerSW );
|
|
spatial_sound_server->init();
|
|
spatial_sound_2d_server = memnew( SpatialSound2DServerSW );
|
|
spatial_sound_2d_server->init();
|
|
|
|
TRACKMOUSEEVENT tme;
|
|
tme.cbSize=sizeof(TRACKMOUSEEVENT);
|
|
tme.dwFlags=TME_LEAVE;
|
|
tme.hwndTrack=hWnd;
|
|
tme.dwHoverTime=HOVER_DEFAULT;
|
|
TrackMouseEvent(&tme);
|
|
|
|
//RegisterTouchWindow(hWnd, 0); // Windows 7
|
|
|
|
_ensure_data_dir();
|
|
|
|
DragAcceptFiles(hWnd,true);
|
|
|
|
move_timer_id = 1;
|
|
}
|
|
|
|
void OS_Windows::set_clipboard(const String& p_text) {
|
|
|
|
if (!OpenClipboard(hWnd)) {
|
|
ERR_EXPLAIN("Unable to open clipboard.");
|
|
ERR_FAIL();
|
|
};
|
|
EmptyClipboard();
|
|
|
|
HGLOBAL mem = GlobalAlloc(GMEM_MOVEABLE, (p_text.length() + 1) * sizeof(CharType));
|
|
if (mem == NULL) {
|
|
ERR_EXPLAIN("Unable to allocate memory for clipboard contents.");
|
|
ERR_FAIL();
|
|
};
|
|
LPWSTR lptstrCopy = (LPWSTR)GlobalLock(mem);
|
|
memcpy(lptstrCopy, p_text.c_str(), (p_text.length() + 1) * sizeof(CharType));
|
|
//memset((lptstrCopy + p_text.length()), 0, sizeof(CharType));
|
|
GlobalUnlock(mem);
|
|
|
|
SetClipboardData(CF_UNICODETEXT, mem);
|
|
|
|
// set the CF_TEXT version (not needed?)
|
|
CharString utf8 = p_text.utf8();
|
|
mem = GlobalAlloc(GMEM_MOVEABLE, utf8.length() + 1);
|
|
if (mem == NULL) {
|
|
ERR_EXPLAIN("Unable to allocate memory for clipboard contents.");
|
|
ERR_FAIL();
|
|
};
|
|
LPTSTR ptr = (LPTSTR)GlobalLock(mem);
|
|
memcpy(ptr, utf8.get_data(), utf8.length());
|
|
ptr[utf8.length()] = 0;
|
|
GlobalUnlock(mem);
|
|
|
|
SetClipboardData(CF_TEXT, mem);
|
|
|
|
CloseClipboard();
|
|
};
|
|
|
|
String OS_Windows::get_clipboard() const {
|
|
|
|
String ret;
|
|
if (!OpenClipboard(hWnd)) {
|
|
ERR_EXPLAIN("Unable to open clipboard.");
|
|
ERR_FAIL_V("");
|
|
};
|
|
|
|
if (IsClipboardFormatAvailable(CF_UNICODETEXT)) {
|
|
|
|
HGLOBAL mem = GetClipboardData(CF_UNICODETEXT);
|
|
if (mem != NULL) {
|
|
|
|
LPWSTR ptr = (LPWSTR)GlobalLock(mem);
|
|
if (ptr != NULL) {
|
|
|
|
ret = String((CharType*)ptr);
|
|
GlobalUnlock(mem);
|
|
};
|
|
};
|
|
|
|
} else if (IsClipboardFormatAvailable(CF_TEXT)) {
|
|
|
|
HGLOBAL mem = GetClipboardData(CF_UNICODETEXT);
|
|
if (mem != NULL) {
|
|
|
|
LPTSTR ptr = (LPTSTR)GlobalLock(mem);
|
|
if (ptr != NULL) {
|
|
|
|
ret.parse_utf8((const char*)ptr);
|
|
GlobalUnlock(mem);
|
|
};
|
|
};
|
|
};
|
|
|
|
CloseClipboard();
|
|
|
|
return ret;
|
|
};
|
|
|
|
|
|
void OS_Windows::delete_main_loop() {
|
|
|
|
if (main_loop)
|
|
memdelete(main_loop);
|
|
main_loop=NULL;
|
|
}
|
|
|
|
void OS_Windows::set_main_loop( MainLoop * p_main_loop ) {
|
|
|
|
input->set_main_loop(p_main_loop);
|
|
main_loop=p_main_loop;
|
|
}
|
|
|
|
void OS_Windows::finalize() {
|
|
|
|
if(main_loop)
|
|
memdelete(main_loop);
|
|
|
|
main_loop=NULL;
|
|
|
|
memdelete(joystick);
|
|
memdelete(input);
|
|
|
|
visual_server->finish();
|
|
memdelete(visual_server);
|
|
#ifdef OPENGL_ENABLED
|
|
if (gl_context)
|
|
memdelete(gl_context);
|
|
#endif
|
|
if (rasterizer)
|
|
memdelete(rasterizer);
|
|
|
|
if (user_proc) {
|
|
SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)user_proc);
|
|
};
|
|
|
|
spatial_sound_server->finish();
|
|
memdelete(spatial_sound_server);
|
|
spatial_sound_2d_server->finish();
|
|
memdelete(spatial_sound_2d_server);
|
|
|
|
//if (debugger_connection_console) {
|
|
// memdelete(debugger_connection_console);
|
|
//}
|
|
|
|
memdelete(sample_manager);
|
|
|
|
audio_server->finish();
|
|
memdelete(audio_server);
|
|
|
|
physics_server->finish();
|
|
memdelete(physics_server);
|
|
|
|
physics_2d_server->finish();
|
|
memdelete(physics_2d_server);
|
|
|
|
monitor_info.clear();
|
|
|
|
}
|
|
void OS_Windows::finalize_core() {
|
|
|
|
memdelete(process_map);
|
|
|
|
if (mempool_dynamic)
|
|
memdelete( mempool_dynamic );
|
|
delete mempool_static;
|
|
|
|
|
|
TCPServerWinsock::cleanup();
|
|
StreamPeerWinsock::cleanup();
|
|
}
|
|
|
|
void OS_Windows::vprint(const char* p_format, va_list p_list, bool p_stderr) {
|
|
|
|
const unsigned int BUFFER_SIZE = 16384;
|
|
char buf[BUFFER_SIZE+1]; // +1 for the terminating character
|
|
int len = vsnprintf(buf,BUFFER_SIZE,p_format,p_list);
|
|
if (len<=0)
|
|
return;
|
|
if(len >= BUFFER_SIZE)
|
|
len = BUFFER_SIZE; // Output is too big, will be truncated
|
|
buf[len]=0;
|
|
|
|
|
|
int wlen = MultiByteToWideChar(CP_UTF8,0,buf,len,NULL,0);
|
|
if (wlen<0)
|
|
return;
|
|
|
|
wchar_t *wbuf = (wchar_t*)malloc((len+1)*sizeof(wchar_t));
|
|
MultiByteToWideChar(CP_UTF8,0,buf,len,wbuf,wlen);
|
|
wbuf[wlen]=0;
|
|
|
|
// Recent MinGW and MSVC compilers seem to disagree on the case here
|
|
#ifdef __MINGW32__
|
|
if (p_stderr)
|
|
fwprintf(stderr, L"%S", wbuf);
|
|
else
|
|
wprintf(L"%S", wbuf);
|
|
#else // MSVC
|
|
if (p_stderr)
|
|
fwprintf(stderr, L"%s", wbuf);
|
|
else
|
|
wprintf(L"%s", wbuf);
|
|
#endif
|
|
|
|
#ifdef STDOUT_FILE
|
|
//vwfprintf(stdo,p_format,p_list);
|
|
#endif
|
|
free(wbuf);
|
|
|
|
fflush(stdout);
|
|
};
|
|
|
|
void OS_Windows::alert(const String& p_alert,const String& p_title) {
|
|
|
|
if (!is_no_window_mode_enabled())
|
|
MessageBoxW(NULL, p_alert.c_str(), p_title.c_str(), MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
|
|
else
|
|
print_line("ALERT: "+p_alert);
|
|
}
|
|
|
|
void OS_Windows::set_mouse_mode(MouseMode p_mode) {
|
|
|
|
if (mouse_mode==p_mode)
|
|
return;
|
|
mouse_mode=p_mode;
|
|
if (p_mode==MOUSE_MODE_CAPTURED) {
|
|
RECT clipRect;
|
|
GetClientRect(hWnd, &clipRect);
|
|
ClientToScreen(hWnd, (POINT*) &clipRect.left);
|
|
ClientToScreen(hWnd, (POINT*) &clipRect.right);
|
|
ClipCursor(&clipRect);
|
|
SetCapture(hWnd);
|
|
center=Point2i(video_mode.width/2,video_mode.height/2);
|
|
POINT pos = { (int) center.x, (int) center.y };
|
|
ClientToScreen(hWnd, &pos);
|
|
SetCursorPos(pos.x, pos.y);
|
|
} else {
|
|
ReleaseCapture();
|
|
ClipCursor(NULL);
|
|
}
|
|
|
|
if (p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_HIDDEN) {
|
|
hCursor = SetCursor(NULL);
|
|
} else {
|
|
SetCursor(hCursor);
|
|
}
|
|
}
|
|
|
|
OS_Windows::MouseMode OS_Windows::get_mouse_mode() const{
|
|
|
|
|
|
return mouse_mode;
|
|
}
|
|
|
|
|
|
|
|
void OS_Windows::warp_mouse_pos(const Point2& p_to) {
|
|
|
|
if (mouse_mode==MOUSE_MODE_CAPTURED) {
|
|
|
|
old_x=p_to.x;
|
|
old_y=p_to.y;
|
|
} else {
|
|
|
|
POINT p;
|
|
p.x=p_to.x;
|
|
p.y=p_to.y;
|
|
ClientToScreen(hWnd,&p);
|
|
|
|
SetCursorPos(p.x,p.y);
|
|
}
|
|
}
|
|
|
|
Point2 OS_Windows::get_mouse_pos() const {
|
|
|
|
return Point2(old_x, old_y);
|
|
}
|
|
|
|
int OS_Windows::get_mouse_button_state() const {
|
|
|
|
return last_button_state;
|
|
}
|
|
|
|
void OS_Windows::set_window_title(const String& p_title) {
|
|
|
|
SetWindowTextW(hWnd,p_title.c_str());
|
|
}
|
|
|
|
void OS_Windows::set_video_mode(const VideoMode& p_video_mode,int p_screen) {
|
|
|
|
|
|
}
|
|
|
|
OS::VideoMode OS_Windows::get_video_mode(int p_screen) const {
|
|
|
|
return video_mode;
|
|
}
|
|
void OS_Windows::get_fullscreen_mode_list(List<VideoMode> *p_list,int p_screen) const {
|
|
|
|
|
|
}
|
|
|
|
int OS_Windows::get_screen_count() const {
|
|
|
|
return monitor_info.size();
|
|
}
|
|
int OS_Windows::get_current_screen() const{
|
|
|
|
HMONITOR monitor = MonitorFromWindow(hWnd,MONITOR_DEFAULTTONEAREST);
|
|
for(int i=0;i<monitor_info.size();i++) {
|
|
if (monitor_info[i].hMonitor==monitor)
|
|
return i;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
void OS_Windows::set_current_screen(int p_screen){
|
|
|
|
ERR_FAIL_INDEX(p_screen,monitor_info.size());
|
|
|
|
Vector2 ofs = get_window_position() - get_screen_position(get_current_screen());
|
|
set_window_position(ofs+get_screen_position(p_screen));
|
|
|
|
}
|
|
|
|
Point2 OS_Windows::get_screen_position(int p_screen) const{
|
|
|
|
ERR_FAIL_INDEX_V(p_screen,monitor_info.size(),Point2());
|
|
return Vector2( monitor_info[p_screen].rect.pos );
|
|
|
|
}
|
|
Size2 OS_Windows::get_screen_size(int p_screen) const{
|
|
|
|
ERR_FAIL_INDEX_V(p_screen,monitor_info.size(),Point2());
|
|
return Vector2( monitor_info[p_screen].rect.size );
|
|
|
|
}
|
|
|
|
int OS_Windows::get_screen_dpi(int p_screen) const {
|
|
|
|
ERR_FAIL_INDEX_V(p_screen,monitor_info.size(),72);
|
|
UINT dpix,dpiy;
|
|
return monitor_info[p_screen].dpi;
|
|
|
|
}
|
|
Point2 OS_Windows::get_window_position() const{
|
|
|
|
RECT r;
|
|
GetWindowRect(hWnd,&r);
|
|
return Point2(r.left,r.top);
|
|
}
|
|
void OS_Windows::set_window_position(const Point2& p_position){
|
|
|
|
if (video_mode.fullscreen) return;
|
|
RECT r;
|
|
GetWindowRect(hWnd,&r);
|
|
MoveWindow(hWnd,p_position.x,p_position.y,r.right-r.left,r.bottom-r.top,TRUE);
|
|
|
|
}
|
|
Size2 OS_Windows::get_window_size() const{
|
|
|
|
RECT r;
|
|
GetClientRect(hWnd,&r);
|
|
return Vector2(r.right-r.left,r.bottom-r.top);
|
|
|
|
}
|
|
void OS_Windows::set_window_size(const Size2 p_size){
|
|
|
|
video_mode.width=p_size.width;
|
|
video_mode.height=p_size.height;
|
|
|
|
if (video_mode.fullscreen) {
|
|
return;
|
|
}
|
|
|
|
|
|
RECT crect;
|
|
GetClientRect(hWnd,&crect);
|
|
|
|
RECT rect;
|
|
GetWindowRect(hWnd,&rect);
|
|
int dx = (rect.right-rect.left)-(crect.right-crect.left);
|
|
int dy = (rect.bottom-rect.top)-(crect.bottom-crect.top);
|
|
|
|
rect.right=rect.left+p_size.width+dx;
|
|
rect.bottom=rect.top+p_size.height+dy;
|
|
|
|
|
|
//print_line("PRE: "+itos(rect.left)+","+itos(rect.top)+","+itos(rect.right-rect.left)+","+itos(rect.bottom-rect.top));
|
|
|
|
/*if (video_mode.resizable) {
|
|
AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE);
|
|
} else {
|
|
AdjustWindowRect(&rect, WS_CAPTION | WS_POPUPWINDOW, FALSE);
|
|
}*/
|
|
|
|
//print_line("POST: "+itos(rect.left)+","+itos(rect.top)+","+itos(rect.right-rect.left)+","+itos(rect.bottom-rect.top));
|
|
|
|
MoveWindow(hWnd,rect.left,rect.top,rect.right-rect.left,rect.bottom-rect.top,TRUE);
|
|
|
|
}
|
|
void OS_Windows::set_window_fullscreen(bool p_enabled){
|
|
|
|
if (video_mode.fullscreen==p_enabled)
|
|
return;
|
|
|
|
|
|
|
|
if (p_enabled) {
|
|
|
|
|
|
if (pre_fs_valid) {
|
|
GetWindowRect(hWnd,&pre_fs_rect);
|
|
//print_line("A: "+itos(pre_fs_rect.left)+","+itos(pre_fs_rect.top)+","+itos(pre_fs_rect.right-pre_fs_rect.left)+","+itos(pre_fs_rect.bottom-pre_fs_rect.top));
|
|
//MapWindowPoints(hWnd, GetParent(hWnd), (LPPOINT) &pre_fs_rect, 2);
|
|
//print_line("B: "+itos(pre_fs_rect.left)+","+itos(pre_fs_rect.top)+","+itos(pre_fs_rect.right-pre_fs_rect.left)+","+itos(pre_fs_rect.bottom-pre_fs_rect.top));
|
|
}
|
|
|
|
|
|
int cs = get_current_screen();
|
|
Point2 pos = get_screen_position(cs);
|
|
Size2 size = get_screen_size(cs);
|
|
|
|
/* r.left = pos.x;
|
|
r.top = pos.y;
|
|
r.bottom = pos.y+size.y;
|
|
r.right = pos.x+size.x;
|
|
*/
|
|
SetWindowLongPtr(hWnd, GWL_STYLE,
|
|
WS_SYSMENU | WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE);
|
|
MoveWindow(hWnd, pos.x, pos.y, size.width, size.height, TRUE);
|
|
|
|
video_mode.fullscreen=true;
|
|
|
|
|
|
} else {
|
|
|
|
RECT rect;
|
|
|
|
if (pre_fs_valid) {
|
|
rect=pre_fs_rect;
|
|
} else {
|
|
rect.left=0;
|
|
rect.right=video_mode.width;
|
|
rect.top=0;
|
|
rect.bottom=video_mode.height;
|
|
}
|
|
|
|
|
|
|
|
if (video_mode.resizable) {
|
|
|
|
SetWindowLongPtr(hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW | WS_VISIBLE);
|
|
//AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE);
|
|
MoveWindow(hWnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE);
|
|
} else {
|
|
|
|
SetWindowLongPtr(hWnd, GWL_STYLE, WS_CAPTION | WS_POPUPWINDOW | WS_VISIBLE);
|
|
//AdjustWindowRect(&rect, WS_CAPTION | WS_POPUPWINDOW, FALSE);
|
|
MoveWindow(hWnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE);
|
|
}
|
|
|
|
video_mode.fullscreen=false;
|
|
pre_fs_valid=true;
|
|
/*
|
|
DWORD dwExStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
|
|
DWORD dwStyle=WS_OVERLAPPEDWINDOW;
|
|
if (!video_mode.resizable) {
|
|
dwStyle &= ~WS_THICKFRAME;
|
|
dwStyle &= ~WS_MAXIMIZEBOX;
|
|
}
|
|
AdjustWindowRectEx(&pre_fs_rect, dwStyle, FALSE, dwExStyle);
|
|
video_mode.fullscreen=false;
|
|
video_mode.width=pre_fs_rect.right-pre_fs_rect.left;
|
|
video_mode.height=pre_fs_rect.bottom-pre_fs_rect.top;
|
|
*/
|
|
}
|
|
|
|
// MoveWindow(hWnd,r.left,r.top,p_size.x,p_size.y,TRUE);
|
|
|
|
|
|
}
|
|
bool OS_Windows::is_window_fullscreen() const{
|
|
|
|
return video_mode.fullscreen;
|
|
}
|
|
void OS_Windows::set_window_resizable(bool p_enabled){
|
|
|
|
if (video_mode.resizable==p_enabled)
|
|
return;
|
|
/*
|
|
GetWindowRect(hWnd,&pre_fs_rect);
|
|
DWORD dwExStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
|
|
DWORD dwStyle=WS_OVERLAPPEDWINDOW;
|
|
if (!p_enabled) {
|
|
dwStyle &= ~WS_THICKFRAME;
|
|
dwStyle &= ~WS_MAXIMIZEBOX;
|
|
}
|
|
AdjustWindowRectEx(&pre_fs_rect, dwStyle, FALSE, dwExStyle);
|
|
*/
|
|
|
|
if (!video_mode.fullscreen) {
|
|
if (p_enabled) {
|
|
SetWindowLongPtr(hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW | WS_VISIBLE);
|
|
} else {
|
|
SetWindowLongPtr(hWnd, GWL_STYLE, WS_CAPTION | WS_MINIMIZEBOX | WS_POPUPWINDOW | WS_VISIBLE);
|
|
|
|
}
|
|
|
|
RECT rect;
|
|
GetWindowRect(hWnd,&rect);
|
|
MoveWindow(hWnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE);
|
|
}
|
|
|
|
video_mode.resizable=p_enabled;
|
|
|
|
}
|
|
bool OS_Windows::is_window_resizable() const{
|
|
|
|
return video_mode.resizable;
|
|
}
|
|
void OS_Windows::set_window_minimized(bool p_enabled){
|
|
|
|
if (p_enabled) {
|
|
maximized=false;
|
|
minimized=true;
|
|
ShowWindow(hWnd,SW_MINIMIZE);
|
|
} else {
|
|
ShowWindow(hWnd,SW_RESTORE);
|
|
maximized=false;
|
|
minimized=false;
|
|
}
|
|
}
|
|
bool OS_Windows::is_window_minimized() const{
|
|
|
|
return minimized;
|
|
|
|
}
|
|
void OS_Windows::set_window_maximized(bool p_enabled){
|
|
|
|
if (p_enabled) {
|
|
maximized=true;
|
|
minimized=false;
|
|
ShowWindow(hWnd,SW_MAXIMIZE);
|
|
} else {
|
|
ShowWindow(hWnd,SW_RESTORE);
|
|
maximized=false;
|
|
minimized=false;
|
|
}
|
|
}
|
|
bool OS_Windows::is_window_maximized() const{
|
|
|
|
return maximized;
|
|
}
|
|
|
|
|
|
void OS_Windows::set_borderless_window(int p_borderless) {
|
|
video_mode.borderless_window = p_borderless;
|
|
}
|
|
|
|
bool OS_Windows::get_borderless_window() {
|
|
return video_mode.borderless_window;
|
|
}
|
|
|
|
void OS_Windows::request_attention() {
|
|
|
|
FLASHWINFO info;
|
|
info.cbSize = sizeof(FLASHWINFO);
|
|
info.hwnd = hWnd;
|
|
info.dwFlags = FLASHW_TRAY;
|
|
info.dwTimeout = 0;
|
|
info.uCount = 2;
|
|
FlashWindowEx(&info);
|
|
}
|
|
|
|
void OS_Windows::print_error(const char* p_function, const char* p_file, int p_line, const char* p_code, const char* p_rationale, ErrorType p_type) {
|
|
|
|
HANDLE hCon = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
if (!hCon || hCon == INVALID_HANDLE_VALUE) {
|
|
|
|
const char* err_details;
|
|
if (p_rationale && p_rationale[0])
|
|
err_details = p_rationale;
|
|
else
|
|
err_details = p_code;
|
|
|
|
switch(p_type) {
|
|
case ERR_ERROR:
|
|
print("ERROR: %s: %s\n", p_function, err_details);
|
|
print(" At: %s:%i\n", p_file, p_line);
|
|
break;
|
|
case ERR_WARNING:
|
|
print("WARNING: %s: %s\n", p_function, err_details);
|
|
print(" At: %s:%i\n", p_file, p_line);
|
|
break;
|
|
case ERR_SCRIPT:
|
|
print("SCRIPT ERROR: %s: %s\n", p_function, err_details);
|
|
print(" At: %s:%i\n", p_file, p_line);
|
|
break;
|
|
}
|
|
|
|
} else {
|
|
|
|
CONSOLE_SCREEN_BUFFER_INFO sbi; //original
|
|
GetConsoleScreenBufferInfo(hCon, &sbi);
|
|
|
|
WORD current_fg = sbi.wAttributes & (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
|
|
WORD current_bg = sbi.wAttributes & (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY);
|
|
|
|
uint32_t basecol = 0;
|
|
switch(p_type) {
|
|
case ERR_ERROR: basecol = FOREGROUND_RED; break;
|
|
case ERR_WARNING: basecol = FOREGROUND_RED | FOREGROUND_GREEN; break;
|
|
case ERR_SCRIPT: basecol = FOREGROUND_RED | FOREGROUND_BLUE; break;
|
|
}
|
|
|
|
basecol |= current_bg;
|
|
|
|
if (p_rationale && p_rationale[0]) {
|
|
|
|
SetConsoleTextAttribute(hCon, basecol | FOREGROUND_INTENSITY);
|
|
switch(p_type) {
|
|
case ERR_ERROR: print("ERROR: "); break;
|
|
case ERR_WARNING: print("WARNING: "); break;
|
|
case ERR_SCRIPT: print("SCRIPT ERROR: "); break;
|
|
}
|
|
|
|
SetConsoleTextAttribute(hCon, current_fg | current_bg | FOREGROUND_INTENSITY);
|
|
print("%s\n", p_rationale);
|
|
|
|
SetConsoleTextAttribute(hCon, basecol);
|
|
switch (p_type) {
|
|
case ERR_ERROR: print(" At: "); break;
|
|
case ERR_WARNING: print(" At: "); break;
|
|
case ERR_SCRIPT: print(" At: "); break;
|
|
}
|
|
|
|
SetConsoleTextAttribute(hCon, current_fg | current_bg);
|
|
print("%s:%i\n", p_file, p_line);
|
|
|
|
} else {
|
|
|
|
SetConsoleTextAttribute(hCon, basecol | FOREGROUND_INTENSITY);
|
|
switch(p_type) {
|
|
case ERR_ERROR: print("ERROR: %s: ", p_function); break;
|
|
case ERR_WARNING: print("WARNING: %s: ", p_function); break;
|
|
case ERR_SCRIPT: print("SCRIPT ERROR: %s: ", p_function); break;
|
|
}
|
|
|
|
SetConsoleTextAttribute(hCon, current_fg | current_bg | FOREGROUND_INTENSITY);
|
|
print("%s\n", p_code);
|
|
|
|
SetConsoleTextAttribute(hCon, basecol);
|
|
switch (p_type) {
|
|
case ERR_ERROR: print(" At: "); break;
|
|
case ERR_WARNING: print(" At: "); break;
|
|
case ERR_SCRIPT: print(" At: "); break;
|
|
}
|
|
|
|
SetConsoleTextAttribute(hCon, current_fg | current_bg);
|
|
print("%s:%i\n", p_file, p_line);
|
|
}
|
|
|
|
SetConsoleTextAttribute(hCon, sbi.wAttributes);
|
|
}
|
|
}
|
|
|
|
|
|
String OS_Windows::get_name() {
|
|
|
|
return "Windows";
|
|
}
|
|
|
|
OS::Date OS_Windows::get_date(bool utc) const {
|
|
|
|
SYSTEMTIME systemtime;
|
|
if (utc)
|
|
GetSystemTime(&systemtime);
|
|
else
|
|
GetLocalTime(&systemtime);
|
|
|
|
Date date;
|
|
date.day=systemtime.wDay;
|
|
date.month=Month(systemtime.wMonth);
|
|
date.weekday=Weekday(systemtime.wDayOfWeek);
|
|
date.year=systemtime.wYear;
|
|
date.dst=false;
|
|
return date;
|
|
}
|
|
OS::Time OS_Windows::get_time(bool utc) const {
|
|
|
|
SYSTEMTIME systemtime;
|
|
if (utc)
|
|
GetSystemTime(&systemtime);
|
|
else
|
|
GetLocalTime(&systemtime);
|
|
|
|
Time time;
|
|
time.hour=systemtime.wHour;
|
|
time.min=systemtime.wMinute;
|
|
time.sec=systemtime.wSecond;
|
|
return time;
|
|
}
|
|
|
|
OS::TimeZoneInfo OS_Windows::get_time_zone_info() const {
|
|
TIME_ZONE_INFORMATION info;
|
|
bool daylight = false;
|
|
if (GetTimeZoneInformation(&info) == TIME_ZONE_ID_DAYLIGHT)
|
|
daylight = true;
|
|
|
|
TimeZoneInfo ret;
|
|
if (daylight) {
|
|
ret.name = info.DaylightName;
|
|
} else {
|
|
ret.name = info.StandardName;
|
|
}
|
|
|
|
ret.bias = info.Bias;
|
|
return ret;
|
|
}
|
|
|
|
uint64_t OS_Windows::get_unix_time() const {
|
|
|
|
FILETIME ft;
|
|
SYSTEMTIME st;
|
|
GetSystemTime(&st);
|
|
SystemTimeToFileTime(&st, &ft);
|
|
|
|
SYSTEMTIME ep;
|
|
ep.wYear = 1970;
|
|
ep.wMonth = 1;
|
|
ep.wDayOfWeek = 4;
|
|
ep.wDay = 1;
|
|
ep.wHour = 0;
|
|
ep.wMinute = 0;
|
|
ep.wSecond = 0;
|
|
ep.wMilliseconds = 0;
|
|
FILETIME fep;
|
|
SystemTimeToFileTime(&ep, &fep);
|
|
|
|
return (*(uint64_t*)&ft - *(uint64_t*)&fep) / 10000000;
|
|
};
|
|
|
|
uint64_t OS_Windows::get_system_time_secs() const {
|
|
|
|
|
|
const uint64_t WINDOWS_TICK = 10000000;
|
|
const uint64_t SEC_TO_UNIX_EPOCH = 11644473600LL;
|
|
|
|
SYSTEMTIME st;
|
|
GetSystemTime(&st);
|
|
FILETIME ft;
|
|
SystemTimeToFileTime(&st,&ft);
|
|
uint64_t ret;
|
|
ret=ft.dwHighDateTime;
|
|
ret<<=32;
|
|
ret|=ft.dwLowDateTime;
|
|
|
|
return (uint64_t)(ret / WINDOWS_TICK - SEC_TO_UNIX_EPOCH);
|
|
}
|
|
|
|
void OS_Windows::delay_usec(uint32_t p_usec) const {
|
|
|
|
if (p_usec < 1000)
|
|
Sleep(1);
|
|
else
|
|
Sleep(p_usec / 1000);
|
|
|
|
}
|
|
uint64_t OS_Windows::get_ticks_usec() const {
|
|
|
|
uint64_t ticks;
|
|
uint64_t time;
|
|
// This is the number of clock ticks since start
|
|
if( !QueryPerformanceCounter((LARGE_INTEGER *)&ticks) )
|
|
ticks = (UINT64)timeGetTime();
|
|
// Divide by frequency to get the time in seconds
|
|
time = ticks * 1000000L / ticks_per_second;
|
|
// Subtract the time at game start to get
|
|
// the time since the game started
|
|
time -= ticks_start;
|
|
return time;
|
|
}
|
|
|
|
|
|
void OS_Windows::process_events() {
|
|
|
|
MSG msg;
|
|
|
|
last_id = joystick->process_joysticks(last_id);
|
|
|
|
while(PeekMessageW(&msg,NULL,0,0,PM_REMOVE)) {
|
|
|
|
|
|
TranslateMessage(&msg);
|
|
DispatchMessageW(&msg);
|
|
|
|
}
|
|
|
|
process_key_events();
|
|
|
|
}
|
|
|
|
void OS_Windows::set_cursor_shape(CursorShape p_shape) {
|
|
|
|
ERR_FAIL_INDEX(p_shape,CURSOR_MAX);
|
|
|
|
if (cursor_shape==p_shape)
|
|
return;
|
|
|
|
static const LPCTSTR win_cursors[CURSOR_MAX]={
|
|
IDC_ARROW,
|
|
IDC_IBEAM,
|
|
IDC_HAND,//finger
|
|
IDC_CROSS,
|
|
IDC_WAIT,
|
|
IDC_APPSTARTING,
|
|
IDC_ARROW,
|
|
IDC_ARROW,
|
|
IDC_NO,
|
|
IDC_SIZENS,
|
|
IDC_SIZEWE,
|
|
IDC_SIZENESW,
|
|
IDC_SIZENWSE,
|
|
IDC_SIZEALL,
|
|
IDC_SIZENS,
|
|
IDC_SIZEWE,
|
|
IDC_HELP
|
|
};
|
|
|
|
SetCursor(LoadCursor(hInstance,win_cursors[p_shape]));
|
|
cursor_shape=p_shape;
|
|
}
|
|
|
|
Error OS_Windows::execute(const String& p_path, const List<String>& p_arguments,bool p_blocking,ProcessID *r_child_id,String* r_pipe,int *r_exitcode) {
|
|
|
|
if (p_blocking && r_pipe) {
|
|
|
|
|
|
String argss;
|
|
argss="\"\""+p_path+"\"";
|
|
|
|
for(int i=0;i<p_arguments.size();i++) {
|
|
|
|
argss+=String(" \"")+p_arguments[i]+"\"";
|
|
}
|
|
|
|
// print_line("ARGS: "+argss);
|
|
//argss+"\"";
|
|
//argss+=" 2>nul";
|
|
|
|
FILE* f=_wpopen(argss.c_str(),L"r");
|
|
|
|
ERR_FAIL_COND_V(!f,ERR_CANT_OPEN);
|
|
|
|
char buf[65535];
|
|
while(fgets(buf,65535,f)) {
|
|
|
|
(*r_pipe)+=buf;
|
|
}
|
|
|
|
int rv = _pclose(f);
|
|
if (r_exitcode)
|
|
*r_exitcode=rv;
|
|
|
|
return OK;
|
|
}
|
|
|
|
String cmdline = "\""+p_path+"\"";
|
|
const List<String>::Element* I = p_arguments.front();
|
|
while (I) {
|
|
|
|
|
|
cmdline += " \""+I->get() + "\"";
|
|
|
|
I = I->next();
|
|
};
|
|
|
|
//cmdline+="\"";
|
|
|
|
ProcessInfo pi;
|
|
ZeroMemory( &pi.si, sizeof(pi.si) );
|
|
pi.si.cb = sizeof(pi.si);
|
|
ZeroMemory( &pi.pi, sizeof(pi.pi) );
|
|
LPSTARTUPINFOW si_w = (LPSTARTUPINFOW) &pi.si;
|
|
|
|
print_line("running cmdline: "+cmdline);
|
|
Vector<CharType> modstr; //windows wants to change this no idea why
|
|
modstr.resize(cmdline.size());
|
|
for(int i=0;i<cmdline.size();i++)
|
|
modstr[i]=cmdline[i];
|
|
int ret = CreateProcessW(NULL, modstr.ptr(), NULL, NULL, 0, NORMAL_PRIORITY_CLASS, NULL, NULL, si_w, &pi.pi);
|
|
ERR_FAIL_COND_V(ret == 0, ERR_CANT_FORK);
|
|
|
|
if (p_blocking) {
|
|
|
|
DWORD ret = WaitForSingleObject(pi.pi.hProcess, INFINITE);
|
|
if (r_exitcode)
|
|
*r_exitcode=ret;
|
|
|
|
} else {
|
|
|
|
ProcessID pid = pi.pi.dwProcessId;
|
|
if (r_child_id) {
|
|
*r_child_id = pid;
|
|
};
|
|
process_map->insert(pid, pi);
|
|
};
|
|
return OK;
|
|
};
|
|
|
|
Error OS_Windows::kill(const ProcessID& p_pid) {
|
|
|
|
HANDLE h;
|
|
|
|
if (process_map->has(p_pid)) {
|
|
h = (*process_map)[p_pid].pi.hProcess;
|
|
process_map->erase(p_pid);
|
|
} else {
|
|
|
|
ERR_FAIL_COND_V(!process_map->has(p_pid), FAILED);
|
|
};
|
|
|
|
int ret = TerminateProcess(h, 0);
|
|
|
|
return ret != 0?OK:FAILED;
|
|
};
|
|
|
|
int OS_Windows::get_process_ID() const {
|
|
return _getpid();
|
|
}
|
|
|
|
Error OS_Windows::set_cwd(const String& p_cwd) {
|
|
|
|
if (_wchdir(p_cwd.c_str())!=0)
|
|
return ERR_CANT_OPEN;
|
|
|
|
return OK;
|
|
}
|
|
|
|
String OS_Windows::get_executable_path() const {
|
|
|
|
wchar_t bufname[4096];
|
|
GetModuleFileNameW(NULL,bufname,4096);
|
|
String s= bufname;
|
|
print_line("EXEC PATHP??: "+s);
|
|
return s;
|
|
}
|
|
|
|
void OS_Windows::set_icon(const Image& p_icon) {
|
|
|
|
|
|
Image icon=p_icon;
|
|
if (icon.get_format()!=Image::FORMAT_RGBA)
|
|
icon.convert(Image::FORMAT_RGBA);
|
|
int w = icon.get_width();
|
|
int h = icon.get_height();
|
|
|
|
/* Create temporary bitmap buffer */
|
|
int icon_len = 40 + h * w * 4;
|
|
Vector<BYTE> v;
|
|
v.resize(icon_len);
|
|
BYTE *icon_bmp = &v[0];
|
|
|
|
encode_uint32(40,&icon_bmp[0]);
|
|
encode_uint32(w,&icon_bmp[4]);
|
|
encode_uint32(h*2,&icon_bmp[8]);
|
|
encode_uint16(1,&icon_bmp[12]);
|
|
encode_uint16(32,&icon_bmp[14]);
|
|
encode_uint32(BI_RGB,&icon_bmp[16]);
|
|
encode_uint32(w*h*4,&icon_bmp[20]);
|
|
encode_uint32(0,&icon_bmp[24]);
|
|
encode_uint32(0,&icon_bmp[28]);
|
|
encode_uint32(0,&icon_bmp[32]);
|
|
encode_uint32(0,&icon_bmp[36]);
|
|
|
|
uint8_t *wr=&icon_bmp[40];
|
|
DVector<uint8_t>::Read r= icon.get_data().read();
|
|
|
|
for(int i=0;i<h;i++) {
|
|
|
|
for(int j=0;j<w;j++) {
|
|
|
|
const uint8_t *rpx = &r[((h-i-1)*w+j)*4];
|
|
uint8_t *wpx = &wr[(i*w+j)*4];
|
|
wpx[0]=rpx[2];
|
|
wpx[1]=rpx[1];
|
|
wpx[2]=rpx[0];
|
|
wpx[3]=rpx[3];
|
|
}
|
|
}
|
|
|
|
|
|
HICON hicon = CreateIconFromResource(icon_bmp, icon_len, TRUE, 0x00030000);
|
|
|
|
/* Set the icon for the window */
|
|
SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM) hicon);
|
|
|
|
/* Set the icon in the task manager (should we do this?) */
|
|
SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM) hicon);
|
|
}
|
|
|
|
|
|
bool OS_Windows::has_environment(const String& p_var) const {
|
|
|
|
return getenv(p_var.utf8().get_data()) != NULL;
|
|
};
|
|
|
|
String OS_Windows::get_environment(const String& p_var) const {
|
|
|
|
wchar_t wval[0x7Fff]; // MSDN says 32767 char is the maximum
|
|
int wlen = GetEnvironmentVariableW(p_var.c_str(),wval,0x7Fff);
|
|
if ( wlen > 0 ) {
|
|
return wval;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
String OS_Windows::get_stdin_string(bool p_block) {
|
|
|
|
if (p_block) {
|
|
char buff[1024];
|
|
return fgets(buff,1024,stdin);
|
|
};
|
|
|
|
return String();
|
|
}
|
|
|
|
|
|
void OS_Windows::enable_for_stealing_focus(ProcessID pid) {
|
|
|
|
AllowSetForegroundWindow(pid);
|
|
|
|
}
|
|
|
|
void OS_Windows::move_window_to_foreground() {
|
|
|
|
SetForegroundWindow(hWnd);
|
|
|
|
}
|
|
|
|
Error OS_Windows::shell_open(String p_uri) {
|
|
|
|
ShellExecuteW(NULL, L"open", p_uri.c_str(), NULL, NULL, SW_SHOWNORMAL);
|
|
return OK;
|
|
}
|
|
|
|
|
|
String OS_Windows::get_locale() const {
|
|
|
|
const _WinLocale *wl = &_win_locales[0];
|
|
|
|
LANGID langid = GetUserDefaultUILanguage();
|
|
String neutral;
|
|
int lang = langid&((1<<9)-1);
|
|
int sublang = langid&~((1<<9)-1);
|
|
|
|
while(wl->locale) {
|
|
|
|
if (wl->main_lang==lang && wl->sublang==SUBLANG_NEUTRAL)
|
|
neutral=wl->locale;
|
|
|
|
if (lang==wl->main_lang && sublang==wl->sublang)
|
|
return wl->locale;
|
|
|
|
|
|
wl++;
|
|
}
|
|
|
|
if (neutral!="")
|
|
return neutral;
|
|
|
|
return "en";
|
|
}
|
|
|
|
|
|
OS::LatinKeyboardVariant OS_Windows::get_latin_keyboard_variant() const {
|
|
|
|
unsigned long azerty[] = {
|
|
0x00020401, // Arabic (102) AZERTY
|
|
0x0001080c, // Belgian (Comma)
|
|
0x0000080c, // Belgian French
|
|
0x0000040c, // French
|
|
0 // <--- STOP MARK
|
|
};
|
|
unsigned long qwertz[] = {
|
|
0x0000041a, // Croation
|
|
0x00000405, // Czech
|
|
0x00000407, // German
|
|
0x00010407, // German (IBM)
|
|
0x0000040e, // Hungarian
|
|
0x0000046e, // Luxembourgish
|
|
0x00010415, // Polish (214)
|
|
0x00000418, // Romanian (Legacy)
|
|
0x0000081a, // Serbian (Latin)
|
|
0x0000041b, // Slovak
|
|
0x00000424, // Slovenian
|
|
0x0001042e, // Sorbian Extended
|
|
0x0002042e, // Sorbian Standard
|
|
0x0000042e, // Sorbian Standard (Legacy)
|
|
0x0000100c, // Swiss French
|
|
0x00000807, // Swiss German
|
|
0 // <--- STOP MARK
|
|
};
|
|
unsigned long dvorak[] = {
|
|
0x00010409, // US-Dvorak
|
|
0x00030409, // US-Dvorak for left hand
|
|
0x00040409, // US-Dvorak for right hand
|
|
0 // <--- STOP MARK
|
|
};
|
|
|
|
char name[ KL_NAMELENGTH + 1 ]; name[0] = 0;
|
|
GetKeyboardLayoutNameA( name );
|
|
|
|
unsigned long hex = strtoul(name, NULL, 16);
|
|
|
|
int i=0;
|
|
while( azerty[i] != 0 ) {
|
|
if (azerty[i] == hex) return LATIN_KEYBOARD_AZERTY;
|
|
i++;
|
|
}
|
|
|
|
i = 0;
|
|
while( qwertz[i] != 0 ) {
|
|
if (qwertz[i] == hex) return LATIN_KEYBOARD_QWERTZ;
|
|
i++;
|
|
}
|
|
|
|
i = 0;
|
|
while( dvorak[i] != 0 ) {
|
|
if (dvorak[i] == hex) return LATIN_KEYBOARD_DVORAK;
|
|
i++;
|
|
}
|
|
|
|
return LATIN_KEYBOARD_QWERTY;
|
|
}
|
|
|
|
void OS_Windows::release_rendering_thread() {
|
|
|
|
gl_context->release_current();
|
|
|
|
}
|
|
|
|
void OS_Windows::make_rendering_thread() {
|
|
|
|
gl_context->make_current();
|
|
}
|
|
|
|
void OS_Windows::swap_buffers() {
|
|
|
|
gl_context->swap_buffers();
|
|
}
|
|
|
|
|
|
void OS_Windows::run() {
|
|
|
|
if (!main_loop)
|
|
return;
|
|
|
|
main_loop->init();
|
|
|
|
uint64_t last_ticks=get_ticks_usec();
|
|
|
|
int frames=0;
|
|
uint64_t frame=0;
|
|
|
|
while (!force_quit) {
|
|
|
|
process_events(); // get rid of pending events
|
|
if (Main::iteration()==true)
|
|
break;
|
|
};
|
|
|
|
main_loop->finish();
|
|
|
|
}
|
|
|
|
|
|
|
|
MainLoop *OS_Windows::get_main_loop() const {
|
|
|
|
return main_loop;
|
|
}
|
|
|
|
String OS_Windows::get_system_dir(SystemDir p_dir) const {
|
|
|
|
|
|
int id;
|
|
|
|
|
|
|
|
switch(p_dir) {
|
|
case SYSTEM_DIR_DESKTOP: {
|
|
id=CSIDL_DESKTOPDIRECTORY;
|
|
} break;
|
|
case SYSTEM_DIR_DCIM: {
|
|
id=CSIDL_MYPICTURES;
|
|
} break;
|
|
case SYSTEM_DIR_DOCUMENTS: {
|
|
id=CSIDL_PERSONAL;
|
|
} break;
|
|
case SYSTEM_DIR_DOWNLOADS: {
|
|
id=0x000C ;
|
|
} break;
|
|
case SYSTEM_DIR_MOVIES: {
|
|
id=CSIDL_MYVIDEO;
|
|
} break;
|
|
case SYSTEM_DIR_MUSIC: {
|
|
id=CSIDL_MYMUSIC;
|
|
} break;
|
|
case SYSTEM_DIR_PICTURES: {
|
|
id=CSIDL_MYPICTURES;
|
|
} break;
|
|
case SYSTEM_DIR_RINGTONES: {
|
|
id=CSIDL_MYMUSIC;
|
|
} break;
|
|
}
|
|
|
|
WCHAR szPath[MAX_PATH];
|
|
HRESULT res = SHGetFolderPathW(NULL,id,NULL,0,szPath);
|
|
ERR_FAIL_COND_V(res!=S_OK,String());
|
|
return String(szPath);
|
|
|
|
}
|
|
String OS_Windows::get_data_dir() const {
|
|
|
|
String an = get_safe_application_name();
|
|
if (an!="") {
|
|
|
|
if (has_environment("APPDATA")) {
|
|
|
|
bool use_godot = Globals::get_singleton()->get("application/use_shared_user_dir");
|
|
if (!use_godot)
|
|
return (OS::get_singleton()->get_environment("APPDATA")+"/"+an).replace("\\","/");
|
|
else
|
|
return (OS::get_singleton()->get_environment("APPDATA")+"/Godot/app_userdata/"+an).replace("\\","/");
|
|
}
|
|
}
|
|
|
|
return Globals::get_singleton()->get_resource_path();
|
|
|
|
|
|
}
|
|
|
|
bool OS_Windows::is_joy_known(int p_device) {
|
|
return input->is_joy_mapped(p_device);
|
|
}
|
|
|
|
String OS_Windows::get_joy_guid(int p_device) const {
|
|
return input->get_joy_guid_remapped(p_device);
|
|
}
|
|
|
|
void OS_Windows::set_use_vsync(bool p_enable) {
|
|
|
|
if (gl_context)
|
|
gl_context->set_use_vsync(p_enable);
|
|
}
|
|
|
|
bool OS_Windows::is_vsync_enabled() const{
|
|
|
|
if (gl_context)
|
|
return gl_context->is_using_vsync();
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
OS_Windows::OS_Windows(HINSTANCE _hInstance) {
|
|
|
|
key_event_pos=0;
|
|
force_quit=false;
|
|
alt_mem=false;
|
|
gr_mem=false;
|
|
shift_mem=false;
|
|
control_mem=false;
|
|
meta_mem=false;
|
|
minimized = false;
|
|
|
|
hInstance=_hInstance;
|
|
pressrc=0;
|
|
old_invalid=true;
|
|
last_id=0;
|
|
mouse_mode=MOUSE_MODE_VISIBLE;
|
|
#ifdef STDOUT_FILE
|
|
stdo=fopen("stdout.txt","wb");
|
|
#endif
|
|
user_proc = NULL;
|
|
|
|
#ifdef RTAUDIO_ENABLED
|
|
AudioDriverManagerSW::add_driver(&driver_rtaudio);
|
|
#endif
|
|
#ifdef XAUDIO2_ENABLED
|
|
AudioDriverManagerSW::add_driver(&driver_xaudio2);
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
OS_Windows::~OS_Windows()
|
|
{
|
|
#ifdef STDOUT_FILE
|
|
fclose(stdo);
|
|
#endif
|
|
}
|
|
|
|
|