Moving third-party windows in Mac OS - c++

I try to support the program in Mac OS. The program must move one specific window of another program (the program must have more than 1 window open) to the specified monitor and expand it to the full screen. For Windows and Linux, this was implemented using a native API, but for MacOS it did not find an API to change Windows from its application. Could find a way to get the window ID but not change its state.
m_currentWindow->mac = 0;
CGWindowListOption option = kCGWindowListOptionAll;
CGWindowID id = 0;
CFArrayRef windows = CGWindowListCreate(option, id);
if(windows == nullptr)
{
qCCritical(actOp) << "windows is null";
return;
}
CFArrayRef desc = CGWindowListCreateDescriptionFromArray(windows);
if(desc == nullptr)
{
qCCritical(actOp) << "windows description is null";
return;
}
CFIndex count = CFArrayGetCount(desc);
qCDebug(actOp) << "finded " << count << " window";
QList<quint32> allWindows;
for(CFIndex i=0; i<count; i++)
{
CFDictionaryRef dictionary = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(desc, i));
quint32 id;
CFNumberGetValue(static_cast<CFNumberRef>(CFDictionaryGetValue(dictionary, kCGWindowNumber)),
kCFNumberSInt32Type, &id);
allWindows << id;
}
CFRelease(desc);
CFRelease(windows);
do
{
QThread::currentThread()->msleep(100);
windows = CGWindowListCreate(option, id);
desc = CGWindowListCreateDescriptionFromArray(windows);
count = CFArrayGetCount(desc);
for(CFIndex i=0; i<count; i++)
{
CFDictionaryRef dictionary = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(desc, i));
quint32 id;
CFNumberGetValue(static_cast<CFNumberRef>(CFDictionaryGetValue(dictionary, kCGWindowNumber)),
kCFNumberSInt32Type, &id);
if(allWindows.contains(id))
continue;
QString name = QString::fromCFString(static_cast<CFStringRef>(
CFDictionaryGetValue(dictionary, kCGWindowOwnerName)));
qCDebug(actOp) << static_cast<CFNumberRef>(CFDictionaryGetValue(dictionary, kCGWindowNumber))
<< " "
<< static_cast<CFStringRef>(CFDictionaryGetValue(dictionary, kCGWindowOwnerName));
if(name.contains(m_browserTitle))
{
m_currentWindow->mac = id;
qCDebug(actOp) << "window is finded";
return;
}
else
allWindows << id;
}
CFRelease(desc);
CFRelease(windows);
}
while(m_currentWindow->mac == 0 && !timer.hasExpired(maxTime));
I tried to look in the direction of QWindow, but could not find how to get NSView, for the method Window::fromWinId, having only the desired window CGWindowID.
Tell me how you would implement such a task. Thanks.

Related

Custom resolution on GCP VM (with T4 GPU on Windows Server 2019)

