Receipt printing via serial port (RS-232) - c++

I'd like to write a c++ program(windows console application) to print receipts from a server (via websocket) to a locally connected(via rs-232) receipt printer(NCR 7197 rev 1.0).
I got both the websocket connection and the serial handle operating and ready.
My problem is, that I can't find any example to guide me through the process of printing, or how to even begin. I mean, do I have to write some bytes or read any before I start passing the "document" to the printer, or do I need to write any config bytes, or anything.
If someone has any suggestions to where to start, or any example preferably in c++, I will be thankful.

I came across the solution that I developed way back than, while cleaning my old projects folder. Seeing the multiple hundred of views on this question, I wanted to post an answer. Here is my solution, but it might be outdated:
The function to connect to a device through a COM port:
// Serial port connection handle connect method
HANDLE connect_h(wchar_t* s_port, int s_flowcontrol, int s_baudrate, int s_bytesize, int s_stopbits, int s_parity) {
wchar_t* port = s_port;
std::wstring ws(port);
std::string port_w(ws.begin(), ws.end());
// Open serial port
HANDLE hSerial;
hSerial = CreateFile(port, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if(hSerial == INVALID_HANDLE_VALUE) {
if(GetLastError() == ERROR_FILE_NOT_FOUND) {
// Serial port does not exist. Inform user.
std::cout << "Error: Serial port does not exists. Port: " << std::endl;
}
// Some other error occurred. Inform user.
std::cout << "Error: Cannot connect to port: " + port_w << std::endl;
std::cout << "Error code: " + GetLastError() << std::endl;
}
// Do some basic settings
DCB dcbSerialParams = { 0 };
dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
if(!GetCommState(hSerial, &dcbSerialParams)) {
// Error getting state
std::cout << "Error: Cannot get port state. Port: " + port_w << std::endl;
std::cout << "Error code: " + GetLastError() << std::endl;
}
dcbSerialParams.BaudRate = s_baudrate;
dcbSerialParams.ByteSize = s_bytesize;
dcbSerialParams.StopBits = s_stopbits;
dcbSerialParams.Parity = s_parity;
// If flowcontrol set to XON/XOFF
if(s_flowcontrol == 1) {
dcbSerialParams.fDtrControl = DTR_CONTROL_DISABLE;
dcbSerialParams.fRtsControl = RTS_CONTROL_DISABLE;
dcbSerialParams.fOutX = true;
dcbSerialParams.fInX = true;
dcbSerialParams.XonChar = 0x11;
dcbSerialParams.XoffChar = 0x16;
}
if(!SetCommState(hSerial, &dcbSerialParams)) {
// error setting serial port state
std::cout << "Error: Cannot set port state. Port: " + port_w << std::endl;
std::cout << "Error code: " + GetLastError() << std::endl;
}
// Set timeouts
COMMTIMEOUTS timeouts = { 0 };
timeouts.ReadIntervalTimeout = 50;
timeouts.ReadTotalTimeoutConstant = 50;
timeouts.ReadTotalTimeoutMultiplier = 10;
timeouts.WriteTotalTimeoutConstant = 50;
timeouts.WriteTotalTimeoutMultiplier = 10;
if (!SetCommTimeouts(hSerial, &timeouts)) {
// Error occureed. Inform user
std::cout << "Error: Cannot set port timeouts. Port: " + port_w << std::endl;
std::cout << "Error code: " + GetLastError() << std::endl;
}
return hSerial;
}
Closing the serial port connection:
// Serial port connection handle close method
void close_h(const HANDLE &hSerial) {
CloseHandle(hSerial);
}
The function to print through the connected COM port:
// Printing recipe via serial port handle
std::string set_print(const HANDLE &hSerial, std::string &document) {
std::string result = "";
std::string printable = "";
// Format flags
bool _bold_flag = false;
bool _underline_flag = false;
bool _italic_flag = false;
// Process document for printing
for(int i = 0; i < (int)document.length(); ++i) {
if(document[i] == '\\') {
switch (document[i + 1]) {
// new line
case 'n':
printable.push_back((char)(0x0A));
i++;
break;
// underline format begin/end
case 'u':
if(!_underline_flag) {
printable.push_back((char)(0x1B));
printable.push_back((char)(0x2D));
printable.push_back('1');
_underline_flag = true;
}
else {
printable.push_back((char)(0x1B));
printable.push_back((char)(0x2D));
printable.push_back('0');
_underline_flag = false;
}
i++;
break;
// bold format begin/end
case 'b':
if (!_bold_flag) {
printable.push_back((char)(0x1B));
printable.push_back((char)(0x47));
printable.push_back('1');
_bold_flag = true;
}
else {
printable.push_back((char)(0x1B));
printable.push_back((char)(0x47));
printable.push_back('0');
_bold_flag = false;
}
i++;
break;
// italic format begin/end
case 'i':
if (!_italic_flag) {
printable.push_back((char)(0x1B));
printable.push_back((char)(0x49));
printable.push_back('1');
_italic_flag = true;
}
else {
printable.push_back((char)(0x1B));
printable.push_back((char)(0x49));
printable.push_back('0');
_italic_flag = false;
}
i++;
break;
// if not recognized
default:
printable.push_back(document[i]);
break;
}
}
else {
printable.push_back(document[i]);
}
}
// Additional push
printable.push_back((char)(0x0A));
printable.push_back((char)(0x0A));
printable.push_back((char)(0x0A));
printable.push_back((char)(0x0A));
printable.push_back((char)(0x0A));
printable.push_back((char)(0x0A));
printable.push_back((char)(0x0A));
printable.push_back((char)(0x0A));
// Add EOF bytes
printable.push_back((char)(0x03));
printable.push_back((char)(0x04));
// Add knife cut command
printable.push_back((char)(0x19));
printable.push_back((char)(0x1B));
printable.push_back((char)(0x76));
// Convert to hexadecimal
int* hex_print = new int[printable.length()];
for(int i = 0; i < (int)printable.length(); ++i) {
hex_print[i] = (int)printable[i];
}
// Print
for(int i = 0; i < (int)printable.length(); ++i) {
int rq[1] = { hex_print[i] };
DWORD rqBytesWritten = 0;
if(!WriteFile(hSerial, rq, 1, &rqBytesWritten, NULL)) {
// error occurred. Report to user.
std::cout << "Error: Can't write byte. written: " + rqBytesWritten << std::endl;
}
}
// return status
return result;
}
And a test main function, to demonstrate the usage of these functions:
// MAIN()
int main(int argc, char* argv[]) {
// Create connection on COM port 1
HANDLE sh_printer = connect_h(L"COM1", 0, 38400);
// Print a string povided in first argument
set_print(sh_printer, args[1]);
// Close the COM port connection
close_h(sh_printer);
return 0;
}
I hope I could be of any help to anyone, who tires to use this ancient way of printing a receipt. :)

