Force Qt-Window to specific screen - c++

I have a Windows-system with two monitors connected to it that itself expand the Windows-desktop. Now I want to start two Qt-applications but need to force each of them to a specific monitor, means application A always has to open it's window on monitor 1, application B always has to open it's window on monitor 2 (no matter where they have been opened the last time and no matter where the mouse is located at the moment).
How can this be done automatically? Can it only be done via the screen-coordinates of the desktop? If yes: how can I force my QWidget-based window to a specific coordinate? If no: how else can this be done?

To get the number of screens at runtime you can use:
int screenCount = QApplication::desktop()->screenCount();
To get the geometry of a screen, you can use:
QRect screenRect = QApplication::desktop()->screenGeometry(1); // 0-indexed, so this would get the second screen
Moving a window to that position (or resizing it) is then trivial:
yourWindow->move(QPoint(screenRect.x(), screenRect.y()));

Related

Drawing a custom mouse cursor

I am in need of drawing a custom mouse cursor using C++ for my application. I am using a device to track hand movements and translate them into a cursor position on the screen.
However, for this application the system cursor may or may not be displayed, so I need to somehow draw onto the screen. I also need to interact with any window. Because of this, I'm not sure of my course of action - I believe I can create a "transparent" window to draw on while passing clicks/drags through onto the next window but am not sure how to go about this.
EDIT: Clarification - This application might be run on a touchscreen device where the cursor is hidden by some means. I'm trying my best to create this application as "hands-off" of the host system as possible - this program is meant to be a drag and drop application that will just work on a system with none or very very minimal actions done to the settings of the host system itself.
Currently, I have the following which draws a cursor at the location of the "mouse" but I cannot figure out how to refresh the screen or draw over the old "cursor" before drawing the new one. This causes a repeated cursor across the screen:
// Some thread
DWORD WINAPI Cursor(LPVOID lpParam) {
auto dc = GetDC(NULL);
std::optional<POINT> point;
while (true) {
if (point.has_value()) {
auto p = point.value();
RoundRect(......); // Paint over old cursor
}
POINT p;
if (GetCursorPos(&p)) {
point.emplace(p);
RoundRect(......); // Paint new cursor
}
}
I've read that simply calling another RoundRect on the same location will clear it but that's obviously not the case or I'm doing it wrong.
I believe my program can have super privileges if it needs to. Working with WINAPI seems to be kind of a hassle compared to this kind of stuff on linux, regarding permissions of working with different windows and allowing clicks and such to go through....
Any suggestions for how to proceed?

How to prevent my app's window from going to another monitor (Electron + macOS)

I've got an app that programmatically moves its window around the user's screen. My problem:
User has two physical monitors
User starts app in primary monitor
App moves window, causing more of it to "overflow" into the secondary monitor than is on the primary monitor
This causes the app's window to entirely jump to that secondary monitor and disappear from the primary monitor.
I would like for the window to stay in the primary monitor unless I want it to go to the secondary monitor, or some defined point (e.g. the center) goes into the secondary monitor, or it would even be ok if the window were split and shown on both. Is there any way to prevent this jump from happening as soon the window intersects more with the secondary monitor?
To reiterate, I'm moving the window programmatically and am using Electron on macOS.
Also, this doesn't appear to happen when I move window manually, but I think this is because it's not using its percentage rule but going on whether or not the point of the mouse has entered the secondary monitor.
Also, I'm open to any kind of solution, including C/C++ or Swift.
EDIT: Here's how I'm currently moving the window:
win.setPosition(x, y);
where win is an instance of Electron's BrowserWindow.
The desired behavior is to move the window anywhere on a display. Currently, if some of the window goes off the edge of the current display enough, it jumps to another display. This is because Apple's default behavior it to automatically move a window to the display with which it overlaps the most and hide it on all other displays. It is worth noting that this is not the behavior when dragging a window manually, but just when moving it programmatically.
I don't know how you are moving this window, or how you want the window to act when it hits the edge, but I will do my best to give you some solutions.
My first thought was for you to create the bounds of the screen when the program starts.
So maybe use GetSystemMetrics(), screen.getPrimaryDisplay(), or perhaps this pseudo-code helps:
var canvas = GetWindowSize()
var canvasWidth = canvas.width
var canvasHeight = canvas.height
if myWindow.x + mywindow.width > canvasWidth
//if you want it to bounce off the edge
then myWindow.direction = oppositeDirection
//if you want it to wrap to the other side of the screen
then myWindow.X -= canvasWidth
You should check out this code of a Bouncing DVD Logo.
Or look at my Asteroids game to see how ship and asteroids move in the screen.
Furthermore, you can use the distance formula to have the window react to the edge when the center gets close. (Although, this would also require you to get the screen bounds x and y.)
Hope that helps!
EDIT:
I think if you were to somehow use the entire screen, (both monitors) your window wouldn't even recognize that there is an edge. That being said, I still don't know what your code actually does.
It's not working when you do win.setPosition(x, y); because of mac OS behaviour.
(I also tried with electron quick start project https://github.com/electron/electron-quick-start, even with the same electron version 6.0.5)
What I did is to programmatically simulate a mouse drag of your app to the right of the screen. To do it you can use robotjs. You will not see the dragging effect, which is pretty close to do a setPosition(x, y)
When you install it you could have an issue when starting you app. Then you have to rebuild rebotJS for your version of electron. https://github.com/octalmage/robotjs/wiki/Electron
So what you will try to do programmatically is
Working code
// get your app bounds and screen bounds
const windowBounds = mainWindow.getBounds()
const displayBounds = electron.screen.getPrimaryDisplay().bounds
setTimeout(() => {
// 1. move mouse upon the top left corner your app
robot.moveMouse(windowBounds.x + 100, windowBounds.y + 5);
// 2. mouse left key, 'down'
robot.mouseToggle("down", "left");
// 3. drag window to (100, 100) from default
robot.dragMouse(displayBounds.width, 0);
// 4. mouse left key toggle 'up'
robot.mouseToggle("up", "left");
}, 100);
Example in main.js: https://playcode.io/electron
I use those version:
"devDependencies": {
"electron": "^6.0.5"
},
"dependencies": {
"robotjs": "^0.6.0"
}
and my versoon of nodejs is v10.20.0
Maybe it's not quite what you asked for, but here's a thought: why not resize the window instead of letting it "overflow" to other screens?
When you predict that window will go out bounds of current display, just reduce its size.
It can be done by something along this lines:
const winWidth = 800;
const winHeight = 600;
const SAFETY_MARGINS = 10;
let mainWindow;
// setPosition //////////////////////////////////////////////////////////////////////////
function setPosition(x, y) {
const displayBounds = screen.getPrimaryDisplay().bounds
const fixedX = Math.min(
displayBounds.x + displayBounds.width,
Math.max(x, displayBounds.x)
);
const fixedY = Math.min(
displayBounds.y + displayBounds.height,
Math.max(y, displayBounds.y)
);
mainWindow.setPosition(fixedX, fixedY);
const fixedWidth = Math.max(SAFETY_MARGINS, Math.min(winWidth, displayBounds.width + displayBounds.x - x, winWidth - displayBounds.x + x));
const fixedHeight = Math.max(SAFETY_MARGINS, Math.min(winHeight, displayBounds.height + displayBounds.y - y, winHeight - displayBounds.y + y));
mainWindow.setSize(fixedWidth, fixedHeight);
}
This version of setPosition will always resize your window so that it won't go beyond display bounds.
You can modify/extend it to allow window to go out of bounds just a bit and to scroll window's content (if needed).
I believe that with some tweaks, for most users, resizing and moving the window just slightly off the screen would look more-or-less like actually moving the window off the screen.
Here's a demo moving the window randomly using this code:
https://github.com/yoava/so-61092503
(I didn't test in with dual screens as I'm only with my laptop now.)

Qt - cross platform behaviour

I am trying to deploy a cross platform Qt application written in c++. It works fine on my Ubuntu Linux but when I run it on Windows the application's main window's position gets set on the very top left point of the screen with the upper frame (that holds the minimize, maximize, close buttons) missing.
That is it until i resize the main window (in this case making the width smaller from the right). When this happens the upper frame and the control buttons appear as in the visualization I provided.
Note: I've removed all widgets on the app so they do not аppear as a distraction.
Note2: It appears the maximize button is disabled, which is not the case inside Ubuntu. I have not set any window flags.
How do i visualize the upper frame at the very start of the application without the need to resize the window. I understand its an OS specific behaviour. Setting the main window's geometry with a starting point with a higher y value does NOT help. It still pops at the very top left of the screen.
try to use QWidget::move to set the Window position after setGeometry.
If the widget is a window, the position is that of the widget on the
desktop, including its frame.
You ask a question about cross-platform UI code, and then you don't show the full code. Please show the full code.
The one line of code you do show does something in the wrong way: if you want to maximize a window, call the appropriate function, instead of setting its absolute size that you think shows the window maximized. Windows, their decorations and their placement are very very platform specific, and you should prefer their cross-platform abstractions over trying to do them yourself.
Concretely: the window positioning handles the decorations (title bar) differently on Windows and on Ubuntu. There is absolutely nothing you can do about it except not position your windows absolutely like this.
In the MainWindow constructor at the end this->setGeometry(0, 0, 1336, 600);
That's the problem. setGeometry deals with the geometry of the client area. This is well documented. You should use move to change the position of the widget frame. To set the size of the frame requires the knowledge of the frame's incremental width and height:
bool setWidgetFrameGeometry(QWidget *w, const QRect &r) {
auto frame = w->frameGeometry().size();
auto client = w->size();
auto delta = frame - client;
auto maxDelta = 128;
if (delta.width() > 0 && delta.width() < maxDelta
&& delta.height() > 0 && delta.height() < maxDelta) {
w->move(r.topLeft());
w->resize(r.size() - delta);
return true;
}
return false;
}
The call may need to be deferred to when the event loop had a chance to run:
auto setter = [this]{ return setWidgetFrameGeometry(this, {0,0,1336,600}); };
if (!setter())
QTimer::singleShot(0, this, setter);

Stop app window overlapping Windows start bar

On my Win7 PC I have the start-bar running vertically, it's about 60px wide. In my win32 application, the created window always appears overlapping the start bar, which looks bad - I just use (0,0) as the top-left position for the window.
How should I be doing it to get (0,0) relative to the desktop, taking the Start Bar into account? Is there a flag, or do I manually need to look up a metric?
There are a few problems here. You don't want to use a hard-coded value like (0,0). That might not even be visible on a multi-monitor system. As you have discovered, you should try to avoid overlapping with the taskbar or other appbars. If there are multiple monitors you should try and start on the monitor where the user has most recently interacted.
There is a simple way to make much of this happen for free. Pass CW_USEDEFAULT as the x and y coordinates when you call CreateWindow. This will let the window manager do the hard work of making your window appear in a sensible location.
You can get the system to tell you the coordinates of the work area. The work area is that part of the desktop that does not contain the taskbar or other appbars. Call SystemParametersInfo passing SPI_GETWORKAREA.
Retrieves the size of the work area on the primary display monitor. The work area is the portion of the screen not obscured by the system taskbar or by application desktop toolbars. The pvParam parameter must point to a RECT structure that receives the coordinates of the work area, expressed in virtual screen coordinates.
To get the work area of a monitor other than the primary display monitor, call the GetMonitorInfo function.
Use SetWindowPlacement. The (0,0) for that function excludes the taskbar and any other appbars.

Getting mouse position unbounded by screen size, c++ & windows

I'm currently writing a c++ console application that grabs the mouse position at regular intervals and sends it to another visual application where it is used to drive some 3d graphics in real time. The visual app is closed source and cannot be altered outside it's limited plug-in functionality.
Currently I'm using the GetCursorPos() function which is easy and fast enough, but I'm running into the issue that all of the data is clipped based on the current screen resolution of 1920x1600 so that all x values are between 0 and 1920 and all y values are between 0 and 1600 no matter how far the mouse is physically moved.
I need to get the mouse position before it's clipped at the edge of the screen, or possibly the deltas which I could use to calculate the current position.
I've seen some references to the windows MouseMove event but I would really not want to implement a window to make it work or especially have it as the active to receive those events.
I'm working in a windows environment and a language change is not feasible.
I might be wrong, but in Win32 land you don't get mouse move messages when the mouse is at the edge of the screen because, well, the mouse isn't moving. The usual way to get an infinite mouse area is to do the following:
Hide the mouse, get exclusive access and record position
Centre mouse to window
When mouse moves, get delta from centre of screen to current position
Centre mouse to window again
The next mouse move should have a delta of (0,0), so ignore it
Go to 3 until end of mouse move operation
Reset position, show the mouse and release exclusive access
If you didn't hide the mouse, then you'd see the mouse moving a small distance and then snapping back to the centre position, which looks nasty.
This method does require a message pump for the mouse move messages so the console application idea probably won't work with this. Can you create a full screen invisible window for grabbing the mouse?
Just get the position, and move it to the center and return the delta yourself
This is how FPS games do it
I don't have any direct experience with raw input, which is probably what you need to tap into. According to MSDN, you have to register the device, then setup your winproc to accept the WM_INPUT messages and then do your calculations based on the raw data.
Here's another relevant link.