I am currently searching for a way to set a fully custom resolution on a Windows Server 2019 VM with GPU (T4, with grid licence, and virtual workstation grid drivers) with C++.
I have tried different way to achieve this, I can make this work on my laptop, but seems to have some limitations on GCP VMs (or Windows Server limitation).
I have tried to do this with ChangeDisplaySettings/ChangeDisplaySettingsEx (winuser.h), I can change to a known resolution, but can't make it works with a custom one (not even with CDS_ENABLE_UNSAFE_MODE).
DWORD deviceIndex = 0;
DISPLAY_DEVICE displayDevice = { 0 };
displayDevice.cb = sizeof(DISPLAY_DEVICE);
while (EnumDisplayDevices(NULL, deviceIndex, &displayDevice, 0)) {
deviceIndex++;
DEVMODE dm = { 0 };
dm.dmSize = sizeof(DEVMODE);
DEVMODE finalDm = { 0 };
finalDm.dmSize = sizeof(DEVMODE);
//Check if able to retrieve current settings
if (!EnumDisplaySettings(displayDevice.DeviceName, ENUM_CURRENT_SETTINGS, &dm)) {
continue;
}
//Check if there is a difference in resolution list if UNSAFE_MODE is enabled or not (it seems to not change anything)
int result = ChangeDisplaySettingsEx(displayDevice.DeviceName, &dm, 0, CDS_DISABLE_UNSAFE_MODES, NULL);
std::cout << "CDS_DISABLE_UNSAFE_MODE" << std::endl;
if (result == DISP_CHANGE_SUCCESSFUL) {
for (int i = 0; EnumDisplaySettings(displayDevice.DeviceName, i, &dm) != 0; i++) {
if (dm.dmBitsPerPel == 32) {
std::cout << i << ". Found available resolution : " << dm.dmPelsWidth << " x " << dm.dmPelsHeight << " x " << dm.dmBitsPerPel << " # " << dm.dmDisplayFrequency << std::endl;
}
}
}
result = ChangeDisplaySettingsEx(displayDevice.DeviceName, &dm, 0, CDS_ENABLE_UNSAFE_MODES, NULL);
std::cout << "CDS_ENABLE_UNSAFE_MODE" << std::endl;
if (result == DISP_CHANGE_SUCCESSFUL) {
for (int i = 0; EnumDisplaySettings(displayDevice.DeviceName, i, &dm) != 0; i++) {
if (dm.dmBitsPerPel == 32) {
std::cout << i << ". Found available resolution : " << dm.dmPelsWidth << " x " << dm.dmPelsHeight << " x " << dm.dmBitsPerPel << " # " << dm.dmDisplayFrequency << std::endl;
}
}
}
std::cout << "Please enter width : ";
int width, height;
std::cin >> width;
std::cout << "Please enter height : ";
std::cin >> height;
dm.dmPelsWidth = width;
dm.dmPelsHeight = height;
if (width > height) {
dm.dmDisplayOrientation = DMDO_DEFAULT;
}
else {
dm.dmDisplayOrientation = DMDO_90;
}
dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYORIENTATION;
//result = ChangeDisplaySettingsEx(displayDevice.DeviceName, &dm, NULL, CDS_TEST, NULL);
result = ChangeDisplaySettingsEx(displayDevice.DeviceName, &dm, NULL, 0, NULL);
if (result != DISP_CHANGE_SUCCESSFUL) {
std::cout << "Impossible to ChangeDisplaySettings" << endl;
}
else {
std::cout << "OK" << endl;
}
break;
}
I then take a look at NVAPI, and same here, I can make it works on my PC but still nothing on the GCP VMs... I have found a way to make NVAPI create and use custom resolution on my local PC, but can't make it works on GCP VM once again... (Code example found here)
NvAPI_Status result = NVAPI_ERROR;
NvU32 primaryDisplayId = 0;
//Testing resolution
int horizontal = 1920, vertical = 1090;
result = NvAPI_Initialize();
if (result != NVAPI_OK) {
printf("Could not initialize NvAPI");
return false;
}
MONITORINFOEX monInfo;
HMONITOR hMon;
const POINT ptZero = { 0, 0 };
// determine the location of the primary monitor
hMon = MonitorFromPoint(ptZero, MONITOR_DEFAULTTOPRIMARY);
ZeroMemory(&monInfo, sizeof(monInfo));
monInfo.cbSize = sizeof(monInfo);
GetMonitorInfo(hMon, &monInfo);
result = NvAPI_DISP_GetGDIPrimaryDisplayId(&primaryDisplayId);
if (result != NVAPI_OK) {
printf("Could not get display ID from device");
NvAPI_Unload();
return false;
}
NvU32 deviceCount = 0;
NV_CUSTOM_DISPLAY cd[NVAPI_MAX_DISPLAYS] = { 0 };
float refreshRate = 60;
// timing computation (to get timing that suits the changes made)
NV_TIMING_FLAG flag = { 0 };
NV_TIMING_INPUT timing = { 0 };
timing.version = NV_TIMING_INPUT_VER;
timing.height = vertical;
timing.width = horizontal;
timing.rr = refreshRate;
timing.flag = flag;
timing.type = NV_TIMING_OVERRIDE_CVT_RB;
result = NvAPI_DISP_GetTiming(primaryDisplayId, &timing, &cd[0].timing);
if (result != NVAPI_OK) {
printf("Failed to get timing for display"); // failed to get custom display timing
NvAPI_Unload();
return false;
}
cd[0].width = horizontal;
cd[0].height = vertical;
cd[0].xRatio = 1;
cd[0].yRatio = 1;
cd[0].srcPartition = { 0, 0, 1.0, 1.0 };
cd[0].depth = 32;
cd[0].version = NV_CUSTOM_DISPLAY_VER;
cd[0].colorFormat = NV_FORMAT_A8R8G8B8;
//Returns NVAPI_ERROR on GCP but NVAPI_OK on my laptop
result = NvAPI_DISP_TryCustomDisplay(&primaryDisplayId, 1, cd);
if (result != NVAPI_OK) {
printf("Could not set custom resolution");
NvAPI_DISP_RevertCustomDisplayTrial(&primaryDisplayId, 1);
NvAPI_Unload();
return false;
}
else {
NvAPI_DISP_SaveCustomDisplay(&primaryDisplayId, 1, true, true);
}
This part works perfectly well on my laptop, I can use a new dynamic resolution (It works with 1920x400, 1920x500, 1920x600), but not on my GCP VM, this parts :
NvAPI_DISP_TryCustomDisplay(&primaryDisplayId, 1, cd);
always returns NVAPI_ERROR
I have found another trick, I can edit this registry entry : HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Video{RANDOM_ID}\0001\NV_Modes
(Here is an old pdf, after some testing, it seems it is still working this way)
If I add some resolution using NVAPI, I can then set through ChangeDisplaySettingsEx function this resolution (it needs GPU driver restart, or Windows restart be able to change to a fresh new added resolution).
But I need to be able to rotate screen, playing with "dmDisplayOrientation", and it does not seem to work on GCP VM once again, if I authorize for example 1920x1090 I can set resolution to this, but cannot set 1090x1920 with a "dmDisplayOrientation = DMDO_90" (even if I authorize 1090x1920 too...)
So if anyone found a way, or have any idea on how to do this, it would be great, I am running out of idea right now...

