diff options
Diffstat (limited to 'platform/windows/display_server_windows.cpp')
-rw-r--r-- | platform/windows/display_server_windows.cpp | 328 |
1 files changed, 282 insertions, 46 deletions
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index 4138f53a9d..e32b377047 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -32,9 +32,11 @@ #include "os_windows.h" +#include "core/config/project_settings.h" #include "core/io/marshalls.h" +#include "drivers/png/png_driver_common.h" #include "main/main.h" -#include "scene/resources/texture.h" +#include "scene/resources/atlas_texture.h" #if defined(GLES3_ENABLED) #include "drivers/gles3/rasterizer_gles3.h" @@ -42,6 +44,8 @@ #include <avrt.h> #include <dwmapi.h> +#include <shlwapi.h> +#include <shobjidl.h> #ifndef DWMWA_USE_IMMERSIVE_DARK_MODE #define DWMWA_USE_IMMERSIVE_DARK_MODE 20 @@ -87,6 +91,7 @@ bool DisplayServerWindows::has_feature(Feature p_feature) const { case FEATURE_HIDPI: case FEATURE_ICON: case FEATURE_NATIVE_ICON: + case FEATURE_NATIVE_DIALOG: case FEATURE_SWAP_BUFFERS: case FEATURE_KEEP_SCREEN_ON: case FEATURE_TEXT_TO_SPEECH: @@ -213,6 +218,129 @@ void DisplayServerWindows::tts_stop() { tts->stop(); } +Error DisplayServerWindows::file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) { + _THREAD_SAFE_METHOD_ + + Vector<Char16String> filter_names; + Vector<Char16String> filter_exts; + for (const String &E : p_filters) { + Vector<String> tokens = E.split(";"); + if (tokens.size() == 2) { + filter_exts.push_back(tokens[0].strip_edges().utf16()); + filter_names.push_back(tokens[1].strip_edges().utf16()); + } else if (tokens.size() == 1) { + filter_exts.push_back(tokens[0].strip_edges().utf16()); + filter_names.push_back(tokens[0].strip_edges().utf16()); + } + } + + Vector<COMDLG_FILTERSPEC> filters; + for (int i = 0; i < filter_names.size(); i++) { + filters.push_back({ (LPCWSTR)filter_names[i].ptr(), (LPCWSTR)filter_exts[i].ptr() }); + } + + HRESULT hr = S_OK; + IFileDialog *pfd = nullptr; + if (p_mode == FILE_DIALOG_MODE_SAVE_FILE) { + hr = CoCreateInstance(CLSID_FileSaveDialog, nullptr, CLSCTX_INPROC_SERVER, IID_IFileSaveDialog, (void **)&pfd); + } else { + hr = CoCreateInstance(CLSID_FileOpenDialog, nullptr, CLSCTX_INPROC_SERVER, IID_IFileOpenDialog, (void **)&pfd); + } + if (SUCCEEDED(hr)) { + DWORD flags; + pfd->GetOptions(&flags); + if (p_mode == FILE_DIALOG_MODE_OPEN_FILES) { + flags |= FOS_ALLOWMULTISELECT; + } + if (p_mode == FILE_DIALOG_MODE_OPEN_DIR) { + flags |= FOS_PICKFOLDERS; + } + if (p_show_hidden) { + flags |= FOS_FORCESHOWHIDDEN; + } + pfd->SetOptions(flags | FOS_FORCEFILESYSTEM); + pfd->SetTitle((LPCWSTR)p_title.utf16().ptr()); + + String dir = ProjectSettings::get_singleton()->globalize_path(p_current_directory); + if (dir == ".") { + dir = OS::get_singleton()->get_executable_path().get_base_dir(); + } + dir = dir.replace("/", "\\"); + + IShellItem *shellitem = nullptr; + hr = SHCreateItemFromParsingName((LPCWSTR)dir.utf16().ptr(), nullptr, IID_IShellItem, (void **)&shellitem); + if (SUCCEEDED(hr)) { + pfd->SetDefaultFolder(shellitem); + pfd->SetFolder(shellitem); + } + + pfd->SetFileName((LPCWSTR)p_filename.utf16().ptr()); + pfd->SetFileTypes(filters.size(), filters.ptr()); + pfd->SetFileTypeIndex(0); + + hr = pfd->Show(nullptr); + if (SUCCEEDED(hr)) { + Vector<String> file_names; + + if (p_mode == FILE_DIALOG_MODE_OPEN_FILES) { + IShellItemArray *results; + hr = static_cast<IFileOpenDialog *>(pfd)->GetResults(&results); + if (SUCCEEDED(hr)) { + DWORD count = 0; + results->GetCount(&count); + for (DWORD i = 0; i < count; i++) { + IShellItem *result; + results->GetItemAt(i, &result); + + PWSTR file_path = nullptr; + hr = result->GetDisplayName(SIGDN_FILESYSPATH, &file_path); + if (SUCCEEDED(hr)) { + file_names.push_back(String::utf16((const char16_t *)file_path)); + CoTaskMemFree(file_path); + } + result->Release(); + } + results->Release(); + } + } else { + IShellItem *result; + hr = pfd->GetResult(&result); + if (SUCCEEDED(hr)) { + PWSTR file_path = nullptr; + hr = result->GetDisplayName(SIGDN_FILESYSPATH, &file_path); + if (SUCCEEDED(hr)) { + file_names.push_back(String::utf16((const char16_t *)file_path)); + CoTaskMemFree(file_path); + } + result->Release(); + } + } + if (!p_callback.is_null()) { + Variant v_status = true; + Variant v_files = file_names; + Variant *v_args[2] = { &v_status, &v_files }; + Variant ret; + Callable::CallError ce; + p_callback.callp((const Variant **)&v_args, 2, ret, ce); + } + } else { + if (!p_callback.is_null()) { + Variant v_status = false; + Variant v_files = Vector<String>(); + Variant *v_args[2] = { &v_status, &v_files }; + Variant ret; + Callable::CallError ce; + p_callback.callp((const Variant **)&v_args, 2, ret, ce); + } + } + pfd->Release(); + + return OK; + } else { + return ERR_CANT_OPEN; + } +} + void DisplayServerWindows::mouse_set_mode(MouseMode p_mode) { _THREAD_SAFE_METHOD_ @@ -341,6 +469,68 @@ String DisplayServerWindows::clipboard_get() const { return ret; } +Ref<Image> DisplayServerWindows::clipboard_get_image() const { + Ref<Image> image; + if (!windows.has(last_focused_window)) { + return image; // No focused window? + } + if (!OpenClipboard(windows[last_focused_window].hWnd)) { + ERR_FAIL_V_MSG(image, "Unable to open clipboard."); + } + UINT png_format = RegisterClipboardFormatA("PNG"); + if (png_format && IsClipboardFormatAvailable(png_format)) { + HANDLE png_handle = GetClipboardData(png_format); + if (png_handle) { + size_t png_size = GlobalSize(png_handle); + uint8_t *png_data = (uint8_t *)GlobalLock(png_handle); + image.instantiate(); + + PNGDriverCommon::png_to_image(png_data, png_size, false, image); + + GlobalUnlock(png_handle); + } + } else if (IsClipboardFormatAvailable(CF_DIB)) { + HGLOBAL mem = GetClipboardData(CF_DIB); + if (mem != NULL) { + BITMAPINFO *ptr = static_cast<BITMAPINFO *>(GlobalLock(mem)); + + if (ptr != NULL) { + BITMAPINFOHEADER *info = &ptr->bmiHeader; + PackedByteArray pba; + + for (LONG y = info->biHeight - 1; y > -1; y--) { + for (LONG x = 0; x < info->biWidth; x++) { + tagRGBQUAD *rgbquad = ptr->bmiColors + (info->biWidth * y) + x; + pba.append(rgbquad->rgbRed); + pba.append(rgbquad->rgbGreen); + pba.append(rgbquad->rgbBlue); + pba.append(rgbquad->rgbReserved); + } + } + image.instantiate(); + image->create_from_data(info->biWidth, info->biHeight, false, Image::Format::FORMAT_RGBA8, pba); + + GlobalUnlock(mem); + } + } + } + + CloseClipboard(); + + return image; +} + +bool DisplayServerWindows::clipboard_has() const { + return (IsClipboardFormatAvailable(CF_TEXT) || + IsClipboardFormatAvailable(CF_UNICODETEXT) || + IsClipboardFormatAvailable(CF_OEMTEXT)); +} + +bool DisplayServerWindows::clipboard_has_image() const { + UINT png_format = RegisterClipboardFormatA("PNG"); + return ((png_format && IsClipboardFormatAvailable(png_format)) || IsClipboardFormatAvailable(CF_DIB)); +} + typedef struct { int count; int screen; @@ -618,7 +808,7 @@ Color DisplayServerWindows::screen_get_pixel(const Point2i &p_position) const { COLORREF col = GetPixel(dc, p.x, p.y); if (col != CLR_INVALID) { ReleaseDC(NULL, dc); - return Color(float(col & 0x000000FF) / 256.0, float((col & 0x0000FF00) >> 8) / 256.0, float((col & 0x00FF0000) >> 16) / 256.0, 1.0); + return Color(float(col & 0x000000FF) / 255.0f, float((col & 0x0000FF00) >> 8) / 255.0f, float((col & 0x00FF0000) >> 16) / 255.0f, 1.0f); } ReleaseDC(NULL, dc); } @@ -2010,6 +2200,38 @@ Key DisplayServerWindows::keyboard_get_keycode_from_physical(Key p_keycode) cons return (Key)(KeyMappingWindows::get_keysym(vk) | modifiers); } +Key DisplayServerWindows::keyboard_get_label_from_physical(Key p_keycode) const { + Key modifiers = p_keycode & KeyModifierMask::MODIFIER_MASK; + Key keycode_no_mod = (Key)(p_keycode & KeyModifierMask::CODE_MASK); + + if (keycode_no_mod == Key::PRINT || + keycode_no_mod == Key::KP_ADD || + keycode_no_mod == Key::KP_5 || + (keycode_no_mod >= Key::KEY_0 && keycode_no_mod <= Key::KEY_9)) { + return p_keycode; + } + + unsigned int scancode = KeyMappingWindows::get_scancode(keycode_no_mod); + if (scancode == 0) { + return p_keycode; + } + + Key keycode = KeyMappingWindows::get_keysym(MapVirtualKey(scancode, MAPVK_VSC_TO_VK)); + + HKL current_layout = GetKeyboardLayout(0); + static BYTE keyboard_state[256]; + memset(keyboard_state, 0, 256); + wchar_t chars[256] = {}; + UINT extended_code = MapVirtualKey(scancode, MAPVK_VSC_TO_VK_EX); + if (ToUnicodeEx(extended_code, scancode, keyboard_state, chars, 255, 4, current_layout) > 0) { + String keysym = String::utf16((char16_t *)chars, 255); + if (!keysym.is_empty()) { + return fix_key_label(keysym[0], keycode) | modifiers; + } + } + return p_keycode; +} + String _get_full_layout_name_from_registry(HKL p_layout) { String id = "SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\" + String::num_int64((int64_t)p_layout, 16, false).lpad(8, "0"); String ret; @@ -2194,55 +2416,65 @@ void DisplayServerWindows::set_native_icon(const String &p_filename) { void DisplayServerWindows::set_icon(const Ref<Image> &p_icon) { _THREAD_SAFE_METHOD_ - ERR_FAIL_COND(!p_icon.is_valid()); - if (icon != p_icon) { - icon = p_icon->duplicate(); - if (icon->get_format() != Image::FORMAT_RGBA8) { - icon->convert(Image::FORMAT_RGBA8); + if (p_icon.is_valid()) { + ERR_FAIL_COND(p_icon->get_width() <= 0 || p_icon->get_height() <= 0); + + Ref<Image> img = p_icon; + if (img != icon) { + img = img->duplicate(); + img->convert(Image::FORMAT_RGBA8); } - } - 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.ptrw(); - - 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]; - const uint8_t *r = icon->get_data().ptr(); - - 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]; + + int w = img->get_width(); + int h = img->get_height(); + + // Create temporary bitmap buffer. + int icon_len = 40 + h * w * 4; + Vector<BYTE> v; + v.resize(icon_len); + BYTE *icon_bmp = v.ptrw(); + + 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]; + const uint8_t *r = img->get_data().ptr(); + + 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); + HICON hicon = CreateIconFromResource(icon_bmp, icon_len, TRUE, 0x00030000); + ERR_FAIL_COND(!hicon); + + icon = img; - // Set the icon for the window. - SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_SMALL, (LPARAM)hicon); + // Set the icon for the window. + SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_SMALL, (LPARAM)hicon); - // Set the icon in the task manager (should we do this?). - SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_BIG, (LPARAM)hicon); + // Set the icon in the task manager (should we do this?). + SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_BIG, (LPARAM)hicon); + } else { + icon = Ref<Image>(); + SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_SMALL, 0); + SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_BIG, 0); + } } void DisplayServerWindows::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) { @@ -3606,6 +3838,10 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } break; case WM_DESTROY: { Input::get_singleton()->flush_buffered_events(); + if (window_mouseover_id == window_id) { + window_mouseover_id = INVALID_WINDOW_ID; + _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_EXIT); + } } break; case WM_SETCURSOR: { if (LOWORD(lParam) == HTCLIENT) { |