Linux. wxFRAME_NO_TASKBAR frame appears on taskbar

Do you have a typical platform dependent issue you're battling with ? Ask it here. Make sure you mention your platform, compiler, and wxWidgets version.
Post Reply
rudolfninja
Experienced Solver
Experienced Solver
Posts: 85
Joined: Tue Aug 28, 2018 1:02 pm
Location: Belarus

Linux. wxFRAME_NO_TASKBAR frame appears on taskbar

Post by rudolfninja » Tue Jul 16, 2019 10:52 am

Hi guys,
The problem is described in the subject. I've got class to draw border around application's window. The border is implemented like shaped-wxFrame with "wxFRAME_NO_TASKBAR" style. According to the documentation, it
Creates an otherwise normal frame but it does not appear in the taskbar under Windows or GTK+ (note that it will minimize to the desktop window under Windows which may seem strange to the users and thus it might be better to use this style only without wxMINIMIZE_BOX style). In wxGTK, the flag is respected only if the window manager supports _NET_WM_STATE_SKIP_TASKBAR hint.
And when the initial border is created it's indeed absent on taskbar. However I noticed when I maximize/minimize application's window few times (for about 15-20, don't ask why - it's just a test case) then the border separates as a different applications and appears on taskbar (see attachment).
Once in a period special helper class checks if application's size and position was changed:

Code: Select all

if (!m_displayMode.IsEqualGeometry(mode))
{
        DUMPER_WARN("Screen sizes have been changed");
        ResetBorder(rc.x, rc.y, rc.x + rc.width, rc.y + rc.height);
        return DS_ERROR_MODE_CHANGED;
}

if(m_displayMode.position != mode.position)
{
        m_displayMode.position = mode.position;
        MoveBorder(rc.x, rc.y);
        return DS_ERROR_MODE_CHANGED;
}
So, if size was changed ResetBorder is called:

Code: Select all

void LinuxDisplayHelperMaster::ResetBorder(int x1, int y1, int x2, int y2)
{
    m_borderWindow->Stop();
    m_borderWindow->SetPosition(x1, y1, x2, y2);
    m_borderWindow->Start();
}
Here is the border class:

Code: Select all

#pragma once

#include "ILinuxBorder.h"
#include <wx/frame.h>
#include <wx/timer.h>

class wxTimerEvent;

class LinuxBorderWindowApp: public wxFrame, public ILinuxBorder
{
public:
    LinuxBorderWindowApp();
    virtual ~LinuxBorderWindowApp();

    void Start() override;
    void Stop() override;
    void ShowBorder(bool show) override;
    void MoveBorder(int x, int y) override;
    void SetPosition(int x1, int y1, int x2, int y2) override;
    void UpdateRegionSizeAndPos();
protected:
    void OnTimer(wxTimerEvent&);
private:
    bool m_highlightFrame = false;
    wxRect m_windowRect;
    wxTimer m_appRefreshTimer;
    int m_borderThickness;
};

Code: Select all

#include "stdafx.h"
#include "LinuxBorderWindowApp.h"
#include "Dumper.h"
#include "WxWidgetsControls/FromDIP.h"

namespace
{
    constexpr int frameThickness = 2;
    const wxColour bckgBrushDark(255, 128, 0);
    const wxColour bckgBrushLight(255, 201, 14);
}

LinuxBorderWindowApp::LinuxBorderWindowApp():
    wxFrame(nullptr, wxID_ANY, wxEmptyString,
                      wxDefaultPosition, wxDefaultSize,
                      0
                      | wxFRAME_SHAPED
                      | wxSIMPLE_BORDER
                      | wxFRAME_NO_TASKBAR
                      | wxSTAY_ON_TOP
                )
{
    m_appRefreshTimer.SetOwner(this);
    m_borderThickness = FromDIP(::frameThickness);
}

LinuxBorderWindowApp::~LinuxBorderWindowApp()
{
    Stop();
}

void LinuxBorderWindowApp::Start()
{
    SetShape(wxRegion());
    if(!m_appRefreshTimer.IsRunning())
    {
        UpdateRegionSizeAndPos();
        SetBackgroundColour(bckgBrushDark);
        Bind(wxEVT_TIMER, &LinuxBorderWindowApp::OnTimer, this);
        m_appRefreshTimer.Start(500);
        Show(true);
    }
}

