Use HBITMAP in dll (Logitech SDK) - c++

I'm writing a plugin for the musicplayer named MusicBee. The plugin is for the Logitech G keyboards LCD. For the Logitech G19 keyboards I will add a nice background from a bitmap file.
The sdk says that I must use HBITMAP to create a background. Now I use the HBITMAP and the background ins't showing. This is the code for the background and logo on the main page of the plugin.
m_lcd.ModifyDisplay(LG_COLOR);
background =(HBITMAP) LoadImage( NULL, L"Logitech/G19logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
m_lcd.SetBackground(background);
logo = m_lcd.AddText(LG_STATIC_TEXT, LG_BIG, DT_CENTER, LGLCD_QVGA_BMP_WIDTH);
m_lcd.SetOrigin(logo, 0, 50);
m_lcd.SetText(logo, _T("MusicBee"));
m_lcd.SetTextFontColor(logo, RGB(0,0,0));
m_lcd.Update();
The full code of my Logitech class:
//-----------------------------------------------------------------
// Logitech File
// C++ Source - Logitech.cpp - version 2012 v1.0
//-----------------------------------------------------------------
//-----------------------------------------------------------------
// Include Files
//-----------------------------------------------------------------
#include "stdafx.h"
#include "Logitech.h"
//-----------------------------------------------------------------
// Logitech methods
//-----------------------------------------------------------------
//This LogitechObject is a instance of the Logitech class for using in the thread
Logitech * Logitech::LogitechObject;
Logitech::Logitech(): stopthread(false), firstTime(true), position(0), duration(0)
{
LogitechObject = this;
}
Logitech::~Logitech()
{
stopthread = true;
this->state = StatePlay::Undefined;
timerThread.detach();
}
void CALLBACK Logitech::TimerProc(void* lpParameter, BOOLEAN TimerOrWaitFired)
{
if(LogitechObject->m_lcd.ButtonTriggered(LG_BUTTON_4))
{
LogitechObject->time = 0;
LogitechObject->m_lcd.SetProgressBarPosition(LogitechObject->progressbar, static_cast<FLOAT>(100));
LogitechObject->m_lcd.Update();
}
}
bool Logitech::getFirstTime()
{
return firstTime;
}
//Initialise Logitech LCD
BOOL Logitech::OnInitDialog()
{
HRESULT hRes = m_lcd.Initialize(_T("MusicBee"), LG_DUAL_MODE, FALSE, TRUE);
if (hRes != S_OK)
{
return FALSE;
}
m_lcd.SetAsForeground(true);
//Create home screen Logitech Color LCD
if(m_lcd.IsDeviceAvailable(LG_COLOR))
{
m_lcd.ModifyDisplay(LG_COLOR);
//m_lcd.SetBackground(RGB(245,245,245));
background =(HBITMAP) LoadImage( NULL, L"Logitech/G19logo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
m_lcd.SetBackground(background);
logo = m_lcd.AddText(LG_STATIC_TEXT, LG_BIG, DT_CENTER, LGLCD_QVGA_BMP_WIDTH);
m_lcd.SetOrigin(logo, 0, 50);
m_lcd.SetText(logo, _T("MusicBee"));
m_lcd.SetTextFontColor(logo, RGB(0,0,0));
m_lcd.Update();
}
//Create home screen Logitech Monochrome LCD
else if(m_lcd.IsDeviceAvailable(LG_MONOCHROME))
{
m_lcd.ModifyDisplay(LG_MONOCHROME);
logo = m_lcd.AddText(LG_STATIC_TEXT, LG_BIG, DT_CENTER, LGLCD_BW_BMP_WIDTH);
m_lcd.SetOrigin(logo, 0, 5);
m_lcd.SetText(logo, _T("MusicBee"));
m_lcd.Update();
}
//Start thread
timerThread = thread(&Logitech::startThread);
//CreateTimerQueueTimer(NULL,NULL,&Logitech::TimerProc,this,0,1250,WT_EXECUTEDEFAULT);
return TRUE; // return TRUE unless you set the focus to a control
}
//Create playing screen for Logitech Monochrome LCD
VOID Logitech::createMonochrome()
{
m_lcd.RemovePage(0);
m_lcd.AddNewPage();
m_lcd.ShowPage(0);
if (logo != 0)
{
delete logo;
logo = 0;
}
artist = m_lcd.AddText(LG_SCROLLING_TEXT, LG_MEDIUM, DT_CENTER, LGLCD_BW_BMP_WIDTH);
m_lcd.SetOrigin(artist, 0, 0);
title = m_lcd.AddText(LG_SCROLLING_TEXT, LG_MEDIUM, DT_CENTER, LGLCD_BW_BMP_WIDTH);
m_lcd.SetOrigin(title, 0, 13);
progressbar = m_lcd.AddProgressBar(LG_FILLED);
m_lcd.SetProgressBarSize(progressbar, 136, 5);
m_lcd.SetOrigin(progressbar, 12, 38);
time = m_lcd.AddText(LG_STATIC_TEXT, LG_SMALL, DT_LEFT, 80);
m_lcd.SetOrigin(time, 12, 29);
time1 = m_lcd.AddText(LG_STATIC_TEXT, LG_SMALL, DT_LEFT, 80);
m_lcd.SetOrigin(time1, 125, 29);
/* playIcon = static_cast<HICON>(LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_PNG2), IMAGE_BITMAP, 16, 16, LR_MONOCHROME));
playIconHandle = m_lcd.AddIcon(playIcon, 16, 16);
m_lcd.SetOrigin(playIconHandle, 2, 29);*/
firstTime = false;
changeArtistTitle(this->artistString, this->albumString, this->titleString, this->duration, this->position);
}
//Create playing screen for Logitech Color LCD
VOID Logitech::createColor()
{
m_lcd.RemovePage(0);
m_lcd.AddNewPage();
m_lcd.ShowPage(0);
if (logo != 0)
{
delete logo;
logo = 0;
}
//background.LoadFromResource(NULL, AfxGetInstanceHandle(), IDB_BITMAP2, _T("BMP"));
background =(HBITMAP) LoadImage( NULL, L"Logitech/G19Background.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
//HBITMAP bmpBkg_ = background.GetHBITMAP();
m_lcd.SetBackground(background);
//m_lcd.SetBackground(RGB(184,220,240));
artist = m_lcd.AddText(LG_SCROLLING_TEXT, LG_MEDIUM, DT_CENTER, LGLCD_QVGA_BMP_WIDTH);
m_lcd.SetOrigin(artist, 5, 5);
m_lcd.SetTextFontColor(artist, RGB(0,0,0));
album = m_lcd.AddText(LG_SCROLLING_TEXT, LG_MEDIUM, DT_CENTER, LGLCD_QVGA_BMP_WIDTH);
m_lcd.SetOrigin(album, 5, 30);
m_lcd.SetTextFontColor(album, RGB(0,0,0));
title = m_lcd.AddText(LG_SCROLLING_TEXT, LG_MEDIUM, DT_CENTER, LGLCD_QVGA_BMP_WIDTH);
m_lcd.SetOrigin(title, 5, 55);
m_lcd.SetTextFontColor(title, RGB(0,0,0));
time = m_lcd.AddText(LG_STATIC_TEXT, LG_SMALL, DT_LEFT, 80);
m_lcd.SetOrigin(time, 5, 80);
m_lcd.SetTextFontColor(time, RGB(0,0,0));
time1 = m_lcd.AddText(LG_STATIC_TEXT, LG_SMALL, DT_LEFT, 40);
m_lcd.SetOrigin(time1, 275, 80);
m_lcd.SetTextFontColor(time1, RGB(0,0,0));
progressbar = m_lcd.AddProgressBar(LG_FILLED);//320�240 pixel color screen
m_lcd.SetProgressBarSize(progressbar, 310, 20);
m_lcd.SetProgressBarColors(progressbar, RGB(25,71,94),NULL);
m_lcd.SetOrigin(progressbar, 5, 100);
/*playIcon = static_cast<HICON>(LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_PNG1), IMAGE_ICON, 16, 16, LR_COLOR));
playIconHandle = m_lcd.AddIcon(playIcon, 16, 16);
m_lcd.SetOrigin(playIconHandle, 5, 29);*/
firstTime = false;
changeArtistTitle(this->artistString, this->albumString, this->titleString, this->duration, this->position);
}
void Logitech::startThread()
{
while(!LogitechObject->stopthread)
{
this_thread::sleep_for( chrono::milliseconds(500) );
if(!LogitechObject->stopthread && LogitechObject->progressbar != NULL)
{
//Update progressbar and position time on the screen after 1 second of music.
if(LogitechObject->state == StatePlay::Playing)
{
this_thread::sleep_for( chrono::milliseconds(500) );
LogitechObject->position++;
float progresstime = ((float)LogitechObject->position / (float)LogitechObject->duration)*100;
LogitechObject->m_lcd.SetProgressBarPosition(LogitechObject->progressbar, static_cast<FLOAT>(progresstime));
LogitechObject->m_lcd.SetText(LogitechObject->time, LogitechObject->getTimeString(LogitechObject->position).c_str());
}
//If music stopped then the progressbar and time must stop immediately
else if(LogitechObject->state == StatePlay::Stopped)
{
LogitechObject->position = 0;
LogitechObject->m_lcd.SetProgressBarPosition(LogitechObject->progressbar, 0);
LogitechObject->m_lcd.SetText(LogitechObject->time, LogitechObject->getTimeString(LogitechObject->position).c_str());
}
LogitechObject->m_lcd.Update();
}
}
}
void Logitech::changeArtistTitle(wstring artistStr, wstring albumStr, wstring titleStr, int duration, int position)
{
this->artistString = artistStr;
this->albumString = albumStr;
this->titleString = titleStr;
this->durationString = getTimeString(duration/1000);
this->position = position;
this->duration = duration/1000;
if(!firstTime)
{
if(m_lcd.IsDeviceAvailable(LG_COLOR))
{
m_lcd.SetText(album, albumStr.c_str());
}
m_lcd.SetText(artist, artistStr.c_str());
m_lcd.SetText(title, titleStr.c_str());
m_lcd.SetText(time, getTimeString(position).c_str());
string s( durationString.begin(), durationString.end() );
if(s.size() < 5)
{
s = "0" + s;
}
wstring ws( s.begin(), s.end() );
m_lcd.SetText(time1, ws.c_str());
ws.clear();
///*playIcon = static_cast<HICON>(LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_PNG1), IMAGE_ICON, 16, 16, LR_COLOR));
//playIconHandle = m_lcd.AddIcon(playIcon, 16, 16);
//m_lcd.SetOrigin(playIconHandle, 5, 29);*/
m_lcd.Update();
artistStr.clear();
albumStr.clear();
titleStr.clear();
}
}
//Set current playing position
void Logitech::setPosition(int pos)
{
this->position = pos/1000;
m_lcd.SetText(time, getTimeString(this->position).c_str());
m_lcd.Update();
}
void Logitech::setDuration(int duration)
{
this->duration = duration/1000;
m_lcd.SetText(time1, getTimeString(this->duration).c_str());
m_lcd.Update();
}
//Change play state of the current playing song
void Logitech::changeState(StatePlay state)
{
this->state = state;
if(state == StatePlay::Playing && firstTime)
{
if(m_lcd.IsDeviceAvailable(LG_COLOR))
{
createColor();
}
else if(m_lcd.IsDeviceAvailable(LG_MONOCHROME))
{
createMonochrome();
}
}
}
//Change int of time to string
wstring Logitech::getTimeString(int time)
{
string minutes = to_string((int)time /60);
string seconds = to_string((int)time%60);
if(minutes.size() < 2)
{
minutes = "0" + minutes;
}
if(seconds.size() < 2)
{
seconds = "0" + seconds;
}
string timeString = minutes + ":" + seconds;
return wstring( timeString.begin(), timeString.end() );
}
What is the problem that the background nog showing?

From your reply in the comments, it seems the problem is with the LoadImage call, so we can ignore the rest of the code for now. Call GetLastError right after the LoadImage call fails and see what it returns.
Only be a few things can go wrong with a LoadImage call like that:
You could have the file name wrong. (GetLastError will return ERROR_FILE_NOT_FOUND)
You're using a relative path, but maybe the current working directory isn't where you think it is. (GetLastError will return ERROR_FILE_NOT_FOUND or ERROR_PATH_NOT_FOUND.)
Maybe that API doesn't work with slashes in place of backslashes.
Maybe you don't have access to read the file. (GetLastError will return ERROR_ACCESS_DENIED or ERROR_NETWORK_ACCESS_DENIED.)
Maybe another application has the file open without sharing. (GetLastError will return ERROR_SHARING_VIOLATION.)
Maybe the file isn't a valid bitmap. (I'm not sure what GetLastError will return here. Perhaps something like ERROR_INVALID_PARAMETER.)
Maybe it's a gigantic bitmap and there's not enough memory to load it. (GetLastError will return ERROR_NOT_ENOUGH_MEMORY.)
In general, when debugging, isolate the point in the code where things go bad (once you know LoadImage failed, you can stop worrying about all the code that happens after that). Then get as much information as you can (e.g., if it's a Windows API call that failed, call GetLastError and re-read the entire MSDN page for that API, use a debugger to examine the relative state at that point). From there, it will usually be pretty obvious what to do. If not, write the simplest program you can that does the same step. If it works, study the differences between your simple example and your actual code.

Related

Convert Gdiplus::Region to ID2D1Geometry* for clipping

I am trying to migrate my graphics interface project from Gdiplus to Direct2D.
Currently, I have a code that calculates clipping area for an rendering object:
Graphics g(hdc);
Region regC = Rect(x, y, cx + padding[2] + padding[0], cy + padding[3] + padding[1]);
RecursRegPos(this->parent, &regC);
RecursRegClip(this->parent, &regC);
g.setClip(g);
...
inline void RecursRegClip(Object *parent, Region* reg)
{
if (parent == CARD_PARENT)
return;
if (parent->overflow != OVISIBLE)
{
Rect regC(parent->getAbsoluteX(), parent->getAbsoluteY(), parent->getCx(), parent->getCy()); // Implementation of these function is not necceassary
GraphicsPath path;
path.Reset();
GetRoundRectPath(&path, regC, parent->borderRadius[0]);
// source https://stackoverflow.com/a/71431813/15220214, but if diameter is zero, just plain rect is returned
reg->Intersect(&path);
}
RecursRegClip(parent->parent, reg);
}
inline void RecursRegPos(Object* parent, Rect* reg)
{
if (parent == CARD_PARENT)
return;
reg->X += parent->getX() + parent->padding[0];
reg->Y += parent->getY() + parent->padding[1];
if (parent->overflow == OSCROLL || parent->overflow == OSCROLLH)
{
reg->X -= parent->scrollLeft;
reg->Y -= parent->scrollTop;
}
RecursRegPos(parent->parent, reg);
}
And now I need to convert it to Direct2D. As You may notice, there is no need to create Graphics object to get complete calculated clipping region, so I it would be cool if there is way to just convert Region to ID2D1Geometry*, that, as far, as I understand from msdn article need to create clipping layer.
Also, there is probably way to convert existing code (RecursRegClip, RecursRegPos) to Direct2D, but I am facing problems, because I need to work with path, but current functions get region as an argument.
Update 1
There is Region::GetData method that returns, as I understand array of points, so maybe there is possibility to define either ID2D1PathGeometry or ID2D1GeometrySink by points?
Update 2
Oh, maybe
ID2D1GeometrySink::AddLines(const D2D1_POINT_2F *points, UINT32 pointsCount)
is what do I need?
Unfortunately, GetData of region based on just (0,0,4,4) rectangle returns 36 mystique values:
Region reg(Rect(0, 0, 4, 4));
auto so = reg.GetDataSize();
BYTE* are = new BYTE[so];
UINT fi = 0;
reg.GetData(are, so, &fi);
wchar_t ou[1024]=L"\0";
for (int i = 0; i < fi; i++)
{
wchar_t buf[10] = L"";
_itow_s(are[i], buf, 10, 10);
wcscat_s(ou, 1024, buf);
wcscat_s(ou, 1024, L", ");
}
// ou - 28, 0, 0, 0, 188, 90, 187, 128, 2, 16, 192, 219, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 64, 0, 0, 128, 64,
I rewrote the solution completely, it seems to be working:
// zclip is ID2D1PathGeometry*
inline void Render(ID2D1HwndRenderTarget *target)
{
ID2D1RoundedRectangleGeometry* mask = nullptr;
ID2D1Layer* clip = nullptr;
if(ONE_OF_PARENTS_CLIPS_THIS || THIS_HAS_BORDER_RADIUS)
{
Region reg = Rect(x, y, cx + padding[2] + padding[0], cy + padding[3] + padding[1]);
RecursRegPos(this->parent, &reg);
D2D1_ROUNDED_RECT maskRect;
maskRect.rect.left = reg.X;
maskRect.rect.top = reg.Y;
maskRect.rect.right = reg.X + reg.Width;
maskRect.rect.bottom = reg.Y + reg.Height;
maskRect.radiusX = this->borderRadius[0];
maskRect.radiusY = this->borderRadius[1];
factory->CreateRoundedRectangleGeometry(maskRect, &mask);
RecursGeoClip(this->parent, mask);
target->CreateLayer(NULL, &clip);
if(zclip)
target->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), zclip), clip);
else
target->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), mask), clip);
SafeRelease(&mask);
}
// Draw stuff here
if (clip)
{
target->PopLayer();
SafeRelease(&clip);
SafeRelease(&mask);
SafeRelease(&zclip);
}
}
...
inline void RecursGeoClip(Object* parent, ID2D1Geometry* geo)
{
if (parent == CARD_PARENT)
return;
ID2D1RoundedRectangleGeometry* maskParent = nullptr;
if (parent->overflow != OVISIBLE)
{
Rect regC(parent->getAbsoluteX(), parent->getAbsoluteY(), parent->getCx(), parent->getCy());
ID2D1GeometrySink* sink = nullptr;
ID2D1PathGeometry* path = nullptr;
SafeRelease(&path);
factory->CreatePathGeometry(&path);
D2D1_ROUNDED_RECT maskRect;
maskRect.rect.left = regC.X;
maskRect.rect.top = regC.Y;
maskRect.rect.right = regC.X + regC.Width;
maskRect.rect.bottom = regC.Y + regC.Height;
maskRect.radiusX = parent->borderRadius[0];
maskRect.radiusY = parent->borderRadius[1];
path->Open(&sink);
factory->CreateRoundedRectangleGeometry(maskRect, &maskParent);
geo->CombineWithGeometry(maskParent, D2D1_COMBINE_MODE_INTERSECT, NULL, sink);
sink->Close();
SafeRelease(&sink);
SafeRelease(&this->zclip);
this->zclip = path;
RecursGeoClip(parent->parent, this->zclip);
}
else
RecursGeoClip(parent->parent, geo);
SafeRelease(&maskParent);
}
Now I can enjoy drawing one image and two rectangles in more than 60 fps, instead of 27 (in case of 200x200 image size, higher size - lower fps) with Gdi+ -_- :

