Skip to content

Commit

Permalink
Implement multi-display support
Browse files Browse the repository at this point in the history
This commit implements multi-display support: the grid will be shown on
the display that currently has the cursor and can be moved through
screen borders.

Fixes #2
  • Loading branch information
lesderid committed Jan 16, 2024
1 parent efc88b7 commit a0d2eab
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 24 deletions.
44 changes: 25 additions & 19 deletions src/keynavish/commands.d
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ private void start()
import core.sys.windows.windows : MoveWindow;

resetGrid();
MoveWindow(windowHandle, 0, 0, grid.rect.width, grid.rect.height, false);

auto virtualScreen = virtualScreenRectangle;
MoveWindow(windowHandle, virtualScreen.left, virtualScreen.top, virtualScreen.width, virtualScreen.height, false);

showWindow();
}
Expand Down Expand Up @@ -108,24 +110,26 @@ private void cut(Direction direction, string arg)
auto value = getCutMoveValue(direction, arg != null ? arg : "0.5");
auto diff = (direction == Direction.up || direction == Direction.down) ? grid.rect.height - value : grid.rect.width - value;

auto virtualScreen = virtualScreenRectangle;

Grid newGrid = grid;
final switch (direction) with (Direction)
{
case up:
newGrid.rect.bottom -= diff;
if (newGrid.rect.bottom < 0) newGrid.rect.bottom = 0;
if (newGrid.rect.bottom < virtualScreen.top) newGrid.rect.bottom = virtualScreen.top;
break;
case down:
newGrid.rect.top += diff;
if (newGrid.rect.top < 0) newGrid.rect.top = 0;
if (newGrid.rect.top < virtualScreen.top) newGrid.rect.top = virtualScreen.top;
break;
case left:
newGrid.rect.right -= diff;
if (newGrid.rect.right < 0) newGrid.rect.right = 0;
if (newGrid.rect.right < virtualScreen.left) newGrid.rect.right = virtualScreen.left;
break;
case right:
newGrid.rect.left += diff;
if (newGrid.rect.left < 0) newGrid.rect.left = 0;
if (newGrid.rect.left < virtualScreen.left) newGrid.rect.left = virtualScreen.left;
break;
}

Expand All @@ -145,7 +149,9 @@ private void move(Direction direction, string arg)

if (!active) return;

auto resolution = deviceResolution;
auto virtualScreen = virtualScreenRectangle;
auto virtualScreenRight = virtualScreen.left + virtualScreen.width;
auto virtualScreenBottom = virtualScreen.top + virtualScreen.height;

auto value = getCutMoveValue(direction, arg != null ? arg : "1");

Expand All @@ -155,37 +161,37 @@ private void move(Direction direction, string arg)
case up:
newGrid.rect.top -= value;
newGrid.rect.bottom -= value;
if (newGrid.rect.top < 0)
if (newGrid.rect.top < virtualScreen.top)
{
newGrid.rect.bottom -= newGrid.rect.top;
newGrid.rect.top = 0;
newGrid.rect.bottom -= (newGrid.rect.top - virtualScreen.top);
newGrid.rect.top = virtualScreen.top;
}
break;
case down:
newGrid.rect.top += value;
newGrid.rect.bottom += value;
if (newGrid.rect.bottom > resolution.height)
if (newGrid.rect.bottom > virtualScreenBottom)
{
newGrid.rect.top -= (newGrid.rect.bottom - resolution.height);
newGrid.rect.bottom = resolution.height;
newGrid.rect.top -= (newGrid.rect.bottom - virtualScreenBottom);
newGrid.rect.bottom = virtualScreenBottom;
}
break;
case left:
newGrid.rect.left -= value;
newGrid.rect.right -= value;
if (newGrid.rect.left < 0)
if (newGrid.rect.left < virtualScreen.left)
{
newGrid.rect.right -= newGrid.rect.left;
newGrid.rect.left = 0;
newGrid.rect.right -= (newGrid.rect.left - virtualScreen.left);
newGrid.rect.left = virtualScreen.left;
}
break;
case right:
newGrid.rect.left += value;
newGrid.rect.right += value;
if (newGrid.rect.right > resolution.width)
if (newGrid.rect.right > virtualScreenRight)
{
newGrid.rect.left -= (newGrid.rect.right - resolution.width);
newGrid.rect.right = resolution.width;
newGrid.rect.left -= (newGrid.rect.right - virtualScreenRight);
newGrid.rect.right = virtualScreenRight;
}
break;
}
Expand All @@ -202,7 +208,7 @@ private void warp()

