CGDisplayCreateImageForRect: how to ignore a specific NSWindow - macos

I'm looking to make a sample "lens" app which show what's is visible under the current mouse location. I've used CGDisplayCreateImageForRect to get a portion of the screen under the mouse location.
Now I would to attach a transparent window at the same location of the mouse and show this lens directly under the mouse position; however under this location there is...my transparent window with the result zoom... ops!
Is there a way to exclude a particular window from the snapshot or another method to get the current image at mouse position by ignoring something behind it?

You can't do it with that function. You can use the CGWindowList API to do it: either CGWindowListCreateImage() or CGWindowListCreateImageFromArray(). These let you specify criteria to select the windows to include or an explicit list of windows.
It's not clearly documented how to obtain the window ID of one of your own windows. The supported way is probably to query information about all on-screen windows using CGWindowListCopyWindowInfo() and then use the properties to identify yours. That said, I believe that the NSWindow property windowNumber does in fact correspond the Core Graphics window ID.

#ken-thomases point me to the right direction. The function I've used to include all windows and exclude my single one is CGWindowListCreateImageFromArray().
The code below is a small example:
// Get onscreen windows
CGWindowID windowIDToExcude = (CGWindowID)[myNSWindow windowNumber];
CFArrayRef onScreenWindows = CGWindowListCreate(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
CFMutableArrayRef finalList = CFArrayCreateMutableCopy(NULL, 0, onScreenWindows);
for (long i = CFArrayGetCount(finalList) - 1; i >= 0; i--) {
CGWindowID window = (CGWindowID)(uintptr_t)CFArrayGetValueAtIndex(finalList, i);
if (window == windowIDToExcude)
CFArrayRemoveValueAtIndex(finalList, i);
}
// Get the composite image
CGImageRef ref = CGWindowListCreateImageFromArray(myRectToGrab, finalList, kCGWindowListOptionAll);

Related

Is there a way to return all Widgets from a parent Window with X11/Xt?

I'm writing an application using X11, Xt, and Motif directly with C. I want to have the ability to list all the children widgets from a parent Window. Is there a way to do this?
I found the following snippet to recursively parse a Motif widget tree here, but I only have an Xlib Window struct, so I want to be able get the children Widgets of that Window, then pass that Widget to something akin to that recursive tree parser.
My current code looks something like this:
int main() {
Display* display;
int screen_num = 0;
display = XOpenDisplay(NULL);
Window window = XRootWindow(display, screen_num);
dumpWidgetTree((Widget)window);
return 0;
}
I tried simply casting Window to Widget, but that just caused a segfault, as expected.
You can get most of the widgets from the window tree. You can use the 'XtWindowToWidget' to translate windowID to widget. This approach will work for the widget in the current app, and will not be able to access windowless widgets (a.k.a. Gadgets).

Getting a notification when other app's window move / resize

I'm creating a screen scraping application. What I'm trying now is to fit a rectangle around the corners of the active windows of a given application (by PID).
I managed that by getting a reference to all the active windows in the workspace and using CGWindowListCopyWindowInfo to match the owner PID. Now I have an array of the active windows, but after showing the rectangle that incorporates all the windows as above stated, I didn't find a way to register to get notifications when those windows are moved and / or resized in order to resize and fit my drawn rectangle.
The following is a sample of the code I used to get the NSWindow's frames of the selected application.
pid_t pid = [selectedApplication processIdentifier];
CFArrayRef windowList = CGWindowListCopyWindowInfo(kCGWindowListOptionAll,kCGNullWindowID);
for (NSMutableDictionary* entry in (NSArray*)windowList) {
pid_t ownerPID = [[entry objectForKey:(id)kCGWindowOwnerPID] integerValue];
if (pid == ownerPID) {
// setting the bounds for each window of the application on match
CGRectMakeWithDictionaryRepresentation((CFDictionaryRef)[entry objectForKey:(id)kCGWindowBounds], &bounds);
.....
Is there any way in which I can register for updates from those NSwindows given a specific PID of an application I don't own ?
Currently this is a limitation of the APIs from Apple. Didn't find any other way to register for update NSNotifications from other applications NSWindows. Maybe this is treated as a security measure.

BitBlt not able to capture the title bar correctly

I am using the code below to capture a screenshot of a window using bltblt. However the titlebar appears completely black in the captured screenshot. I am running the code on Windows 8.1. Is there a way i can correctly capture the title bar.
// Retrieve the handle to a display device context for the sourceWindow
hdcScreen = GetDC(ss);
// Retrieve the handle to a display device context for the dest window
hdcWindow = GetDC(hWnd);
//Get the client area for size calculation
RECT rcClient;
GetWindowRect(ss, &rcClient);
if (!BitBlt(hdcWindow,
0, 0,
rcClient.right - rcClient.left, rcClient.bottom - rcClient.top,
hdcScreen,
0, 0,
SRCCOPY|CAPTUREBLT))
{
MessageBox(hWnd, L"BitBlt has failed", L"Failed", MB_OK);
goto done;
}
EDIT:
The window i am displaying the screenshot in would cover the entire desktop and will be constantly updating the screenshot of the window just behind it. Also, the window displaying the screenshot will always be the topmost window.
The information you want is not all available from the window DC. Themes get painted over the top.
If you want an exactly visual representation you need to find the screen coordinates of the window (or part of it) and then blit from the screen DC.
If the window is not displayed, you may have an insurmountable problem. As far as I know the theme (since at least Windows Vista) is not part of the Window DC but is painted over the top using non-GDI techniques. The GDI simply does not have the capabilities to paint sophisticated blends and transparency effect. Until Windows 8 it was still possible to select the old classic themes but now they're gone. You may find that the title bar simply isn't painted in the NCPAINT handler any more.

Is it possible to have a QWidget without a display?

I have a console-only win32 application (which can be launched as a windows service) where I want to create QWidget objects and interact with them.
These QWidget will not be rendered locally (because there is no display) but I need to access painting operations (to create screenshot of them for example) and I need to intercept mouse and keyboard operations.
The aim of the service is to provide a display of the widget to a remote application. So the image, mouse and keyboard are meant to be redirected over the network.
Is it possible to have QWidget objects in a console application? What is the preferred method to intercept painting, mouse and keyboard operations in that case?
If this is possible, is it still possible with QAxWidget objects?
Have a peek at Qt for embedded Linux.. Qt is designed so you can do this, but it is non-trivial.
I do suspect you're not on the right track, though, if you have a console-mode service that needs a keyboard, mouse and a graphical UI. The need to interact with a user tells me that it should not be a service, and the need for a mouse suggests that it shouldn't be a console app either.
You can create a QApplication without a Gui using one of the provided constructors:
QApplication::QApplication(int&, char**, bool GuiEnabled)
In order to do GUI operations you'll still need a GUI available. For example, on Linux it will still require that X be running and available. I believe there are certain restrictions on what can and can't happen but can't find the blog post on http://labs.qt.nokia.com that provides details.
At the moment i myself am trying to do something similar. The approach i have taken is creating subclass of QGraphicsScene and overriding QGraphicsScene::sceneChanged event. Then it goes as follows (pseudocode):
QApplication app;
MyGraphicsScene* mgs = new MyGraphicsScene();
MyWidget* mw = new MyWidget();
mgs->addWidget(mw);
Now each time change happens your sceneChanged will be invoked. There you can get snapshot of scene as QImage. In my case i move pixel data to texture and render it as overlay of my game:
void QEOverlay::sceneChanged(QList<QRectF> list)
{
//loop through all screen areas that changed
foreach(QRectF rect, list)
{
//generate a rectangle that is a while number, and fits withing the screen
if(rect.x() < 0) rect.setX(0);
if(rect.y() < 0) rect.setY(0);
if(rect.right() > _width) rect.setRight(_width);
if(rect.bottom() > _height) rect.setRight(_height);
rect = QRectF(Round(rect.x()),Round(rect.y()),Round(rect.width()),Round(rect.height()));
//Create an image, create a qpainter, fill the image with transparent color, and then render a part of the scene to it
QImage image(rect.width(),rect.height(),QImage::Format_ARGB32);
QPainter p(&image);
image.fill(0);
render(&p,image.rect(),rect);
if(tex.lock())
{
for (u32 y = 0; y < image.height(); y++)
{
for (u32 x = 0; x < image.width(); x++)
{
QRgb pix = image.pixel(x, y);
tex.color(x + rect.left(), y + rect.top(), Color(qRed(pix), qGreen(pix), qBlue(pix), qAlpha(pix)));
}
}
tex.unlock();
}
}
}
There is issue with this approach. You still need to redirect keyboard and mouse input events to your subclass. That does not work out for me very well, there are certain issues like mouse click not focusing QLineEdit or elements in QWebView.

win32: detect if start menu is auto-hidden?

I want to position a window at the bottom of the screen. If the start menu is present, I want it to lie along the top of the start menu. If it is not (or it is auto-hidden), I still want it to be in the same position as it would be if the start menu was there, meaning there will be a gap of a few pixels.
Currently I get the monitor work area, position the window at the bottom, and always offset by 20 pixels or so. If the start menu isn't there, this works well. If it is, however, the work area also shrinks (as it should), and I end up double-offsetting.
How would I fix the issue?
To get the work area of the screen not obscured by the system taskbar or by application desktop toolbars, you can use SystemParametersInfo() with SPI_GETWORKAREA as uiAction parameter. The pvParam parameter must point to a RECT structure that receives the coordinates of the work area, expressed in virtual screen coordinates. For example:
RECT rectWorkArea;
SystemParametersInfo(SPI_GETWORKAREA, 0, &rectWorkArea, 0);
As you said in the comment, to get the bounding rectangle of the taskbar, we can call SHAppBarMessage(ABM_GETTASKBARPOS, ...)
To determine the position of the taskbar (whether it is currently at the bottom, top, left, or right of the screen), you could use the following calculation:
type
TTaskBarPos = (_TOP, _BOTTOM, _LEFT, _RIGHT);
var
iScrW, iScrH: Longint;
iScrW := GetSystemMetrics(SM_CXSCREEN);
iScrH := GetSystemMetrics(SM_CXSCREEN);
if (rectTaskbar.Top > iScrH div 2) and (rectTaskbar.Right >= iScrW) then
Result := _BOTTOM
else if (rectTaskbar.Top < iScrH div 2) and (rectTaskbar.Bottom <= iScrW div 2) then
Result := _TOP
else if (rectTaskbar.Left < iScrW div 2) and (rectTaskbar.Top <= 0) then
Result := _LEFT
else
Result := _RIGHT;
They should be enough to solve your current problem. However, if you need to know (for another reason) the current taskbar settings of the autohide and always-on-top states, you can use SHAppBarMessage(ABM_GETSTATE, ...).
If you need to be notified that that the taskbar's autohide or always-on-top state has changed, you have to intercept ABN_STATECHANGE message.
Are you are using or have access to .NET in your project?
If so, you can use the Screen.PrimaryScreen.WorkingArea.Height property to determine the bottom of the screen excluding the task bar.
You can also grab the total screen height by getting the Screen.PrimaryScreen.Bounds.Height property (which includes the task bar in the total height value).
Comparing these values, if they're the same, the task bar isn't present. Otherwise, the task bar is and you can adjust accordingly.

Resources