Why is an XImage's data pointer null when capturing using XShmGetImage?

I'm writing a program to rapidly capture images from one window, modify them, and output them to another window using Xlib with C++. I have this working via XGetImage:
#include <iostream>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
const int TEST_SIZE = 512;
int main()
{
// Open default display
Display *display = XOpenDisplay(nullptr);
int screen = DefaultScreen(display);
Window rootWin = RootWindow(display, screen);
GC graphicsContext = DefaultGC(display, screen);
// Create new window and subscribe to events
long blackPixel = BlackPixel(display, screen);
long whitePixel = WhitePixel(display, screen);
Window newWin = XCreateSimpleWindow(display, rootWin, 0, 0, TEST_SIZE, TEST_SIZE, 1, blackPixel, whitePixel);
XMapWindow(display, newWin);
XSelectInput(display, newWin, ExposureMask | KeyPressMask);
// Main event loop for new window
XImage *image;
XEvent event;
bool exposed = false;
bool killWindow = false;
while (!killWindow)
{
// Handle pending events
if (XPending(display) > 0)
{
XNextEvent(display, &event);
if (event.type == Expose)
{
exposed = true;
} else if (event.type == NoExpose)
{
exposed = false;
} else if (event.type == KeyPress)
{
killWindow = true;
}
}
// Capture the original image
image = XGetImage(display, rootWin, 0, 0, TEST_SIZE, TEST_SIZE, AllPlanes, ZPixmap);
// Modify the image
if (image->data != nullptr)
{
long pixel = 0;
for (int x = 0; x < image->width; x++)
{
for (int y = 0; y < image->height; y++)
{
// Invert the color of each pixel
pixel = XGetPixel(image, x, y);
XPutPixel(image, x, y, ~pixel);
}
}
}
// Output the modified image
if (exposed && killWindow == false)
{
XPutImage(display, newWin, graphicsContext, image, 0, 0, 0, 0, TEST_SIZE, TEST_SIZE);
}
XDestroyImage(image);
}
// Goodbye
XCloseDisplay(display);
}
It generates output like this: https://streamable.com/hovg9
I'm happy to have gotten this far, but from what I've read the performance isn't going to scale very well because it has to allocate space for a new image at every frame. In fact, without the call to XDestroyImage at the end of the loop this program fills up all 16GB of memory on my machine in a matter of seconds!
It seems the recommended approach here is to to set up a shared memory space where X can write the contents of each frame and my program can subsequently read and modify them without the need for any extra allocation. Since the call to XShmGetImage blocks and waits for IPC I believe this means I won't have to worry about any concurrency issues in the shared space.
I've attempted to implement the XShmGetImage approach with the following code:
#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <X11/Xlib.h>
#include <X11/extensions/XShm.h>
#include <X11/Xutil.h>
const int TEST_SIZE = 512;
int main()
{
// Open default display
Display *display = XOpenDisplay(nullptr);
int screen = DefaultScreen(display);
Window rootWin = RootWindow(display, screen);
GC graphicsContext = DefaultGC(display, screen);
// Create new window and subscribe to events
long blackPixel = BlackPixel(display, screen);
long whitePixel = WhitePixel(display, screen);
Window newWin = XCreateSimpleWindow(display, rootWin, 0, 0, TEST_SIZE, TEST_SIZE, 1, blackPixel, whitePixel);
XMapWindow(display, newWin);
XSelectInput(display, newWin, ExposureMask | KeyPressMask);
// Allocate shared memory for image capturing
Visual *visual = DefaultVisual(display, 0);
XShmSegmentInfo shminfo;
int depth = DefaultDepth(display, screen);
XImage *image = XShmCreateImage(display, visual, depth, ZPixmap, nullptr, &shminfo, TEST_SIZE, TEST_SIZE);
shminfo.shmid = shmget(IPC_PRIVATE, image->bytes_per_line * image->height, IPC_CREAT | 0666);
shmat(shminfo.shmid, nullptr, 0);
shminfo.shmaddr = image->data;
shminfo.readOnly = False;
XShmAttach(display, &shminfo);
// Main event loop for new window
XEvent event;
bool exposed = false;
bool killWindow = false;
while (!killWindow)
{
// Handle pending events
if (XPending(display) > 0)
{
XNextEvent(display, &event);
if (event.type == Expose)
{
exposed = true;
} else if (event.type == NoExpose)
{
exposed = false;
} else if (event.type == KeyPress)
{
killWindow = true;
}
}
// Capture the original image
XShmGetImage(display, rootWin, image, 0, 0, AllPlanes);
// Modify the image
if (image->data != nullptr) // NEVER TRUE. DATA IS ALWAYS NULL!
{
long pixel = 0;
for (int x = 0; x < image->width; x++)
{
for (int y = 0; y < image->height; y++)
{
// Invert the color of each pixel
pixel = XGetPixel(image, x, y);
XPutPixel(image, x, y, ~pixel);
}
}
}
// Output the modified image
if (exposed && killWindow == false)
{
XShmPutImage(display, newWin, graphicsContext, image, 0, 0, 0, 0, 512, 512, false);
}
}
// Goodbye
XFree(image);
XCloseDisplay(display);
}
Somehow, the images are still being captured and sent to the new window, but the data pointer within the XImage is always null. I can't modify any of the contents and any usage of the XGetPixel and XPutPixel macros will fail. Notice in this video that none of the colors are being inverted like before: https://streamable.com/dckyv
This doesn't make any sense to me. Clearly the data is still being transferred between the windows, but where is it in the XImage structure? How can I access it via code?
It turns out I wasn't reading the signature for shmat correctly. It doesn't have a void return type, it returns a void pointer. This needs to be assigned directly to the XImage's data pointer in order for the data pointer to do anything.
In the call to XShmPutImage the shared memory location is referenced internally rather than the XImage's data pointer which is why this was still working even without utilizing the return value from shmat.
Here's the working code, along with a few other additions which I made for error handling and teardown:
#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <X11/Xlib.h>
#include <X11/extensions/XShm.h>
#include <X11/Xutil.h>
const int TEST_SIZE = 512;
static int handleXError(Display *display, XErrorEvent *event)
{
printf("XErrorEvent triggered!\n");
printf("error_code: %d", event->error_code);
printf("minor_code: %d", event->minor_code);
printf("request_code: %d", event->request_code);
printf("resourceid: %lu", event->resourceid);
printf("serial: %d", event->error_code);
printf("type: %d", event->type);
return 0;
}
int main()
{
// Open default display
XSetErrorHandler(handleXError);
Display *display = XOpenDisplay(nullptr);
int screen = DefaultScreen(display);
Window rootWin = RootWindow(display, screen);
GC graphicsContext = DefaultGC(display, screen);
// Create new window and subscribe to events
long blackPixel = BlackPixel(display, screen);
long whitePixel = WhitePixel(display, screen);
Window newWin = XCreateSimpleWindow(display, rootWin, 0, 0, TEST_SIZE, TEST_SIZE, 1, blackPixel, whitePixel);
XMapWindow(display, newWin);
XSelectInput(display, newWin, ExposureMask | KeyPressMask);
// Allocate shared memory for image capturing
XShmSegmentInfo shminfo;
Visual *visual = DefaultVisual(display, screen);
int depth = DefaultDepth(display, screen);
XImage *image = XShmCreateImage(display, visual, depth, ZPixmap, nullptr, &shminfo, TEST_SIZE, TEST_SIZE);
shminfo.shmid = shmget(IPC_PRIVATE, image->bytes_per_line * image->height, IPC_CREAT | 0777);
image->data = (char*)shmat(shminfo.shmid, 0, 0);
shminfo.shmaddr = image->data;
shminfo.readOnly = False;
XShmAttach(display, &shminfo);
XSync(display, false);
shmctl(shminfo.shmid, IPC_RMID, 0);
// Main event loop for new window
XEvent event;
bool exposed = false;
bool killWindow = false;
while (!killWindow)
{
// Handle pending events
if (XPending(display) > 0)
{
XNextEvent(display, &event);
if (event.type == Expose)
{
exposed = true;
} else if (event.type == NoExpose)
{
exposed = false;
} else if (event.type == KeyPress)
{
killWindow = true;
}
}
// Capture the original image
XShmGetImage(display, rootWin, image, 0, 0, AllPlanes);
// Modify the image
if(image->data != nullptr) // NEVER TRUE. DATA IS ALWAYS NULL!
{
long pixel = 0;
for (int x = 0; x < image->width; x++)
{
for (int y = 0; y < image->height; y++)
{
// Invert the color of each pixel
pixel = XGetPixel(image, x, y);
XPutPixel(image, x, y, ~pixel);
}
}
}
// Output the modified image
if (exposed && killWindow == false)
{
XShmPutImage(display, newWin, graphicsContext, image, 0, 0, 0, 0, 512, 512, false);
}
}
// Goodbye
XShmDetach(display, &shminfo);
XDestroyImage(image);
shmdt(shminfo.shmaddr);
XCloseDisplay(display);
}

