diff --git a/defos/defos.lua b/defos/defos.lua index 36d5d0b..239fbcb 100644 --- a/defos/defos.lua +++ b/defos/defos.lua @@ -11,10 +11,20 @@ according to the document, we do not need to load user32.dll manually: local C = ffi.C if ffi.os == "Windows" then + local GWL_STYLE = -16 + local WS_POPUP = 0x80000000 + local WS_CAPTION = 0x00C00000 + local WS_SYSMENU = 0x00080000 + local WS_SIZEBOX = 0x00040000 + local WS_MAXIMIZEBOX = 0x00010000 + local WS_MINIMIZEBOX = 0x00020000 + local SWP_NOZORDER = 0x0004 + local SW_MAXIMIZE = 3 + -- add definitions here, -- to make it clear and avoid re-define exception (for struct) when calling a method more than 1 time ffi.cdef([[ - + typedef long LONG; typedef int BOOL; typedef unsigned long DWORD; @@ -32,7 +42,7 @@ if ffi.os == "Windows" then typedef HANDLE HWND; typedef HICON HCURSOR; typedef BYTE BCHAR; - + static const int CCHDEVICENAME = 32; static const int CCHFORMNAME = 32; @@ -49,7 +59,7 @@ if ffi.os == "Windows" then LONG x; LONG y; } POINT, *PPOINT, *NPPOINT, *LPPOINT; - + typedef struct _POINTL { LONG x; @@ -75,7 +85,7 @@ if ffi.os == "Windows" then HCURSOR hCursor; POINT ptScreenPos; } CURSORINFO, *PCURSORINFO, *LPCURSORINFO; - + typedef struct _devicemode { BCHAR dmDeviceName[CCHDEVICENAME]; WORD dmSpecVersion; @@ -98,7 +108,7 @@ if ffi.os == "Windows" then DWORD dmDisplayOrientation; DWORD dmDisplayFixedOutput; }; - + short dmColor; short dmDuplex; short dmYResolution; @@ -123,8 +133,17 @@ if ffi.os == "Windows" then DWORD dmPanningWidth; DWORD dmPanningHeight; } DEVMODE, *PDEVMODE; - - + + typedef struct tagWINDOWPLACEMENT { + UINT length; + UINT flags; + UINT showCmd; + POINT ptMinPosition; + POINT ptMaxPosition; + RECT rcNormalPosition; + } WINDOWPLACEMENT, *PWINDOWPLACEMENT, *LPWINDOWPLACEMENT; + + int GetSystemMetrics(int nIndex); @@ -151,8 +170,14 @@ if ffi.os == "Windows" then BOOL GetMonitorInfoW(HMONITOR hMonitor, LPMONITORINFO lpmi); BOOL MoveWindow(HWND hWnd, int X, int Y, int nWidth, int nHeight, BOOL bRepaint); - + HWND GetDesktopWindow(void); + + BOOL GetWindowPlacement(HWND hWnd, WINDOWPLACEMENT *lpwndpl); + + BOOL SetWindowPlacement(HWND hWnd,const WINDOWPLACEMENT *lpwndpl); + + BOOL ShowWindow(HWND hWnd,int nCmdShow); ]]) @@ -189,8 +214,6 @@ if ffi.os == "Windows" then -- @usage set_window_style(bit.band(bit.bnot(WS_MAXIMIZEBOX), bit.bnot(WS_MINIMIZEBOX))) or set_window_style(bit.bnot(WS_SIZEBOX)) -- @return true if success to change style, or false local function set_window_style(style) - - local GWL_STYLE = -16 local ptr = C.GetActiveWindow() local value = C.GetWindowLongPtrA(ptr, GWL_STYLE) @@ -202,12 +225,12 @@ if ffi.os == "Windows" then local function get_monitor_info() --https://blogs.msdn.microsoft.com/oldnewthing/20050505-04/?p=35703/ - local MONITOR_DEFAULTTONULL = 0x00000000 -- Returns NULL. - local MONITOR_DEFAULTTOPRIMARY = 0x00000001 -- Returns a handle to the primary display monitor. + --local MONITOR_DEFAULTTONULL = 0x00000000 -- Returns NULL. + --local MONITOR_DEFAULTTOPRIMARY = 0x00000001 -- Returns a handle to the primary display monitor. local MONITOR_DEFAULTTONEAREST = 0x00000002 -- Returns a handle to the display monitor that is nearest to the window. local hwnd = C.GetActiveWindow() - local monitor = C.MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY) + local monitor = C.MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST) local mi = ffi.new("MONITORINFO") mi.cbSize = ffi.sizeof("MONITORINFO") @@ -217,10 +240,53 @@ if ffi.os == "Windows" then return (success and mi) or nil end --- https://github.com/glfw/glfw-legacy/tree/master/lib --- https://github.com/luapower/winapi/blob/master/winapi/window.lua ---http://www.glfw.org/GLFWReference27.pdf -function M.get_mouse_pos() + -- Retrieves the show state and the restored, minimized, and maximized positions of window. + -- @return WINDOWPLACEMENT if success, or nil + local function get_window_placement(hwnd) + local placement = ffi.new("WINDOWPLACEMENT") + placement.length = ffi.sizeof("WINDOWPLACEMENT") + + local successed = C.GetWindowPlacement(hwnd, placement) + + return (successed > 0 and placement) or nil + end + + -- set the window placement, always used to restore window state + local function set_window_placement(hwnd, placement) + if hwnd and placement then + C.SetWindowPlacement(hwnd, placement) + end + end + + local is_maximize = false + local is_fullscreen = false; + local previous_state = {style = nil, placement=nil} + + function M.is_maximize() + return is_maximize + end + + function M.toggle_maximize() + if is_fullscreen then + M.toggle_fullscreen() + end + + local hwnd = C.GetActiveWindow() + + if is_maximize then + set_window_placement(hwnd, previous_state.placement) + is_maximize = false + else + previous_state.placement = get_window_placement(hwnd) + C.ShowWindow(hwnd, SW_MAXIMIZE) + is_maximize = true + end + end + + -- https://github.com/glfw/glfw-legacy/tree/master/lib + -- https://github.com/luapower/winapi/blob/master/winapi/window.lua + --http://www.glfw.org/GLFWReference27.pdf + function M.get_mouse_pos() -- definitions --ffi.cdef[[ @@ -238,69 +304,57 @@ function M.get_mouse_pos() return {x = pci.ptScreenPos.x, y = pci.ptScreenPos.y} end - local is_fullscreen = false; - local previous_state = {style = nil, rect = nil} - function M.is_fullscreen() return is_fullscreen end function M.toggle_fullscreen() - - -- TODO: shall we move const to out to make it re-usable? - local GWL_STYLE = -16 - local WS_POPUP = 0x80000000 - local WS_CAPTION = 0x00C00000 - local WS_SYSMENU = 0x00080000 - local WS_MINIMIZEBOX = 0x00020000 - local WS_MAXIMIZEBOX = 0x00010000 - local SWP_NOZORDER = 0x0004 - + + if is_maximize then + -- if itis maximized, then we need to restore it then toggle fullscreen + M.toggle_maximize() + end + local hwnd = C.GetActiveWindow() - + if not is_fullscreen then - - -- first remember current style and rect - local prect = ffi.new("RECT") - - C.GetWindowRect(hwnd, prect) - -- TODO: exception handle? - previous_state.rect = prect previous_state.style = C.GetWindowLongPtrA(hwnd, GWL_STYLE) - + previous_state.placement = get_window_placement(hwnd) + + -- get desktop rect, or we can use get_monitor_info? local rc = ffi.new("RECT") - local dhwnd = C.GetDesktopWindow() -- get desktop handle, or we can use get_monitor_info - - C.GetWindowRect(dhwnd, rc) -- get desktop rect - - local windowed_fullscreen_style = bit.band(bit.bnot(WS_CAPTION), bit.bnot(WS_SYSMENU), bit.bnot(WS_MINIMIZEBOX), bit.bnot(WS_MAXIMIZEBOX)) - + local dhwnd = C.GetDesktopWindow() + C.GetWindowRect(dhwnd, rc) + + -- for fullscreen, we remove these styles + local windowed_fullscreen_style = bit.band( + bit.bnot(WS_CAPTION), + bit.bnot(WS_SYSMENU), + bit.bnot(WS_MINIMIZEBOX), + bit.bnot(WS_MAXIMIZEBOX), + bit.bnot(WS_SIZEBOX)) + if set_window_style(windowed_fullscreen_style) then - if C.SetWindowPos(hwnd, nil, 0, 0, rc.right, rc.bottom, SWP_NOZORDER) then + if C.SetWindowPos(hwnd, nil, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, SWP_NOZORDER) then -- we are in fullscreen mode is_fullscreen = true end end else -- TODO: exception handle? - -- restore the style + -- restore the style and position/size C.SetWindowLongPtrA(hwnd, GWL_STYLE, previous_state.style) - C.SetWindowPos(hwnd, - nil, - previous_state.rect.left, - previous_state.rect.top, - previous_state.rect.right - previous_state.rect.left, - previous_state.rect.bottom - previous_state.rect.top, - SWP_NOZORDER) + set_window_placement(hwnd, previous_state.placement) + is_fullscreen = false end end function M.set_window_size(pos_x, pos_y, width, height) - local HWND_TOP = 0 - local SWP_NOMOVE = 0x0002 - local SWP_NOZORDER = 0x0004 + --local HWND_TOP = 0 + --local SWP_NOMOVE = 0x0002 + --local SWP_NOZORDER = 0x0004 local SM_CXSCREEN = 0 local SM_CYSCREEN = 1 @@ -327,22 +381,16 @@ function M.get_mouse_pos() function M.disable_window_resize() - local WS_SIZEBOX = 0x00040000 - set_window_style(bit.bnot(WS_SIZEBOX)) end function M.disable_maximize_button() - local WS_MAXIMIZEBOX = 0x00010000 - set_window_style(bit.bnot(WS_MAXIMIZEBOX)) end function M.disable_minimize_button() - local WS_MINIMIZEBOX = 0x00020000 - set_window_style(bit.bnot(WS_MINIMIZEBOX)) end