I wrote a small library some time ago to read information about the front or top window, mainly the name of the window, on Windows, Mac OS X and Linux. Here you can find the source code: https://github.com/pcmantinker/Qt-Window-Title-Reader
I use the native Windows API for Windows, the X11 libraries for Linux, and Cocoa for Mac OS X.
Here is a small example of how to get active windows on Mac OS X using objective-C ++:
Mac.h
#ifndef MAC_H #define MAC_H #include <QtCore> #include "windowinfo.h" class Mac { public: Mac(); QList<WindowInfo> getActiveWindows(); }; #endif // MAC_H
Mac.mm
#include "mac.h" #include "Cocoa/Cocoa.h" Mac::Mac() { } QList<WindowInfo> Mac::getActiveWindows() { QList<WindowInfo> windowTitles; // get frontmost process for currently active application ProcessSerialNumber psn = { 0L, 0L }; OSStatus err = GetFrontProcess(&psn); CFStringRef processName = NULL; err = CopyProcessName(&psn, &processName); NSString *pname = (NSString *)processName; // loop through all application windows CFArrayRef windowList = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements, kCGNullWindowID); for (NSMutableDictionary* entry in (NSArray*)windowList) { NSString* ownerName = [entry objectForKey:(id)kCGWindowOwnerName]; NSString *name = [entry objectForKey:@"kCGWindowName" ]; NSInteger ownerPID = [[entry objectForKey:(id)kCGWindowOwnerPID] integerValue]; NSInteger layer = [[entry objectForKey:@"kCGWindowLayer"] integerValue]; if(layer == 0) { if([ownerName isEqualToString:pname]) { NSRange range; range.location = 0; range.length = [ownerName length]; unichar *chars = new unichar[range.length]; [ownerName getCharacters:chars range:range]; QString owner = QString::fromUtf16(chars, range.length); range.length = [name length]; chars = new unichar[range.length]; [name getCharacters:chars range:range]; QString windowTitle = QString::fromUtf16(chars, range.length); delete[] chars; long pid = (long)ownerPID; WindowInfo wi; wi.setProcessName(owner); wi.setWindowTitle(windowTitle); wi.setPID(pid); windowTitles.append(wi); } } } CFRelease(windowList); CFRelease(processName); return windowTitles; }
Note that in Cocoa there is no direct way to get only the topmost window. You get a set of windows and look through them to find the one you want.
Here is the code for the Windows API:
win.h
#ifndef WIN_H #define WIN_H #include <QtCore> #include "qt_windows.h" #include "psapi.h" #include "windowinfo.h" class win : public QObject { Q_OBJECT public: win(); QList<WindowInfo> getActiveWindows(); private: TCHAR buf[255]; }; #endif // WIN_H
win.cpp
#include "win.h" win::win() { } QList<WindowInfo> win::getActiveWindows() { QList<WindowInfo> windowTitles; HWND foregroundWindow = GetForegroundWindow(); DWORD* processID = new DWORD; GetWindowText(foregroundWindow, buf, 255); GetWindowThreadProcessId(foregroundWindow, processID); DWORD p = *processID; HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, p); TCHAR szProcessName[MAX_PATH]; if (NULL != hProcess ) { HMODULE hMod; DWORD cbNeeded; if ( EnumProcessModules( hProcess, &hMod, sizeof(hMod), &cbNeeded) ) { GetModuleBaseName( hProcess, hMod, szProcessName, sizeof(szProcessName)/sizeof(TCHAR) ); } } CloseHandle(hProcess); long pid = (long)p; QString windowTitle, processName; #ifdef UNICODE windowTitle = QString::fromUtf16((ushort*)buf); processName = QString::fromUtf16((ushort*)szProcessName); #else windowTitle = QString::fromLocal8Bit(buf); processName = QString::fromLocal8Bit(szProcessName); #endif WindowInfo wi; wi.setPID(pid); wi.setWindowTitle(windowTitle); wi.setProcessName(processName); windowTitles.append(wi); return windowTitles; }
Here is the code for Linux / X11:
linux_x11.h
#ifndef LINUX_X11_H #define LINUX_X11_H #include <QtCore> #include <X11/Xh> #include <X11/Xlib.h> #include <X11/Xutil.h> #include <X11/Xatom.h> #include "windowinfo.h" class linux_x11 { public: linux_x11(); QList<WindowInfo> getActiveWindows(); private: Window* active(Display *disp, unsigned long *len); char *name (Display *disp, Window win); int *pid(Display *disp, Window win); QString processName(long pid); }; #endif // LINUX_X11_H
linux_x11.cpp
/* Linux/X11 specific code for obtaining information about the frontmost window */ #include "linux_x11.h" #include <sstream> #include <stdlib.h> #include <stdio.h> linux_x11::linux_x11() { } /** * Returns the window name for a specific window on a display ***/ char *linux_x11::name (Display *disp, Window win) { Atom prop = XInternAtom(disp,"WM_NAME",False), type; int form; unsigned long remain, len; unsigned char *list; if (XGetWindowProperty(disp,win,prop,0,1024,False,AnyPropertyType, &type,&form,&len,&remain,&list) != Success) return NULL; return (char*)list; } /** * Returns the pid for a specific window on a display ***/ int* linux_x11::pid(Display *disp, Window win) { Atom prop = XInternAtom(disp,"_NET_WM_PID",False), type; int form; unsigned long remain, len; unsigned char *list; if (XGetWindowProperty(disp,win,prop,0,1024,False,AnyPropertyType, &type,&form,&len,&remain,&list) != Success) return NULL; return (int*)list; } /** * Returns the active window on a specific display ***/ Window * linux_x11::active (Display *disp, unsigned long *len) { Atom prop = XInternAtom(disp,"_NET_ACTIVE_WINDOW",False), type; int form; unsigned long remain; unsigned char *list; if (XGetWindowProperty(disp,XDefaultRootWindow(disp),prop,0,1024,False,XA_WINDOW, &type,&form,len,&remain,&list) != Success) return NULL; return (Window*)list; } /** * Returns process name from pid (processes output from /proc/<pid>/status) ***/ QString linux_x11::processName(long pid) { // construct command string QString command = "cat /proc/" + QString("%1").arg(pid) + "/status"; // capture output in a FILE pointer returned from popen FILE * output = popen(command.toStdString().c_str(), "r"); // initialize a buffer for storing the first line of the output char buffer[1024]; // put the contents of the buffer into a QString QString line = QString::fromUtf8(fgets(buffer, sizeof(buffer), output)); // close the process pipe pclose(output); // take right substring of line returned to get process name return line.right(line.length() - 6).replace("\n", ""); } QList<WindowInfo> linux_x11::getActiveWindows() { QList<WindowInfo> windowTitles; unsigned long len; Display *disp = XOpenDisplay(NULL); Window *list; char *n; int* p; list = (Window*)active(disp,&len); if((int)len > 0) { for (int i=0;i<(int)len;i++) { n = name(disp,list[i]); p = pid(disp, list[i]); long p_id = 0; QString pName; QString windowTitle; if(p!=NULL) { p_id = *p; // dereference pointer for obtaining pid pName = processName(p_id); } if(n!=NULL) windowTitle = QString::fromUtf8(n); WindowInfo wi; wi.setWindowTitle(windowTitle); wi.setProcessName(pName); wi.setPID(p_id); windowTitles.append(wi); delete n; delete p; } } delete list; XCloseDisplay (disp); return windowTitles; }
X11 code can get pretty ugly and hard to understand, but that should get you started. It has been quite a while since I dealt with X11 directly, so I canβt tell you exactly what each helper method is doing right now.
I abstract the code in such a way that each piece of code defined on the platform has the same method signature. Then I check to see if it was compiled on Mac OS X, Windows, or Linux and instantiated the correct classes. Here's how it all relates:
#include "windowtitlereader.h" WindowTitleReader::WindowTitleReader() { qDebug() << "WindowTitleReader::WindowTitleReader()"; // refresh window reading every 10ms timer = new QTimer(this); timer->setInterval(10); timer->start(); connect(timer, SIGNAL(timeout()), this, SLOT(getWindowTitle())); } WindowTitleReader::~WindowTitleReader() { delete timer; delete m_pid; delete m_processName; } void WindowTitleReader::getWindowTitle() { qDebug() << "WindowTitleReader::getWindowTitle()";
You can change the refresh rate to a lower refresh rate if you want, but I run the updates every 10 ms to get a real-time update in the focus of the window.
I basically wrote this library to read the window names of web browsers and video games, so that I can evaluate how long people play certain games on their computers. I wrote this as part of the game show application I am creating.