MFC Custom Button control

I have created a custom button in mfc(VS 2013)DLL. It worked Fine. I have created a test dialog exe to test the dll. Then I added some code to simulate hover effect. Now when I run the Dialog exe, the button appears as wanted but when move the cursor on top of the button, the gradient changes but it throws an Debug Assertion.
void CBootstrapButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
dc = CDC::FromHandle(lpDrawItemStruct->hDC);//Get device context object
m_bIsFocused = (lpDrawItemStruct->itemState & ODS_FOCUS);
m_bIsDisabled = (lpDrawItemStruct->itemState & ODS_DISABLED);
m_bIsPressed = (lpDrawItemStruct->itemState & ODS_SELECTED);
//Preparing the Region to Draw
CRect btnRect = lpDrawItemStruct->rcItem;
int iCX = lpDrawItemStruct->rcItem.right;
int iCY = lpDrawItemStruct->rcItem.bottom;
CRgn Rgn1;//<-this is where I am getting assertion error
Rgn1.CreateRoundRectRgn(0, 0, lpDrawItemStruct->rcItem.right, lpDrawItemStruct->rcItem.bottom, 10, 10);
dc->SelectClipRgn(&Rgn1);
MemDC.CreateCompatibleDC(dc);
pDC = &MemDC;
Bmp.CreateCompatibleBitmap(dc, iCX, iCY);
OldBitmap = MemDC.SelectObject(&Bmp);
border = RGB(219, 219, 219);//Button Border color
/******* Some Color Logic*******/
//Applying Border
pDC->RoundRect(0, 0, lpDrawItemStruct->rcItem.right, lpDrawItemStruct->rcItem.bottom, 10, 10);
pDC->FillSolidRect(&lpDrawItemStruct->rcItem, border);
if (m_bIsFocused)
{
pDC->DrawFocusRect(&lpDrawItemStruct->rcItem);
}
//Applying Gradients
CRgn Rgn2;
Rgn2.CreateRoundRectRgn(1, 1, lpDrawItemStruct->rcItem.right - 1, lpDrawItemStruct->rcItem.bottom - 1, 10, 10);
pDC->SelectClipRgn(&Rgn2);
RECT rect = lpDrawItemStruct->rcItem;
for (int i = 0; i<rect.bottom; i++)
{
int r, g, b;
r = r1 + (i * (r2 - r1) / rect.bottom);
g = g1 + (i * (g2 - g1) / rect.bottom);
b = b1 + (i * (b2 - b1) / rect.bottom);
pDC->FillSolidRect(0, i, rect.right, 1, RGB(r, g, b));
}
//Start Drawing Text
CString strText;
GetWindowText(strText);
int iOldMode = pDC->SetBkMode(TRANSPARENT);
COLORREF crOldColor;
//Setting Text Color
if (btnStyle==BTN_DEFAULT)
{
crOldColor = pDC->SetTextColor(RGB(0, 0, 0));
}
else
{
crOldColor = pDC->SetTextColor(RGB(255, 255, 255));
}
//Setting Font for Text (Default Font Name=Open Sans,Default Font Size=24)
//TODO Font Size according to Button Size.
CFont font;
font.CreateFont(
24, // nHeight//Font Size
0, // nWidth
0, // nEscapement
0, // nOrientation
FW_NORMAL, // nWeight
FALSE, // bItalic
FALSE, // bUnderline
0, // cStrikeOut
ANSI_CHARSET, // nCharSet
OUT_DEFAULT_PRECIS, // nOutPrecision
CLIP_DEFAULT_PRECIS, // nClipPrecision
DEFAULT_QUALITY, // nQuality
DEFAULT_PITCH | FF_SWISS, // nPitchAndFamily
_T("Open Sans"));
CFont* def_font = pDC->SelectObject(&font);
//Draw Text
pDC->DrawText(strText, &lpDrawItemStruct->rcItem, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
dc->BitBlt(0, 0, iCX, iCY, pDC, 0, 0, SRCCOPY);
pDC->SetTextColor(crOldColor);
pDC->SetBkMode(iOldMode);
pDC->SelectObject(OldBitmap);
pDC->SelectObject(def_font);
//End Drawing Text
//Cleaning up
pDC->DeleteDC();
dc->SelectClipRgn(NULL);
dc->Detach();
}
Code for hover Effect
void CBootstrapButton::OnMouseMove(UINT nFlags, CPoint point)
{
CWnd* wndUnderMouse = NULL;
CWnd* wndActive = this;
TRACKMOUSEEVENT csTME;
CButton::OnMouseMove(nFlags, point);
ClientToScreen(&point);
wndUnderMouse = WindowFromPoint(point);
if (nFlags & MK_LBUTTON && m_bMouseOnButton == FALSE) return;
wndActive = GetActiveWindow();
if (wndUnderMouse && wndUnderMouse->m_hWnd == m_hWnd && wndActive)
{
if (!m_bMouseOnButton)
{
m_bMouseOnButton = TRUE;
Invalidate();
csTME.cbSize = sizeof(csTME);
csTME.dwFlags = TME_LEAVE;
csTME.hwndTrack = m_hWnd;
::_TrackMouseEvent(&csTME);
} // if
}
else CancelHover();
}
void CBootstrapButton::CancelHover()
{
if (m_bMouseOnButton)
{
m_bMouseOnButton = FALSE;
Invalidate();
}
}
Try
this->ReleaseDC(dc);
instead of
dc->Detach();
at the end of your function.