Related

http response from recv() is missing raw/binary data in the body C++ sockets

working on a http client that downloads data from a website using HTTP/1.1 (not https on port 443). The program is working for the most part, but when I attempt to write the data to a file, the data in the file does not match with the data when I physically save the file from a browser. Here is the code below:
#include <fstream>
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string>
#include <cstring>
using namespace std;
string resolveToIPAddress(string, string);
int getPortNum(string);
string getUrlPath(string);
string getDomain(string);
string getDomainWithFile(string);
string getUrlWithNoPort(string);
string getFileFromURL(string);
string getFilePath(string);
bool hasHTTPProtocol(string);
bool hasPortNum(string);
bool checkIfValidProtocol(string);
int main(int argc, char ** argv) {
//Create a file to be opened for output
ofstream out_file;
out_file.open("output", ios::out | ios::binary);
int connection_port = 0, send_req, recv_req;
string ip_addr = "", domain = "", ipFromDomain = "", file_to_get = "", http_response = "";
ip_addr = argv[1]; //point ip address argument to the 2nd indice in the arguments
//Check if http is valid
if(hasHTTPProtocol(ip_addr) == true) {
//if there is a port number, Get port number (extract it from URL)
if(hasPortNum(ip_addr)) {
connection_port = getPortNum(ip_addr);
domain = getDomain(ip_addr);
}
//if there is no port number, set default port number = 80 and set domain to truncated ip address
else {
connection_port = 80;
domain = getDomain(ip_addr);
}
file_to_get = getFileFromURL(ip_addr);
//For Testing purposes only
//cout << "Domain: " << domain << endl;
//cout << getDomainWithFile(ip_addr) << endl;
//cout << "URL without port: " << getUrlWithNoPort(ip_addr) << endl;
//cout << "URL Path: " << getUrlPath(ip_addr);
//cout << "Path and File: " << getFilePath(ip_addr) << endl;
//cout << "Domain and File: " << getDomainWithFile(ip_addr) << endl;
// cout << "Port Number: " << connection_port << endl;
// cout << "File requested: " << file_to_get << endl;
//resolve domain to ipAddress
ipFromDomain = resolveToIPAddress(domain, to_string(connection_port));
//Connect to iP Address with port and return metadata
//Create the socket
int http_client_socket = socket(AF_INET, SOCK_STREAM, 0);
// connect address and contain socket
struct sockaddr_in connection_addr;
connection_addr.sin_family = AF_INET; //set the addressfamily type to INET
connection_addr.sin_port = htons(connection_port); //set socket to parsed port number
//cout << "ip address: " << ipFromDomain << endl; //checking to see if ip address is well converted
inet_aton(ipFromDomain.c_str(), &connection_addr.sin_addr); //convert ip address from IPV4 notation string and store it into structure
//Connect to server address
if(connect(http_client_socket, (struct sockaddr *) &connection_addr, sizeof(connection_addr)) != 0) {
out_file << "NOCONNECTION" << endl;
out_file.close();
exit(0);
}
//Logic for HTTP GET Request
string http_request = "GET /" + getFilePath(ip_addr) + " HTTP/1.1\r\nHost:" + domain + "\r\nConnection: close\r\n\r\n";
//cout << http_request << endl;
send_req = send(http_client_socket, http_request.c_str(), http_request.length(), 0);
if ( send_req != http_request.length() ) { std::cerr << "Oops on send\n"; }
char buff[BUFSIZ];
int n;
while ( (n=recv(http_client_socket, buff, sizeof(buff), 0)) > 0 ) {
http_response.append(buff, n);
}
//Testing
//cout << http_response << endl;
//Test for 404, if there is a 404 then close the program and exit with "FILENOTFOUND" in output file
if(http_response.find("HTTP/1.1 404") != string::npos) {
out_file << "FILENOTFOUND" << endl;
out_file.close();
exit(0);
}
const char * http_body_data = strstr(http_response.c_str(), "\r\n\r\n");
if(http_body_data) {
http_body_data += 4;
}
//out_file << http_response;
//close the file
out_file << http_body_data;
out_file.close();
//close the socket
close(http_client_socket);
} else {
out_file << "INVALIDPROTOCOL" << endl;
out_file.close();
exit(0);
}
return 0;
}
string getUrlWithNoPort(string url) {
if(hasHTTPProtocol(url))
return url.substr(7, url.length() - 7);
return url;
}
//Get URL without port and path
string getDomain(string url) {
string urlWithoutPortandPath = "";
int i = 0;
//Check if full URL has a protocol
if (hasHTTPProtocol(url)) {
//if it has a protocol truncate the protocol from FQDN
i = 7;
while (url[i] != '/') {
//for (int i = 7; i < url.length(); i++) {
if (url[i] == ':') {
break;
}
urlWithoutPortandPath += url[i];
i++;
//}
}
return urlWithoutPortandPath;
}
//if it does not have a protocol remove port number and path
while (url[i] != '/') {
//for (int i = 0; i < url.length(); i++) {
if (url[i] == ':') {
break;
}
urlWithoutPortandPath += url[i];
i++;
}
return urlWithoutPortandPath;
}
string getDomainWithFile(string url) {
string urlWithoutPortandPath = "";
//Check if full URL has a protocol
if (hasHTTPProtocol(url)) {
//if it has a protocol truncate the protocol from FQDN
for (int i = 7; i < url.length(); i++) {
if (url[i] == ':') {
break;
}
urlWithoutPortandPath += url[i];
}
return urlWithoutPortandPath;
}
//if it does not have a protocol remove port number and path
for (int i = 0; i < url.length(); i++) {
if (url[i] == ':') {
break;
}
urlWithoutPortandPath += url[i];
}
return urlWithoutPortandPath;
}
bool hasHTTPProtocol(string url) {
string httpProtocol = url.substr(0, 7);
if(httpProtocol == "http://")
return true;
return false;
}
int getPortNum(string url) {
string port = "";
int portNum, portIdx = 0, pathIdx = 0, portEndIdx = 0;
if(hasHTTPProtocol(url)) {
for(int i = 7; i < url.length(); i++) {
if (url[i] == ':')
portIdx = i + 1;
}
}
string fromPortToPath = url.substr(portIdx, url.length() - portIdx);
//cout << "Port to Path: " << fromPortToPath << endl;
for (int i = 0; i < fromPortToPath.length(); i++) {
if (fromPortToPath[i] == '/') {
pathIdx = i + 1;
portEndIdx = i;
break;
}
}
port = fromPortToPath.substr(0, portEndIdx);
portNum = stoi(port);
return portNum;
}
string getUrlPath(string url) {
string urlPath = "";
int pathIdx = 0, portIdx = 0, portEndIdx = 0;
if(hasHTTPProtocol(url)) {
for(int i = 7; i < url.length(); i++) {
if (url[i] == ':')
portIdx = i + 1;
}
}
string fromPortToPath = url.substr(portIdx, url.length() - portIdx);
cout << "Port to Path: " << fromPortToPath << endl;
for (int i = 0; i < fromPortToPath.length(); i++) {
if (fromPortToPath[i] == '/') {
pathIdx = i + 1;
portEndIdx = i;
break;
}
}
urlPath = fromPortToPath.substr(portEndIdx + 1, fromPortToPath.length() - pathIdx );
return urlPath;
}
bool hasPortNum(string url) {
if(hasHTTPProtocol(url)) {
for (int i = 7; i < url.length(); i++) {
if (url[i] == ':')
return true;
}
} else {
for (int i = 0; i < url.length(); i++) {
if (url[i] == ':')
return true;
}
}
return false;
}
//Resolves a string hostname e.g. google.com into an ipaddress (practically a DNS function)
string resolveToIPAddress(string urlString, string portNum) {
struct addrinfo hints, *results;
struct addrinfo *result;
int error, sock_id;
string numericalIPS[100];
//set all bits in hints to zero
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
if((error = getaddrinfo(urlString.c_str(), portNum.c_str(), &hints, &results)) != 0) {
cout << "error " << error << ":" << gai_strerror(error) << endl;
}
int i = 0;
//loop through results
for(result = results; result != nullptr; result = result->ai_next) {
struct sockaddr_in *ip_addr;
ip_addr = (struct sockaddr_in *)result->ai_addr;
numericalIPS[i] = inet_ntoa(ip_addr->sin_addr);
i++;
}
return numericalIPS[0];
}
string getFileFromURL(string url) {
int idxToFile;
string file_request;
string path_to_file = getDomainWithFile(url);
for (int i = 0; i < path_to_file.length(); i++) {
if(path_to_file[i] == '/') {
idxToFile = i + 1;
}
}
file_request = path_to_file.substr(idxToFile, path_to_file.length() - idxToFile);
return file_request;
}
string getFilePath(string url) {
string domainPathAndFile = getDomainWithFile(url);
string pathAndFile;
int idxToPath;
for (int i = 0; i < domainPathAndFile.length(); i++) {
if(domainPathAndFile[i] == '/') {
idxToPath = i + 1;
break;
}
}
pathAndFile = domainPathAndFile.substr(idxToPath, domainPathAndFile.length() - idxToPath);
return pathAndFile;
}
I used this website as a testing medium: http://www.testingmcafeesites.com/testcat_ac.html
When I do the following:
wget http://www.testingmcafeesites.com/testcat_ac.html
I get the following:
<html>
<head>
<title>URL for testing - Category Art/Culure</title>
</head>
<body>
<code>
http://www.testingmcafeesites.com/testcat_ac.html<br>
<br>
This is an example URL which should be categorized as an art/culture website with a minimal risk reputation score.<br>
This page simply displays this text without any specific content on it, it is just for testing purpose.<br>
<br>
<b>If you can see this text, it was not blocked by any filter!</b><br>
<br>
</code>
</body>
</html>
However, with my program, I get the following and there are spaces as well in the first couple of lines:
<html>
<head>
<title>URL for testing - Category Art/Culure</title>
</head>
<body>
<code>
http://www.testingmcafeesites.com/testcat_ac.html<br>
<br>
This is an example URL which should be categorized as an art/culture website with a minimal risk reputation score.<br>
This page simply displays this text without any specific content on it, it is just for testing purpose.<br>
<br>
<b>If you can see this text, it was not blocked by any filter!</b><br>
<br>
</code>
</body>
Sadly, this platform does not illustrate the spaces. What can I do to improve the body of the http response in my code. I was able to remove the header of the response with strstr but I feel as if I am missing something else. Thanks!
I suggest that you use
wget http://www.testingmcafeesites.com/testcat_ac.html -O testcat_ac.html
Open the file testcat_ac.html in a hex editor and you see it contains a lot of unprintable characters, null bytes. Dealing with such data as a C string http_response.c_str() is not productive. Use std::string equivalent functions.