void LinuxBorderWindowApp::Stop()
{
    if(m_appRefreshTimer.IsRunning())
    {
        m_appRefreshTimer.Stop();
        Unbind(wxEVT_TIMER, &LinuxBorderWindowApp::OnTimer, this);
    }
    Show(false);
}

void LinuxBorderWindowApp::SetPosition(int x1, int y1, int x2, int y2)
{
    m_windowRect.x = x1;
    m_windowRect.y = y1;
    m_windowRect.width = x2 - x1;
    m_windowRect.height = y2 - y1;
}

void LinuxBorderWindowApp::OnTimer(wxTimerEvent&)
{
    SetBackgroundColour(m_highlightFrame ? bckgBrushLight : bckgBrushDark);
    m_highlightFrame = !m_highlightFrame;
    UpdateRegionSizeAndPos();
}

void LinuxBorderWindowApp::UpdateRegionSizeAndPos()
{
    wxRegion shapeRegion;
    shapeRegion.Union(wxRect(0, 0, m_windowRect.width, m_borderThickness)); // upper border
    shapeRegion.Union(wxRect(0, 0, m_borderThickness, m_windowRect.height)); // left border
    shapeRegion.Union(wxRect(0, 0 + m_windowRect.height - m_borderThickness, m_windowRect.width, m_borderThickness)); // bot border
    shapeRegion.Union(wxRect(0 + m_windowRect.width - m_borderThickness, 0, m_borderThickness, m_windowRect.height)); // right border
    SetSize(m_windowRect.x, m_windowRect.y, m_windowRect.width, m_windowRect.height);
    SetShape(shapeRegion);
    this->Update();
}

void LinuxBorderWindowApp::ShowBorder(bool show)
{
    wxFrame::Show(show);
}
void LinuxBorderWindowApp::MoveBorder(int x, int y)
{
    wxFrame::Move(x, y);
}
And here is how wxApp instance is initialized from my app:

Code: Select all

int wxStart(int argc, char** argv)
{
    return wxEntry(argc, argv);
}

bool LinuxSessionHelperModule::BeforeMainProc(int argc, char* argv[])
{
    atexit(ExitHandler);
    XInitThreads();
    CXErrorHandlerUtils::InstallErrorHandler();

    // here some unrelated code
        if (id != invalidDisplayId.nix.displayId)
        {
           // here some unrelated code
            std::thread(wxStart, argc, argv).detach();
        }
    }

    return true;
}
wxApp itself is implemented using wxIMPLEMENT_APP_NO_MAIN macro.

So my questions are why border sometimes appears on taskbar even if wxFRAME_NO_TASKBAR is used and how to solve it?
P.S. actually, I don't expect successful solution, bit hope that we'll find it.

Thanks.
Attachments
taskbar.png

ONEEYEMAN
Part Of The Furniture
Part Of The Furniture
Posts: 3352
Joined: Sat Apr 16, 2005 7:22 am
Location: USA, Ukraine

Re: Linux. wxFRAME_NO_TASKBAR frame appears on taskbar

Post by ONEEYEMAN » Tue Jul 16, 2019 2:13 pm

Hi,
note that it will minimize to the desktop window under Windows which may seem strange to the users and thus it might be better to use this style only without wxMINIMIZE_BOX style
How do you minimize/maximize the window?

Thank you.

rudolfninja
Experienced Solver
Experienced Solver
Posts: 85
Joined: Tue Aug 28, 2018 1:02 pm
Location: Belarus

Re: Linux. wxFRAME_NO_TASKBAR frame appears on taskbar

Post by rudolfninja » Tue Jul 16, 2019 2:16 pm

By double-clicking on it's titlebar or by clicking on corresponding button on window's titlebar.
In the documentation you've quoted this note actual only for Windows (if I understand it correctly), but my problem is reproduced on Linux. Also, as it seen from the border's code, I don't use wxMINIMIZE_BOX style when creating border.
Also want to clarify, that I maximize\minimize application's window, but not border itself. Border just changes its size according to application's size.

Post Reply