Want some text to erase its self in game

Ok so just kind of want to know a trick to get around this.
So i want the text "window will close in 10 seconds" to erase every time it goes through the loop and replace with the next number counting down. But all i get now is overlapping.
I just want it to count down and display as it goes.
//FILE: Main.cpp
//PROGR: Hank Bates
//PURPOSE: To display text on screen for 10 seconds.
//EXTRA ADD ON FILES: Slendermanswriting.ttf
// PrometheusSiren.wav
#include <allegro5\allegro.h>
#include <allegro5\allegro_font.h>
#include <allegro5\allegro_ttf.h>
#include <allegro5\allegro_native_dialog.h>
#include <allegro5\allegro_audio.h>
#include <allegro5\allegro_acodec.h>
int main(void)
{
//summon the fonts and stuff
ALLEGRO_DISPLAY *display = NULL;
ALLEGRO_FONT *font50;
ALLEGRO_FONT *font36;
ALLEGRO_FONT *font18;
ALLEGRO_SAMPLE *song;
int a = 100;
if (!al_init())
{
al_show_native_message_box(NULL, NULL, NULL,
"failed to initialize allegro!", NULL, NULL);
return -1;
}
//set up some of the display settings
al_set_new_display_flags(ALLEGRO_WINDOWED | ALLEGRO_RESIZABLE);
display = al_create_display(640, 480);
al_set_window_title(display, "A bad horror game");
if (!display)
{
al_show_native_message_box(NULL, NULL, NULL,
"failed to initialize display!", NULL, NULL);
return -1;
}
al_init_font_addon();
al_init_ttf_addon();
//Install Slender Man font here
font50 = al_load_font("Slendermanswriting.ttf", 50, 0);
font36 = al_load_font("Slendermanswriting.ttf", 36, 0);
font18 = al_load_font("Slendermanswriting.ttf", 18, 0);
//set up music here
al_install_audio();
al_init_acodec_addon();
al_reserve_samples(1);
song = al_load_sample("PrometheusSiren.wav");
//play song this will loop around and around like a record man!
al_play_sample(song, 1, 0, 1, ALLEGRO_PLAYMODE_LOOP, NULL);
int screen_w = al_get_display_width(display);
int screen_h = al_get_display_height(display);
al_clear_to_color(al_map_rgb(0, 0, 0));
al_draw_text(font50, al_map_rgb(255, 0, 0), 12, 50, 0, "SLENDER MAN IS COMING");
al_draw_text(font36, al_map_rgb(255, 5, 10), 200, 100, 0, "RUN AND HIDE");
al_draw_text(font18, al_map_rgb(100, 15, 18), 150, 150, 0, "ENJOY THE PROMETHEUS SIREN MUSIC");
int timer = 10;
while (timer != 0)
{
al_draw_textf(font18, al_map_rgb(255, 255, 255), screen_w / 3, 300, ALLEGRO_ALIGN_CENTRE,
"TURN UP YOUR VOLUME TO %i PRECENT!", a);
al_draw_textf(font18, al_map_rgb(255, 255, 255), screen_w / 2, 400, ALLEGRO_ALIGN_CENTRE,
"WINDOW WILL CLOSE IN %i seconds!", timer);
al_flip_display();
al_rest(1.0);
timer = timer - 1;
}
al_rest(10.0);
//destroy stuff
al_destroy_font(font18);
al_destroy_font(font50);
al_destroy_font(font36);
al_destroy_display(display);
al_destroy_sample(song);
//pew pew pew, bang.... all destoryed :)
return 0;
}
Move the background clear and first three text outputs into the main loop. You need to redraw everything on each frame.
//FILE: Main.cpp
//PROGR: Hank Bates
//PURPOSE: To display text on screen for 10 seconds.
//EXTRA ADD ON FILES: Slendermanswriting.ttf
// PrometheusSiren.wav
#include <allegro5\allegro.h>
#include <allegro5\allegro_font.h>
#include <allegro5\allegro_ttf.h>
#include <allegro5\allegro_native_dialog.h>
#include <allegro5\allegro_audio.h>
#include <allegro5\allegro_acodec.h>
int main(void)
{
//summon the fonts and stuff
ALLEGRO_DISPLAY *display = NULL;
ALLEGRO_FONT *font50;
ALLEGRO_FONT *font36;
ALLEGRO_FONT *font18;
ALLEGRO_SAMPLE *song;
int a = 100;
int time_left = 10;
//test redaw
ALLEGRO_EVENT_QUEUE *event_queue = NULL;
ALLEGRO_TIMER *timer = NULL;
bool redraw = true;
if (!al_init())
{
al_show_native_message_box(NULL, NULL, NULL,
"failed to initialize allegro!", NULL, NULL);
return -1;
}
//set up some of the display settings
al_set_new_display_flags(ALLEGRO_WINDOWED | ALLEGRO_RESIZABLE);
display = al_create_display(640, 480);
al_set_window_title(display, "A bad horror game");
if (!display)
{
al_show_native_message_box(NULL, NULL, NULL,
"failed to initialize display!", NULL, NULL);
return -1;
}
al_init_font_addon();
al_init_ttf_addon();
//Install Slender Man font here
font50 = al_load_font("Slendermanswriting.ttf", 50, 0);
font36 = al_load_font("Slendermanswriting.ttf", 36, 0);
font18 = al_load_font("Slendermanswriting.ttf", 18, 0);
//set up music here
al_install_audio();
al_init_acodec_addon();
al_reserve_samples(1);
//upload the song here
song = al_load_sample("PrometheusSiren.wav");
//play song this will loop around and around like a record man!
al_play_sample(song, 1, 0, 1, ALLEGRO_PLAYMODE_LOOP, NULL);
int screen_w = al_get_display_width(display);
int screen_h = al_get_display_height(display);
al_clear_to_color(al_map_rgb(0, 0, 0));
while (time_left != 0)
{
al_flip_display();
al_clear_to_color(al_map_rgb(0, 0, 0));
al_draw_text(font50, al_map_rgb(255, 0, 0), 12, 50, 0, "SLENDER MAN IS COMING");
al_draw_text(font36, al_map_rgb(255, 5, 10), 200, 100, 0, "RUN AND HIDE");
al_draw_text(font18, al_map_rgb(100, 15, 18), 150, 150, 0, "ENJOY THE PROMETHEUS SIREN MUSIC");
al_draw_textf(font18, al_map_rgb(255, 255, 255), screen_w / 3, 300, ALLEGRO_ALIGN_CENTRE,
"TURN UP YOUR VOLUME TO %i PRECENT!", a);
al_draw_textf(font18, al_map_rgb(255, 255, 255), screen_w / 2, 400, ALLEGRO_ALIGN_CENTRE,
"WINDOW WILL CLOSE IN %i seconds!", time_left);
al_rest(1.0);
time_left = time_left - 1;
}
al_flip_display();
al_rest(0.0);
//destroy stuff
al_destroy_font(font18);
al_destroy_font(font50);
al_destroy_font(font36);
al_destroy_display(display);
al_destroy_sample(song);
al_destroy_timer(timer);
//pew pew pew, bang.... all destoryed :)
return 0;
}
Yo i got it. Thanks for the point in the right question.