vkDestroyDevice returns vkDevice has not been Destroyed and EXC_BAD_ACCESS

Running MacOS Catalina and VSCode.
This is how I initialize my device
void Renderer::InitDevice() {
{
uint32_t gpu_count = 0;
vkEnumeratePhysicalDevices(Instance, &gpu_count, nullptr);
std::vector<VkPhysicalDevice> gpu_list(gpu_count);
vkEnumeratePhysicalDevices(Instance, &gpu_count, gpu_list.data()); //populates gpu_list with all gpu handles
GPU = gpu_list[0]; //grab first gpu. debug and figure out a ranking system
vkGetPhysicalDeviceProperties(GPU, &GPU_device_properties);
}
{
uint32_t family_count = 0;
vkGetPhysicalDeviceQueueFamilyProperties(GPU, &family_count, nullptr);
std::vector<VkQueueFamilyProperties> family_properties(family_count);
vkGetPhysicalDeviceQueueFamilyProperties(GPU, &family_count, family_properties.data());
bool found_graphics_bit = false;
for ( u_int32_t i = 0; i<family_count; i++){
if(family_properties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT){
found_graphics_bit = true;
graphics_family_index = i;
}
}
if (!found_graphics_bit){
assert(1 && "Vulkan Error: Queue family supporting graphics card not found");
std::exit(-1);
}
}
{
uint32_t layer_count = 0;
vkEnumerateInstanceLayerProperties(&layer_count, nullptr);
std::vector<VkLayerProperties> layer_properties(layer_count);
vkEnumerateInstanceLayerProperties(&layer_count, layer_properties.data()); //something about system layers
std::cout << "Instance Layers: \n";
for (auto &layer_property: layer_properties){
std::cout << "\t" << layer_property.layerName << "\n";
std::cout << "\t\t" << layer_property.description << "\n";
}
std::cout << "\n";
}
{
uint32_t layer_count = 0;
vkEnumerateDeviceLayerProperties(GPU, &layer_count, nullptr);
std::vector<VkLayerProperties> layer_properties(layer_count);
vkEnumerateDeviceLayerProperties(GPU, &layer_count, layer_properties.data()); //something about system layers
std::cout << "Device Layers: \n";
for (auto &layer_property: layer_properties){
std::cout << "\t" << layer_property.layerName << "\n";
std::cout << "\t\t" << layer_property.description << "\n";
}
std::cout << "\n";
}
float queue_priorities[] { 1.0f };
VkDeviceQueueCreateInfo device_queue_info = {};
//need to understand queue properties
device_queue_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
device_queue_info.queueFamilyIndex = graphics_family_index;
device_queue_info.queueCount = 1;
device_queue_info.pQueuePriorities = queue_priorities;
VkDeviceCreateInfo device_info{};
device_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
device_info.queueCreateInfoCount = 1;
device_info.pQueueCreateInfos = &device_queue_info;
device_info.enabledLayerCount = device_layers.size();
device_info.ppEnabledLayerNames = device_layers.data();
device_info.enabledExtensionCount = device_extensions.size();
device_info.ppEnabledExtensionNames = device_extensions.data();
auto err = vkCreateDevice(GPU, &device_info, nullptr, &Device);
if (err != VK_SUCCESS){
assert(1 && "Vulkan Error: Device Creation Failed");
std::exit(-1);
}
}
Vulkan device creation return VK_SUCCESS when this error occurs.
And this is how I destroy it
void Renderer::DeinitDevice() {
vkDeviceWaitIdle(Device);
vkDestroyDevice(Device, nullptr); //uncommenting this causes program to crash.
Device = nullptr;
}
on vkDeviceWaitIdle(Device); or if that is removed it happens on vkDestroyDevice(Device, nullptr);
I get EXC_BAD_ACCESS
and
#"2020-05-17 15:06:54.832625-0400 main[4816:29494] flock failed to lock maps file: errno = 35\r\n"
#"UNASSIGNED-ObjectTracker-ObjectLeak(ERROR / SPEC): msgNum: 699204130 - Validation Error: [ UNASSIGNED-ObjectTracker-ObjectLeak ] Object 0: handle = 0x10104e018, type = VK_OBJECT_TYPE_DEVICE; | MessageID = 0x29ad0222 | OBJ ERROR : VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT object VkDevice 0x10104e018[] has not been destroyed.\r\n"
#" Objects: 1\r\n"
#" [0] 0x10104e018, type: 3, name: NULL\r\n"
I was following the vulkan-tutorial and I couldn't really understand that but I found a few videos that explained the things the tutorial really doesn't go over. So far I've put this together but I'm not sure what's causing the error. I've seen other similar errors but can't seem to find out how they fixed it.
The code I didn't post that actually contained the issue was this
Renderer::~Renderer(){
DestroyInstance();
DeinitDevice();
}
I transposed these two from the tutorial I was following on accident. and should be this
Renderer::~Renderer(){
DeinitDevice();
DestroyInstance();
}

