mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 10:44:56 +00:00
Bug 1418749 - Add a TaskbarProgress implementation for gtk3/x11. r=paolo,karlt
This adds support for download progress reporting via the XApp method currently used in the Cinnamon desktop, by establishing a new X11 window property to be supported/read by the window manager. See https://github.com/linuxmint/xapps/blob/master/libxapp/xapp-gtk-window.c, as well as https://github.com/linuxmint/muffin/commit/39045da0ea06f for more details. The property-setting code lives in nsWindow - it's a small and stable enough chunk that it made more sense to do this than actually depend on another external library. As nsWindow is already using x11 calls, this seemed the safest place for it, without affecting the build. The TaskbarProgress instance is initialized via the DownloadsTaskbar js module, and is handed a pointer to the current main window to call SetProgress on. Most of the javascript side of this is in line with how the other platforms are handled. Without a supporting window manager/desktop environment (currently just Cinnamon/Muffin 3.6,) the simplest way to observe working behavior is by calling 'xprop -spy' on the browser window being testing and watching for updates during a download. --HG-- extra : rebase_source : 0606f6c87116204ec290c19276072d0c1c35691e
This commit is contained in:
parent
43fb829da9
commit
1a8239407b
@ -40,6 +40,12 @@ XPCOMUtils.defineLazyGetter(this, "gMacTaskbarProgress", function() {
|
||||
.getService(Ci.nsITaskbarProgress);
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gGtkTaskbarProgress", function() {
|
||||
return ("@mozilla.org/widget/taskbarprogress/gtk;1" in Cc) &&
|
||||
Cc["@mozilla.org/widget/taskbarprogress/gtk;1"]
|
||||
.getService(Ci.nsIGtkTaskbarProgress);
|
||||
});
|
||||
|
||||
// DownloadsTaskbar
|
||||
|
||||
/**
|
||||
@ -89,6 +95,10 @@ var DownloadsTaskbar = {
|
||||
// On Windows, the indicator is currently hidden because we have no
|
||||
// previous browser window, thus we should attach the indicator now.
|
||||
this._attachIndicator(aBrowserWindow);
|
||||
} else if (gGtkTaskbarProgress) {
|
||||
this._taskbarProgress = gGtkTaskbarProgress;
|
||||
|
||||
this._attachGtkTaskbarProgress(aBrowserWindow);
|
||||
} else {
|
||||
// The taskbar indicator is not available on this platform.
|
||||
return;
|
||||
@ -143,6 +153,35 @@ var DownloadsTaskbar = {
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* In gtk3, the window itself implements the progress interface.
|
||||
*/
|
||||
_attachGtkTaskbarProgress(aWindow) {
|
||||
// Set the current window.
|
||||
this._taskbarProgress.setPrimaryWindow(aWindow);
|
||||
|
||||
// If the DownloadSummary object has already been created, we should update
|
||||
// the state of the new indicator, otherwise it will be updated as soon as
|
||||
// the DownloadSummary view is registered.
|
||||
if (this._summary) {
|
||||
this.onSummaryChanged();
|
||||
}
|
||||
|
||||
aWindow.addEventListener("unload", () => {
|
||||
// Locate another browser window, excluding the one being closed.
|
||||
let browserWindow = RecentWindow.getMostRecentBrowserWindow();
|
||||
if (browserWindow) {
|
||||
// Move the progress indicator to the other browser window.
|
||||
this._attachGtkTaskbarProgress(browserWindow);
|
||||
} else {
|
||||
// The last browser window has been closed. We remove the reference to
|
||||
// the taskbar progress object so that the indicator will be registered
|
||||
// again on the next browser window that is opened.
|
||||
this._taskbarProgress = null;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// DownloadSummary view
|
||||
|
||||
onSummaryChanged() {
|
||||
|
111
widget/gtk/TaskbarProgress.cpp
Normal file
111
widget/gtk/TaskbarProgress.cpp
Normal file
@ -0,0 +1,111 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/Logging.h"
|
||||
|
||||
#include "TaskbarProgress.h"
|
||||
#include "nsWindow.h"
|
||||
#include "WidgetUtils.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
|
||||
using mozilla::LogLevel;
|
||||
static mozilla::LazyLogModule gGtkTaskbarProgressLog("nsIGtkTaskbarProgress");
|
||||
|
||||
/******************************************************************************
|
||||
* TaskbarProgress
|
||||
******************************************************************************/
|
||||
|
||||
NS_IMPL_ISUPPORTS(TaskbarProgress, nsIGtkTaskbarProgress, nsITaskbarProgress)
|
||||
|
||||
TaskbarProgress::TaskbarProgress()
|
||||
: mPrimaryWindow(nullptr)
|
||||
{
|
||||
MOZ_LOG(gGtkTaskbarProgressLog, LogLevel::Info,
|
||||
("%p TaskbarProgress()", this));
|
||||
}
|
||||
|
||||
TaskbarProgress::~TaskbarProgress()
|
||||
{
|
||||
MOZ_LOG(gGtkTaskbarProgressLog, LogLevel::Info,
|
||||
("%p ~TaskbarProgress()", this));
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TaskbarProgress::SetProgressState(nsTaskbarProgressState aState,
|
||||
uint64_t aCurrentValue,
|
||||
uint64_t aMaxValue)
|
||||
{
|
||||
#ifdef MOZ_X11
|
||||
NS_ENSURE_ARG_RANGE(aState, 0, STATE_PAUSED);
|
||||
|
||||
if (aState == STATE_NO_PROGRESS || aState == STATE_INDETERMINATE) {
|
||||
NS_ENSURE_TRUE(aCurrentValue == 0, NS_ERROR_INVALID_ARG);
|
||||
NS_ENSURE_TRUE(aMaxValue == 0, NS_ERROR_INVALID_ARG);
|
||||
}
|
||||
|
||||
NS_ENSURE_TRUE((aCurrentValue <= aMaxValue), NS_ERROR_ILLEGAL_VALUE);
|
||||
|
||||
// See TaskbarProgress::SetPrimaryWindow: if we're running in headless
|
||||
// mode, mPrimaryWindow will be null.
|
||||
if (!mPrimaryWindow) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
gulong progress;
|
||||
|
||||
if (aMaxValue == 0) {
|
||||
progress = 0;
|
||||
} else {
|
||||
// Rounding down to ensure we don't set to 'full' until the operation
|
||||
// is completely finished.
|
||||
progress = (gulong) (((double)aCurrentValue / aMaxValue) * 100.0);
|
||||
}
|
||||
|
||||
// Check if the resultant value is the same as the previous call, and
|
||||
// ignore this update if it is.
|
||||
|
||||
if (progress == mCurrentProgress) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mCurrentProgress = progress;
|
||||
|
||||
MOZ_LOG(gGtkTaskbarProgressLog, LogLevel::Debug,
|
||||
("GtkTaskbarProgress::SetProgressState progress: %lu", progress));
|
||||
|
||||
mPrimaryWindow->SetProgress(progress);
|
||||
#endif
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TaskbarProgress::SetPrimaryWindow(mozIDOMWindowProxy* aWindow)
|
||||
{
|
||||
NS_ENSURE_TRUE(aWindow != nullptr, NS_ERROR_ILLEGAL_VALUE);
|
||||
|
||||
auto* parent = nsPIDOMWindowOuter::From(aWindow);
|
||||
RefPtr<nsIWidget> widget = mozilla::widget::WidgetUtils::DOMWindowToWidget(parent);
|
||||
|
||||
// Only nsWindows have a native window, HeadlessWidgets do not. Stop here if the
|
||||
// window does not have one.
|
||||
if (!widget->GetNativeData(NS_NATIVE_WINDOW)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mPrimaryWindow = static_cast<nsWindow*>(widget.get());
|
||||
|
||||
// Clear our current progress. We get a forced update from the DownloadsTaskbar
|
||||
// after returning from this function - zeroing out our progress will make sure the
|
||||
// new window gets the property set on it immediately, rather than waiting for the
|
||||
// progress value to change (which could be a while depending on size.)
|
||||
mCurrentProgress = 0;
|
||||
|
||||
MOZ_LOG(gGtkTaskbarProgressLog, LogLevel::Debug,
|
||||
("GtkTaskbarProgress::SetPrimaryWindow window: %p", mPrimaryWindow.get()));
|
||||
|
||||
return NS_OK;
|
||||
}
|
34
widget/gtk/TaskbarProgress.h
Normal file
34
widget/gtk/TaskbarProgress.h
Normal file
@ -0,0 +1,34 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:expandtab:shiftwidth=4:tabstop=4:
|
||||
*/
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef TaskbarProgress_h_
|
||||
#define TaskbarProgress_h_
|
||||
|
||||
#include "nsIGtkTaskbarProgress.h"
|
||||
|
||||
class nsWindow;
|
||||
|
||||
class TaskbarProgress final : public nsIGtkTaskbarProgress
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIGTKTASKBARPROGRESS
|
||||
NS_DECL_NSITASKBARPROGRESS
|
||||
|
||||
TaskbarProgress();
|
||||
|
||||
protected:
|
||||
~TaskbarProgress();
|
||||
|
||||
// We track the progress value so we can avoid updating the X window property
|
||||
// unnecessarily.
|
||||
unsigned long mCurrentProgress;
|
||||
|
||||
RefPtr<nsWindow> mPrimaryWindow;
|
||||
};
|
||||
|
||||
#endif // #ifndef TaskbarProgress_h_
|
@ -48,6 +48,7 @@ UNIFIED_SOURCES += [
|
||||
'nsToolkit.cpp',
|
||||
'nsWidgetFactory.cpp',
|
||||
'ScreenHelperGTK.cpp',
|
||||
'TaskbarProgress.cpp',
|
||||
'WakeLockListener.cpp',
|
||||
'WidgetTraceEvent.cpp',
|
||||
'WidgetUtilsGtk.cpp',
|
||||
|
@ -26,6 +26,7 @@
|
||||
#ifdef MOZ_WIDGET_GTK
|
||||
#include "nsApplicationChooser.h"
|
||||
#endif
|
||||
#include "TaskbarProgress.h"
|
||||
#include "nsColorPicker.h"
|
||||
#include "nsFilePicker.h"
|
||||
#include "nsSound.h"
|
||||
@ -71,6 +72,7 @@ NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsDragService, nsDragService::GetInstan
|
||||
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsISound, nsSound::GetInstance)
|
||||
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(ScreenManager, ScreenManager::GetAddRefedSingleton)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsImageToPixbuf)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(TaskbarProgress)
|
||||
|
||||
|
||||
// from nsWindow.cpp
|
||||
@ -203,6 +205,7 @@ NS_DEFINE_NAMED_CID(NS_FILEPICKER_CID);
|
||||
#ifdef MOZ_WIDGET_GTK
|
||||
NS_DEFINE_NAMED_CID(NS_APPLICATIONCHOOSER_CID);
|
||||
#endif
|
||||
NS_DEFINE_NAMED_CID(NS_GTK_TASKBARPROGRESS_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_SOUND_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_TRANSFERABLE_CID);
|
||||
#ifdef MOZ_X11
|
||||
@ -237,6 +240,7 @@ static const mozilla::Module::CIDEntry kWidgetCIDs[] = {
|
||||
#ifdef MOZ_WIDGET_GTK
|
||||
{ &kNS_APPLICATIONCHOOSER_CID, false, nullptr, nsApplicationChooserConstructor, Module::MAIN_PROCESS_ONLY },
|
||||
#endif
|
||||
{ &kNS_GTK_TASKBARPROGRESS_CID, false, nullptr, TaskbarProgressConstructor},
|
||||
{ &kNS_SOUND_CID, false, nullptr, nsISoundConstructor, Module::MAIN_PROCESS_ONLY },
|
||||
{ &kNS_TRANSFERABLE_CID, false, nullptr, nsTransferableConstructor },
|
||||
#ifdef MOZ_X11
|
||||
@ -273,6 +277,7 @@ static const mozilla::Module::ContractIDEntry kWidgetContracts[] = {
|
||||
#ifdef MOZ_WIDGET_GTK
|
||||
{ "@mozilla.org/applicationchooser;1", &kNS_APPLICATIONCHOOSER_CID, Module::MAIN_PROCESS_ONLY },
|
||||
#endif
|
||||
{ "@mozilla.org/widget/taskbarprogress/gtk;1", &kNS_GTK_TASKBARPROGRESS_CID },
|
||||
{ "@mozilla.org/sound;1", &kNS_SOUND_CID, Module::MAIN_PROCESS_ONLY },
|
||||
{ "@mozilla.org/widget/transferable;1", &kNS_TRANSFERABLE_CID },
|
||||
#ifdef MOZ_X11
|
||||
|
@ -7039,3 +7039,65 @@ nsWindow::GetWaylandSurface()
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_X11
|
||||
/* XApp progress support currently works by setting a property
|
||||
* on a window with this Atom name. A supporting window manager
|
||||
* will notice this and pass it along to whatever handling has
|
||||
* been implemented on that end (e.g. passing it on to a taskbar
|
||||
* widget.) There is no issue if WM support is lacking, this is
|
||||
* simply ignored in that case.
|
||||
*
|
||||
* See https://github.com/linuxmint/xapps/blob/master/libxapp/xapp-gtk-window.c
|
||||
* for further details.
|
||||
*/
|
||||
|
||||
#define PROGRESS_HINT "_NET_WM_XAPP_PROGRESS"
|
||||
|
||||
static void
|
||||
set_window_hint_cardinal (Window xid,
|
||||
const gchar *atom_name,
|
||||
gulong cardinal)
|
||||
{
|
||||
GdkDisplay *display;
|
||||
|
||||
display = gdk_display_get_default ();
|
||||
|
||||
if (cardinal > 0)
|
||||
{
|
||||
XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
|
||||
xid,
|
||||
gdk_x11_get_xatom_by_name_for_display (display, atom_name),
|
||||
XA_CARDINAL, 32,
|
||||
PropModeReplace,
|
||||
(guchar *) &cardinal, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
XDeleteProperty (GDK_DISPLAY_XDISPLAY (display),
|
||||
xid,
|
||||
gdk_x11_get_xatom_by_name_for_display (display, atom_name));
|
||||
}
|
||||
}
|
||||
#endif // MOZ_X11
|
||||
|
||||
void
|
||||
nsWindow::SetProgress(unsigned long progressPercent)
|
||||
{
|
||||
#ifdef MOZ_X11
|
||||
|
||||
if (!mIsX11Display) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mShell) {
|
||||
return;
|
||||
}
|
||||
|
||||
progressPercent = CLAMP(progressPercent, 0, 100);
|
||||
|
||||
set_window_hint_cardinal(GDK_WINDOW_XID(gtk_widget_get_window(mShell)),
|
||||
PROGRESS_HINT,
|
||||
progressPercent);
|
||||
#endif // MOZ_X11
|
||||
}
|
@ -228,6 +228,8 @@ public:
|
||||
virtual void EndRemoteDrawingInRegion(mozilla::gfx::DrawTarget* aDrawTarget,
|
||||
LayoutDeviceIntRegion& aInvalidRegion) override;
|
||||
|
||||
void SetProgress(unsigned long progressPercent);
|
||||
|
||||
private:
|
||||
void UpdateAlpha(mozilla::gfx::SourceSurface* aSourceSurface, nsIntRect aBoundsRect);
|
||||
|
||||
|
@ -84,6 +84,11 @@ if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
|
||||
if CONFIG['MOZ_X11']:
|
||||
DIRS += ['gtkxtbin']
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'nsIGtkTaskbarProgress.idl',
|
||||
'nsITaskbarProgress.idl',
|
||||
]
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'nsIAppShell.idl',
|
||||
'nsIBaseWindow.idl',
|
||||
|
22
widget/nsIGtkTaskbarProgress.idl
Normal file
22
widget/nsIGtkTaskbarProgress.idl
Normal file
@ -0,0 +1,22 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsITaskbarProgress.idl"
|
||||
|
||||
interface mozIDOMWindowProxy;
|
||||
|
||||
/**
|
||||
* Allow the TaskbarProgress instance to set a new target window.
|
||||
*/
|
||||
|
||||
[scriptable, uuid(39f6fc5a-2386-4bc6-941c-d7479253bc3f)]
|
||||
interface nsIGtkTaskbarProgress : nsITaskbarProgress
|
||||
{
|
||||
/**
|
||||
* Sets the window that is considered primary for purposes of
|
||||
* setting the XApp progress property.
|
||||
*/
|
||||
|
||||
void setPrimaryWindow(in mozIDOMWindowProxy aWindow);
|
||||
};
|
@ -155,6 +155,10 @@
|
||||
#define NS_MACWEBAPPUTILS_CID \
|
||||
{ 0xe9096367, 0xddd9, 0x45e4, { 0xb7, 0x62, 0x49, 0xc0, 0xc1, 0x8b, 0x71, 0x19 } }
|
||||
|
||||
// {a9339876-0027-430f-b953-84c9c11c2da3}
|
||||
#define NS_GTK_TASKBARPROGRESS_CID \
|
||||
{ 0xa9339876, 0x0027, 0x430f, { 0xb9, 0x53, 0x84, 0xc9, 0xc1, 0x1c, 0x2d, 0xa3 } }
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//Printing
|
||||
//-----------------------------------------------------------
|
||||
|
Loading…
Reference in New Issue
Block a user