How to use Windows API to realize line by line printing

I want to use Windows API to realize the line by line printing of the printer.
For example, if there is only one message at present, print one message. Then the printer waits and the paper stops printing. When there is information next time, continue printing on the same paper.
With windows API, I can only print by page in the following way. I don't know how to print by line. After one line is finished, the printing is suspended and the next line continues to be printed after waiting for a new task.
bool CMFCApplication1Dlg::printTicket(CString& szPrinter, CString&
szContent)
{
static DOCINFO di = { sizeof(DOCINFO), (LPTSTR)TEXT("printer"),NULL };
HDC hdcPrint = CreateDC(nullptr, szPrinter.GetBuffer(), nullptr, nullptr);
if (hdcPrint != 0)
{
if (StartDoc(hdcPrint, &di) > 0)
{
StartPage(hdcPrint);
SaveDC(hdcPrint);
int xDistance = 20;
int yDistance = 20;
LOGFONT logFont = { 0 };
logFont.lfCharSet = DEFAULT_CHARSET;
logFont.lfPitchAndFamily = DEFAULT_PITCH;
logFont.lfWeight = FW_NORMAL;
logFont.lfHeight = 60;
logFont.lfWeight = 36;
HFONT hFont = CreateFontIndirect(&logFont);
SelectObject(hdcPrint, hFont);
TextOut(hdcPrint, xDistance, yDistance, szContent.GetBuffer(), szContent.GetLength());
RestoreDC(hdcPrint, -1);
EndPage(hdcPrint);
EndDoc(hdcPrint);
}
else
{
cout << "StartDoc failed!" << endl;
string errorCode = to_string(GetLastError());
cout << "Error code is:" << errorCode << endl;
return false;
}
DeleteDC(hdcPrint);
}
else
{
cout << "CreateDC failed!" << endl;
string errorCode = to_string(GetLastError());
cout << "Error code is :" << errorCode << endl;
return false;
}
return true;
}
bool CMFCApplication1Dlg::SetPrinterParameters(CString& szPrinter)
{
HANDLE hPrinter = nullptr;
PRINTER_INFO_2* pi2 = nullptr;
DEVMODE* pDevMode = nullptr;
PRINTER_DEFAULTS pd;
DWORD dwNeeded = 0;
BOOL bFlag;
LONG lFlag;
WCHAR szDevName[MAX_PATH] = L"";
DWORD dwLength = MAX_PATH;
if (!GetDefaultPrinter(szDevName, &dwLength))
{
return false;
}
szPrinter = szDevName;
return true;
}
Maybe this has something to do with the printer driver?
I use C# winspool library and can't meet this requirement too, maybe I don't know how to use them.
Hope your help.

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();
}

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;
}