RtMidiIn not finding Ports in Release version c++

I can't find a related solution so i finally post here.
I have a very small program that uses the RTMidi library to map the controllers keys as hotkeys. Everything works fine in debug mode and releasing the app doesn't give me any errors either, but the RTMidiIn class isn't finding any Ports in the release version.
This is my code
MidiToMacro::MidiToMacro(QWidget *parent)
: QMainWindow(parent)
{
m_ui.setupUi(this);
RtMidiIn *midiin;
try
{
midiin = new RtMidiIn(RtMidi::WINDOWS_MM);
}
catch (RtMidiError &error) {
m_ui.uiLog->append("midiin initiation failed!");
error.printMessage();
exit(EXIT_FAILURE);
}
unsigned int nPorts = midiin->getPortCount();
QString inputCount = QString::number(nPorts);
m_ui.uiLog->append("There are " + inputCount + " MIDI input sources
available.");
std::string portName;
std::string akai = "Akai MPK49 2";
opened = 1000;
for (unsigned int i = 0; i < nPorts;++i)
{
try
{
portName = midiin->getPortName(i);
if (portName == akai)
{
midiin->openPort(i);
midiin->setCallback(&mycallback, this);
opened = i;
}
}
catch (RtMidiError &error)
{
//not printing an error
error.printMessage();
delete midiin;
}
QString portnumber = QString::number(i);
m_ui.uiLog->append(" Input Port #" + portnumber + ": " + QString::fromStdString(portName));
}
if (opened == 1000)
{
m_ui.uiLog->append("Error finding Akai Controller!");
}
Forgot to define WINDOWS_MM in the preprocessor definitions of release.

Get device path based on USB VID:PID in Linux

If I plug in a device, say /dev/ttyUSB0 and I want to get the number 0 based on its VID:PID (found with lsusb), how could I do that in C++ Linux? I have this code to find one printer device, if it's helpful at all:
int printer_open (void)
{
char printer_location[] = "/dev/usb/lpX";
struct stat buf;
// continuously try all numbers until stat returns true for the connected printer
for (int i = 0; i < 10; i++)
{
printer_location[11] = '0' + i;
if (!stat (printer_location, &buf))
break;
}
return 0;
}
You could use libusb
apt-get install build-essential libudev-dev
Here is a good example: http://www.dreamincode.net/forums/topic/148707-introduction-to-using-libusb-10/
and here is the lib description: http://libusb.sourceforge.net/api-1.0/
int main() {
libusb_context *context = NULL;
libusb_device **list = NULL;
int rc = 0;
ssize_t count = 0;
rc = libusb_init(&context);
assert(rc == 0);
count = libusb_get_device_list(context, &list);
assert(count > 0);
for (size_t idx = 0; idx < count; ++idx) {
libusb_device *device = list[idx];
libusb_device_descriptor desc = {0};
rc = libusb_get_device_descriptor(device, &desc);
assert(rc == 0);
printf("Vendor:Device = %04x:%04x\n", desc.idVendor, desc.idProduct);
}
}
And if you compile your code don't forget to add the lib reference -I/usr/include/libusb-1.0/ and - lusb-1.0
libusb can't get it actually. So look at this file instead: /proc/bus/input/devices
Example line from the file:
I: Bus=0003 Vendor=1a2c Product=0c23 Version=0110
N: Name="USB USB Keyboard"
P: Phys=usb-0000:00:14.0-3/input0
S: Sysfs=/devices/pci0000:00/0000:00:14.0/usb1/1-3/1-3:1.0/0003:1A2C:0C23.0015/input/input30
U: Uniq=
H: Handlers=sysrq kbd event10 leds
B: PROP=0
B: EV=120013
B: KEY=1000000000007 ff800000000007ff febeffdff3cfffff fffffffffffffffe
B: MSC=10
B: LED=7
This function gets the event number from the device with the matching VID:PID:
#include <string>
#include <iostream>
#include <fstream>
void open_device (std::string device_vid, std::string device_pid)
{
try
{
std::ifstream file_input;
std::size_t pos;
std::string device_path, current_line, search_str, event_str;
std::string device_list_file = "/proc/bus/input/devices";
bool vid_pid_found = false;
int fd = 0;
bool debug = true;
// 1. open device list file
file_input.open(device_list_file.c_str());
if (!file_input.is_open())
{
std::cerr << "file_input.open >> " << std::strerror(errno) << std::endl;
throw -2;
}
// 2. search for first VID:PID and get event number
search_str = "Vendor=" + device_vid + " Product=" + device_pid;
while (getline(file_input, current_line))
{
if (!vid_pid_found)
{
pos = current_line.find(search_str, 0);
if (pos != std::string::npos)
{
vid_pid_found = true;
search_str = "event";
}
}
else
{
pos = current_line.find(search_str, 0);
if (pos != std::string::npos)
{
event_str = current_line.substr(pos);
// find space and substring event##
pos = event_str.find(' ', 0);
event_str = event_str.substr(0, pos);
break;
}
}
}
// 3. build device path
device_path = "/dev/input/" + event_str;
if (debug) std::cout << "device_path = " << device_path << std::endl;
// 4. connect to device
fd = open (device_path.c_str(), O_RDONLY);
if (fd < 0)
{
std::cerr << "open >> errno = " << std::strerror(errno) << std::endl;
throw -3;
}
}
catch (const std::exception &e)
{
std::cerr << "e.what() = " << e.what() << std::endl;
throw -1;
}
return;
}

Progress bar for copying files

I am trying to create a progress bar for a file copy in Qt. This is as close as I could find, however I believe this doesn't work because according to the Qt class documentation:
Unlike other QIODevice implementations, such as QTcpSocket, QFile does
not emit the aboutToClose(), bytesWritten(), or readyRead() signals.
This implementation detail means that QFile is not suitable for
reading and writing certain types of files, such as device files on
Unix platforms.
How can I do something like this? I don't know how to implement my own signals.
Here is my code:
void Replicator::anotbaandbFile(QDir source, QDir target)
{
source.setFilter(QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks);
target.setFilter(QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks);
qDebug() << "Scanning: " << source.path();
QStringList sourceFileList = source.entryList();
QStringList targetFileList = target.entryList();
for (int aCount = 0; aCount < sourceFileList.count(); aCount++)
{
bool found = false;
for (int bCount = 0; bCount < targetFileList.count(); bCount++)
if (sourceFileList.at(aCount) == targetFileList.at(bCount))
found = true;
if (found == false)
{
sourceFile = new QFile(source.absolutePath()+"/"+sourceFileList.at(aCount));
targetFile = new QFile(target.absolutePath()+"/"+sourceFileList.at(aCount));
progressBar->setMinimum(0);
progressBar->setMaximum(sourceFile->size());
written = 0;
connect(sourceFile,SIGNAL(bytesWritten(qint64)),SLOT(onWrite(qint64)));
sourceFile->copy(targetFile->fileName());
//QFile::copy(source.absolutePath()+"/"+sourceFileList.at(aCount), target.absolutePath()+"/"+sourceFileList.at(aCount));
qDebug() << source.absolutePath()+"/"+sourceFileList.at(aCount) << " " << target.absolutePath()+"/"+sourceFileList.at(aCount);
}
}
}
and
void Replicator::onWrite(qint64 w)
{
written += w;
progressBar->setValue( written );
}
new code modified from above
if (found == false)
{
sourceFile = new QFile(source.absolutePath()+"/"+sourceFileList.at(aCount));
targetFile = new QFile(target.absolutePath()+"/"+sourceFileList.at(aCount));
progressBar->setMinimum(0);
progressBar->setMaximum(sourceFile->size());
QByteArray buffer;
for (int count = 0; !(buffer = sourceFile->read(1000000)).isEmpty(); count+=1000000)
{
targetFile->write(buffer);
progressBar->setValue(count);
}
//targetFile->write(buffer);
//QFile::copy(source.absolutePath()+"/"+sourceFileList.at(aCount), target.absolutePath()+"/"+sourceFileList.at(aCount));
qDebug() << "copying " << sourceFile->fileName() << " to " << targetFile->fileName();
}
You can simply copy large files by portions of fixed size, than count portions already copied and callculate of percentage of work by dividing it to overal count of portions.
int iWorkPercentage = (int)(((float)iPortionsProcessed / (float)iOveralPortions) * 100);