Loading images from resource don't work(C++ Dll)

I'm writing a Logitech plugin for MusicBee.
The only problem I have is that the images of the artwork, play and pause image don't load.
I have tested a lot of different ways to use the images in the VS2012 resource file. But non of them works. I must load images in a dll file and the image must be HICON or HBITMAP.
Below is een full class of my project. Full project can be found at Bitbucket.
//This is the code I have used for loading an image.
playIcon = LoadIcon (GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON1));
playIconHandle = m_lcd.AddIcon(playIcon, 16, 16);
m_lcd.SetOrigin(playIconHandle, 2, 2);
//-----------------------------------------------------------------
// Logitech File
// C++ Source - Logitech.cpp - version 2012 v1.0
//-----------------------------------------------------------------
//-----------------------------------------------------------------
// Include Files
//-----------------------------------------------------------------
#include "stdafx.h"
#include "Logitech.h"
//-----------------------------------------------------------------
// Logitech methods
//-----------------------------------------------------------------
//This LogitechObject is a instance of the Logitech class for using in the thread
Logitech * Logitech::LogitechObject;
Logitech::Logitech(): stopthread(false), firstTime(true), position(0), duration(0)
{
LogitechObject = this;
}
Logitech::~Logitech()
{
stopthread = true;
this->state = StatePlay::Undefined;
timerThread.detach();
}
bool Logitech::getFirstTime()
{
return firstTime;
}
//Initialise Logitech LCD
BOOL Logitech::OnInitDialog()
{
HRESULT hRes = m_lcd.Initialize(_T("MusicBee"), LG_DUAL_MODE, FALSE, TRUE);
if (hRes != S_OK)
{
return FALSE;
}
m_lcd.SetAsForeground(true);
//Create home screen Logitech Color LCD
if(m_lcd.IsDeviceAvailable(LG_COLOR))
{
m_lcd.ModifyDisplay(LG_COLOR);
m_lcd.SetBackground(RGB(245,245,245));
logo = m_lcd.AddText(LG_STATIC_TEXT, LG_BIG, DT_CENTER, LGLCD_QVGA_BMP_WIDTH);
m_lcd.SetOrigin(logo, 0, 50);
m_lcd.SetText(logo, _T("MusicBee"));
m_lcd.SetTextFontColor(logo, RGB(0,0,0));
m_lcd.Update();
}
//Create home screen Logitech Monochrome LCD
else if(m_lcd.IsDeviceAvailable(LG_MONOCHROME))
{
m_lcd.ModifyDisplay(LG_MONOCHROME);
logo = m_lcd.AddText(LG_STATIC_TEXT, LG_BIG, DT_CENTER, LGLCD_BW_BMP_WIDTH);
m_lcd.SetOrigin(logo, 0, 5);
m_lcd.SetText(logo, _T("MusicBee"));
m_lcd.Update();
}
//Start thread
timerThread = thread(&Logitech::startThread);
return TRUE; // return TRUE unless you set the focus to a control
}
//Create playing screen for Logitech Monochrome LCD
VOID Logitech::createMonochrome()
{
m_lcd.RemovePage(0);
m_lcd.AddNewPage();
m_lcd.ShowPage(0);
if (logo != 0)
{
delete logo;
logo = 0;
}
artist = m_lcd.AddText(LG_SCROLLING_TEXT, LG_MEDIUM, DT_CENTER, LGLCD_BW_BMP_WIDTH);
m_lcd.SetOrigin(artist, 0, 0);
title = m_lcd.AddText(LG_SCROLLING_TEXT, LG_MEDIUM, DT_CENTER, LGLCD_BW_BMP_WIDTH);
m_lcd.SetOrigin(title, 0, 13);
progressbar = m_lcd.AddProgressBar(LG_FILLED);
m_lcd.SetProgressBarSize(progressbar, 136, 5);
m_lcd.SetOrigin(progressbar, 12, 38);
time = m_lcd.AddText(LG_STATIC_TEXT, LG_SMALL, DT_LEFT, 80);
m_lcd.SetOrigin(time, 12, 29);
time1 = m_lcd.AddText(LG_STATIC_TEXT, LG_SMALL, DT_LEFT, 80);
m_lcd.SetOrigin(time1, 125, 29);
playIcon = static_cast<HICON>(LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_PNG2), IMAGE_BITMAP, 16, 16, LR_MONOCHROME));
playIconHandle = m_lcd.AddIcon(playIcon, 16, 16);
m_lcd.SetOrigin(playIconHandle, 2, 29);
firstTime = false;
changeArtistTitle(this->artistString, this->albumString, this->titleString, this->duration, this->position);
}
//Create playing screen for Logitech Color LCD
VOID Logitech::createColor()
{
m_lcd.RemovePage(0);
m_lcd.AddNewPage();
m_lcd.ShowPage(0);
if (logo != 0)
{
delete logo;
logo = 0;
}
//background.LoadFromResource(NULL, AfxGetInstanceHandle(), IDB_G19BACKGROUND, _T("PNG"));
//HBITMAP bmpBkg_ = background.GetHBITMAP();
//m_lcd.SetBackground(bmpBkg_);
m_lcd.SetBackground(RGB(184,220,240));
artist = m_lcd.AddText(LG_SCROLLING_TEXT, LG_MEDIUM, DT_CENTER, LGLCD_QVGA_BMP_WIDTH);
m_lcd.SetOrigin(artist, 5, 5);
m_lcd.SetTextFontColor(artist, RGB(0,0,0));
album = m_lcd.AddText(LG_SCROLLING_TEXT, LG_MEDIUM, DT_CENTER, LGLCD_QVGA_BMP_WIDTH);
m_lcd.SetOrigin(album, 5, 30);
m_lcd.SetTextFontColor(album, RGB(0,0,0));
title = m_lcd.AddText(LG_SCROLLING_TEXT, LG_MEDIUM, DT_CENTER, LGLCD_QVGA_BMP_WIDTH);
m_lcd.SetOrigin(title, 5, 55);
m_lcd.SetTextFontColor(title, RGB(0,0,0));
time = m_lcd.AddText(LG_STATIC_TEXT, LG_SMALL, DT_LEFT, 80);
m_lcd.SetOrigin(time, 5, 80);
m_lcd.SetTextFontColor(time, RGB(0,0,0));
time1 = m_lcd.AddText(LG_STATIC_TEXT, LG_SMALL, DT_LEFT, 40);
m_lcd.SetOrigin(time1, 275, 80);
m_lcd.SetTextFontColor(time1, RGB(0,0,0));
progressbar = m_lcd.AddProgressBar(LG_FILLED);//320×240 pixel color screen
m_lcd.SetProgressBarSize(progressbar, 310, 20);
m_lcd.SetProgressBarColors(progressbar, RGB(25,71,94),NULL);
m_lcd.SetOrigin(progressbar, 5, 100);
/*playIcon = static_cast<HICON>(LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_PNG1), IMAGE_ICON, 16, 16, LR_COLOR));
playIconHandle = m_lcd.AddIcon(playIcon, 16, 16);
m_lcd.SetOrigin(playIconHandle, 5, 29);*/
firstTime = false;
changeArtistTitle(this->artistString, this->albumString, this->titleString, this->duration, this->position);
}
void Logitech::startThread()
{
while(!LogitechObject->stopthread)
{
this_thread::sleep_for( chrono::milliseconds(500) );
if(!LogitechObject->stopthread && LogitechObject->progressbar != NULL)
{
//Update progressbar and position time on the screen after 1 second of music.
if(LogitechObject->state == StatePlay::Playing)
{
this_thread::sleep_for( chrono::milliseconds(500) );
LogitechObject->position++;
float progresstime = ((float)LogitechObject->position / (float)LogitechObject->duration)*100;
LogitechObject->m_lcd.SetProgressBarPosition(LogitechObject->progressbar, static_cast<FLOAT>(progresstime));
LogitechObject->m_lcd.SetText(LogitechObject->time, LogitechObject->getTimeString(LogitechObject->position).c_str());
}
//If music stopped then the progressbar and time must stop immediately
else if(LogitechObject->state == StatePlay::Stopped)
{
LogitechObject->position = 0;
LogitechObject->m_lcd.SetProgressBarPosition(LogitechObject->progressbar, 0);
LogitechObject->m_lcd.SetText(LogitechObject->time, LogitechObject->getTimeString(LogitechObject->position).c_str());
}
LogitechObject->m_lcd.Update();
}
}
}
void Logitech::changeArtistTitle(wstring artistStr, wstring albumStr, wstring titleStr, int duration, int position)
{
this->artistString = artistStr;
this->albumString = albumStr;
this->titleString = titleStr;
this->durationString = getTimeString(duration/1000);
this->position = position;
this->duration = duration/1000;
if(!firstTime)
{
if(m_lcd.IsDeviceAvailable(LG_COLOR))
{
m_lcd.SetText(album, albumStr.c_str());
}
m_lcd.SetText(artist, artistStr.c_str());
m_lcd.SetText(title, titleStr.c_str());
m_lcd.SetText(time, getTimeString(position).c_str());
string s( durationString.begin(), durationString.end() );
if(s.size() < 5)
{
s = "0" + s;
}
wstring ws( s.begin(), s.end() );
m_lcd.SetText(time1, ws.c_str());
ws.clear();
///*playIcon = static_cast<HICON>(LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_PNG1), IMAGE_ICON, 16, 16, LR_COLOR));
//playIconHandle = m_lcd.AddIcon(playIcon, 16, 16);
//m_lcd.SetOrigin(playIconHandle, 5, 29);*/
m_lcd.Update();
artistStr.clear();
albumStr.clear();
titleStr.clear();
}
}
//Set current playing position
void Logitech::setPosition(int pos)
{
this->position = pos/1000;
m_lcd.SetText(time, getTimeString(this->position).c_str());
m_lcd.Update();
}
void Logitech::setDuration(int duration)
{
this->duration = duration/1000;
m_lcd.SetText(time1, getTimeString(this->duration).c_str());
m_lcd.Update();
}
//Change play state of the current playing song
void Logitech::changeState(StatePlay state)
{
this->state = state;
if(state == StatePlay::Playing && firstTime)
{
if(m_lcd.IsDeviceAvailable(LG_COLOR))
{
createColor();
}
else if(m_lcd.IsDeviceAvailable(LG_MONOCHROME))
{
createMonochrome();
}
}
}
//Change int of time to string
wstring Logitech::getTimeString(int time)
{
string minutes = to_string((int)time /60);
string seconds = to_string((int)time%60);
if(minutes.size() < 2)
{
minutes = "0" + minutes;
}
if(seconds.size() < 2)
{
seconds = "0" + seconds;
}
string timeString = minutes + ":" + seconds;
return wstring( timeString.begin(), timeString.end() );
}
LoadImage(GetModuleHandle(NULL), ...)
This cannot be correct in a plugin. GetModuleHandle(NULL) returns a handle to the EXE, not your DLL. Assuming you embedded your resources in your plugin DLL, you will need to use the module handle for your DLL, the one you get handed to you in your DllMain() function, 1st argument.