if (!active) return;

auto resolution = deviceResolution;
auto resolution = primaryDeviceResolution;

auto middleX = grid.rect.left + grid.rect.width / 2;
auto middleY = grid.rect.top + grid.rect.height / 2;
Expand Down
54 changes: 50 additions & 4 deletions src/keynavish/grid.d
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ HPEN mainPen;
HPEN borderPen;

@property
Tuple!(int, "width", int, "height") deviceResolution()
Tuple!(int, "width", int, "height") primaryDeviceResolution()
{
import core.sys.windows.windows : GetDC, GetDeviceCaps, HORZRES, VERTRES;

Expand All @@ -32,6 +32,41 @@ Tuple!(int, "width", int, "height") deviceResolution()
return resolution;
}

@property
RECT[] displayRectangles()
{
import core.sys.windows.windows : EnumDisplayMonitors, MONITORENUMPROC, BOOL, TRUE, HMONITOR, HDC, LPRECT, LPARAM;

RECT[] displayRectangles = [];

static extern(Windows) BOOL callback(HMONITOR, HDC, LPRECT rectangle, LPARAM userData)
{
RECT[]* displayRectangles = cast(RECT[]*) cast(void*) userData;

*displayRectangles ~= *rectangle;

return TRUE;
}

EnumDisplayMonitors(null, null, &callback, cast(LPARAM) cast(void*) &displayRectangles);

return displayRectangles;
}

@property
Tuple!(int, "width", int, "height", int, "left", int, "top") virtualScreenRectangle()
{
import core.sys.windows.windows : GetSystemMetrics, SM_CXVIRTUALSCREEN, SM_CYVIRTUALSCREEN, SM_XVIRTUALSCREEN, SM_YVIRTUALSCREEN;

auto virtualScreen = typeof(return)();
virtualScreen.width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
virtualScreen.height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
virtualScreen.left = GetSystemMetrics(SM_XVIRTUALSCREEN);
virtualScreen.top = GetSystemMetrics(SM_YVIRTUALSCREEN);

return virtualScreen;
}

const(Grid) grid()
{
return grid_;
Expand All @@ -55,9 +90,18 @@ void tryPopGrid()

void resetGrid()
{
auto resolution = deviceResolution;
import core.sys.windows.windows : POINT, GetCursorPos;
import std.algorithm : find;
import std.range : empty;

POINT cursorPosition;
auto result = GetCursorPos(&cursorPosition);
assert(result);

grid_.rect = RECT(0, 0, resolution.width, resolution.height);
auto cursorScreen = displayRectangles.find!(r => r.contains(cursorPosition));
assert(!cursorScreen.empty);

grid_.rect = cursorScreen[0];
grid_.rows = 2;
grid_.columns = 2;
gridStack = typeof(gridStack)();
Expand All @@ -84,13 +128,15 @@ void paintGrid(HDC deviceContext)
import std.algorithm : map;
import std.range : repeat, join, array;

auto virtualScreen = virtualScreenRectangle;

auto pointArrays = splitGrid.map!(r => [
POINT(r.left, r.top),
POINT(r.right, r.top),
POINT(r.right, r.bottom),
POINT(r.left, r.bottom),
POINT(r.left, r.top)
]).join;
].map!(p => POINT(p.x - virtualScreen.left, p.y - virtualScreen.top))).join;

DWORD[] sizes = uint(5).repeat(pointArrays.length).array;

Expand Down
6 changes: 6 additions & 0 deletions src/keynavish/helpers.d
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ LONG height(RECT rect)
return rect.bottom - rect.top;
}

bool contains(RECT rect, POINT point)
{
return point.x >= rect.left && point.x < rect.right &&
point.y >= rect.top && point.y < rect.bottom;
}

string expandPath(string inputString)
{
import std.process : environment;
Expand Down
2 changes: 1 addition & 1 deletion src/keynavish/window.d
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ void registerWindowClass()

void createWindow()
{
auto resolution = deviceResolution;
auto resolution = virtualScreenRectangle;

windowHandle = CreateWindowEx(WS_EX_LAYERED | WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
windowClassName.ptr,
Expand Down

0 comments on commit a0d2eab

Please sign in to comment.