Compare commits

..

9 Commits

Author SHA1 Message Date
Alexander Mikhaylenko
c3aa25e4e3 workspaceAnimation: Support multiple screens
Currently, there's one animation for the whole canvas. While it looks fine
with just one screen, it causes windows to move between screens when
switching workspaces. Instead, have a separate animation on each screen,
and sync their progress so that at any given time the progress "fraction"
is the same between all screens. Clip all animations to their screens so
that the windows don't leak to other screens.

If a window is placed between every screen, can end up in multiple
animations, in that case each part is still animated separately.

Fixes https://gitlab.gnome.org/GNOME/gnome-shell/issues/1213

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1326
2020-07-21 12:51:46 +00:00
Alexander Mikhaylenko
cfd792fe86 workspaceAnimation: Group sticky windows and moving window
Since the transitions consists of window clones now, all the clones appear
above sticky windows. Clone sticky windows as well, and treat them same as
moving window instead.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1326
2020-07-21 12:51:46 +00:00
Alexander Mikhaylenko
7bdcc503cb workspaceAnimation: Use a workspace strip
Currently, the workspace swipe transition only has one workspace in each
direction. This works until you try to do multiple swipes in quick
succession. The second swipe would continue the existing transition, which
only has 2 or 3 workspaces in it, and will hit a wall.

To prevent this, take all workspaces and arrange them into a column or row,
depending on the layout, and use that as a transition.

For the transition that happens when focusing a window on another workspace
(for example, via Alt+Tab), still use only two workspaces instead of all of
them.

Since we don't support layouts other than single rows/columns anymore,
diagonal transitions aren't supported anymore, and will be shown as
horizontal or vertical instead.

Since nw alt-tab and gesture transitions are different, don't allow to do
both at once, that is, disable swipe tracker when a programmatic transition
is going. This will also conveniently cancel a gesture transition if a
programmatic one is initiated while a gesture is in progress.

Fixes https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2612

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1326
2020-07-21 12:51:46 +00:00
Alexander Mikhaylenko
856a4d27e0 workspaceAnimation: Use window clones
Instead of reparenting windows, clone them. This will allow to properly
support multi-monitor setups in subsequent commits.

Block window mapping animation while the animation is running to prevent
new windows appearing during the animation from being visible at the same
time as their clones.

Fixes https://gitlab.gnome.org/GNOME/mutter/issues/929

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1326
2020-07-21 12:51:46 +00:00
Alexander Mikhaylenko
9d1fe27221 workspaceAnimation: Add a background
In future we will need to use window clones to better support multiple
monitors. To avoid having to hide every window, show wallpapers behind
the workspace transition: one per monitor.

Put the wallpaper into a separate class right away, later it will be
useful to make the animation per-monitor.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1326
2020-07-21 12:51:46 +00:00
Alexander Mikhaylenko
407abeb178 workspaceAnimation: Add to uiGroup insead of window_group
This will allow to hide window group completely in the following commits.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1326
2020-07-21 12:51:46 +00:00
Alexander Mikhaylenko
95fc8021d2 workspaceAnimation: Extract WorkspaceGroup
Simplify the code a bit. The workspace group is relatively self-contained,
so split it from the general animation. Reimplement _syncStacking().

This will help a lot later, with workspace strip and multi-monitor support.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1326
2020-07-21 12:51:46 +00:00
Alexander Mikhaylenko
060cbb65f6 workspaceAnimation: Stop depending on shellwm
https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1326
2020-07-21 12:51:46 +00:00
Alexander Mikhaylenko
1759e27ef7 workspaceAnimation: Split from WindowManager
It's already too complex, and will get more complex in future, split it
out. Update the code style.

https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1326
2020-07-21 12:51:45 +00:00
46 changed files with 3361 additions and 1923 deletions

View File

@@ -1,191 +0,0 @@
<!DOCTYPE node PUBLIC
'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
<node>
<!--
org.gnome.Mutter.ScreenCast:
@short_description: Screen cast interface
This API is private and not intended to be used outside of the integrated
system that uses libmutter. No compatibility between versions are
promised.
-->
<interface name="org.gnome.Mutter.ScreenCast">
<!--
CreateSession:
@properties: Properties
@session_path: Path to the new session object
* "remote-desktop-session-id" (s): The ID of a remote desktop session.
Remote desktop driven screen casts
are started and stopped by the remote
desktop session.
* "disable-animations" (b): Set to "true" if the screen cast application
would prefer animations to be globally
disabled, while the session is running. Default
is "false". Available since version 3.
-->
<method name="CreateSession">
<arg name="properties" type="a{sv}" direction="in" />
<arg name="session_path" type="o" direction="out" />
</method>
<!--
Version:
@short_description: API version
-->
<property name="Version" type="i" access="read" />
</interface>
<!--
org.gnome.Mutter.ScreenCast.Session:
@short_description: Screen cast session
-->
<interface name="org.gnome.Mutter.ScreenCast.Session">
<!--
Start:
Start the screen cast session
-->
<method name="Start" />
<!--
Stop:
Stop the screen cast session
-->
<method name="Stop" />
<!--
Closed:
The session has closed.
-->
<signal name="Closed" />
<!--
RecordMonitor:
@connector: Connector of the monitor to record
@properties: Properties
@stream_path: Path to the new stream object
Record a single monitor.
Available @properties include:
* "cursor-mode" (u): Cursor mode. Default: 'hidden' (see below)
Available since API version 2.
* "is-recording" (b): Whether this is a screen recording. May be
be used for choosing panel icon.
Default: false. Available since API version 4.
Available cursor mode values:
0: hidden - cursor is not included in the stream
1: embedded - cursor is included in the framebuffer
2: metadata - cursor is included as metadata in the PipeWire stream
-->
<method name="RecordMonitor">
<arg name="connector" type="s" direction="in" />
<arg name="properties" type="a{sv}" direction="in" />
<arg name="stream_path" type="o" direction="out" />
</method>
<!--
RecordWindow:
@properties: Properties used determining what window to select
@stream_path: Path to the new stream object
Supported since API version 2.
Record a single window. The cursor will not be included.
Available @properties include:
* "window-id" (t): Id of the window to record.
* "cursor-mode" (u): Cursor mode. Default: 'hidden' (see RecordMonitor).
* "is-recording" (b): Whether this is a screen recording. May be
be used for choosing panel icon.
Default: false. Available since API version 4.
-->
<method name="RecordWindow">
<arg name="properties" type="a{sv}" direction="in" />
<arg name="stream_path" type="o" direction="out" />
</method>
<!--
RecordArea:
@x: X position of the recorded area
@y: Y position of the recorded area
@width: width of the recorded area
@height: height of the recorded area
@properties: Properties
@stream_path: Path to the new stream object
Record an area of the stage. The coordinates are in stage coordinates.
The size of the stream does not necessarily match the size of the
recorded area, and will depend on DPI scale of the affected monitors.
Available @properties include:
* "cursor-mode" (u): Cursor mode. Default: 'hidden' (see below)
Available since API version 2.
* "is-recording" (b): Whether this is a screen recording. May be
be used for choosing panel icon.
Default: false. Available since API version 4.
Available cursor mode values:
0: hidden - cursor is not included in the stream
1: embedded - cursor is included in the framebuffer
2: metadata - cursor is included as metadata in the PipeWire stream
-->
<method name="RecordArea">
<arg name="x" type="i" direction="in" />
<arg name="y" type="i" direction="in" />
<arg name="width" type="i" direction="in" />
<arg name="height" type="i" direction="in" />
<arg name="properties" type="a{sv}" direction="in" />
<arg name="stream_path" type="o" direction="out" />
</method>
</interface>
<!--
org.gnome.Mutter.ScreenCast.Stream:
@short_description: Screen cast stream
-->
<interface name="org.gnome.Mutter.ScreenCast.Stream">
<!--
PipeWireStreamAdded:
@short_description: Pipewire stream added
A signal emitted when PipeWire stream for the screen cast stream has
been created. The @node_id corresponds to the PipeWire stream node.
-->
<signal name="PipeWireStreamAdded">
<annotation name="org.gtk.GDBus.C.Name" value="pipewire-stream-added"/>
<arg name="node_id" type="u" direction="out" />
</signal>
<!--
Parameters:
@short_description: Optional stream parameters
Available parameters include:
* "position" (ii): Position of the source of the stream in the
compositor coordinate space.
* "size" (ii): Size of the source of the stream in the compositor
coordinate space.
-->
<property name="Parameters" type="a{sv}" access="read" />
</interface>
</node>

View File

@@ -70,14 +70,6 @@
-->
<property name="AnimationsEnabled" type="b" access="read"/>
<!--
ScreenSize:
@short_description: The size of the screen
Since: 3
-->
<property name="ScreenSize" type="(ii)" access="read"/>
<property name="version" type="u" access="read"/>
</interface>
</node>

View File

@@ -28,7 +28,6 @@
<file preprocess="xml-stripblanks">org.freedesktop.UPower.xml</file>
<file preprocess="xml-stripblanks">org.gnome.Magnifier.xml</file>
<file preprocess="xml-stripblanks">org.gnome.Magnifier.ZoomRegion.xml</file>
<file preprocess="xml-stripblanks">org.gnome.Mutter.ScreenCast.xml</file>
<file preprocess="xml-stripblanks">org.gnome.ScreenSaver.xml</file>
<file preprocess="xml-stripblanks">org.gnome.SessionManager.EndSessionDialog.xml</file>
<file preprocess="xml-stripblanks">org.gnome.SessionManager.Inhibitor.xml</file>

View File

@@ -1,7 +1,7 @@
[Unit]
Description=GNOME Shell on Wayland
# On wayland, force a session shutdown
OnFailure=org.gnome.Shell-disable-extensions.service gnome-session-shutdown.target
OnFailure=gnome-shell-disable-extensions.service gnome-session-shutdown.target
OnFailureJobMode=replace-irreversibly
CollectMode=inactive-or-failed
RefuseManualStart=on
@@ -13,21 +13,18 @@ Requisite=gnome-session-initialized.target
PartOf=gnome-session-initialized.target
Before=gnome-session-initialized.target
# The units already conflict because they use the same BusName
#Conflicts=gnome-shell-x11.service
[Service]
Slice=session.slice
Type=notify
# NOTE: This can be replaced with ConditionEnvironment=XDG_SESSION_TYPE=%I
# with systemd >= 245. Also, the current solution is kind of painful
# as systemd had a bug where it retries the condition.
# Only start if the template instance matches the session type.
ExecCondition=/bin/sh -c 'test "$XDG_SESSION_TYPE" = "%I" || exit 2'
ExecStart=@bindir@/gnome-shell
# Exit code 1 means we are probably *not* dealing with an extension failure
SuccessExitStatus=1
# unset some environment variables that were set by the shell and won't work now that the shell is gone
ExecStopPost=-systemctl --user unset-environment GNOME_SETUP_DISPLAY WAYLAND_DISPLAY DISPLAY XAUTHORITY
# Exit code 1 means we are probably *not* dealing with an extension failure
SuccessExitStatus=1
# On wayland we cannot restart
Restart=no
# Kill any stubborn child processes after this long

View File

@@ -6,5 +6,5 @@ Requisite=gnome-session-initialized.target
PartOf=gnome-session-initialized.target
Before=gnome-session-initialized.target
Wants=org.gnome.Shell@wayland.service
Wants=org.gnome.Shell@x11.service
Requires=gnome-shell-wayland.service
After=gnome-shell-wayland.service

View File

@@ -1,7 +1,7 @@
[Unit]
Description=GNOME Shell on X11
# On X11, try to show the GNOME Session Failed screen
OnFailure=org.gnome.Shell-disable-extensions.service gnome-session-failed.target
OnFailure=gnome-shell-disable-extensions.service gnome-session-failed.target
OnFailureJobMode=replace
CollectMode=inactive-or-failed
RefuseManualStart=on
@@ -13,24 +13,18 @@ Requisite=gnome-session-initialized.target
PartOf=gnome-session-initialized.target
Before=gnome-session-initialized.target
# The units already conflict because they use the same BusName
#Conflicts=gnome-shell-wayland.service
# Limit startup frequency more than the default
StartLimitIntervalSec=15s
StartLimitBurst=3
[Service]
Slice=session.slice
Type=notify
# NOTE: This can be replaced with ConditionEnvironment=XDG_SESSION_TYPE=%I
# with systemd >= 245. Also, the current solution is kind of painful
# as systemd had a bug where it retries the condition.
# Only start if the template instance matches the session type.
ExecCondition=/bin/sh -c 'test "$XDG_SESSION_TYPE" = "%I" || exit 2'
ExecStart=@bindir@/gnome-shell
# Exit code 1 means we are probably *not* dealing with an extension failure
SuccessExitStatus=1
# On X11 we do not need to unset any variables
# On X11 we want to restart on-success (Alt+F2 + r) and on-failure.
Restart=always
# Do not wait before restarting the shell

View File

@@ -0,0 +1,10 @@
[Unit]
Description=GNOME Shell on X11
DefaultDependencies=no
Requisite=gnome-session-initialized.target
PartOf=gnome-session-initialized.target
Before=gnome-session-initialized.target
Requires=gnome-shell-x11.service
After=gnome-shell-x11.service

View File

@@ -101,21 +101,22 @@ if have_systemd
unitconf.set('bindir', bindir)
configure_file(
input: 'org.gnome.Shell@x11.service.in',
output: 'org.gnome.Shell@x11.service',
input: 'gnome-shell-x11.service.in',
output: 'gnome-shell-x11.service',
configuration: unitconf,
install_dir: systemduserunitdir
)
configure_file(
input: 'org.gnome.Shell@wayland.service.in',
output: 'org.gnome.Shell@wayland.service',
input: 'gnome-shell-wayland.service.in',
output: 'gnome-shell-wayland.service',
configuration: unitconf,
install_dir: systemduserunitdir
)
units = files('org.gnome.Shell.target',
'org.gnome.Shell-disable-extensions.service')
units = files('gnome-shell-x11.target',
'gnome-shell-wayland.target',
'gnome-shell-disable-extensions.service')
install_data(units, install_dir: systemduserunitdir)
endif

View File

@@ -3,8 +3,13 @@ private_headers = [
'gactionobservable.h',
'gactionobserver.h',
'shell-network-agent.h',
'shell-recorder-src.h'
]
if not enable_recorder
private_headers += 'shell-recorder.h'
endif
exclude_directories = [
'calendar-server',
'hotplug-sniffer',

View File

@@ -25,8 +25,6 @@ var ServiceImplementation = class {
// subclasses may override this to disable automatic shutdown
this._autoShutdown = true;
this._queueShutdownCheck();
}
// subclasses may override this to own additional names

View File

@@ -8,12 +8,6 @@ dbus_services = {
'org.gnome.Shell.Notifications': 'notifications',
}
if enable_recorder
dbus_services += {
'org.gnome.Shell.Screencast': 'screencast',
}
endif
config_dir = '@0@/..'.format(meson.current_build_dir())
foreach service, dir : dbus_services

View File

@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/org/gnome/Shell/Screencast/js">
<file>main.js</file>
<file>screencastService.js</file>
<file>dbusService.js</file>
<file>misc/config.js</file>
<file>misc/fileUtils.js</file>
</gresource>
</gresources>

View File

@@ -1,11 +0,0 @@
/* exported main */
const { DBusService } = imports.dbusService;
const { ScreencastService } = imports.screencastService;
function main() {
const service = new DBusService(
'org.gnome.Shell.Screencast',
new ScreencastService());
service.run();
}

View File

@@ -1,458 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported ScreencastService */
const { Gio, GLib, Gst } = imports.gi;
const { loadInterfaceXML, loadSubInterfaceXML } = imports.misc.fileUtils;
const { ServiceImplementation } = imports.dbusService;
const ScreencastIface = loadInterfaceXML('org.gnome.Shell.Screencast');
const IntrospectIface = loadInterfaceXML('org.gnome.Shell.Introspect');
const IntrospectProxy = Gio.DBusProxy.makeProxyWrapper(IntrospectIface);
const ScreenCastIface = loadSubInterfaceXML(
'org.gnome.Mutter.ScreenCast', 'org.gnome.Mutter.ScreenCast');
const ScreenCastSessionIface = loadSubInterfaceXML(
'org.gnome.Mutter.ScreenCast.Session', 'org.gnome.Mutter.ScreenCast');
const ScreenCastStreamIface = loadSubInterfaceXML(
'org.gnome.Mutter.ScreenCast.Stream', 'org.gnome.Mutter.ScreenCast');
const ScreenCastProxy = Gio.DBusProxy.makeProxyWrapper(ScreenCastIface);
const ScreenCastSessionProxy = Gio.DBusProxy.makeProxyWrapper(ScreenCastSessionIface);
const ScreenCastStreamProxy = Gio.DBusProxy.makeProxyWrapper(ScreenCastStreamIface);
const DEFAULT_PIPELINE = 'vp8enc min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux';
const DEFAULT_FRAMERATE = 30;
const DEFAULT_DRAW_CURSOR = true;
const PipelineState = {
INIT: 0,
PLAYING: 1,
FLUSHING: 2,
STOPPED: 3,
};
const SessionState = {
INIT: 0,
ACTIVE: 1,
STOPPED: 2,
};
var Recorder = class {
constructor(sessionPath, x, y, width, height, filePath, options,
invocation,
onErrorCallback) {
this._startInvocation = invocation;
this._dbusConnection = invocation.get_connection();
this._onErrorCallback = onErrorCallback;
this._stopInvocation = null;
this._pipelineIsPlaying = false;
this._sessionIsActive = false;
this._x = x;
this._y = y;
this._width = width;
this._height = height;
this._filePath = filePath;
this._pipelineString = DEFAULT_PIPELINE;
this._framerate = DEFAULT_FRAMERATE;
this._drawCursor = DEFAULT_DRAW_CURSOR;
this._applyOptions(options);
this._watchSender(invocation.get_sender());
this._initSession(sessionPath);
}
_applyOptions(options) {
for (const option in options)
options[option] = options[option].deep_unpack();
if (options['pipeline'] !== undefined)
this._pipelineString = options['pipeline'];
if (options['framerate'] !== undefined)
this._framerate = options['framerate'];
if ('draw-cursor' in options)
this._drawCursor = options['draw-cursor'];
}
_watchSender(sender) {
this._nameWatchId = this._dbusConnection.watch_name(
sender,
Gio.BusNameWatcherFlags.NONE,
null,
this._senderVanished.bind(this));
}
_unwatchSender() {
if (this._nameWatchId !== 0) {
this._dbusConnection.unwatch_name(this._nameWatchId);
this._nameWatchId = 0;
}
}
_senderVanished() {
this._unwatchSender();
this.stopRecording(null);
}
_notifyStopped() {
this._unwatchSender();
if (this._onStartedCallback)
this._onStartedCallback(this, false);
else if (this._onStoppedCallback)
this._onStoppedCallback(this);
else
this._onErrorCallback(this);
}
_onSessionClosed() {
switch (this._pipelineState) {
case PipelineState.STOPPED:
break;
default:
this._pipeline.set_state(Gst.State.NULL);
log(`Unexpected pipeline state: ${this._pipelineState}`);
break;
}
this._notifyStopped();
}
_initSession(sessionPath) {
this._sessionProxy = new ScreenCastSessionProxy(Gio.DBus.session,
'org.gnome.Mutter.ScreenCast',
sessionPath);
this._sessionProxy.connectSignal('Closed', this._onSessionClosed.bind(this));
}
_startPipeline(nodeId) {
this._ensurePipeline(nodeId);
const bus = this._pipeline.get_bus();
bus.add_watch(bus, this._onBusMessage.bind(this));
this._pipeline.set_state(Gst.State.PLAYING);
this._pipelineState = PipelineState.PLAYING;
this._onStartedCallback(this, true);
this._onStartedCallback = null;
}
startRecording(onStartedCallback) {
this._onStartedCallback = onStartedCallback;
const [streamPath] = this._sessionProxy.RecordAreaSync(
this._x, this._y,
this._width, this._height,
{
'is-recording': GLib.Variant.new('b', true),
'cursor-mode': GLib.Variant.new('u', this._drawCursor ? 1 : 0),
});
this._streamProxy = new ScreenCastStreamProxy(Gio.DBus.session,
'org.gnome.ScreenCast.Stream',
streamPath);
this._streamProxy.connectSignal('PipeWireStreamAdded',
(proxy, sender, params) => {
const [nodeId] = params;
this._startPipeline(nodeId);
});
this._sessionProxy.StartSync();
this._sessionState = SessionState.ACTIVE;
}
stopRecording(onStoppedCallback) {
this._pipelineState = PipelineState.FLUSHING;
this._onStoppedCallback = onStoppedCallback;
this._pipeline.send_event(Gst.Event.new_eos());
}
_stopSession() {
this._sessionProxy.StopSync();
this._sessionState = SessionState.STOPPED;
}
_onBusMessage(bus, message, _) {
switch (message.type) {
case Gst.MessageType.EOS:
this._pipeline.set_state(Gst.State.NULL);
switch (this._pipelineState) {
case PipelineState.FLUSHING:
this._pipelineState = PipelineState.STOPPED;
break;
default:
break;
}
switch (this._sessionState) {
case SessionState.ACTIVE:
this._stopSession();
break;
case SessionState.STOPPED:
this._notifyStopped();
break;
default:
break;
}
break;
default:
break;
}
return true;
}
_substituteThreadCount(pipelineDescr) {
const numProcessors = GLib.get_num_processors();
const numThreads = Math.min(Math.max(1, numProcessors), 64);
return pipelineDescr.replace(/%T/, numThreads);
}
_ensurePipeline(nodeId) {
const framerate = this._framerate;
let fullPipeline = `
pipewiresrc path=${nodeId}
do-timestamp=true
keepalive-time=1000
resend-last=true !
video/x-raw,max-framerate=${framerate}/1 !
videoconvert !
${this._pipelineString} !
filesink location=${this._filePath}`;
fullPipeline = this._substituteThreadCount(fullPipeline);
this._pipeline = Gst.parse_launch_full(fullPipeline,
null,
Gst.ParseFlags.FATAL_ERRORS);
}
};
var ScreencastService = class extends ServiceImplementation {
constructor() {
super(ScreencastIface, '/org/gnome/Shell/Screencast');
Gst.init(null);
this._recorders = new Map();
this._senders = new Map();
this._lockdownSettings = new Gio.Settings({
schema_id: 'org.gnome.desktop.lockdown',
});
this._proxy = new ScreenCastProxy(Gio.DBus.session,
'org.gnome.Mutter.ScreenCast',
'/org/gnome/Mutter/ScreenCast');
this._introspectProxy = new IntrospectProxy(Gio.DBus.session,
'org.gnome.Shell.Introspect',
'/org/gnome/Shell/Introspect');
}
_removeRecorder(sender) {
this._recorders.delete(sender);
if (this._recorders.size === 0)
this.release();
}
_addRecorder(sender, recorder) {
this._recorders.set(sender, recorder);
if (this._recorders.size === 1)
this.hold();
}
_getAbsolutePath(filename) {
if (GLib.path_is_absolute(filename))
return filename;
let videoDir = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_VIDEOS);
if (!GLib.file_test(videoDir, GLib.FileTest.EXISTS))
videoDir = GLib.get_home_dir();
return GLib.build_filenamev([videoDir, filename]);
}
_generateFilePath(template) {
let filename = '';
let escape = false;
[...template].forEach(c => {
if (escape) {
switch (c) {
case '%':
filename += '%';
break;
case 'd': {
const datetime = GLib.DateTime.new_now_local();
const datestr = datetime.format('%0x');
const datestrEscaped = datestr.replace(/\//g, '-');
filename += datestrEscaped;
break;
}
case 't': {
const datetime = GLib.DateTime.new_now_local();
const datestr = datetime.format('%0X');
const datestrEscaped = datestr.replace(/\//g, ':');
filename += datestrEscaped;
break;
}
default:
log(`Warning: Unknown escape ${c}`);
}
escape = false;
} else if (c === '%') {
escape = true;
} else {
filename += c;
}
});
if (escape)
filename += '%';
return this._getAbsolutePath(filename);
}
ScreencastAsync(params, invocation) {
let returnValue = [false, ''];
if (this._lockdownSettings.get_boolean('disable-save-to-disk')) {
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
return;
}
const sender = invocation.get_sender();
if (this._recorders.get(sender)) {
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
return;
}
const [sessionPath] = this._proxy.CreateSessionSync({});
const [fileTemplate, options] = params;
const [screenWidth, screenHeight] = this._introspectProxy.ScreenSize;
const filePath = this._generateFilePath(fileTemplate);
let recorder;
try {
recorder = new Recorder(
sessionPath,
0, 0,
screenWidth, screenHeight,
fileTemplate,
options,
invocation,
_recorder => this._removeRecorder(sender));
} catch (error) {
log(`Failed to create recorder: ${error.message}`);
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
return;
}
this._addRecorder(sender, recorder);
try {
recorder.startRecording(
(_, result) => {
if (result) {
returnValue = [true, filePath];
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
} else {
this._removeRecorder(sender);
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
}
});
} catch (error) {
log(`Failed to start recorder: ${error.message}`);
this._removeRecorder(sender);
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
}
}
ScreencastAreaAsync(params, invocation) {
let returnValue = [false, ''];
if (this._lockdownSettings.get_boolean('disable-save-to-disk')) {
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
return;
}
const sender = invocation.get_sender();
if (this._recorders.get(sender)) {
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
return;
}
const [sessionPath] = this._proxy.CreateSessionSync({});
const [x, y, width, height, fileTemplate, options] = params;
const filePath = this._generateFilePath(fileTemplate);
let recorder;
try {
recorder = new Recorder(
sessionPath,
x, y,
width, height,
filePath,
options,
invocation,
_recorder => this._removeRecorder(sender));
} catch (error) {
log(`Failed to create recorder: ${error.message}`);
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
return;
}
this._addRecorder(sender, recorder);
try {
recorder.startRecording(
(_, result) => {
if (result) {
returnValue = [true, filePath];
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
} else {
this._removeRecorder(sender);
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
}
});
} catch (error) {
log(`Failed to start recorder: ${error.message}`);
this._removeRecorder(sender);
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
}
}
StopScreencastAsync(params, invocation) {
const sender = invocation.get_sender();
const recorder = this._recorders.get(sender);
if (!recorder) {
invocation.return_value(GLib.Variant.new('(b)', [false]));
return;
}
recorder.stopRecording(() => {
this._removeRecorder(sender);
invocation.return_value(GLib.Variant.new('(b)', [true]));
});
}
};

View File

@@ -953,15 +953,16 @@ var LoginDialog = GObject.registerClass({
if (this.opacity == 255 && this._authPrompt.verificationStatus == AuthPrompt.AuthPromptStatus.NOT_VERIFYING)
return;
if (this._authPrompt.verificationStatus !== AuthPrompt.AuthPromptStatus.NOT_VERIFYING)
this._authPrompt.reset();
this._bindOpacity();
this.ease({
opacity: 255,
duration: _FADE_ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => this._unbindOpacity(),
onComplete: () => {
if (this._authPrompt.verificationStatus != AuthPrompt.AuthPromptStatus.NOT_VERIFYING)
this._authPrompt.reset();
this._unbindOpacity();
},
});
}

View File

@@ -92,6 +92,7 @@
<file>ui/ripples.js</file>
<file>ui/runDialog.js</file>
<file>ui/screenShield.js</file>
<file>ui/screencast.js</file>
<file>ui/screenshot.js</file>
<file>ui/scripting.js</file>
<file>ui/search.js</file>
@@ -111,6 +112,7 @@
<file>ui/windowManager.js</file>
<file>ui/windowPreview.js</file>
<file>ui/workspace.js</file>
<file>ui/workspaceAnimation.js</file>
<file>ui/workspaceSwitcherPopup.js</file>
<file>ui/workspaceThumbnail.js</file>
<file>ui/workspacesView.js</file>
@@ -136,6 +138,7 @@
<file>ui/status/volume.js</file>
<file>ui/status/bluetooth.js</file>
<file>ui/status/remoteAccess.js</file>
<file>ui/status/screencast.js</file>
<file>ui/status/system.js</file>
<file>ui/status/thunderbolt.js</file>
</gresource>

View File

@@ -1,6 +1,6 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported collectFromDatadirs, recursivelyDeleteDir,
recursivelyMoveDir, loadInterfaceXML, loadSubInterfaceXML */
recursivelyMoveDir, loadInterfaceXML */
const { Gio, GLib } = imports.gi;
const Config = imports.misc.config;
@@ -67,19 +67,14 @@ function recursivelyMoveDir(srcDir, destDir) {
}
let _ifaceResource = null;
function ensureIfaceResource() {
if (_ifaceResource)
return;
// don't use global.datadir so the method is usable from tests/tools
let dir = GLib.getenv('GNOME_SHELL_DATADIR') || Config.PKGDATADIR;
let path = `${dir}/gnome-shell-dbus-interfaces.gresource`;
_ifaceResource = Gio.Resource.load(path);
_ifaceResource._register();
}
function loadInterfaceXML(iface) {
ensureIfaceResource();
if (!_ifaceResource) {
// don't use global.datadir so the method is usable from tests/tools
let dir = GLib.getenv('GNOME_SHELL_DATADIR') || Config.PKGDATADIR;
let path = `${dir}/gnome-shell-dbus-interfaces.gresource`;
_ifaceResource = Gio.Resource.load(path);
_ifaceResource._register();
}
let uri = `resource:///org/gnome/shell/dbus-interfaces/${iface}.xml`;
let f = Gio.File.new_for_uri(uri);
@@ -93,25 +88,3 @@ function loadInterfaceXML(iface) {
return null;
}
function loadSubInterfaceXML(iface, ifaceFile) {
let xml = loadInterfaceXML(ifaceFile);
if (!xml)
return null;
let ifaceStartTag = `<interface name="${iface}">`;
let ifaceStopTag = '</interface>';
let ifaceStartIndex = xml.indexOf(ifaceStartTag);
let ifaceEndIndex = xml.indexOf(ifaceStopTag, ifaceStartIndex + 1) + ifaceStopTag.length;
let xmlHeader = '<!DOCTYPE node PUBLIC\n' +
'\'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\'\n' +
'\'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\'>\n' +
'<node>\n';
let xmlFooter = '</node>';
return (
xmlHeader +
xml.substr(ifaceStartIndex, ifaceEndIndex - ifaceStartIndex) +
xmlFooter);
}

View File

@@ -5,7 +5,7 @@ const INTROSPECT_SCHEMA = 'org.gnome.shell';
const INTROSPECT_KEY = 'introspect';
const APP_WHITELIST = ['org.freedesktop.impl.portal.desktop.gtk'];
const INTROSPECT_DBUS_API_VERSION = 3;
const INTROSPECT_DBUS_API_VERSION = 2;
const { loadInterfaceXML } = imports.misc.fileUtils;
@@ -59,11 +59,6 @@ var IntrospectService = class {
this._settings.connect('notify::enable-animations',
this._syncAnimationsEnabled.bind(this));
this._syncAnimationsEnabled();
const monitorManager = Meta.MonitorManager.get();
monitorManager.connect('monitors-changed',
this._syncScreenSize.bind(this));
this._syncScreenSize();
}
_isStandaloneApp(app) {
@@ -214,28 +209,10 @@ var IntrospectService = class {
}
}
_syncScreenSize() {
const oldScreenWidth = this._screenWidth;
const oldScreenHeight = this._screenHeight;
this._screenWidth = global.screen_width;
this._screenHeight = global.screen_height;
if (oldScreenWidth !== this._screenWidth ||
oldScreenHeight !== this._screenHeight) {
const variant = new GLib.Variant('(ii)',
[this._screenWidth, this._screenHeight]);
this._dbusImpl.emit_property_changed('ScreenSize', variant);
}
}
get AnimationsEnabled() {
return this._animationsEnabled;
}
get ScreenSize() {
return [this._screenWidth, this._screenHeight];
}
get version() {
return INTROSPECT_DBUS_API_VERSION;
}

View File

@@ -1,7 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported findUrls, spawn, spawnCommandLine, spawnApp, trySpawnAppCommandline,
trySpawnCommandLine, formatTime, formatTimeSpan, createTimeLabel,
insertSorted, ensureActorVisibleInScrollView, wiggle */
/* exported findUrls, spawn, spawnCommandLine, spawnApp, trySpawnCommandLine,
formatTime, formatTimeSpan, createTimeLabel, insertSorted,
ensureActorVisibleInScrollView, wiggle */
const { Clutter, Gio, GLib, Shell, St, GnomeDesktop } = imports.gi;
const Gettext = imports.gettext;
@@ -86,49 +86,18 @@ function spawnCommandLine(commandLine) {
}
}
// trySpawnAppCommandline:
// @command: The command to spawn
//
// Runs @command as if it was an application, handling startup notification
// and placing it into a separate systemd scope.
function trySpawnAppCommandline(command) {
const quoted = command.replace(/%/g, '%%');
// This cannot fail currently
const app = Gio.AppInfo.create_from_commandline(quoted, null,
Gio.AppInfoCreateFlags.SUPPORTS_STARTUP_NOTIFICATION);
// Launching applications does not check whether the executable exists,
// it'll just log an error later on. So do an explicit check here.
const exec = app.get_executable();
if (!GLib.find_program_in_path(exec)) {
throw new GLib.SpawnError({
code: GLib.SpawnError.NOENT,
message: _('Command not found'),
});
}
try {
let context = global.create_app_launch_context(0, -1);
app.launch([], context);
} catch (err) {
// Replace "Error invoking global.create_app_launch_context: " with
// something nicer
err.message = err.message.replace(/[^:]*: /, '%s\n'.format(_('Could not parse command:')));
throw err;
}
}
// spawnApp:
// @argv: an argv array
//
// Runs @argv as if it was an application, handling startup notification
// and placing it into a separate systemd scope.
function spawnApp(argv) {
try {
trySpawnAppCommandline(argv.join(' '));
} catch (err) {
let app = Gio.AppInfo.create_from_commandline(argv.join(' '), null,
Gio.AppInfoCreateFlags.SUPPORTS_STARTUP_NOTIFICATION);
let context = global.create_app_launch_context(0, -1);
app.launch([], context);
} catch (err) {
_handleSpawnError(argv[0], err);
}
}

View File

@@ -2335,8 +2335,13 @@ var AppFolderDialog = GObject.registerClass({
}
_withinDialog(x, y) {
const childExtents = this.child.get_transformed_extents();
return childExtents.contains_point(new Graphene.Point({ x, y }));
const childAllocation =
Shell.util_get_transformed_allocation(this.child);
return x > childAllocation.x1 &&
x < childAllocation.x2 &&
y > childAllocation.y1 &&
y < childAllocation.y2;
}
_setupDragMonitor() {

View File

@@ -347,8 +347,6 @@ var Background = GObject.registerClass({
this.set_color(color);
else
this.set_gradient(shadingType, color, secondColor);
this._setLoaded();
}
_watchFile(file) {
@@ -767,27 +765,10 @@ var BackgroundManager = class BackgroundManager {
this._updateBackgroundActor();
});
let loadedSignalId;
if (background.isLoaded) {
GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
this.emit('loaded');
return GLib.SOURCE_REMOVE;
});
} else {
loadedSignalId = background.connect('loaded', () => {
background.disconnect(loadedSignalId);
loadedSignalId = null;
this.emit('loaded');
});
}
backgroundActor.connect('destroy', () => {
if (changeSignalId)
background.disconnect(changeSignalId);
if (loadedSignalId)
background.disconnect(loadedSignalId);
if (backgroundActor.loadedSignalId)
background.disconnect(backgroundActor.loadedSignalId);
});

View File

@@ -1,7 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported BoxPointer */
const { Clutter, GObject, St } = imports.gi;
const { Clutter, GObject, Shell, St } = imports.gi;
const Main = imports.ui.main;
@@ -453,16 +453,15 @@ var BoxPointer = GObject.registerClass({
let alignment = this._arrowAlignment;
let monitorIndex = Main.layoutManager.findIndexForActor(sourceActor);
this._sourceExtents = sourceActor.get_transformed_extents();
this._sourceAllocation = Shell.util_get_transformed_allocation(sourceActor);
this._workArea = Main.layoutManager.getWorkAreaForMonitor(monitorIndex);
// Position correctly relative to the sourceActor
let sourceNode = sourceActor.get_theme_node();
let sourceContentBox = sourceNode.get_content_box(sourceActor.get_allocation_box());
let sourceTopLeft = this._sourceExtents.get_top_left();
let sourceBottomRight = this._sourceExtents.get_bottom_right();
let sourceCenterX = sourceTopLeft.x + sourceContentBox.x1 + (sourceContentBox.x2 - sourceContentBox.x1) * this._sourceAlignment;
let sourceCenterY = sourceTopLeft.y + sourceContentBox.y1 + (sourceContentBox.y2 - sourceContentBox.y1) * this._sourceAlignment;
let sourceAllocation = this._sourceAllocation;
let sourceCenterX = sourceAllocation.x1 + sourceContentBox.x1 + (sourceContentBox.x2 - sourceContentBox.x1) * this._sourceAlignment;
let sourceCenterY = sourceAllocation.y1 + sourceContentBox.y1 + (sourceContentBox.y2 - sourceContentBox.y1) * this._sourceAlignment;
let [, , natWidth, natHeight] = this.get_preferred_size();
// We also want to keep it onscreen, and separated from the
@@ -482,16 +481,16 @@ var BoxPointer = GObject.registerClass({
switch (this._arrowSide) {
case St.Side.TOP:
resY = sourceBottomRight.y + gap;
resY = sourceAllocation.y2 + gap;
break;
case St.Side.BOTTOM:
resY = sourceTopLeft.y - natHeight - gap;
resY = sourceAllocation.y1 - natHeight - gap;
break;
case St.Side.LEFT:
resX = sourceBottomRight.x + gap;
resX = sourceAllocation.x2 + gap;
break;
case St.Side.RIGHT:
resX = sourceTopLeft.x - natWidth - gap;
resX = sourceAllocation.x1 - natWidth - gap;
break;
}
@@ -587,30 +586,29 @@ var BoxPointer = GObject.registerClass({
}
_calculateArrowSide(arrowSide) {
let sourceTopLeft = this._sourceExtents.get_top_left();
let sourceBottomRight = this._sourceExtents.get_bottom_right();
let sourceAllocation = this._sourceAllocation;
let [, , boxWidth, boxHeight] = this.get_preferred_size();
let workarea = this._workArea;
switch (arrowSide) {
case St.Side.TOP:
if (sourceBottomRight.y + boxHeight > workarea.y + workarea.height &&
boxHeight < sourceTopLeft.y - workarea.y)
if (sourceAllocation.y2 + boxHeight > workarea.y + workarea.height &&
boxHeight < sourceAllocation.y1 - workarea.y)
return St.Side.BOTTOM;
break;
case St.Side.BOTTOM:
if (sourceTopLeft.y - boxHeight < workarea.y &&
boxHeight < workarea.y + workarea.height - sourceBottomRight.y)
if (sourceAllocation.y1 - boxHeight < workarea.y &&
boxHeight < workarea.y + workarea.height - sourceAllocation.y2)
return St.Side.TOP;
break;
case St.Side.LEFT:
if (sourceBottomRight.x + boxWidth > workarea.x + workarea.width &&
boxWidth < sourceTopLeft.x - workarea.x)
if (sourceAllocation.x2 + boxWidth > workarea.x + workarea.width &&
boxWidth < sourceAllocation.x1 - workarea.x)
return St.Side.RIGHT;
break;
case St.Side.RIGHT:
if (sourceTopLeft.x - boxWidth < workarea.x &&
boxWidth < workarea.x + workarea.width - sourceBottomRight.x)
if (sourceAllocation.x1 - boxWidth < workarea.x &&
boxWidth < workarea.x + workarea.width - sourceAllocation.x2)
return St.Side.LEFT;
break;
}

View File

@@ -388,16 +388,17 @@ var _Draggable = class _Draggable {
const [, newAllocatedWidth] = this._dragActor.get_preferred_width(-1);
const [, newAllocatedHeight] = this._dragActor.get_preferred_height(-1);
const transformedExtents = this._dragActor.get_transformed_extents();
const transformedAllocation =
Shell.util_get_transformed_allocation(this._dragActor);
// Set the actor's scale such that it will keep the same
// transformed size when it's reparented to the uiGroup
this._dragActor.set_scale(
transformedExtents.get_width() / newAllocatedWidth,
transformedExtents.get_height() / newAllocatedHeight);
transformedAllocation.get_width() / newAllocatedWidth,
transformedAllocation.get_height() / newAllocatedHeight);
this._dragOffsetX = transformedExtents.origin.x - this._dragStartX;
this._dragOffsetY = transformedExtents.origin.y - this._dragStartY;
this._dragOffsetX = transformedAllocation.x1 - this._dragStartX;
this._dragOffsetY = transformedAllocation.y1 - this._dragStartY;
this._dragOrigParent.remove_actor(this._dragActor);
Main.uiGroup.add_child(this._dragActor);

View File

@@ -457,15 +457,6 @@ var LayoutManager = GObject.registerClass({
}
}
_waitLoaded(bgManager) {
return new Promise(resolve => {
const id = bgManager.connect('loaded', () => {
bgManager.disconnect(id);
resolve();
});
});
}
_updateBackgrounds() {
for (let i = 0; i < this._bgManagers.length; i++)
this._bgManagers[i].destroy();
@@ -473,7 +464,7 @@ var LayoutManager = GObject.registerClass({
this._bgManagers = [];
if (Main.sessionMode.isGreeter)
return Promise.resolve();
return;
for (let i = 0; i < this.monitors.length; i++) {
let bgManager = this._createBackgroundManager(i);
@@ -482,8 +473,6 @@ var LayoutManager = GObject.registerClass({
if (i != this.primaryIndex && this._startingUp)
bgManager.backgroundActor.hide();
}
return Promise.all(this._bgManagers.map(this._waitLoaded));
}
_updateKeyboardBox() {
@@ -642,7 +631,7 @@ var LayoutManager = GObject.registerClass({
// When starting a normal user session, we want to grow it out of the middle
// of the screen.
async _prepareStartupAnimation() {
_prepareStartupAnimation() {
// During the initial transition, add a simple actor to block all events,
// so they don't get delivered to X11 windows that have been transformed.
this._coverPane = new Clutter.Actor({ opacity: 0,
@@ -659,6 +648,8 @@ var LayoutManager = GObject.registerClass({
} else if (Main.sessionMode.isGreeter) {
this.panelBox.translation_y = -this.panelBox.height;
} else {
this._updateBackgrounds();
// We need to force an update of the regions now before we scale
// the UI group to get the correct allocation for the struts.
this._updateRegions();
@@ -674,8 +665,6 @@ var LayoutManager = GObject.registerClass({
this.uiGroup.scale_x = this.uiGroup.scale_y = 0.75;
this.uiGroup.opacity = 0;
global.window_group.set_clip(monitor.x, monitor.y, monitor.width, monitor.height);
await this._updateBackgrounds();
}
this.emit('startup-prepared');
@@ -1228,9 +1217,8 @@ class HotCorner extends Clutter.Actor {
return;
if (Main.overview.shouldToggleByCornerOrButton()) {
this._ripples.playAnimation(this._x, this._y);
Main.overview.toggle();
if (Main.overview.animationInProgress)
this._ripples.playAnimation(this._x, this._y);
}
}

View File

@@ -3,10 +3,10 @@
ctrlAltTabManager, padOsdService, osdWindowManager,
osdMonitorLabeler, shellMountOpDBusService, shellDBusService,
shellAccessDialogDBusService, shellAudioSelectionDBusService,
screenSaverDBus, uiGroup, magnifier, xdndHandler, keyboard,
kbdA11yDialog, introspectService, start, pushModal, popModal,
activateWindow, createLookingGlass, initializeDeferredWork,
getThemeStylesheet, setThemeStylesheet */
screenSaverDBus, screencastService, uiGroup, magnifier,
xdndHandler, keyboard, kbdA11yDialog, introspectService,
start, pushModal, popModal, activateWindow, createLookingGlass,
initializeDeferredWork, getThemeStylesheet, setThemeStylesheet */
const { Clutter, Gio, GLib, GObject, Meta, Shell, St } = imports.gi;
@@ -34,6 +34,7 @@ const LoginManager = imports.misc.loginManager;
const LookingGlass = imports.ui.lookingGlass;
const NotificationDaemon = imports.ui.notificationDaemon;
const WindowAttentionHandler = imports.ui.windowAttentionHandler;
const Screencast = imports.ui.screencast;
const ScreenShield = imports.ui.screenShield;
const Scripting = imports.ui.scripting;
const SessionMode = imports.ui.sessionMode;
@@ -73,6 +74,7 @@ var shellAudioSelectionDBusService = null;
var shellDBusService = null;
var shellMountOpDBusService = null;
var screenSaverDBus = null;
var screencastService = null;
var modalCount = 0;
var actionMode = Shell.ActionMode.NONE;
var modalActorFocusStack = [];
@@ -198,6 +200,7 @@ function _initializeUI() {
uiGroup = layoutManager.uiGroup;
padOsdService = new PadOsd.PadOsdService();
screencastService = new Screencast.ScreencastService();
xdndHandler = new XdndHandler.XdndHandler();
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
osdWindowManager = new OsdWindow.OsdWindowManager();

View File

@@ -736,11 +736,13 @@ class AggregateMenu extends PanelMenu.Button {
this._volume = new imports.ui.status.volume.Indicator();
this._brightness = new imports.ui.status.brightness.Indicator();
this._system = new imports.ui.status.system.Indicator();
this._screencast = new imports.ui.status.screencast.Indicator();
this._location = new imports.ui.status.location.Indicator();
this._nightLight = new imports.ui.status.nightLight.Indicator();
this._thunderbolt = new imports.ui.status.thunderbolt.Indicator();
this._indicators.add_child(this._thunderbolt);
this._indicators.add_child(this._screencast);
this._indicators.add_child(this._location);
this._indicators.add_child(this._nightLight);
if (this._network)

View File

@@ -183,9 +183,10 @@ var Button = GObject.registerClass({
}
_onDestroy() {
super._onDestroy();
if (this.menu)
this.menu.destroy();
super._onDestroy();
}
});

View File

@@ -881,10 +881,9 @@ var PopupMenu = class extends PopupMenuBase {
let state = event.get_state();
// if user has a modifier down (except capslock and numlock)
// if user has a modifier down (except capslock)
// then don't handle the key press here
state &= ~Clutter.ModifierType.LOCK_MASK;
state &= ~Clutter.ModifierType.MOD2_MASK;
state &= Clutter.ModifierType.MODIFIER_MASK;
if (state)
@@ -1325,7 +1324,7 @@ var PopupMenuManager = class {
removeMenu(menu) {
if (menu == this.activeMenu)
this._grabHelper.ungrab({ actor: menu.actor });
this._closeMenu(false, menu);
let position = this._findMenu(menu);
if (position == -1) // not a menu we manage

View File

@@ -196,7 +196,7 @@ class RunDialog extends ModalDialog.ModalDialog {
let execArg = this._terminalSettings.get_string(EXEC_ARG_KEY);
command = '%s %s %s'.format(exec, execArg, input);
}
Util.trySpawnAppCommandline(command);
Util.trySpawnCommandLine(command);
} catch (e) {
// Mmmh, that failed - see if @input matches an existing file
let path = null;

146
js/ui/screencast.js Normal file
View File

@@ -0,0 +1,146 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const { Gio, GLib, Shell } = imports.gi;
const Signals = imports.signals;
const Main = imports.ui.main;
const { loadInterfaceXML } = imports.misc.fileUtils;
const ScreencastIface = loadInterfaceXML('org.gnome.Shell.Screencast');
var ScreencastService = class {
constructor() {
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreencastIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell/Screencast');
Gio.DBus.session.own_name('org.gnome.Shell.Screencast', Gio.BusNameOwnerFlags.REPLACE, null, null);
this._recorders = new Map();
this._lockdownSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.lockdown' });
Main.sessionMode.connect('updated', this._sessionUpdated.bind(this));
}
get isRecording() {
return this._recorders.size > 0;
}
_ensureRecorderForSender(sender) {
let recorder = this._recorders.get(sender);
if (!recorder) {
recorder = new Shell.Recorder({ stage: global.stage,
display: global.display });
recorder._watchNameId =
Gio.bus_watch_name(Gio.BusType.SESSION, sender, 0, null,
this._onNameVanished.bind(this));
this._recorders.set(sender, recorder);
this.emit('updated');
}
return recorder;
}
_sessionUpdated() {
if (Main.sessionMode.allowScreencast)
return;
for (let sender of this._recorders.keys())
this._stopRecordingForSender(sender);
}
_onNameVanished(connection, name) {
this._stopRecordingForSender(name);
}
_stopRecordingForSender(sender) {
let recorder = this._recorders.get(sender);
if (!recorder)
return false;
Gio.bus_unwatch_name(recorder._watchNameId);
recorder.close();
this._recorders.delete(sender);
this.emit('updated');
return true;
}
_applyOptionalParameters(recorder, options) {
for (let option in options)
options[option] = options[option].deep_unpack();
if (options['pipeline'])
recorder.set_pipeline(options['pipeline']);
if (options['framerate'])
recorder.set_framerate(options['framerate']);
if ('draw-cursor' in options)
recorder.set_draw_cursor(options['draw-cursor']);
}
ScreencastAsync(params, invocation) {
let returnValue = [false, ''];
if (!Main.sessionMode.allowScreencast ||
this._lockdownSettings.get_boolean('disable-save-to-disk')) {
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
return;
}
let sender = invocation.get_sender();
let recorder = this._ensureRecorderForSender(sender);
if (!recorder.is_recording()) {
let [fileTemplate, options] = params;
recorder.set_file_template(fileTemplate);
this._applyOptionalParameters(recorder, options);
let [success, fileName] = recorder.record();
returnValue = [success, fileName ? fileName : ''];
if (!success)
this._stopRecordingForSender(sender);
}
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
}
ScreencastAreaAsync(params, invocation) {
let returnValue = [false, ''];
if (!Main.sessionMode.allowScreencast ||
this._lockdownSettings.get_boolean('disable-save-to-disk')) {
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
return;
}
let sender = invocation.get_sender();
let recorder = this._ensureRecorderForSender(sender);
if (!recorder.is_recording()) {
let [x, y, width, height, fileTemplate, options] = params;
if (x < 0 || y < 0 ||
width <= 0 || height <= 0 ||
x + width > global.screen_width ||
y + height > global.screen_height) {
invocation.return_error_literal(Gio.IOErrorEnum,
Gio.IOErrorEnum.CANCELLED,
"Invalid params");
return;
}
recorder.set_file_template(fileTemplate);
recorder.set_area(x, y, width, height);
this._applyOptionalParameters(recorder, options);
let [success, fileName] = recorder.record();
returnValue = [success, fileName ? fileName : ''];
if (!success)
this._stopRecordingForSender(sender);
}
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
}
StopScreencastAsync(params, invocation) {
let success = this._stopRecordingForSender(invocation.get_sender());
invocation.return_value(GLib.Variant.new('(b)', [success]));
}
};
Signals.addSignalMethods(ScreencastService.prototype);

View File

@@ -24,8 +24,7 @@ class RemoteAccessApplet extends PanelMenu.SystemIndicator {
return;
this._handles = new Set();
this._sharedIndicator = null;
this._recordingIndicator = null;
this._indicator = null;
this._menuSection = null;
controller.connect('new-handle', (o, handle) => {
@@ -34,49 +33,32 @@ class RemoteAccessApplet extends PanelMenu.SystemIndicator {
}
_ensureControls() {
if (this._sharedIndicator && this._recordingIndicator)
if (this._indicator)
return;
this._sharedIndicator = this._addIndicator();
this._sharedIndicator.icon_name = 'screen-shared-symbolic';
this._sharedIndicator.add_style_class_name('remote-access-indicator');
this._sharedItem =
this._indicator = this._addIndicator();
this._indicator.icon_name = 'screen-shared-symbolic';
this._indicator.add_style_class_name('remote-access-indicator');
this._item =
new PopupMenu.PopupSubMenuMenuItem(_("Screen is Being Shared"),
true);
this._sharedItem.menu.addAction(_("Turn off"),
() => {
for (let handle of this._handles) {
if (!handle.is_recording)
handle.stop();
}
});
this._sharedItem.icon.icon_name = 'screen-shared-symbolic';
this.menu.addMenuItem(this._sharedItem);
this._recordingIndicator = this._addIndicator();
this._recordingIndicator.icon_name = 'media-record-symbolic';
this._recordingIndicator.add_style_class_name('screencast-indicator');
}
_isScreenShared() {
return [...this._handles].some(handle => !handle.is_recording);
}
_isRecording() {
return [...this._handles].some(handle => handle.is_recording);
this._item.menu.addAction(_("Turn off"),
() => {
for (let handle of this._handles)
handle.stop();
});
this._item.icon.icon_name = 'screen-shared-symbolic';
this.menu.addMenuItem(this._item);
}
_sync() {
if (this._isScreenShared()) {
this._sharedIndicator.visible = true;
this._sharedItem.visible = true;
if (this._handles.size == 0) {
this._indicator.visible = false;
this._item.visible = false;
} else {
this._sharedIndicator.visible = false;
this._sharedItem.visible = false;
this._indicator.visible = true;
this._item.visible = true;
}
this._recordingIndicator.visible = this._isRecording();
}
_onStopped(handle) {
@@ -88,7 +70,9 @@ class RemoteAccessApplet extends PanelMenu.SystemIndicator {
this._handles.add(handle);
handle.connect('stopped', this._onStopped.bind(this));
this._ensureControls();
this._sync();
if (this._handles.size == 1) {
this._ensureControls();
this._sync();
}
}
});

View File

@@ -0,0 +1,25 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported Indicator */
const GObject = imports.gi.GObject;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
var Indicator = GObject.registerClass(
class Indicator extends PanelMenu.SystemIndicator {
_init() {
super._init();
this._indicator = this._addIndicator();
this._indicator.icon_name = 'media-record-symbolic';
this._indicator.add_style_class_name('screencast-indicator');
this._sync();
Main.screencastService.connect('updated', this._sync.bind(this));
}
_sync() {
this._indicator.visible = Main.screencastService.isRecording;
}
});

View File

@@ -14,9 +14,9 @@ const WindowMenu = imports.ui.windowMenu;
const PadOsd = imports.ui.padOsd;
const EdgeDragAction = imports.ui.edgeDragAction;
const CloseDialog = imports.ui.closeDialog;
const SwipeTracker = imports.ui.swipeTracker;
const SwitchMonitor = imports.ui.switchMonitor;
const IBusManager = imports.misc.ibusManager;
const WorkspaceAnimation = imports.ui.workspaceAnimation;
const { loadInterfaceXML } = imports.misc.fileUtils;
@@ -42,11 +42,6 @@ const GsdWacomProxy = Gio.DBusProxy.makeProxyWrapper(GsdWacomIface);
const WINDOW_DIMMER_EFFECT_NAME = "gnome-shell-window-dimmer";
Gio._promisify(Shell,
'util_start_systemd_unit', 'util_start_systemd_unit_finish');
Gio._promisify(Shell,
'util_stop_systemd_unit', 'util_stop_systemd_unit_finish');
var DisplayChangeDialog = GObject.registerClass(
class DisplayChangeDialog extends ModalDialog.ModalDialog {
_init(wm) {
@@ -566,7 +561,6 @@ var WindowManager = class {
this._resizing = new Set();
this._resizePending = new Set();
this._destroying = new Set();
this._movingWindow = null;
this._dimmedWindows = [];
@@ -576,15 +570,6 @@ var WindowManager = class {
this._isWorkspacePrepended = false;
this._switchData = null;
this._shellwm.connect('kill-switch-workspace', shellwm => {
if (this._switchData) {
if (this._switchData.inProgress)
this._switchWorkspaceDone(shellwm);
else if (!this._switchData.gestureActivated)
this._finishWorkspaceSwitch(this._switchData);
}
});
this._shellwm.connect('kill-window-effects', (shellwm, actor) => {
this._minimizeWindowDone(shellwm, actor);
this._mapWindowDone(shellwm, actor);
@@ -606,7 +591,6 @@ var WindowManager = class {
this._shellwm.connect('confirm-display-change', this._confirmDisplayChange.bind(this));
this._shellwm.connect('create-close-dialog', this._createCloseDialog.bind(this));
this._shellwm.connect('create-inhibit-shortcuts-dialog', this._createInhibitShortcutsDialog.bind(this));
global.display.connect('restacked', this._syncStacking.bind(this));
this._workspaceSwitcherPopup = null;
this._tilePreview = null;
@@ -906,40 +890,56 @@ var WindowManager = class {
global.display.connect('init-xserver', (display, task) => {
IBusManager.getIBusManager().restartDaemon(['--xim']);
/* Timeout waiting for start job completion after 5 seconds */
let cancellable = new Gio.Cancellable();
GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 5, () => {
cancellable.cancel();
return GLib.SOURCE_REMOVE;
});
try {
if (!Shell.util_start_systemd_unit('gsd-xsettings.target', 'fail'))
log('Not starting gsd-xsettings; waiting for gnome-session to do so');
this._startX11Services(task, cancellable);
/* Leave this watchdog timeout so don't block indefinitely here */
let timeoutId = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 5, () => {
Gio.DBus.session.unwatch_name(watchId);
log('Warning: Failed to start gsd-xsettings');
task.return_boolean(true);
timeoutId = 0;
return GLib.SOURCE_REMOVE;
});
/* When gsd-xsettings daemon is started, we are good to resume */
let watchId = Gio.DBus.session.watch_name(
'org.gnome.SettingsDaemon.XSettings',
Gio.BusNameWatcherFlags.NONE,
() => {
Gio.DBus.session.unwatch_name(watchId);
if (timeoutId > 0) {
task.return_boolean(true);
GLib.source_remove(timeoutId);
}
},
null);
} catch (e) {
log('Error starting gsd-xsettings: %s'.format(e.message));
task.return_boolean(true);
}
return true;
});
global.display.connect('x11-display-closing', () => {
if (!Meta.is_wayland_compositor())
return;
this._stopX11Services(null);
try {
Shell.util_stop_systemd_unit('gsd-xsettings.target', 'fail');
} catch (e) {
log('Error stopping gsd-xsettings: %s'.format(e.message));
}
IBusManager.getIBusManager().restartDaemon();
});
Main.overview.connect('showing', () => {
for (let i = 0; i < this._dimmedWindows.length; i++)
this._undimWindow(this._dimmedWindows[i]);
if (this._switchData) {
if (this._switchData.gestureActivated)
this._switchWorkspaceStop();
this._swipeTracker.enabled = false;
}
});
Main.overview.connect('hiding', () => {
for (let i = 0; i < this._dimmedWindows.length; i++)
this._dimWindow(this._dimmedWindows[i]);
this._swipeTracker.enabled = true;
});
this._windowMenuManager = new WindowMenu.WindowMenuManager();
@@ -950,13 +950,6 @@ var WindowManager = class {
global.workspace_manager.override_workspace_layout(Meta.DisplayCorner.TOPLEFT,
false, -1, 1);
let swipeTracker = new SwipeTracker.SwipeTracker(global.stage,
Shell.ActionMode.NORMAL, { allowDrag: false, allowScroll: false });
swipeTracker.connect('begin', this._switchWorkspaceBegin.bind(this));
swipeTracker.connect('update', this._switchWorkspaceUpdate.bind(this));
swipeTracker.connect('end', this._switchWorkspaceEnd.bind(this));
this._swipeTracker = swipeTracker;
let appSwitchAction = new AppSwitchAction();
appSwitchAction.connect('activated', this._switchApp.bind(this));
global.stage.add_action(appSwitchAction);
@@ -988,36 +981,14 @@ var WindowManager = class {
global.display.connect('in-fullscreen-changed', updateUnfullscreenGesture);
global.stage.add_action(topDragAction);
}
async _startX11Services(task, cancellable) {
try {
await Shell.util_start_systemd_unit(
'gnome-session-x11-services-ready.target', 'fail', cancellable);
} catch (e) {
// Ignore NOT_SUPPORTED error, which indicates we are not systemd
// managed and gnome-session will have taken care of everything
// already.
// Note that we do log cancellation from here.
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_SUPPORTED))
log('Error starting X11 services: %s'.format(e.message));
} finally {
task.return_boolean(true);
}
}
this._workspaceAnimation =
new WorkspaceAnimation.WorkspaceAnimationController();
async _stopX11Services(cancellable) {
try {
await Shell.util_stop_systemd_unit(
'gnome-session-x11-services.target', 'fail', cancellable);
} catch (e) {
// Ignore NOT_SUPPORTED error, which indicates we are not systemd
// managed and gnome-session will have taken care of everything
// already.
// Note that we do log cancellation from here.
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_SUPPORTED))
log('Error stopping X11 services: %s'.format(e.message));
}
this._shellwm.connect('kill-switch-workspace', () => {
this._workspaceAnimation.cancelSwitchAnimation();
this._switchWorkspaceDone();
});
}
_showPadOsd(display, device, settings, imagePath, editionMode, monitorIndex) {
@@ -1140,8 +1111,7 @@ var WindowManager = class {
}
_shouldAnimate() {
return !(Main.overview.visible ||
(this._switchData && this._switchData.gestureActivated));
return !(Main.overview.visible || this._workspaceAnimation.gestureActive);
}
_shouldAnimateActor(actor, types) {
@@ -1637,376 +1607,26 @@ var WindowManager = class {
return !(this._allowedKeybindings[binding.get_name()] & Main.actionMode);
}
_syncStacking() {
if (this._switchData == null)
return;
let windows = global.get_window_actors();
let lastCurSibling = null;
let lastDirSibling = [];
for (let i = 0; i < windows.length; i++) {
if (windows[i].get_parent() == this._switchData.curGroup) {
this._switchData.curGroup.set_child_above_sibling(windows[i], lastCurSibling);
lastCurSibling = windows[i];
} else {
for (let dir of Object.values(Meta.MotionDirection)) {
let info = this._switchData.surroundings[dir];
if (!info || windows[i].get_parent() != info.actor)
continue;
let sibling = lastDirSibling[dir];
if (sibling == undefined)
sibling = null;
info.actor.set_child_above_sibling(windows[i], sibling);
lastDirSibling[dir] = windows[i];
break;
}
}
}
}
_getPositionForDirection(direction, fromWs, toWs) {
let xDest = 0, yDest = 0;
let oldWsIsFullscreen = fromWs.list_windows().some(w => w.is_fullscreen());
let newWsIsFullscreen = toWs.list_windows().some(w => w.is_fullscreen());
// We have to shift windows up or down by the height of the panel to prevent having a
// visible gap between the windows while switching workspaces. Since fullscreen windows
// hide the panel, they don't need to be shifted up or down.
let shiftHeight = Main.panel.height;
if (direction == Meta.MotionDirection.UP ||
direction == Meta.MotionDirection.UP_LEFT ||
direction == Meta.MotionDirection.UP_RIGHT)
yDest = -global.screen_height + (oldWsIsFullscreen ? 0 : shiftHeight);
else if (direction == Meta.MotionDirection.DOWN ||
direction == Meta.MotionDirection.DOWN_LEFT ||
direction == Meta.MotionDirection.DOWN_RIGHT)
yDest = global.screen_height - (newWsIsFullscreen ? 0 : shiftHeight);
if (direction == Meta.MotionDirection.LEFT ||
direction == Meta.MotionDirection.UP_LEFT ||
direction == Meta.MotionDirection.DOWN_LEFT)
xDest = -global.screen_width;
else if (direction == Meta.MotionDirection.RIGHT ||
direction == Meta.MotionDirection.UP_RIGHT ||
direction == Meta.MotionDirection.DOWN_RIGHT)
xDest = global.screen_width;
return [xDest, yDest];
}
_prepareWorkspaceSwitch(from, to, direction) {
if (this._switchData)
return;
let wgroup = global.window_group;
let windows = global.get_window_actors();
let switchData = {};
this._switchData = switchData;
switchData.curGroup = new Clutter.Actor();
switchData.movingWindowBin = new Clutter.Actor();
switchData.windows = [];
switchData.surroundings = {};
switchData.gestureActivated = false;
switchData.inProgress = false;
switchData.container = new Clutter.Actor();
switchData.container.add_actor(switchData.curGroup);
wgroup.add_actor(switchData.movingWindowBin);
wgroup.add_actor(switchData.container);
let workspaceManager = global.workspace_manager;
let curWs = workspaceManager.get_workspace_by_index(from);
for (let dir of Object.values(Meta.MotionDirection)) {
let ws = null;
if (to < 0)
ws = curWs.get_neighbor(dir);
else if (dir == direction)
ws = workspaceManager.get_workspace_by_index(to);
if (ws == null || ws == curWs) {
switchData.surroundings[dir] = null;
continue;
}
let [x, y] = this._getPositionForDirection(dir, curWs, ws);
let info = {
index: ws.index(),
actor: new Clutter.Actor(),
xDest: x,
yDest: y,
};
switchData.surroundings[dir] = info;
switchData.container.add_actor(info.actor);
switchData.container.set_child_above_sibling(info.actor, null);
info.actor.set_position(x, y);
}
wgroup.set_child_above_sibling(switchData.movingWindowBin, null);
for (let i = 0; i < windows.length; i++) {
let actor = windows[i];
let window = actor.get_meta_window();
if (!window.showing_on_its_workspace())
continue;
if (window.is_on_all_workspaces())
continue;
let record = { window: actor,
parent: actor.get_parent() };
if (this._movingWindow && window == this._movingWindow) {
record.parent.remove_child(actor);
switchData.movingWindow = record;
switchData.windows.push(switchData.movingWindow);
switchData.movingWindowBin.add_child(actor);
} else if (window.get_workspace().index() == from) {
record.parent.remove_child(actor);
switchData.windows.push(record);
switchData.curGroup.add_child(actor);
} else {
let visible = false;
for (let dir of Object.values(Meta.MotionDirection)) {
let info = switchData.surroundings[dir];
if (!info || info.index != window.get_workspace().index())
continue;
record.parent.remove_child(actor);
switchData.windows.push(record);
info.actor.add_child(actor);
visible = true;
break;
}
actor.visible = visible;
}
}
for (let i = 0; i < switchData.windows.length; i++) {
let w = switchData.windows[i];
w.windowDestroyId = w.window.connect('destroy', () => {
switchData.windows.splice(switchData.windows.indexOf(w), 1);
});
}
}
_finishWorkspaceSwitch(switchData) {
this._switchData = null;
for (let i = 0; i < switchData.windows.length; i++) {
let w = switchData.windows[i];
w.window.disconnect(w.windowDestroyId);
w.window.get_parent().remove_child(w.window);
w.parent.add_child(w.window);
if (!w.window.get_meta_window().get_workspace().active)
w.window.hide();
}
switchData.container.destroy();
switchData.movingWindowBin.destroy();
this._movingWindow = null;
}
_switchWorkspace(shellwm, from, to, direction) {
if (!Main.sessionMode.hasWorkspaces || !this._shouldAnimate()) {
shellwm.completed_switch_workspace();
return;
}
this._prepareWorkspaceSwitch(from, to, direction);
this._switchData.inProgress = true;
this._switchInProgress = true;
let workspaceManager = global.workspace_manager;
let fromWs = workspaceManager.get_workspace_by_index(from);
let toWs = workspaceManager.get_workspace_by_index(to);
let [xDest, yDest] = this._getPositionForDirection(direction, fromWs, toWs);
/* @direction is the direction that the "camera" moves, so the
* screen contents have to move one screen's worth in the
* opposite direction.
*/
xDest = -xDest;
yDest = -yDest;
this._switchData.container.ease({
x: xDest,
y: yDest,
duration: WINDOW_ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
onComplete: () => this._switchWorkspaceDone(shellwm),
this._workspaceAnimation.animateSwitch(from, to, direction, () => {
this._shellwm.completed_switch_workspace();
this._switchInProgress = false;
});
}
_switchWorkspaceDone(shellwm) {
this._finishWorkspaceSwitch(this._switchData);
shellwm.completed_switch_workspace();
}
_directionForProgress(progress) {
if (global.workspace_manager.layout_rows === -1) {
return progress > 0
? Meta.MotionDirection.DOWN
: Meta.MotionDirection.UP;
} else if (Clutter.get_default_text_direction() === Clutter.TextDirection.RTL) {
return progress > 0
? Meta.MotionDirection.LEFT
: Meta.MotionDirection.RIGHT;
} else {
return progress > 0
? Meta.MotionDirection.RIGHT
: Meta.MotionDirection.LEFT;
}
}
_getProgressRange() {
if (!this._switchData)
return [0, 0];
let lower = 0;
let upper = 0;
let horiz = global.workspace_manager.layout_rows !== -1;
let baseDistance;
if (horiz)
baseDistance = global.screen_width;
else
baseDistance = global.screen_height;
let direction = this._directionForProgress(-1);
let info = this._switchData.surroundings[direction];
if (info !== null) {
let distance = horiz ? info.xDest : info.yDest;
lower = -Math.abs(distance) / baseDistance;
}
direction = this._directionForProgress(1);
info = this._switchData.surroundings[direction];
if (info !== null) {
let distance = horiz ? info.xDest : info.yDest;
upper = Math.abs(distance) / baseDistance;
}
return [lower, upper];
}
_switchWorkspaceBegin(tracker, monitor) {
if (Meta.prefs_get_workspaces_only_on_primary() &&
monitor !== Main.layoutManager.primaryIndex)
_switchWorkspaceDone() {
if (!this._switchInProgress)
return;
let workspaceManager = global.workspace_manager;
let horiz = workspaceManager.layout_rows !== -1;
tracker.orientation = horiz
? Clutter.Orientation.HORIZONTAL
: Clutter.Orientation.VERTICAL;
let activeWorkspace = workspaceManager.get_active_workspace();
let baseDistance;
if (horiz)
baseDistance = global.screen_width;
else
baseDistance = global.screen_height;
let progress;
if (this._switchData && this._switchData.gestureActivated) {
this._switchData.container.remove_all_transitions();
if (!horiz)
progress = -this._switchData.container.y / baseDistance;
else if (Clutter.get_default_text_direction() === Clutter.TextDirection.RTL)
progress = this._switchData.container.x / baseDistance;
else
progress = -this._switchData.container.x / baseDistance;
} else {
this._prepareWorkspaceSwitch(activeWorkspace.index(), -1);
progress = 0;
}
let points = [];
let [lower, upper] = this._getProgressRange();
if (lower !== 0)
points.push(lower);
points.push(0);
if (upper !== 0)
points.push(upper);
tracker.confirmSwipe(baseDistance, points, progress, 0);
}
_switchWorkspaceUpdate(tracker, progress) {
if (!this._switchData)
return;
let direction = this._directionForProgress(progress);
let info = this._switchData.surroundings[direction];
let xPos = 0;
let yPos = 0;
if (info) {
if (global.workspace_manager.layout_rows === -1)
yPos = -Math.round(progress * global.screen_height);
else if (Clutter.get_default_text_direction() === Clutter.TextDirection.RTL)
xPos = Math.round(progress * global.screen_width);
else
xPos = -Math.round(progress * global.screen_width);
}
this._switchData.container.set_position(xPos, yPos);
}
_switchWorkspaceEnd(tracker, duration, endProgress) {
if (!this._switchData)
return;
let workspaceManager = global.workspace_manager;
let activeWorkspace = workspaceManager.get_active_workspace();
let newWs = activeWorkspace;
let xDest = 0;
let yDest = 0;
if (endProgress !== 0) {
let direction = this._directionForProgress(endProgress);
newWs = activeWorkspace.get_neighbor(direction);
xDest = -this._switchData.surroundings[direction].xDest;
yDest = -this._switchData.surroundings[direction].yDest;
}
let switchData = this._switchData;
switchData.gestureActivated = true;
this._switchData.container.ease({
x: xDest,
y: yDest,
duration,
mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
onComplete: () => {
if (!newWs.active)
this.actionMoveWorkspace(newWs);
this._finishWorkspaceSwitch(switchData);
},
});
}
_switchWorkspaceStop() {
this._switchData.container.x = 0;
this._switchData.container.y = 0;
this._finishWorkspaceSwitch(this._switchData);
this._shellwm.completed_switch_workspace();
this._switchInProgress = false;
}
_showTilePreview(shellwm, window, tileRect, monitorIndex) {
@@ -2208,7 +1828,7 @@ var WindowManager = class {
// This won't have any effect for "always sticky" windows
// (like desktop windows or docks)
this._movingWindow = window;
this._workspaceAnimation.movingWindow = window;
window.change_workspace(workspace);
global.display.clear_mouse_mode();

475
js/ui/workspaceAnimation.js Normal file
View File

@@ -0,0 +1,475 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported WorkspaceAnimationController */
const { Clutter, GObject, Meta, Shell } = imports.gi;
const Background = imports.ui.background;
const Layout = imports.ui.layout;
const Main = imports.ui.main;
const SwipeTracker = imports.ui.swipeTracker;
const WINDOW_ANIMATION_TIME = 250;
const WorkspaceGroup = GObject.registerClass(
class WorkspaceGroup extends Clutter.Actor {
_init(workspace, monitor, movingWindow) {
super._init();
this._workspace = workspace;
this._monitor = monitor;
this._movingWindow = movingWindow;
this._windowRecords = [];
this._createWindows();
this.connect('destroy', this._onDestroy.bind(this));
this._restackedId = global.display.connect('restacked',
this._syncStacking.bind(this));
}
get workspace() {
return this._workspace;
}
_shouldShowWindow(window) {
if (!window.showing_on_its_workspace())
return false;
const geometry = global.display.get_monitor_geometry(this._monitor.index);
const [intersects, intersection_] = window.get_frame_rect().intersect(geometry);
if (!intersects)
return false;
const isSticky =
window.is_on_all_workspaces() || window === this._movingWindow;
// No workspace means we should show windows that are on all workspaces
if (!this._workspace)
return isSticky;
// Otherwise only show windows that are (only) on that workspace
return !isSticky && window.located_on_workspace(this._workspace);
}
_syncStacking() {
const windowActors = global.get_window_actors().filter(w =>
this._shouldShowWindow(w.meta_window));
let lastRecord;
for (const windowActor of windowActors) {
const record = this._windowRecords.find(r => r.windowActor === windowActor);
this.set_child_above_sibling(record.clone, lastRecord ? lastRecord.clone : null);
lastRecord = record;
}
}
_createWindows() {
const windowActors = global.get_window_actors().filter(w =>
this._shouldShowWindow(w.meta_window));
for (const windowActor of windowActors) {
const clone = new Clutter.Clone({
source: windowActor,
x: windowActor.x - this._monitor.x,
y: windowActor.y - this._monitor.y,
});
this.add_child(clone);
const record = { windowActor, clone };
record.windowDestroyId = windowActor.connect('destroy', () => {
clone.destroy();
this._windowRecords.splice(this._windowRecords.indexOf(record), 1);
});
this._windowRecords.push(record);
}
}
_removeWindows() {
for (const record of this._windowRecords) {
record.windowActor.disconnect(record.windowDestroyId);
record.clone.destroy();
}
this._windowRecords = [];
}
_onDestroy() {
global.display.disconnect(this._restackedId);
this._removeWindows();
}
});
const MonitorGroup = GObject.registerClass({
Properties: {
'progress': GObject.ParamSpec.double(
'progress', 'progress', 'progress',
GObject.ParamFlags.READWRITE,
-Infinity, Infinity, 0),
},
}, class MonitorGroup extends Clutter.Actor {
_init(monitor, workspaceIndices, movingWindow) {
super._init({
clip_to_allocation: true,
});
this._monitor = monitor;
const constraint = new Layout.MonitorConstraint({ index: monitor.index });
this.add_constraint(constraint);
const background = new Meta.BackgroundGroup();
this.add_child(background);
this._container = new Clutter.Actor();
this.add_child(this._container);
const stickyGroup = new WorkspaceGroup(null, monitor, movingWindow);
this.add_child(stickyGroup);
this._workspaceGroups = [];
const workspaceManager = global.workspace_manager;
const vertical = workspaceManager.layout_rows === -1;
const activeWorkspace = workspaceManager.get_active_workspace();
let x = 0;
let y = 0;
for (const i of workspaceIndices) {
const ws = workspaceManager.get_workspace_by_index(i);
const fullscreen = ws.list_windows().some(w => w.get_monitor() === monitor.index && w.is_fullscreen());
if (i > 0 && vertical && !fullscreen && monitor.index === Main.layoutManager.primaryIndex) {
// We have to shift windows up or down by the height of the panel to prevent having a
// visible gap between the windows while switching workspaces. Since fullscreen windows
// hide the panel, they don't need to be shifted up or down.
y -= Main.panel.height;
}
const group = new WorkspaceGroup(ws, monitor, movingWindow);
this._workspaceGroups.push(group);
this._container.add_child(group);
group.set_position(x, y);
if (vertical)
y += this.baseDistance;
else if (Clutter.get_default_text_direction() === Clutter.TextDirection.RTL)
x -= this.baseDistance;
else
x += this.baseDistance;
}
this.progress = this.getWorkspaceProgress(activeWorkspace);
this._bgManager = new Background.BackgroundManager({
container: background,
monitorIndex: monitor.index,
controlPosition: false,
});
this.connect('destroy', this._onDestroy.bind(this));
}
_onDestroy() {
this._bgManager.destroy();
}
get baseDistance() {
if (global.workspace_manager.layout_rows === -1)
return this._monitor.height;
else
return this._monitor.width;
}
get progress() {
if (global.workspace_manager.layout_rows === -1)
return -this._container.y / this.baseDistance;
else if (this.get_text_direction() === Clutter.TextDirection.RTL)
return this._container.x / this.baseDistance;
else
return -this._container.x / this.baseDistance;
}
set progress(p) {
if (global.workspace_manager.layout_rows === -1)
this._container.y = -Math.round(p * this.baseDistance);
else if (this.get_text_direction() === Clutter.TextDirection.RTL)
this._container.x = Math.round(p * this.baseDistance);
else
this._container.x = -Math.round(p * this.baseDistance);
}
get index() {
return this._monitor.index;
}
getWorkspaceProgress(workspace) {
const group = this._workspaceGroups.find(g =>
g.workspace.index() === workspace.index());
return this._getWorkspaceGroupProgress(group);
}
_getWorkspaceGroupProgress(group) {
if (global.workspace_manager.layout_rows === -1)
return group.y / this.baseDistance;
else if (this.get_text_direction() === Clutter.TextDirection.RTL)
return -group.x / this.baseDistance;
else
return group.x / this.baseDistance;
}
getSnapPoints() {
return this._workspaceGroups.map(g =>
this._getWorkspaceGroupProgress(g));
}
findClosestWorkspace(progress) {
const distances = this.getSnapPoints().map(p =>
Math.abs(p - progress));
const index = distances.indexOf(Math.min(...distances));
return this._workspaceGroups[index].workspace;
}
_interpolateProgress(progress, monitorGroup) {
if (this.index === monitorGroup.index)
return progress;
const points1 = monitorGroup.getSnapPoints();
const points2 = this.getSnapPoints();
const upper = points1.indexOf(points1.find(p => p >= progress));
const lower = points1.indexOf(points1.slice().reverse().find(p => p <= progress));
if (points1[upper] === points1[lower])
return points2[upper];
const t = (progress - points1[lower]) / (points1[upper] - points1[lower]);
return points2[lower] + (points2[upper] - points2[lower]) * t;
}
updateSwipeForMonitor(progress, monitorGroup) {
this.progress = this._interpolateProgress(progress, monitorGroup);
}
});
var WorkspaceAnimationController = class {
constructor() {
this._movingWindow = null;
this._switchData = null;
Main.overview.connect('showing', () => {
if (this._switchData) {
if (this._switchData.gestureActivated)
this._finishWorkspaceSwitch(this._switchData);
this._swipeTracker.enabled = false;
}
});
Main.overview.connect('hiding', () => {
this._swipeTracker.enabled = true;
});
let swipeTracker = new SwipeTracker.SwipeTracker(global.stage,
Shell.ActionMode.NORMAL, { allowDrag: false, allowScroll: false });
swipeTracker.connect('begin', this._switchWorkspaceBegin.bind(this));
swipeTracker.connect('update', this._switchWorkspaceUpdate.bind(this));
swipeTracker.connect('end', this._switchWorkspaceEnd.bind(this));
this._swipeTracker = swipeTracker;
}
_prepareWorkspaceSwitch(workspaceIndices) {
if (this._switchData)
return;
const workspaceManager = global.workspace_manager;
const nWorkspaces = workspaceManager.get_n_workspaces();
const switchData = {};
this._switchData = switchData;
switchData.monitors = [];
switchData.gestureActivated = false;
switchData.inProgress = false;
if (!workspaceIndices)
workspaceIndices = [...Array(nWorkspaces).keys()];
const monitors = Meta.prefs_get_workspaces_only_on_primary()
? [Main.layoutManager.primaryMonitor] : Main.layoutManager.monitors;
for (const monitor of monitors) {
if (Meta.prefs_get_workspaces_only_on_primary() &&
monitor.index !== Main.layoutManager.primaryIndex)
continue;
const group = new MonitorGroup(monitor, workspaceIndices, this.movingWindow);
Main.uiGroup.insert_child_above(group, global.window_group);
switchData.monitors.push(group);
}
}
_finishWorkspaceSwitch(switchData) {
this._switchData = null;
switchData.monitors.forEach(m => m.destroy());
this.movingWindow = null;
}
animateSwitch(from, to, direction, onComplete) {
this._swipeTracker.enabled = false;
let workspaceIndices = [];
switch (direction) {
case Meta.MotionDirection.UP:
case Meta.MotionDirection.LEFT:
case Meta.MotionDirection.UP_LEFT:
case Meta.MotionDirection.UP_RIGHT:
workspaceIndices = [to, from];
break;
case Meta.MotionDirection.DOWN:
case Meta.MotionDirection.RIGHT:
case Meta.MotionDirection.DOWN_LEFT:
case Meta.MotionDirection.DOWN_RIGHT:
workspaceIndices = [from, to];
break;
}
if (Clutter.get_default_text_direction() === Clutter.TextDirection.RTL &&
direction !== Meta.MotionDirection.UP &&
direction !== Meta.MotionDirection.DOWN)
workspaceIndices.reverse();
this._prepareWorkspaceSwitch(workspaceIndices);
this._switchData.inProgress = true;
const fromWs = global.workspace_manager.get_workspace_by_index(from);
const toWs = global.workspace_manager.get_workspace_by_index(to);
for (const monitorGroup of this._switchData.monitors) {
monitorGroup.progress = monitorGroup.getWorkspaceProgress(fromWs);
const progress = monitorGroup.getWorkspaceProgress(toWs);
const params = {
duration: WINDOW_ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
};
if (monitorGroup.index === Main.layoutManager.primaryIndex) {
params.onComplete = () => {
this._finishWorkspaceSwitch(this._switchData);
onComplete();
this._swipeTracker.enabled = true;
};
}
monitorGroup.ease_property('progress', progress, params);
}
}
_findMonitorGroup(monitorIndex) {
return this._switchData.monitors.find(m => m.index === monitorIndex);
}
_switchWorkspaceBegin(tracker, monitor) {
if (Meta.prefs_get_workspaces_only_on_primary() &&
monitor !== Main.layoutManager.primaryIndex)
return;
const workspaceManager = global.workspace_manager;
const horiz = workspaceManager.layout_rows !== -1;
tracker.orientation = horiz
? Clutter.Orientation.HORIZONTAL
: Clutter.Orientation.VERTICAL;
if (this._switchData && this._switchData.gestureActivated) {
for (const group of this._switchData.monitors)
group.remove_all_transitions();
} else {
this._prepareWorkspaceSwitch();
}
const monitorGroup = this._findMonitorGroup(monitor);
const baseDistance = monitorGroup.baseDistance;
const progress = monitorGroup.progress;
const closestWs = monitorGroup.findClosestWorkspace(progress);
const cancelProgress = monitorGroup.getWorkspaceProgress(closestWs);
const points = monitorGroup.getSnapPoints();
this._switchData.baseMonitorGroup = monitorGroup;
tracker.confirmSwipe(baseDistance, points, progress, cancelProgress);
}
_switchWorkspaceUpdate(tracker, progress) {
if (!this._switchData)
return;
for (const monitorGroup of this._switchData.monitors)
monitorGroup.updateSwipeForMonitor(progress, this._switchData.baseMonitorGroup);
}
_switchWorkspaceEnd(tracker, duration, endProgress) {
if (!this._switchData)
return;
const switchData = this._switchData;
switchData.gestureActivated = true;
const newWs = switchData.baseMonitorGroup.findClosestWorkspace(endProgress);
for (const monitorGroup of this._switchData.monitors) {
const progress = monitorGroup.getWorkspaceProgress(newWs);
const params = {
duration,
mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
};
if (monitorGroup.index === Main.layoutManager.primaryIndex) {
params.onComplete = () => {
if (!newWs.active)
newWs.activate(global.get_current_time());
this._finishWorkspaceSwitch(switchData);
};
}
monitorGroup.ease_property('progress', progress, params);
}
}
get gestureActive() {
return this._switchData !== null && this._switchData.gestureActivated;
}
cancelSwitchAnimation() {
if (!this._switchData)
return;
if (this._switchData.gestureActivated)
return;
this._finishWorkspaceSwitch(this._switchData);
}
set movingWindow(movingWindow) {
this._movingWindow = movingWindow;
}
get movingWindow() {
return this._movingWindow;
}
};

View File

@@ -96,10 +96,9 @@ gnome_desktop_dep = dependency('gnome-desktop-3.0', version: gnome_desktop_req)
bt_dep = dependency('gnome-bluetooth-1.0', version: bt_req, required: false)
gst_dep = dependency('gstreamer-1.0', version: gst_req, required: false)
gst_base_dep = dependency('gstreamer-base-1.0', required: false)
pipewire_dep = dependency('libpipewire-0.3', required: false)
recorder_deps = []
enable_recorder = gst_dep.found() and gst_base_dep.found() and pipewire_dep.found()
enable_recorder = gst_dep.found() and gst_base_dep.found()
if enable_recorder
recorder_deps += [gst_dep, gst_base_dep, gtk_dep, x11_dep]
endif

315
po/ca.po
View File

@@ -10,7 +10,7 @@ msgid ""
msgstr ""
"Project-Id-Version: HEAD\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
"POT-Creation-Date: 2020-07-22 01:49+0000\n"
"POT-Creation-Date: 2020-06-05 23:11+0000\n"
"PO-Revision-Date: 2020-05-15 20:39+0200\n"
"Last-Translator: Robert Antoni Buj Gelonch <rbuj@fedoraproject.org>\n"
"Language-Team: Catalan <tradgnome@softcatala.org>\n"
@@ -64,8 +64,8 @@ msgid ""
"Allows access to internal debugging and monitoring tools using the Alt-F2 "
"dialog."
msgstr ""
"Permet l'accés a les eines de depuració i de seguiment internes a través del"
" diàleg de l'Alt+F2."
"Permet l'accés a les eines de depuració i de seguiment internes a través del "
"diàleg de l'Alt+F2."
#: data/org.gnome.shell.gschema.xml.in:16
msgid "UUIDs of extensions to enable"
@@ -74,9 +74,9 @@ msgstr ""
#: data/org.gnome.shell.gschema.xml.in:17
msgid ""
"GNOME Shell extensions have a UUID property; this key lists extensions which"
" should be loaded. Any extension that wants to be loaded needs to be in this"
" list. You can also manipulate this list with the EnableExtension and "
"GNOME Shell extensions have a UUID property; this key lists extensions which "
"should be loaded. Any extension that wants to be loaded needs to be in this "
"list. You can also manipulate this list with the EnableExtension and "
"DisableExtension D-Bus methods on org.gnome.Shell."
msgstr ""
"Les extensions del GNOME Shell tenen un identificador universal. Aquesta "
@@ -92,19 +92,18 @@ msgstr ""
#: data/org.gnome.shell.gschema.xml.in:27
msgid ""
"GNOME Shell extensions have a UUID property; this key lists extensions which"
" should be disabled, even if loaded as part of the current mode. You can "
"also manipulate this list with the EnableExtension and DisableExtension "
"D-Bus methods on org.gnome.Shell. This key takes precedence over the "
"“enabled-extensions” setting."
"GNOME Shell extensions have a UUID property; this key lists extensions which "
"should be disabled, even if loaded as part of the current mode. You can also "
"manipulate this list with the EnableExtension and DisableExtension D-Bus "
"methods on org.gnome.Shell. This key takes precedence over the “enabled-"
"extensions” setting."
msgstr ""
"Les extensions del GNOME Shell tenen un identificador universal. Aquesta "
"clau conté una llista de les extensions que s'han de carregar. Qualsevol "
"extensió que s'hagi de carregar ha de ser a la llista. Podeu modificar "
"aquesta llista amb els mètodes de D-Bus «EnableExtension» (activa una "
"extensió) i «DisableExtension» (desactiva una extensió) a "
"org.gnome.Shell.Aquesta clau té preferència sobre el paràmetre «enabled-"
"extensions»."
"extensió) i «DisableExtension» (desactiva una extensió) a org.gnome.Shell."
"Aquesta clau té preferència sobre el paràmetre «enabled-extensions»."
#: data/org.gnome.shell.gschema.xml.in:37
msgid "Disable user extensions"
@@ -128,21 +127,20 @@ msgid ""
"running version. Enabling this option will disable this check and try to "
"load all extensions regardless of the versions they claim to support."
msgstr ""
"El GNOME Shell només carregarà extensions que afirmin ser compatibles amb la"
" versió en execució. Si s'activa aquesta opció, es desactivarà la "
"comprovació i es provarà de carregar totes les extensions sense tenir en "
"compte les versions amb què afirmin ser compatibles."
"El GNOME Shell només carregarà extensions que afirmin ser compatibles amb la "
"versió en execució. Si s'activa aquesta opció, es desactivarà la comprovació "
"i es provarà de carregar totes les extensions sense tenir en compte les "
"versions amb què afirmin ser compatibles."
#: data/org.gnome.shell.gschema.xml.in:54
msgid "List of desktop file IDs for favorite applications"
msgstr ""
"Llista d'identificadors de fitxers d'escriptori de les aplicacions "
"preferides"
"Llista d'identificadors de fitxers d'escriptori de les aplicacions preferides"
#: data/org.gnome.shell.gschema.xml.in:55
msgid ""
"The applications corresponding to these identifiers will be displayed in the"
" favorites area."
"The applications corresponding to these identifiers will be displayed in the "
"favorites area."
msgstr ""
"Es mostraran, a l'àrea de preferits, les aplicacions que corresponguin a "
"aquests identificadors."
@@ -160,8 +158,7 @@ msgstr ""
msgid "History for command (Alt-F2) dialog"
msgstr "Historial de les ordres utilitzades en el diàleg de l'Alt+F2"
#. Translators: looking glass is a debugger and inspector tool, see
#. https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
#. Translators: looking glass is a debugger and inspector tool, see https://wiki.gnome.org/Projects/GnomeShell/LookingGlass
#: data/org.gnome.shell.gschema.xml.in:74
msgid "History for the looking glass dialog"
msgstr "Historial del depurador del GNOME Shell"
@@ -172,8 +169,8 @@ msgstr "Mostra sempre l'element de menú «Surt» al menú d'usuari."
#: data/org.gnome.shell.gschema.xml.in:79
msgid ""
"This key overrides the automatic hiding of the “Log out” menu item in "
"single-user, single-session situations."
"This key overrides the automatic hiding of the “Log out” menu item in single-"
"user, single-session situations."
msgstr ""
"Aquesta clau sobreescriu l'ocultació automàtica de l'element de menú «Surt» "
"quan només hi ha un usuari i un sol tipus de sessió."
@@ -182,7 +179,7 @@ msgstr ""
msgid ""
"Whether to remember password for mounting encrypted or remote filesystems"
msgstr ""
"Si s'han de recordar les contrasenyes dels punts de muntatge xifrat o "
"Si s'han de recordar les contrasenyes dels punts de muntatge encriptats o "
"els sistemes de fitxers remots"
#: data/org.gnome.shell.gschema.xml.in:87
@@ -193,7 +190,7 @@ msgid ""
"state of the checkbox."
msgstr ""
"El GNOME Shell us demanarà la contrasenya quan es munti un dispositiu "
"xifrat o un sistema de fitxers remot. Si es pot desar la contrasenya per "
"encriptat o un sistema de fitxers remot. Si es pot desar la contrasenya per "
"a utilitzar-la en el futur, es mostrarà la casella de selecció «Recorda la "
"contrasenya». Aquesta clau estableix el valor per defecte d'aquesta casella "
"de selecció."
@@ -229,110 +226,93 @@ msgstr ""
"Habilita una API D-BUS que permet la introspecció de l'estat de l'aplicació "
"del Shell."
#: data/org.gnome.shell.gschema.xml.in:114
msgid "Layout of the app picker"
msgstr "Disposició del selector d'aplicacions"
#: data/org.gnome.shell.gschema.xml.in:115
msgid ""
"Layout of the app picker. Each entry in the array is a page. Pages are "
"stored in the order they appear in GNOME Shell. Each page contains an "
"“application id” → 'data' pair. Currently, the following values are stored "
"as 'data': • “position”: the position of the application icon in the page"
msgstr ""
"Disposició del selector d'aplicacions. Cada entrada de la matriu és una "
"pàgina. Les pàgines s'emmagatzemen en l'ordre en què apareixen al GNOME "
"Shell. Cada pàgina conté un «id de l'aplicació» → parell de «dades». "
"Actualment els valors següents s'emmagatzemen com a «dades»: • «posició» la "
"posició de la icona de l'aplicació a la pàgina"
#: data/org.gnome.shell.gschema.xml.in:130
#: data/org.gnome.shell.gschema.xml.in:119
msgid "Keybinding to open the application menu"
msgstr "Vinculació per a obrir el menú d'aplicació"
#: data/org.gnome.shell.gschema.xml.in:131
#: data/org.gnome.shell.gschema.xml.in:120
msgid "Keybinding to open the application menu."
msgstr "La vinculació per a obrir el menú d'aplicació."
#: data/org.gnome.shell.gschema.xml.in:137
#: data/org.gnome.shell.gschema.xml.in:126
msgid "Keybinding to open the “Show Applications” view"
msgstr "Vinculació de tecles per a obrir la vista «Mostra les aplicacions»"
#: data/org.gnome.shell.gschema.xml.in:138
#: data/org.gnome.shell.gschema.xml.in:127
msgid ""
"Keybinding to open the “Show Applications” view of the Activities Overview."
msgstr ""
"Vinculació de tecles per a obrir la vista «Mostra les aplicacions» de les "
"activitats de la vista general."
#: data/org.gnome.shell.gschema.xml.in:145
#: data/org.gnome.shell.gschema.xml.in:134
msgid "Keybinding to open the overview"
msgstr "Vinculació per a obrir la vista general"
#: data/org.gnome.shell.gschema.xml.in:146
#: data/org.gnome.shell.gschema.xml.in:135
msgid "Keybinding to open the Activities Overview."
msgstr "Vinculació per a obrir la vista general d'activitats."
#: data/org.gnome.shell.gschema.xml.in:152
#: data/org.gnome.shell.gschema.xml.in:141
msgid "Keybinding to toggle the visibility of the notification list"
msgstr ""
"La vinculació per a commutar la visibilitat de la llista de notificacions"
#: data/org.gnome.shell.gschema.xml.in:153
#: data/org.gnome.shell.gschema.xml.in:142
msgid "Keybinding to toggle the visibility of the notification list."
msgstr ""
"La vinculació per a commutar la visibilitat de la llista de notificacions."
#: data/org.gnome.shell.gschema.xml.in:159
#: data/org.gnome.shell.gschema.xml.in:148
msgid "Keybinding to focus the active notification"
msgstr "Vinculació per a posar el focus a la notificació activa"
#: data/org.gnome.shell.gschema.xml.in:160
#: data/org.gnome.shell.gschema.xml.in:149
msgid "Keybinding to focus the active notification."
msgstr "Vinculació per a posar el focus a la notificació activa."
#: data/org.gnome.shell.gschema.xml.in:166
#: data/org.gnome.shell.gschema.xml.in:155
msgid "Switch to application 1"
msgstr "Commuta a l'aplicació 1"
#: data/org.gnome.shell.gschema.xml.in:170
#: data/org.gnome.shell.gschema.xml.in:159
msgid "Switch to application 2"
msgstr "Commuta a l'aplicació 2"
#: data/org.gnome.shell.gschema.xml.in:174
#: data/org.gnome.shell.gschema.xml.in:163
msgid "Switch to application 3"
msgstr "Commuta a l'aplicació 3"
#: data/org.gnome.shell.gschema.xml.in:178
#: data/org.gnome.shell.gschema.xml.in:167
msgid "Switch to application 4"
msgstr "Commuta a l'aplicació 4"
#: data/org.gnome.shell.gschema.xml.in:182
#: data/org.gnome.shell.gschema.xml.in:171
msgid "Switch to application 5"
msgstr "Commuta a l'aplicació 5"
#: data/org.gnome.shell.gschema.xml.in:186
#: data/org.gnome.shell.gschema.xml.in:175
msgid "Switch to application 6"
msgstr "Commuta a l'aplicació 6"
#: data/org.gnome.shell.gschema.xml.in:190
#: data/org.gnome.shell.gschema.xml.in:179
msgid "Switch to application 7"
msgstr "Commuta a l'aplicació 7"
#: data/org.gnome.shell.gschema.xml.in:194
#: data/org.gnome.shell.gschema.xml.in:183
msgid "Switch to application 8"
msgstr "Commuta a l'aplicació 8"
#: data/org.gnome.shell.gschema.xml.in:198
#: data/org.gnome.shell.gschema.xml.in:187
msgid "Switch to application 9"
msgstr "Commuta a l'aplicació 9"
#: data/org.gnome.shell.gschema.xml.in:207
#: data/org.gnome.shell.gschema.xml.in:234
#: data/org.gnome.shell.gschema.xml.in:196
#: data/org.gnome.shell.gschema.xml.in:223
msgid "Limit switcher to current workspace."
msgstr "Limita el canviador a l'espai de treball actual."
#: data/org.gnome.shell.gschema.xml.in:208
#: data/org.gnome.shell.gschema.xml.in:197
msgid ""
"If true, only applications that have windows on the current workspace are "
"shown in the switcher. Otherwise, all applications are included."
@@ -341,21 +321,21 @@ msgstr ""
"de treball actual es mostren en el canviador. En cas contrari es mostren "
"totes les aplicacions."
#: data/org.gnome.shell.gschema.xml.in:225
#: data/org.gnome.shell.gschema.xml.in:214
msgid "The application icon mode."
msgstr "El mode d'icona de les aplicacions."
#: data/org.gnome.shell.gschema.xml.in:226
#: data/org.gnome.shell.gschema.xml.in:215
msgid ""
"Configures how the windows are shown in the switcher. Valid possibilities "
"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-only” "
"(shows only the application icon) or “both”."
"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
"only” (shows only the application icon) or “both”."
msgstr ""
"Configureu com es mostren les finestres en l'intercanviador. Els valors "
"possibles són: «thumbnail-only» (mostra una miniatura de la finestra), «app-"
"icon-only» (mostra la icona de l'aplicació) o «both» (ambdues coses)."
#: data/org.gnome.shell.gschema.xml.in:235
#: data/org.gnome.shell.gschema.xml.in:224
msgid ""
"If true, only windows from the current workspace are shown in the switcher. "
"Otherwise, all windows are included."
@@ -363,60 +343,60 @@ msgstr ""
"Si és «true» (cert), només les finestres de l'espai de treball actual es "
"mostren en el canviador. En cas contrari, es mostren totes les finestres."
#: data/org.gnome.shell.gschema.xml.in:245
#: data/org.gnome.shell.gschema.xml.in:234
msgid "Locations"
msgstr "Ubicacions"
#: data/org.gnome.shell.gschema.xml.in:246
#: data/org.gnome.shell.gschema.xml.in:235
msgid "The locations to show in world clocks"
msgstr "Les ubicacions a mostrar en els rellotges del món"
#: data/org.gnome.shell.gschema.xml.in:256
#: data/org.gnome.shell.gschema.xml.in:245
msgid "Automatic location"
msgstr "Ubicació automàtica"
#: data/org.gnome.shell.gschema.xml.in:257
#: data/org.gnome.shell.gschema.xml.in:246
msgid "Whether to fetch the current location or not"
msgstr "Si s'ha d'obtenir la ubicació actual o no"
#: data/org.gnome.shell.gschema.xml.in:264
#: data/org.gnome.shell.gschema.xml.in:253
msgid "Location"
msgstr "Ubicació"
#: data/org.gnome.shell.gschema.xml.in:265
#: data/org.gnome.shell.gschema.xml.in:254
msgid "The location for which to show a forecast"
msgstr "La ubicació a mostrar la predicció del temps"
#: data/org.gnome.shell.gschema.xml.in:277
#: data/org.gnome.shell.gschema.xml.in:266
msgid "Attach modal dialog to the parent window"
msgstr "Adjunta el diàleg modal a la finestra pare"
#: data/org.gnome.shell.gschema.xml.in:278
#: data/org.gnome.shell.gschema.xml.in:287
#: data/org.gnome.shell.gschema.xml.in:295
#: data/org.gnome.shell.gschema.xml.in:303
#: data/org.gnome.shell.gschema.xml.in:311
#: data/org.gnome.shell.gschema.xml.in:267
#: data/org.gnome.shell.gschema.xml.in:276
#: data/org.gnome.shell.gschema.xml.in:284
#: data/org.gnome.shell.gschema.xml.in:292
#: data/org.gnome.shell.gschema.xml.in:300
msgid ""
"This key overrides the key in org.gnome.mutter when running GNOME Shell."
msgstr ""
"Si s'executa el GNOME Shell, aquesta clau sobreescriu la clau "
"«org.gnome.mutter»."
"Si s'executa el GNOME Shell, aquesta clau sobreescriu la clau «org.gnome."
"mutter»."
#: data/org.gnome.shell.gschema.xml.in:286
#: data/org.gnome.shell.gschema.xml.in:275
msgid "Enable edge tiling when dropping windows on screen edges"
msgstr ""
"Habilita el mosaic a les vores en deixar anar les finestres a les vores de "
"la pantalla"
#: data/org.gnome.shell.gschema.xml.in:294
#: data/org.gnome.shell.gschema.xml.in:283
msgid "Workspaces are managed dynamically"
msgstr "Els espais de treball es gestionen dinàmicament"
#: data/org.gnome.shell.gschema.xml.in:302
#: data/org.gnome.shell.gschema.xml.in:291
msgid "Workspaces only on primary monitor"
msgstr "Només en el monitor principal hi ha espais de treball"
#: data/org.gnome.shell.gschema.xml.in:310
#: data/org.gnome.shell.gschema.xml.in:299
msgid "Delay focus changes in mouse mode until the pointer stops moving"
msgstr ""
"Retarda el canvi del focus, quan s'està en mode ratolí, fins que el punter "
@@ -454,7 +434,7 @@ msgstr "Visiteu la pàgina d'inici de l'extensió"
#: js/gdm/authPrompt.js:135 js/ui/audioDeviceSelection.js:57
#: js/ui/components/networkAgent.js:110 js/ui/components/polkitAgent.js:139
#: js/ui/endSessionDialog.js:369 js/ui/extensionDownloader.js:183
#: js/ui/endSessionDialog.js:369 js/ui/extensionDownloader.js:181
#: js/ui/shellMountOperation.js:376 js/ui/shellMountOperation.js:386
#: js/ui/status/network.js:916 subprojects/extensions-app/js/main.js:149
msgid "Cancel"
@@ -496,7 +476,7 @@ msgstr "Nom d'usuari"
msgid "Login Window"
msgstr "Finestra d'entrada"
#: js/gdm/util.js:355
#: js/gdm/util.js:345
msgid "Authentication error"
msgstr "Error d'autenticació"
@@ -505,7 +485,7 @@ msgstr "Error d'autenticació"
#. as a cue to display our own message.
#. Translators: this message is shown below the password entry field
#. to indicate the user can swipe their finger instead
#: js/gdm/util.js:481
#: js/gdm/util.js:471
msgid "(or swipe finger)"
msgstr "(o passeu el dit)"
@@ -515,8 +495,7 @@ msgctxt "search-result"
msgid "Power Off"
msgstr "Apaga"
#. Translators: A list of keywords that match the power-off action, separated
#. by semicolons
#. Translators: A list of keywords that match the power-off action, separated by semicolons
#: js/misc/systemActions.js:96
msgid "power off;shutdown;reboot;restart;halt;stop"
msgstr "apaga;atura;reinicia"
@@ -527,8 +506,7 @@ msgctxt "search-result"
msgid "Lock Screen"
msgstr "Bloqueja la pantalla"
#. Translators: A list of keywords that match the lock screen action,
#. separated by semicolons
#. Translators: A list of keywords that match the lock screen action, separated by semicolons
#: js/misc/systemActions.js:104
msgid "lock screen"
msgstr "bloca la pantalla"
@@ -539,8 +517,7 @@ msgctxt "search-result"
msgid "Log Out"
msgstr "Surt"
#. Translators: A list of keywords that match the logout action, separated by
#. semicolons
#. Translators: A list of keywords that match the logout action, separated by semicolons
#: js/misc/systemActions.js:112
msgid "logout;log out;sign off"
msgstr "desconnecta;sortida;surt"
@@ -551,8 +528,7 @@ msgctxt "search-result"
msgid "Suspend"
msgstr "Atura temporalment"
#. Translators: A list of keywords that match the suspend action, separated by
#. semicolons
#. Translators: A list of keywords that match the suspend action, separated by semicolons
#: js/misc/systemActions.js:120
msgid "suspend;sleep"
msgstr "atura temporalment;dorm"
@@ -563,14 +539,12 @@ msgctxt "search-result"
msgid "Switch User"
msgstr "Canvia d'usuari"
#. Translators: A list of keywords that match the switch user action,
#. separated by semicolons
#. Translators: A list of keywords that match the switch user action, separated by semicolons
#: js/misc/systemActions.js:128
msgid "switch user"
msgstr "canvia d'usuari"
#. Translators: A list of keywords that match the lock orientation action,
#. separated by semicolons
#. Translators: A list of keywords that match the lock orientation action, separated by semicolons
#: js/misc/systemActions.js:135
msgid "lock orientation;unlock orientation;screen;rotation"
msgstr "bloqueja l'orientació;desbloqueja l'orientació;pantalla;rotació"
@@ -746,36 +720,36 @@ msgstr "Denega l'accés"
msgid "Grant Access"
msgstr "Permet l'accés"
#: js/ui/appDisplay.js:1297
#: js/ui/appDisplay.js:956
msgid "Unnamed Folder"
msgstr "Carpeta sense nom"
#. Translators: This is the heading of a list of open windows
#: js/ui/appDisplay.js:2767 js/ui/panel.js:75
#: js/ui/appDisplay.js:2215 js/ui/panel.js:75
msgid "Open Windows"
msgstr "Obre finestres"
#: js/ui/appDisplay.js:2786 js/ui/panel.js:82
#: js/ui/appDisplay.js:2234 js/ui/panel.js:82
msgid "New Window"
msgstr "Finestra nova"
#: js/ui/appDisplay.js:2802
#: js/ui/appDisplay.js:2250
msgid "Launch using Integrated Graphics Card"
msgstr "Inicia usant una targeta gràfica integrada"
#: js/ui/appDisplay.js:2803
#: js/ui/appDisplay.js:2251
msgid "Launch using Discrete Graphics Card"
msgstr "Inicia usant una targeta gràfica discreta"
#: js/ui/appDisplay.js:2831 js/ui/dash.js:239
#: js/ui/appDisplay.js:2279 js/ui/dash.js:239
msgid "Remove from Favorites"
msgstr "Suprimeix dels preferits"
#: js/ui/appDisplay.js:2837
#: js/ui/appDisplay.js:2285
msgid "Add to Favorites"
msgstr "Afegeix als preferits"
#: js/ui/appDisplay.js:2847 js/ui/panel.js:93
#: js/ui/appDisplay.js:2295 js/ui/panel.js:93
msgid "Show Details"
msgstr "Mostra els detalls"
@@ -805,7 +779,7 @@ msgstr "Auriculars"
msgid "Headset"
msgstr "Auriculars amb micròfon"
#: js/ui/audioDeviceSelection.js:68 js/ui/status/volume.js:272
#: js/ui/audioDeviceSelection.js:68 js/ui/status/volume.js:273
msgid "Microphone"
msgstr "Micròfon"
@@ -821,8 +795,7 @@ msgstr "Paràmetres de la pantalla"
msgid "Settings"
msgstr "Paràmetres"
#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0"
#. (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
#. Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday).
#: js/ui/calendar.js:36
msgctxt "calendar-no-work"
msgid "06"
@@ -832,6 +805,7 @@ msgstr "06"
#. *
#. * NOTE: These grid abbreviations are always shown together
#. * and in order, e.g. "S M T W T F S".
#.
#: js/ui/calendar.js:65
msgctxt "grid sunday"
msgid "S"
@@ -878,6 +852,7 @@ msgstr "Ds"
#. * standalone, when this is a month of the current year.
#. * "%OB" is the new format specifier introduced in glibc 2.27,
#. * in most cases you should not change it.
#.
#: js/ui/calendar.js:392
msgid "%OB"
msgstr "%OB"
@@ -890,6 +865,7 @@ msgstr "%OB"
#. * "%OB" is the new format specifier introduced in glibc 2.27,
#. * in most cases you should not use the old "%B" here unless you
#. * absolutely know what you are doing.
#.
#: js/ui/calendar.js:402
msgid "%OB %Y"
msgstr "%OB de %Y"
@@ -971,8 +947,7 @@ msgstr "Obre amb %s"
#: js/ui/components/networkAgent.js:92
msgid ""
"Alternatively you can connect by pushing the “WPS” button on your router."
msgstr ""
"També us podeu connectar prement el botó «WPS» del vostre encaminador."
msgstr "També us podeu connectar prement el botó «WPS» del vostre encaminador."
#: js/ui/components/networkAgent.js:104 js/ui/status/network.js:227
#: js/ui/status/network.js:318 js/ui/status/network.js:919
@@ -1007,7 +982,7 @@ msgid ""
"“%s”."
msgstr ""
"Per a accedir a la xarxa sense fil «%s» calen les contrasenyes o les claus "
"de xifratge."
"d'encriptació."
#: js/ui/components/networkAgent.js:318 js/ui/components/networkAgent.js:685
msgid "Wired 802.1X authentication"
@@ -1068,8 +1043,7 @@ msgstr "Autentica"
msgid "Sorry, that didnt work. Please try again."
msgstr "No ha funcionat. Torneu-ho a provar."
#. Translators: this is the other person changing their old IM name to their
#. new
#. Translators: this is the other person changing their old IM name to their new
#. IM name.
#: js/ui/components/telepathyClient.js:822
#, javascript-format
@@ -1094,6 +1068,7 @@ msgstr "Quadre d'aplicacions"
#. * shown - it is shown just below the time in the top bar (e.g.,
#. * "Tue 9:29 AM"). The string itself should become a full date, e.g.,
#. * "February 17 2015".
#.
#: js/ui/dateMenu.js:79
msgid "%B %-d %Y"
msgstr "%-d %B de %Y"
@@ -1101,19 +1076,18 @@ msgstr "%-d %B de %Y"
#. Translators: This is the accessible name of the date button shown
#. * below the time in the shell; it should combine the weekday and the
#. * date, e.g. "Tuesday February 17 2015".
#.
#: js/ui/dateMenu.js:86
msgid "%A %B %e %Y"
msgstr "%A, %-e %B de %Y"
#. Translators: Shown on calendar heading when selected day occurs on current
#. year
#. Translators: Shown on calendar heading when selected day occurs on current year
#: js/ui/dateMenu.js:151
msgctxt "calendar heading"
msgid "%B %-d"
msgstr "%-d %B"
#. Translators: Shown on calendar heading when selected day occurs on
#. different year
#. Translators: Shown on calendar heading when selected day occurs on different year
#: js/ui/dateMenu.js:154
msgctxt "calendar heading"
msgid "%B %-d %Y"
@@ -1129,6 +1103,7 @@ msgstr "Demà"
#. Translators: Shown in calendar event list for all day events
#. * Keep it short, best if you can use less then 10 characters
#.
#: js/ui/dateMenu.js:180
msgctxt "event list time"
msgid "All Day"
@@ -1247,8 +1222,7 @@ msgstr "Reinicia i instal·la les actualitzacions"
#: js/ui/endSessionDialog.js:91
#, javascript-format
msgid ""
"The system will automatically restart and install updates in %d second."
msgid "The system will automatically restart and install updates in %d second."
msgid_plural ""
"The system will automatically restart and install updates in %d seconds."
msgstr[0] ""
@@ -1319,15 +1293,15 @@ msgstr "%s (remot)"
msgid "%s (console)"
msgstr "%s (consola)"
#: js/ui/extensionDownloader.js:187
#: js/ui/extensionDownloader.js:185
msgid "Install"
msgstr "Instal·la"
#: js/ui/extensionDownloader.js:193
#: js/ui/extensionDownloader.js:191
msgid "Install Extension"
msgstr "Instal·la l'extensió"
#: js/ui/extensionDownloader.js:194
#: js/ui/extensionDownloader.js:192
#, javascript-format
msgid "Download and install “%s” from extensions.gnome.org?"
msgstr "Voleu baixar i instal·lar «%s» d'extensions.gnome.org?"
@@ -1362,11 +1336,11 @@ msgstr "Una aplicació vol inhabilitar les dreceres"
msgid "You can restore shortcuts by pressing %s."
msgstr "Podeu restaurar les dreceres si premeu %s."
#: js/ui/inhibitShortcutsDialog.js:100
#: js/ui/inhibitShortcutsDialog.js:98
msgid "Deny"
msgstr "Denega"
#: js/ui/inhibitShortcutsDialog.js:107
#: js/ui/inhibitShortcutsDialog.js:105
msgid "Allow"
msgstr "Permet"
@@ -1380,8 +1354,8 @@ msgstr "Tecles lentes inactives"
#: js/ui/kbdA11yDialog.js:34
msgid ""
"You just held down the Shift key for 8 seconds. This is the shortcut for the"
" Slow Keys feature, which affects the way your keyboard works."
"You just held down the Shift key for 8 seconds. This is the shortcut for the "
"Slow Keys feature, which affects the way your keyboard works."
msgstr ""
"Heu mantingut premuda la tecla de majúscules durant 8 segons. Aquesta és la "
"drecera per a la funcionalitat «tecles lentes», que afecta la forma de "
@@ -1400,8 +1374,8 @@ msgid ""
"You just pressed the Shift key 5 times in a row. This is the shortcut for "
"the Sticky Keys feature, which affects the way your keyboard works."
msgstr ""
"Heu premut la tecla de majúscules 5 cops seguides. Aquesta és la drecera per"
" la funcionalitat de les tecles enganxoses, que afecta la manera en què "
"Heu premut la tecla de majúscules 5 cops seguides. Aquesta és la drecera per "
"la funcionalitat de les tecles enganxoses, que afecta la manera en què "
"funciona el teclat."
#: js/ui/kbdA11yDialog.js:46
@@ -1435,7 +1409,7 @@ msgstr "Desactiva"
msgid "Leave Off"
msgstr "Deixa-ho desactivat"
#: js/ui/keyboard.js:225
#: js/ui/keyboard.js:207
msgid "Region & Language Settings"
msgstr "Configuració de regió i idioma"
@@ -1497,8 +1471,8 @@ msgid ""
"Running a session as a privileged user should be avoided for security "
"reasons. If possible, you should log in as a normal user."
msgstr ""
"Cal evitar iniciar sessions com a usuari privilegiat per raons de seguretat."
" Si és possible, entreu com a un usuari normal."
"Cal evitar iniciar sessions com a usuari privilegiat per raons de seguretat. "
"Si és possible, entreu com a un usuari normal."
#: js/ui/main.js:337
msgid "Screen Lock disabled"
@@ -1508,7 +1482,7 @@ msgstr "La pantalla de bloqueig està inhabilitada"
msgid "Screen Locking requires the GNOME display manager."
msgstr "El bloqueig de pantalla requereix el gestor de pantalla del GNOME."
#: js/ui/messageTray.js:1476
#: js/ui/messageTray.js:1547
msgid "System Information"
msgstr "Informació de l'ordinador"
@@ -1520,13 +1494,13 @@ msgstr "Artista desconegut"
msgid "Unknown title"
msgstr "Títol desconegut"
#: js/ui/overview.js:74
#: js/ui/overview.js:73
msgid "Undo"
msgstr "Desfés"
#. Translators: This is the main view to select
#. activities. See also note for "Activities" string.
#: js/ui/overview.js:87
#: js/ui/overview.js:86
msgid "Overview"
msgstr "Vista general"
@@ -1534,7 +1508,7 @@ msgstr "Vista general"
#. in the search entry when no search is
#. active; it should not exceed ~30
#. characters.
#: js/ui/overview.js:108
#: js/ui/overview.js:107
msgid "Type to search"
msgstr "Teclegeu per a cercar"
@@ -1625,6 +1599,7 @@ msgstr "El GNOME necessita bloquejar la pantalla"
#. screenshield. The user is probably very upset at this
#. point, but any application using global grabs is broken
#. Just tell him to stop using this app
#.
#. XXX: another option is to kick the user into the gdm login
#. screen, where we're not affected by grabs
#: js/ui/screenShield.js:244 js/ui/screenShield.js:602
@@ -1866,15 +1841,14 @@ msgstr "<desconegut>"
msgid "%s Off"
msgstr "%s apagat"
#. Translators: %s is a network identifier
# N.T.: p. ex. Connectat amb fil
#. Translators: %s is a network identifier
#: js/ui/status/network.js:427
#, javascript-format
msgid "%s Connected"
msgstr "Connectat %s"
#. Translators: this is for network devices that are physically present but
#. are not
#. Translators: this is for network devices that are physically present but are not
#. under NetworkManager's control (and thus cannot be used in the menu);
#. %s is a network identifier
#: js/ui/status/network.js:432
@@ -1894,23 +1868,20 @@ msgstr "%s s'està desconnectant"
msgid "%s Connecting"
msgstr "%s s'està connectant"
#. Translators: this is for network connections that require some kind of key
#. or password; %s is a network identifier
#. Translators: this is for network connections that require some kind of key or password; %s is a network identifier
#: js/ui/status/network.js:445
#, javascript-format
msgid "%s Requires Authentication"
msgstr "%s requereix autenticació"
#. Translators: this is for devices that require some kind of firmware or
#. kernel
#. Translators: this is for devices that require some kind of firmware or kernel
#. module, which is missing; %s is a network identifier
#: js/ui/status/network.js:453
#, javascript-format
msgid "Firmware Missing For %s"
msgstr "Manca el microprogramari per %s"
#. Translators: this is for a network device that cannot be activated (for
#. example it
#. Translators: this is for a network device that cannot be activated (for example it
#. is disabled by rfkill, or it has no coverage; %s is a network identifier
#: js/ui/status/network.js:457
#, javascript-format
@@ -2013,8 +1984,7 @@ msgstr "%s no està connectat"
msgid "connecting…"
msgstr "s'està connectant..."
#. Translators: this is for network connections that require some kind of key
#. or password
#. Translators: this is for network connections that require some kind of key or password
#: js/ui/status/network.js:1423
msgid "authentication required"
msgstr "cal autenticació"
@@ -2194,34 +2164,38 @@ msgstr "S'ha produït un error d'autorització a Thunderbolt"
msgid "Could not authorize the Thunderbolt device: %s"
msgstr "No s'ha pogut autoritzar el dispositiu Thunderbolt: %s"
#: js/ui/status/volume.js:155
#: js/ui/status/volume.js:154
msgid "Volume changed"
msgstr "S'ha canviat el volum"
#: js/ui/status/volume.js:217
#: js/ui/status/volume.js:225
msgid "Volume"
msgstr "Volum"
#. Translators: this is for display mirroring i.e. cloning.
#. * Try to keep it under around 15 characters.
#.
#: js/ui/switchMonitor.js:17
msgid "Mirror"
msgstr "Mirall"
#. Translators: this is for the desktop spanning displays.
#. * Try to keep it under around 15 characters.
#.
#: js/ui/switchMonitor.js:22
msgid "Join Displays"
msgstr "Uneix pantalles"
#. Translators: this is for using only an external display.
#. * Try to keep it under around 15 characters.
#.
#: js/ui/switchMonitor.js:27
msgid "External Only"
msgstr "Només extern"
#. Translators: this is for using only the laptop display.
#. * Try to keep it under around 15 characters.
#.
#: js/ui/switchMonitor.js:32
msgid "Built-in Only"
msgstr "Només l'integrat"
@@ -2240,11 +2214,11 @@ msgstr "Llisqueu amunt per a desbloquejar"
msgid "Click or press a key to unlock"
msgstr "Feu clic o premeu una tecla per a desbloquejar"
#: js/ui/unlockDialog.js:555
#: js/ui/unlockDialog.js:550
msgid "Unlock Window"
msgstr "Desbloqueja la finestra"
#: js/ui/unlockDialog.js:564
#: js/ui/unlockDialog.js:559
msgid "Log in as another user"
msgstr "Entra amb un altre usuari"
@@ -2268,6 +2242,7 @@ msgstr "Mantenir aquesta configuració de la pantalla?"
#. Translators: this and the following message should be limited in length,
#. to avoid ellipsizing the labels.
#.
#: js/ui/windowManager.js:64
msgid "Revert Settings"
msgstr "Descarta els canvis"
@@ -2378,12 +2353,12 @@ msgstr "Utilitza un mode específic, p. ex. «gdm» per la pantalla d'entrada"
msgid "List possible modes"
msgstr "Llista els modes possibles"
#: src/shell-app.c:268
#: src/shell-app.c:286
msgctxt "program"
msgid "Unknown"
msgstr "Desconegut"
#: src/shell-app.c:519
#: src/shell-app.c:537
#, c-format
msgid "Failed to launch “%s”"
msgstr "No s'ha pogut iniciar «%s»"
@@ -2439,8 +2414,8 @@ msgid ""
"If you remove the extension, you need to return to download it if you want "
"to enable it again"
msgstr ""
"Si suprimiu una extensió, us cal tornar-la a baixar si voleu habilitar-la de"
" nou"
"Si suprimiu una extensió, us cal tornar-la a baixar si voleu habilitar-la de "
"nou"
#: subprojects/extensions-app/js/main.js:150
msgid "Remove"
@@ -2502,11 +2477,11 @@ msgstr "Quant a les extensions"
#: subprojects/extensions-app/data/ui/extensions-window.ui:27
msgid ""
"To find and add extensions, visit <a "
"href=\"https://extensions.gnome.org\">extensions.gnome.org</a>."
"To find and add extensions, visit <a href=\"https://extensions.gnome.org"
"\">extensions.gnome.org</a>."
msgstr ""
"Per a visitar i afegir extensions, visiteu <a "
"href=\"https://extensions.gnome.org\">extensions.gnome.org</a>."
"Per a visitar i afegir extensions, visiteu <a href=\"https://extensions."
"gnome.org\">extensions.gnome.org</a>."
#: subprojects/extensions-app/data/ui/extensions-window.ui:35
msgid "Warning"
@@ -2539,8 +2514,8 @@ msgid ""
"Were very sorry, but it was not possible to get the list of installed "
"extensions. Make sure you are logged into GNOME and try again."
msgstr ""
"No s'ha pogut obtenir la llista d'extensions instal·lades. Assegureu-vos que"
" heu entrat al GNOME i torneu a provar-ho."
"No s'ha pogut obtenir la llista d'extensions instal·lades. Assegureu-vos que "
"heu entrat al GNOME i torneu a provar-ho."
#: subprojects/extensions-app/data/ui/extensions-window.ui:273
msgid "Extension Updates Ready"
@@ -2582,7 +2557,8 @@ msgstr ""
#: subprojects/extensions-tool/src/command-create.c:339
msgid ""
"UUID is a globally-unique identifier for your extension.\n"
"This should be in the format of an email address (clicktofocus@janedoe.example.com)\n"
"This should be in the format of an email address (clicktofocus@janedoe."
"example.com)\n"
msgstr ""
"L'UUID és un identificador únic global per l'extensió\n"
"Ha de tenir el format d'una adreça de correu (exemple@softcatala.org)\n"
@@ -2957,3 +2933,4 @@ msgstr[1] "%u entrades"
#: subprojects/gvc/gvc-mixer-control.c:2766
msgid "System Sounds"
msgstr "Sons del sistema"

161
po/es.po
View File

@@ -9,8 +9,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gnome-shell.master\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
"POT-Creation-Date: 2020-07-27 06:59+0000\n"
"PO-Revision-Date: 2020-07-29 09:47+0200\n"
"POT-Creation-Date: 2020-06-05 23:11+0000\n"
"PO-Revision-Date: 2020-06-08 16:06+0200\n"
"Last-Translator: Daniel Mustieles <daniel.mustieles@gmail.com>\n"
"Language-Team: Spanish - Spain <gnome-es-list@gnome.org>\n"
"Language: es_ES\n"
@@ -221,112 +221,95 @@ msgstr ""
"Activa una API de D-Bus que permite la introspección del estado de la "
"aplicación de la shell."
#: data/org.gnome.shell.gschema.xml.in:114
msgid "Layout of the app picker"
msgstr "Distribución del selector de aplicaciones"
#: data/org.gnome.shell.gschema.xml.in:115
msgid ""
"Layout of the app picker. Each entry in the array is a page. Pages are "
"stored in the order they appear in GNOME Shell. Each page contains an "
"“application id” → 'data' pair. Currently, the following values are stored "
"as 'data': • “position”: the position of the application icon in the page"
msgstr ""
"Distribución del selector de aplicaciones Cada entrada en el vector es una "
"página. Las páginas se guardan en el orden en que aparecen en GNOME Shell. "
"Cada página contiene una pareja “application id” → 'data'. Actualmente se "
"guardan los siguientes valores como 'data': • “position”:la posición del "
"icono de la aplicación en la página"
#: data/org.gnome.shell.gschema.xml.in:130
#: data/org.gnome.shell.gschema.xml.in:119
msgid "Keybinding to open the application menu"
msgstr "Asociación de teclas para abrir el menú de la aplicación"
#: data/org.gnome.shell.gschema.xml.in:131
#: data/org.gnome.shell.gschema.xml.in:120
msgid "Keybinding to open the application menu."
msgstr "Asociación de teclas para abrir el menú de la aplicación."
#: data/org.gnome.shell.gschema.xml.in:137
#: data/org.gnome.shell.gschema.xml.in:126
msgid "Keybinding to open the “Show Applications” view"
msgstr "Asociación de teclas para la vista «Mostrar aplicaciones»"
#: data/org.gnome.shell.gschema.xml.in:138
#: data/org.gnome.shell.gschema.xml.in:127
msgid ""
"Keybinding to open the “Show Applications” view of the Activities Overview."
msgstr ""
"Asociación de teclas para abrir la vista «Mostrar aplicaciones» de la vista "
"de actividades."
#: data/org.gnome.shell.gschema.xml.in:145
#: data/org.gnome.shell.gschema.xml.in:134
msgid "Keybinding to open the overview"
msgstr "Asociación de teclas para la vista general"
#: data/org.gnome.shell.gschema.xml.in:146
#: data/org.gnome.shell.gschema.xml.in:135
msgid "Keybinding to open the Activities Overview."
msgstr "Asociación de teclas para abrir la Vista de actividades."
#: data/org.gnome.shell.gschema.xml.in:152
#: data/org.gnome.shell.gschema.xml.in:141
msgid "Keybinding to toggle the visibility of the notification list"
msgstr ""
"Asociación de teclas para cambiar la visibilidad de la lista de "
"notificaciones"
#: data/org.gnome.shell.gschema.xml.in:153
#: data/org.gnome.shell.gschema.xml.in:142
msgid "Keybinding to toggle the visibility of the notification list."
msgstr ""
"Asociación de teclas para cambiar la visibilidad de la lista de "
"notificaciones."
#: data/org.gnome.shell.gschema.xml.in:159
#: data/org.gnome.shell.gschema.xml.in:148
msgid "Keybinding to focus the active notification"
msgstr "Asociación de teclas para dar el foco a la notificación activa"
#: data/org.gnome.shell.gschema.xml.in:160
#: data/org.gnome.shell.gschema.xml.in:149
msgid "Keybinding to focus the active notification."
msgstr "Asociación de teclas para dar el foco a la notificación activa."
#: data/org.gnome.shell.gschema.xml.in:166
#: data/org.gnome.shell.gschema.xml.in:155
msgid "Switch to application 1"
msgstr "Cambiar a la aplicación 1"
#: data/org.gnome.shell.gschema.xml.in:170
#: data/org.gnome.shell.gschema.xml.in:159
msgid "Switch to application 2"
msgstr "Cambiar a la aplicación 2"
#: data/org.gnome.shell.gschema.xml.in:174
#: data/org.gnome.shell.gschema.xml.in:163
msgid "Switch to application 3"
msgstr "Cambiar a la aplicación 3"
#: data/org.gnome.shell.gschema.xml.in:178
#: data/org.gnome.shell.gschema.xml.in:167
msgid "Switch to application 4"
msgstr "Cambiar a la aplicación 4"
#: data/org.gnome.shell.gschema.xml.in:182
#: data/org.gnome.shell.gschema.xml.in:171
msgid "Switch to application 5"
msgstr "Cambiar a la aplicación 5"
#: data/org.gnome.shell.gschema.xml.in:186
#: data/org.gnome.shell.gschema.xml.in:175
msgid "Switch to application 6"
msgstr "Cambiar a la aplicación 6"
#: data/org.gnome.shell.gschema.xml.in:190
#: data/org.gnome.shell.gschema.xml.in:179
msgid "Switch to application 7"
msgstr "Cambiar a la aplicación 7"
#: data/org.gnome.shell.gschema.xml.in:194
#: data/org.gnome.shell.gschema.xml.in:183
msgid "Switch to application 8"
msgstr "Cambiar a la aplicación 8"
#: data/org.gnome.shell.gschema.xml.in:198
#: data/org.gnome.shell.gschema.xml.in:187
msgid "Switch to application 9"
msgstr "Cambiar a la aplicación 9"
#: data/org.gnome.shell.gschema.xml.in:207
#: data/org.gnome.shell.gschema.xml.in:234
#: data/org.gnome.shell.gschema.xml.in:196
#: data/org.gnome.shell.gschema.xml.in:223
msgid "Limit switcher to current workspace."
msgstr "Limitar el intercambiador al área de trabajo actual."
#: data/org.gnome.shell.gschema.xml.in:208
#: data/org.gnome.shell.gschema.xml.in:197
msgid ""
"If true, only applications that have windows on the current workspace are "
"shown in the switcher. Otherwise, all applications are included."
@@ -335,11 +318,11 @@ msgstr ""
"trabajo actual se muestran en el selector. Si no, se incluyen todas las "
"aplicaciones."
#: data/org.gnome.shell.gschema.xml.in:225
#: data/org.gnome.shell.gschema.xml.in:214
msgid "The application icon mode."
msgstr "El modo de icono de la aplicación."
#: data/org.gnome.shell.gschema.xml.in:226
#: data/org.gnome.shell.gschema.xml.in:215
msgid ""
"Configures how the windows are shown in the switcher. Valid possibilities "
"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
@@ -349,7 +332,7 @@ msgstr ""
"son «thumbnail-only» (muestra una miniatura de la ventana), «app-icon-"
"only» (sólo muestra el icono de la aplicación) «both»."
#: data/org.gnome.shell.gschema.xml.in:235
#: data/org.gnome.shell.gschema.xml.in:224
msgid ""
"If true, only windows from the current workspace are shown in the switcher. "
"Otherwise, all windows are included."
@@ -357,59 +340,59 @@ msgstr ""
"Si es cierto, sólo se muestran en el selector las ventanas del área de "
"trabajo actual. Si no, se incluyen todas las ventanas."
#: data/org.gnome.shell.gschema.xml.in:245
#: data/org.gnome.shell.gschema.xml.in:234
msgid "Locations"
msgstr "Ubicaciones"
#: data/org.gnome.shell.gschema.xml.in:246
#: data/org.gnome.shell.gschema.xml.in:235
msgid "The locations to show in world clocks"
msgstr "Las ubicaciones que mostrar en los relojes del mundo"
#: data/org.gnome.shell.gschema.xml.in:256
#: data/org.gnome.shell.gschema.xml.in:245
msgid "Automatic location"
msgstr "Ubicación automática"
#: data/org.gnome.shell.gschema.xml.in:257
#: data/org.gnome.shell.gschema.xml.in:246
msgid "Whether to fetch the current location or not"
msgstr "Indica si se debe o no obtener la ubicación actual"
#: data/org.gnome.shell.gschema.xml.in:264
#: data/org.gnome.shell.gschema.xml.in:253
msgid "Location"
msgstr "Ubicación"
#: data/org.gnome.shell.gschema.xml.in:265
#: data/org.gnome.shell.gschema.xml.in:254
msgid "The location for which to show a forecast"
msgstr "La ubicación para la que mostrar la predicción"
#: data/org.gnome.shell.gschema.xml.in:277
#: data/org.gnome.shell.gschema.xml.in:266
msgid "Attach modal dialog to the parent window"
msgstr "Acoplar un diálogo modal a la ventana padre"
#: data/org.gnome.shell.gschema.xml.in:278
#: data/org.gnome.shell.gschema.xml.in:287
#: data/org.gnome.shell.gschema.xml.in:295
#: data/org.gnome.shell.gschema.xml.in:303
#: data/org.gnome.shell.gschema.xml.in:311
#: data/org.gnome.shell.gschema.xml.in:267
#: data/org.gnome.shell.gschema.xml.in:276
#: data/org.gnome.shell.gschema.xml.in:284
#: data/org.gnome.shell.gschema.xml.in:292
#: data/org.gnome.shell.gschema.xml.in:300
msgid ""
"This key overrides the key in org.gnome.mutter when running GNOME Shell."
msgstr ""
"Esta clave sobrescribe la clave en org.gnome.mutter al ejecutar GNOME Shell."
#: data/org.gnome.shell.gschema.xml.in:286
#: data/org.gnome.shell.gschema.xml.in:275
msgid "Enable edge tiling when dropping windows on screen edges"
msgstr ""
"Activar el mosaico en los bordes al arrastrar ventanas a los bordes de la "
"ventana"
#: data/org.gnome.shell.gschema.xml.in:294
#: data/org.gnome.shell.gschema.xml.in:283
msgid "Workspaces are managed dynamically"
msgstr "Las áreas de trabajo se gestionan dinámicamente"
#: data/org.gnome.shell.gschema.xml.in:302
#: data/org.gnome.shell.gschema.xml.in:291
msgid "Workspaces only on primary monitor"
msgstr "Áreas de trabajo solo en la pantalla principal"
#: data/org.gnome.shell.gschema.xml.in:310
#: data/org.gnome.shell.gschema.xml.in:299
msgid "Delay focus changes in mouse mode until the pointer stops moving"
msgstr ""
"Retardo al cambiar el foco del ratón hasta que el puntero deja de moverse"
@@ -446,7 +429,7 @@ msgstr "Visitar la página web de la extensión"
#: js/gdm/authPrompt.js:135 js/ui/audioDeviceSelection.js:57
#: js/ui/components/networkAgent.js:110 js/ui/components/polkitAgent.js:139
#: js/ui/endSessionDialog.js:369 js/ui/extensionDownloader.js:183
#: js/ui/endSessionDialog.js:369 js/ui/extensionDownloader.js:181
#: js/ui/shellMountOperation.js:376 js/ui/shellMountOperation.js:386
#: js/ui/status/network.js:916 subprojects/extensions-app/js/main.js:149
msgid "Cancel"
@@ -488,7 +471,7 @@ msgstr "Nombre de usuario"
msgid "Login Window"
msgstr "Ventana de inicio de sesión"
#: js/gdm/util.js:355
#: js/gdm/util.js:345
msgid "Authentication error"
msgstr "Error de autenticación"
@@ -497,7 +480,7 @@ msgstr "Error de autenticación"
#. as a cue to display our own message.
#. Translators: this message is shown below the password entry field
#. to indicate the user can swipe their finger instead
#: js/gdm/util.js:481
#: js/gdm/util.js:471
msgid "(or swipe finger)"
msgstr "(o pase el dedo)"
@@ -731,36 +714,36 @@ msgstr "Denegar acceso"
msgid "Grant Access"
msgstr "Conceder acceso"
#: js/ui/appDisplay.js:1297
#: js/ui/appDisplay.js:956
msgid "Unnamed Folder"
msgstr "Carpeta sin nombre"
#. Translators: This is the heading of a list of open windows
#: js/ui/appDisplay.js:2767 js/ui/panel.js:75
#: js/ui/appDisplay.js:2215 js/ui/panel.js:75
msgid "Open Windows"
msgstr "Ventanas abiertas"
#: js/ui/appDisplay.js:2786 js/ui/panel.js:82
#: js/ui/appDisplay.js:2234 js/ui/panel.js:82
msgid "New Window"
msgstr "Ventana nueva"
#: js/ui/appDisplay.js:2802
#: js/ui/appDisplay.js:2250
msgid "Launch using Integrated Graphics Card"
msgstr "Lanzar usando la tarjeta gráfica integrada"
#: js/ui/appDisplay.js:2803
#: js/ui/appDisplay.js:2251
msgid "Launch using Discrete Graphics Card"
msgstr "Lanzar usando la tarjeta gráfica discreta"
#: js/ui/appDisplay.js:2831 js/ui/dash.js:239
#: js/ui/appDisplay.js:2279 js/ui/dash.js:239
msgid "Remove from Favorites"
msgstr "Quitar de los favoritos"
#: js/ui/appDisplay.js:2837
#: js/ui/appDisplay.js:2285
msgid "Add to Favorites"
msgstr "Añadir a los favoritos"
#: js/ui/appDisplay.js:2847 js/ui/panel.js:93
#: js/ui/appDisplay.js:2295 js/ui/panel.js:93
msgid "Show Details"
msgstr "Mostrar detalles"
@@ -790,7 +773,7 @@ msgstr "Auriculares"
msgid "Headset"
msgstr "Manos libres"
#: js/ui/audioDeviceSelection.js:68 js/ui/status/volume.js:272
#: js/ui/audioDeviceSelection.js:68 js/ui/status/volume.js:273
msgid "Microphone"
msgstr "Micrófono"
@@ -1094,12 +1077,14 @@ msgstr "%A %e de %B de %Y"
#. Translators: Shown on calendar heading when selected day occurs on current year
#: js/ui/dateMenu.js:151
#| msgid "%B %-d %Y"
msgctxt "calendar heading"
msgid "%B %-d"
msgstr "%B %-d"
#. Translators: Shown on calendar heading when selected day occurs on different year
#: js/ui/dateMenu.js:154
#| msgid "%B %-d %Y"
msgctxt "calendar heading"
msgid "%B %-d %Y"
msgstr "%B %-d %Y"
@@ -1301,15 +1286,15 @@ msgstr "%s (remoto)"
msgid "%s (console)"
msgstr "%s (consola)"
#: js/ui/extensionDownloader.js:187
#: js/ui/extensionDownloader.js:185
msgid "Install"
msgstr "Instalar"
#: js/ui/extensionDownloader.js:193
#: js/ui/extensionDownloader.js:191
msgid "Install Extension"
msgstr "Instalar extensión"
#: js/ui/extensionDownloader.js:194
#: js/ui/extensionDownloader.js:192
#, javascript-format
msgid "Download and install “%s” from extensions.gnome.org?"
msgstr "¿Descargar e instalar «%s» desde extensions.gnome.org?"
@@ -1342,11 +1327,11 @@ msgstr "Una aplicación quiere inhibir los atajos"
msgid "You can restore shortcuts by pressing %s."
msgstr "Puede restaurar los atajos pulsando %s."
#: js/ui/inhibitShortcutsDialog.js:100
#: js/ui/inhibitShortcutsDialog.js:98
msgid "Deny"
msgstr "Denegar"
#: js/ui/inhibitShortcutsDialog.js:107
#: js/ui/inhibitShortcutsDialog.js:105
msgid "Allow"
msgstr "Permitir"
@@ -1415,7 +1400,7 @@ msgstr "Apagar"
msgid "Leave Off"
msgstr "Dejar apagado"
#: js/ui/keyboard.js:225
#: js/ui/keyboard.js:207
msgid "Region & Language Settings"
msgstr "Configuración de región e idioma"
@@ -1488,7 +1473,7 @@ msgstr "Pantalla de bloqueo desactivada"
msgid "Screen Locking requires the GNOME display manager."
msgstr "La pantalla de bloqueo necesita el gestor de pantallas de GNOME."
#: js/ui/messageTray.js:1476
#: js/ui/messageTray.js:1547
msgid "System Information"
msgstr "Información del sistema"
@@ -1500,13 +1485,13 @@ msgstr "Artista desconocido"
msgid "Unknown title"
msgstr "Título desconocido"
#: js/ui/overview.js:74
#: js/ui/overview.js:73
msgid "Undo"
msgstr "Deshacer"
#. Translators: This is the main view to select
#. activities. See also note for "Activities" string.
#: js/ui/overview.js:87
#: js/ui/overview.js:86
msgid "Overview"
msgstr "Vista general"
@@ -1514,7 +1499,7 @@ msgstr "Vista general"
#. in the search entry when no search is
#. active; it should not exceed ~30
#. characters.
#: js/ui/overview.js:108
#: js/ui/overview.js:107
msgid "Type to search"
msgstr "Escribir para buscar"
@@ -2168,11 +2153,11 @@ msgstr "Error de autorización de Thunderbolt"
msgid "Could not authorize the Thunderbolt device: %s"
msgstr "No se pudo autorizar el dispositivo Thunderbolt: %s"
#: js/ui/status/volume.js:155
#: js/ui/status/volume.js:154
msgid "Volume changed"
msgstr "Volumen modificado"
#: js/ui/status/volume.js:217
#: js/ui/status/volume.js:225
msgid "Volume"
msgstr "Volumen"
@@ -2218,11 +2203,11 @@ msgstr "Deslizar para desbloquear"
msgid "Click or press a key to unlock"
msgstr "Pulse con el ratón o una tecla para desbloquear"
#: js/ui/unlockDialog.js:555
#: js/ui/unlockDialog.js:550
msgid "Unlock Window"
msgstr "Desbloquear ventana"
#: js/ui/unlockDialog.js:564
#: js/ui/unlockDialog.js:559
msgid "Log in as another user"
msgstr "Iniciar sesión como otro usuario"
@@ -2359,12 +2344,12 @@ msgstr ""
msgid "List possible modes"
msgstr "Listar los modos posibles"
#: src/shell-app.c:268
#: src/shell-app.c:286
msgctxt "program"
msgid "Unknown"
msgstr "Desconocido"
#: src/shell-app.c:519
#: src/shell-app.c:537
#, c-format
msgid "Failed to launch “%s”"
msgstr "Falló al lanzar «%s»"

160
po/tr.po
View File

@@ -17,8 +17,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gnome-shell\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell/issues\n"
"POT-Creation-Date: 2020-07-29 07:55+0000\n"
"PO-Revision-Date: 2020-07-30 00:49+0300\n"
"POT-Creation-Date: 2020-06-11 10:28+0000\n"
"PO-Revision-Date: 2020-05-16 12:12+0300\n"
"Last-Translator: Emin Tufan Çetin <etcetin@gmail.com>\n"
"Language-Team: Türkçe <gnome-turk@gnome.org>\n"
"Language: tr\n"
@@ -225,107 +225,91 @@ msgstr ""
"Kabuğun uygulama durumunu gözden geçirmeyi sağlayan D-Bus APIʼsını "
"etkinleştirir."
#: data/org.gnome.shell.gschema.xml.in:114
msgid "Layout of the app picker"
msgstr "Uygulama seçicinin düzeni"
#: data/org.gnome.shell.gschema.xml.in:115
msgid ""
"Layout of the app picker. Each entry in the array is a page. Pages are "
"stored in the order they appear in GNOME Shell. Each page contains an "
"“application id” → 'data' pair. Currently, the following values are stored "
"as 'data': • “position”: the position of the application icon in the page"
msgstr ""
"Uygulama seçicinin düzeni. Dizideki her girdi bir sayfadır. Sayfalar "
"GNOME Kabuğunda göründüğü sırayla depolanır. Her sayfa bir “application id” "
"→ 'data' çifti içerir. Şimdilik şu değerler 'data' olarak depolanır: "
"• “position”: sayfadaki uygulama simgesinin konumu"
#: data/org.gnome.shell.gschema.xml.in:130
#: data/org.gnome.shell.gschema.xml.in:119
msgid "Keybinding to open the application menu"
msgstr "Uygulama menüsünü açmak için klavye kısayolu"
#: data/org.gnome.shell.gschema.xml.in:131
#: data/org.gnome.shell.gschema.xml.in:120
msgid "Keybinding to open the application menu."
msgstr "Uygulama menüsünü açmak için klavye kısayolu."
#: data/org.gnome.shell.gschema.xml.in:137
#: data/org.gnome.shell.gschema.xml.in:126
msgid "Keybinding to open the “Show Applications” view"
msgstr "“Uygulamaları Göster” görünümünü açmak için klavye kısayolu"
#: data/org.gnome.shell.gschema.xml.in:138
#: data/org.gnome.shell.gschema.xml.in:127
msgid ""
"Keybinding to open the “Show Applications” view of the Activities Overview."
msgstr ""
"Etkinlikler Genel Görünümünün “Uygulamaları Göster” görünümünü açmak için "
"klavye kısayolu."
#: data/org.gnome.shell.gschema.xml.in:145
#: data/org.gnome.shell.gschema.xml.in:134
msgid "Keybinding to open the overview"
msgstr "Genel görünümü açmak için klavye kısayolu"
#: data/org.gnome.shell.gschema.xml.in:146
#: data/org.gnome.shell.gschema.xml.in:135
msgid "Keybinding to open the Activities Overview."
msgstr "Etkinlikler Genel Görünümünü açmak için klavye kısayolu."
#: data/org.gnome.shell.gschema.xml.in:152
#: data/org.gnome.shell.gschema.xml.in:141
msgid "Keybinding to toggle the visibility of the notification list"
msgstr "Bildirim listesinin görünürlüğünü değiştirmek için klavye kısayolu"
#: data/org.gnome.shell.gschema.xml.in:153
#: data/org.gnome.shell.gschema.xml.in:142
msgid "Keybinding to toggle the visibility of the notification list."
msgstr "Bildirim listesinin görünürlüğünü değiştirmek için klavye kısayolu."
#: data/org.gnome.shell.gschema.xml.in:159
#: data/org.gnome.shell.gschema.xml.in:148
msgid "Keybinding to focus the active notification"
msgstr "Etkin bildirime odaklanmak için klavye kısayolu"
#: data/org.gnome.shell.gschema.xml.in:160
#: data/org.gnome.shell.gschema.xml.in:149
msgid "Keybinding to focus the active notification."
msgstr "Etkin bildirime odaklanmak için klavye kısayolu."
#: data/org.gnome.shell.gschema.xml.in:166
#: data/org.gnome.shell.gschema.xml.in:155
msgid "Switch to application 1"
msgstr "Uygulama 1ʼe geç"
#: data/org.gnome.shell.gschema.xml.in:170
#: data/org.gnome.shell.gschema.xml.in:159
msgid "Switch to application 2"
msgstr "Uygulama 2ʼye geç"
#: data/org.gnome.shell.gschema.xml.in:174
#: data/org.gnome.shell.gschema.xml.in:163
msgid "Switch to application 3"
msgstr "Uygulama 3ʼe geç"
#: data/org.gnome.shell.gschema.xml.in:178
#: data/org.gnome.shell.gschema.xml.in:167
msgid "Switch to application 4"
msgstr "Uygulama 4ʼe geç"
#: data/org.gnome.shell.gschema.xml.in:182
#: data/org.gnome.shell.gschema.xml.in:171
msgid "Switch to application 5"
msgstr "Uygulama 5ʼe geç"
#: data/org.gnome.shell.gschema.xml.in:186
#: data/org.gnome.shell.gschema.xml.in:175
msgid "Switch to application 6"
msgstr "Uygulama 6ʼya geç"
#: data/org.gnome.shell.gschema.xml.in:190
#: data/org.gnome.shell.gschema.xml.in:179
msgid "Switch to application 7"
msgstr "Uygulama 7ʼye geç"
#: data/org.gnome.shell.gschema.xml.in:194
#: data/org.gnome.shell.gschema.xml.in:183
msgid "Switch to application 8"
msgstr "Uygulama 8ʼe geç"
#: data/org.gnome.shell.gschema.xml.in:198
#: data/org.gnome.shell.gschema.xml.in:187
msgid "Switch to application 9"
msgstr "Uygulama 9ʼa geç"
#: data/org.gnome.shell.gschema.xml.in:207
#: data/org.gnome.shell.gschema.xml.in:234
#: data/org.gnome.shell.gschema.xml.in:196
#: data/org.gnome.shell.gschema.xml.in:223
msgid "Limit switcher to current workspace."
msgstr "Geçiş menüsünü geçerli çalışma alanıyla sınırla."
#: data/org.gnome.shell.gschema.xml.in:208
#: data/org.gnome.shell.gschema.xml.in:197
msgid ""
"If true, only applications that have windows on the current workspace are "
"shown in the switcher. Otherwise, all applications are included."
@@ -334,11 +318,11 @@ msgstr ""
"uygulamalar geçiş menüsünde gösterilir. Aksi halde, tüm uygulamalar "
"görünecektir."
#: data/org.gnome.shell.gschema.xml.in:225
#: data/org.gnome.shell.gschema.xml.in:214
msgid "The application icon mode."
msgstr "Uygulama simge kipi."
#: data/org.gnome.shell.gschema.xml.in:226
#: data/org.gnome.shell.gschema.xml.in:215
msgid ""
"Configures how the windows are shown in the switcher. Valid possibilities "
"are “thumbnail-only” (shows a thumbnail of the window), “app-icon-"
@@ -349,7 +333,7 @@ msgstr ""
"only” (yalnızca uygulama simgesini gösterir) ya da “both” (her ikisi) "
"değerleridir."
#: data/org.gnome.shell.gschema.xml.in:235
#: data/org.gnome.shell.gschema.xml.in:224
msgid ""
"If true, only windows from the current workspace are shown in the switcher. "
"Otherwise, all windows are included."
@@ -357,59 +341,59 @@ msgstr ""
"Eğer bu seçenek etkinse, yalnızca geçerli çalışma alanındaki pencereler "
"geçiş menüsünde gösterilir. Aksi halde, tüm pencereler görünecektir."
#: data/org.gnome.shell.gschema.xml.in:245
#: data/org.gnome.shell.gschema.xml.in:234
msgid "Locations"
msgstr "Konumlar"
#: data/org.gnome.shell.gschema.xml.in:246
#: data/org.gnome.shell.gschema.xml.in:235
msgid "The locations to show in world clocks"
msgstr "Dünya saatlerinde gösterilecek konumlar"
#: data/org.gnome.shell.gschema.xml.in:256
#: data/org.gnome.shell.gschema.xml.in:245
msgid "Automatic location"
msgstr "Kendiliğinden konumlama"
#: data/org.gnome.shell.gschema.xml.in:257
#: data/org.gnome.shell.gschema.xml.in:246
msgid "Whether to fetch the current location or not"
msgstr "Geçerli konumun getirilip getirilmeyeceğini belirler"
#: data/org.gnome.shell.gschema.xml.in:264
#: data/org.gnome.shell.gschema.xml.in:253
msgid "Location"
msgstr "Konum"
#: data/org.gnome.shell.gschema.xml.in:265
#: data/org.gnome.shell.gschema.xml.in:254
msgid "The location for which to show a forecast"
msgstr "Hava durumunun gösterileceği konum"
#: data/org.gnome.shell.gschema.xml.in:277
#: data/org.gnome.shell.gschema.xml.in:266
msgid "Attach modal dialog to the parent window"
msgstr "Yardımcı iletişim penceresini üst pencereye iliştir"
#: data/org.gnome.shell.gschema.xml.in:278
#: data/org.gnome.shell.gschema.xml.in:287
#: data/org.gnome.shell.gschema.xml.in:295
#: data/org.gnome.shell.gschema.xml.in:303
#: data/org.gnome.shell.gschema.xml.in:311
#: data/org.gnome.shell.gschema.xml.in:267
#: data/org.gnome.shell.gschema.xml.in:276
#: data/org.gnome.shell.gschema.xml.in:284
#: data/org.gnome.shell.gschema.xml.in:292
#: data/org.gnome.shell.gschema.xml.in:300
msgid ""
"This key overrides the key in org.gnome.mutter when running GNOME Shell."
msgstr ""
"Bu anahtar, GNOME Shell çalışırken org.gnome.mutter içindeki anahtarı "
"geçersiz kılar."
#: data/org.gnome.shell.gschema.xml.in:286
#: data/org.gnome.shell.gschema.xml.in:275
msgid "Enable edge tiling when dropping windows on screen edges"
msgstr ""
"Pencereler ekran kenarlarında bırakıldığında kenar döşemeyi etkinleştir"
#: data/org.gnome.shell.gschema.xml.in:294
#: data/org.gnome.shell.gschema.xml.in:283
msgid "Workspaces are managed dynamically"
msgstr "Çalışma alanları dinamik olarak yönetilir"
#: data/org.gnome.shell.gschema.xml.in:302
#: data/org.gnome.shell.gschema.xml.in:291
msgid "Workspaces only on primary monitor"
msgstr "Çalışma alanları yalnızca birincil ekranda"
#: data/org.gnome.shell.gschema.xml.in:310
#: data/org.gnome.shell.gschema.xml.in:299
msgid "Delay focus changes in mouse mode until the pointer stops moving"
msgstr ""
"Fare kipinde odak değişikliklerini işaretçi hareketi durana kadar beklet"
@@ -446,7 +430,7 @@ msgstr "Uzantı ana sayfasını ziyaret et"
#: js/gdm/authPrompt.js:135 js/ui/audioDeviceSelection.js:57
#: js/ui/components/networkAgent.js:110 js/ui/components/polkitAgent.js:139
#: js/ui/endSessionDialog.js:369 js/ui/extensionDownloader.js:183
#: js/ui/endSessionDialog.js:369 js/ui/extensionDownloader.js:181
#: js/ui/shellMountOperation.js:376 js/ui/shellMountOperation.js:386
#: js/ui/status/network.js:916 subprojects/extensions-app/js/main.js:149
msgid "Cancel"
@@ -484,11 +468,11 @@ msgstr "(örneğin, kullanıcı veya %s)"
msgid "Username"
msgstr "Kullanıcı Adı"
#: js/gdm/loginDialog.js:1253
#: js/gdm/loginDialog.js:1254
msgid "Login Window"
msgstr "Oturum Açma Penceresi"
#: js/gdm/util.js:355
#: js/gdm/util.js:345
msgid "Authentication error"
msgstr "Kimlik doğrulama hatası"
@@ -497,7 +481,7 @@ msgstr "Kimlik doğrulama hatası"
#. as a cue to display our own message.
#. Translators: this message is shown below the password entry field
#. to indicate the user can swipe their finger instead
#: js/gdm/util.js:481
#: js/gdm/util.js:471
msgid "(or swipe finger)"
msgstr "(ya da parmak izi okut)"
@@ -726,36 +710,36 @@ msgstr "Erişimi Reddet"
msgid "Grant Access"
msgstr "Erişime İzin Ver"
#: js/ui/appDisplay.js:1297
#: js/ui/appDisplay.js:902
msgid "Unnamed Folder"
msgstr "Adsız Klasör"
#. Translators: This is the heading of a list of open windows
#: js/ui/appDisplay.js:2767 js/ui/panel.js:75
#: js/ui/appDisplay.js:2241 js/ui/panel.js:75
msgid "Open Windows"
msgstr "Açık Pencereler"
#: js/ui/appDisplay.js:2786 js/ui/panel.js:82
#: js/ui/appDisplay.js:2260 js/ui/panel.js:82
msgid "New Window"
msgstr "Yeni Pencere"
#: js/ui/appDisplay.js:2802
#: js/ui/appDisplay.js:2276
msgid "Launch using Integrated Graphics Card"
msgstr "Tümleşik Ekran Kartıyla Başlat"
#: js/ui/appDisplay.js:2803
#: js/ui/appDisplay.js:2277
msgid "Launch using Discrete Graphics Card"
msgstr "Ayrık Ekran Kartıyla Başlat"
#: js/ui/appDisplay.js:2831 js/ui/dash.js:239
#: js/ui/appDisplay.js:2305 js/ui/dash.js:239
msgid "Remove from Favorites"
msgstr "Sık Kullanılanlardan Çıkar"
#: js/ui/appDisplay.js:2837
#: js/ui/appDisplay.js:2311
msgid "Add to Favorites"
msgstr "Sık Kullanılanlara Ekle"
#: js/ui/appDisplay.js:2847 js/ui/panel.js:93
#: js/ui/appDisplay.js:2321 js/ui/panel.js:93
msgid "Show Details"
msgstr "Ayrıntıları Göster"
@@ -785,7 +769,7 @@ msgstr "Kulaklıklar"
msgid "Headset"
msgstr "Kulaklıklı Mikrofon"
#: js/ui/audioDeviceSelection.js:68 js/ui/status/volume.js:272
#: js/ui/audioDeviceSelection.js:68 js/ui/status/volume.js:273
msgid "Microphone"
msgstr "Mikrofon"
@@ -1289,15 +1273,15 @@ msgstr "%s (uzak)"
msgid "%s (console)"
msgstr "%s (uçbirim)"
#: js/ui/extensionDownloader.js:187
#: js/ui/extensionDownloader.js:185
msgid "Install"
msgstr "Kur"
#: js/ui/extensionDownloader.js:193
#: js/ui/extensionDownloader.js:191
msgid "Install Extension"
msgstr "Uzantı Yükle"
#: js/ui/extensionDownloader.js:194
#: js/ui/extensionDownloader.js:192
#, javascript-format
msgid "Download and install “%s” from extensions.gnome.org?"
msgstr "extensions.gnome.org üstünden “%s” uzantısı indirilip kurulsun mu?"
@@ -1330,11 +1314,11 @@ msgstr "Bir uygulama, kısayolları baskılamak istiyor"
msgid "You can restore shortcuts by pressing %s."
msgstr "%s kısayoluyla, kısayolları geri yükleyebilirsiniz."
#: js/ui/inhibitShortcutsDialog.js:100
#: js/ui/inhibitShortcutsDialog.js:98
msgid "Deny"
msgstr "Reddet"
#: js/ui/inhibitShortcutsDialog.js:107
#: js/ui/inhibitShortcutsDialog.js:105
msgid "Allow"
msgstr "İzin ver"
@@ -1400,7 +1384,7 @@ msgstr "Kapat"
msgid "Leave Off"
msgstr "Kapalı Bırak"
#: js/ui/keyboard.js:225
#: js/ui/keyboard.js:207
msgid "Region & Language Settings"
msgstr "Bölge ve Dil Ayarları"
@@ -1473,7 +1457,7 @@ msgstr "Ekran Kilidi devre dışı"
msgid "Screen Locking requires the GNOME display manager."
msgstr "Ekran Kilitleme, GNOME ekran yöneticisi gerektirir."
#: js/ui/messageTray.js:1476
#: js/ui/messageTray.js:1547
msgid "System Information"
msgstr "Sistem Bilgisi"
@@ -1485,13 +1469,13 @@ msgstr "Bilinmeyen sanatçı"
msgid "Unknown title"
msgstr "Bilinmeyen başlık"
#: js/ui/overview.js:74
#: js/ui/overview.js:73
msgid "Undo"
msgstr "Geri Al"
#. Translators: This is the main view to select
#. activities. See also note for "Activities" string.
#: js/ui/overview.js:87
#: js/ui/overview.js:86
msgid "Overview"
msgstr "Genel Görünüm"
@@ -1499,7 +1483,7 @@ msgstr "Genel Görünüm"
#. in the search entry when no search is
#. active; it should not exceed ~30
#. characters.
#: js/ui/overview.js:108
#: js/ui/overview.js:107
msgid "Type to search"
msgstr "Aramak için yaz"
@@ -2147,11 +2131,11 @@ msgstr "Thunderbolt yetkilendirme hatası"
msgid "Could not authorize the Thunderbolt device: %s"
msgstr "Thunderbolt aygıtı yetkilendirilemedi: %s"
#: js/ui/status/volume.js:155
#: js/ui/status/volume.js:154
msgid "Volume changed"
msgstr "Bölüm değişti"
#: js/ui/status/volume.js:217
#: js/ui/status/volume.js:225
msgid "Volume"
msgstr "Bölüm"
@@ -2197,11 +2181,11 @@ msgstr "Kilidi açmak için yukarı kaydır"
msgid "Click or press a key to unlock"
msgstr "Kilidi açmak için tıklayın veya tuşa basın"
#: js/ui/unlockDialog.js:555
#: js/ui/unlockDialog.js:550
msgid "Unlock Window"
msgstr "Kilit Açma Penceresi"
#: js/ui/unlockDialog.js:564
#: js/ui/unlockDialog.js:559
msgid "Log in as another user"
msgstr "Başka kullanıcı olarak oturum aç"
@@ -2335,12 +2319,12 @@ msgstr "Oturum açma ekranı için -“gdm” gibi- özel kip kullan"
msgid "List possible modes"
msgstr "Olası kipleri listele"
#: src/shell-app.c:268
#: src/shell-app.c:286
msgctxt "program"
msgid "Unknown"
msgstr "Bilinmeyen"
#: src/shell-app.c:519
#: src/shell-app.c:537
#, c-format
msgid "Failed to launch “%s”"
msgstr "“%s” başlatılamadı"

View File

@@ -163,6 +163,15 @@ libshell_private_sources = [
'shell-app-cache.c',
]
if enable_recorder
libshell_sources += ['shell-recorder.c']
libshell_public_headers += ['shell-recorder.h']
libshell_private_sources += ['shell-recorder-src.c']
libshell_private_headers += ['shell-recorder-src.h']
endif
libshell_enums = gnome.mkenums_simple('shell-enum-types',
sources: libshell_public_headers
)

425
src/shell-recorder-src.c Normal file
View File

@@ -0,0 +1,425 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#include "config.h"
#define GST_USE_UNSTABLE_API
#include <gst/base/gstpushsrc.h>
#include "shell-recorder-src.h"
struct _ShellRecorderSrc
{
GstPushSrc parent;
GMutex mutex;
GstCaps *caps;
GMutex queue_lock;
GCond queue_cond;
GQueue *queue;
gboolean eos;
gboolean flushing;
guint memory_used;
guint memory_used_update_idle;
};
struct _ShellRecorderSrcClass
{
GstPushSrcClass parent_class;
};
enum {
PROP_0,
PROP_CAPS,
PROP_MEMORY_USED
};
#define shell_recorder_src_parent_class parent_class
G_DEFINE_TYPE(ShellRecorderSrc, shell_recorder_src, GST_TYPE_PUSH_SRC);
static void
shell_recorder_src_init (ShellRecorderSrc *src)
{
gst_base_src_set_format (GST_BASE_SRC (src), GST_FORMAT_TIME);
gst_base_src_set_live (GST_BASE_SRC (src), TRUE);
src->queue = g_queue_new ();
g_mutex_init (&src->mutex);
g_mutex_init (&src->queue_lock);
g_cond_init (&src->queue_cond);
}
static gboolean
shell_recorder_src_memory_used_update_idle (gpointer data)
{
ShellRecorderSrc *src = data;
g_mutex_lock (&src->mutex);
src->memory_used_update_idle = 0;
g_mutex_unlock (&src->mutex);
g_object_notify (G_OBJECT (src), "memory-used");
return FALSE;
}
/* The memory_used property is used to monitor buffer usage,
* so we marshal notification back to the main loop thread.
*/
static void
shell_recorder_src_update_memory_used (ShellRecorderSrc *src,
int delta)
{
g_mutex_lock (&src->mutex);
src->memory_used += delta;
if (src->memory_used_update_idle == 0)
{
src->memory_used_update_idle = g_idle_add (shell_recorder_src_memory_used_update_idle, src);
g_source_set_name_by_id (src->memory_used_update_idle, "[gnome-shell] shell_recorder_src_memory_used_update_idle");
}
g_mutex_unlock (&src->mutex);
}
/* _negotiate() is called when we have to decide on a format. We
* use the configured format */
static gboolean
shell_recorder_src_negotiate (GstBaseSrc * base_src)
{
ShellRecorderSrc *src = SHELL_RECORDER_SRC (base_src);
gboolean result;
result = gst_base_src_set_caps (base_src, src->caps);
return result;
}
static gboolean
shell_recorder_src_unlock (GstBaseSrc * base_src)
{
ShellRecorderSrc *src = SHELL_RECORDER_SRC (base_src);
g_mutex_lock (&src->queue_lock);
src->flushing = TRUE;
g_cond_signal (&src->queue_cond);
g_mutex_unlock (&src->queue_lock);
return TRUE;
}
static gboolean
shell_recorder_src_unlock_stop (GstBaseSrc * base_src)
{
ShellRecorderSrc *src = SHELL_RECORDER_SRC (base_src);
g_mutex_lock (&src->queue_lock);
src->flushing = FALSE;
g_cond_signal (&src->queue_cond);
g_mutex_unlock (&src->queue_lock);
return TRUE;
}
static gboolean
shell_recorder_src_start (GstBaseSrc * base_src)
{
ShellRecorderSrc *src = SHELL_RECORDER_SRC (base_src);
g_mutex_lock (&src->queue_lock);
src->flushing = FALSE;
src->eos = FALSE;
g_cond_signal (&src->queue_cond);
g_mutex_unlock (&src->queue_lock);
return TRUE;
}
static gboolean
shell_recorder_src_stop (GstBaseSrc * base_src)
{
ShellRecorderSrc *src = SHELL_RECORDER_SRC (base_src);
g_mutex_lock (&src->queue_lock);
src->flushing = TRUE;
src->eos = FALSE;
g_queue_foreach (src->queue, (GFunc) gst_buffer_unref, NULL);
g_queue_clear (src->queue);
g_cond_signal (&src->queue_cond);
g_mutex_unlock (&src->queue_lock);
return TRUE;
}
static gboolean
shell_recorder_src_send_event (GstElement * element, GstEvent * event)
{
ShellRecorderSrc *src = SHELL_RECORDER_SRC (element);
gboolean res;
if (GST_EVENT_TYPE (event) == GST_EVENT_EOS)
{
shell_recorder_src_close (src);
gst_event_unref (event);
res = TRUE;
}
else
{
res = GST_CALL_PARENT_WITH_DEFAULT (GST_ELEMENT_CLASS, send_event, (element,
event), FALSE);
}
return res;
}
/* The create() virtual function is responsible for returning the next buffer.
* We just pop buffers off of the queue and block if necessary.
*/
static GstFlowReturn
shell_recorder_src_create (GstPushSrc *push_src,
GstBuffer **buffer_out)
{
ShellRecorderSrc *src = SHELL_RECORDER_SRC (push_src);
GstBuffer *buffer;
g_mutex_lock (&src->queue_lock);
while (TRUE) {
/* int the flushing state we just return FLUSHING */
if (src->flushing) {
g_mutex_unlock (&src->queue_lock);
return GST_FLOW_FLUSHING;
}
buffer = g_queue_pop_head (src->queue);
/* we have a buffer, exit the loop to handle it */
if (buffer != NULL)
break;
/* no buffer, check EOS */
if (src->eos) {
g_mutex_unlock (&src->queue_lock);
return GST_FLOW_EOS;
}
/* wait for something to happen and try again */
g_cond_wait (&src->queue_cond, &src->queue_lock);
}
g_mutex_unlock (&src->queue_lock);
shell_recorder_src_update_memory_used (src,
- (int)(gst_buffer_get_size(buffer) / 1024));
*buffer_out = buffer;
return GST_FLOW_OK;
}
static void
shell_recorder_src_set_caps (ShellRecorderSrc *src,
const GstCaps *caps)
{
if (caps == src->caps)
return;
if (src->caps != NULL)
{
gst_caps_unref (src->caps);
src->caps = NULL;
}
if (caps)
{
/* The capabilities will be negotated with the downstream element
* and set on the pad when the first buffer is pushed.
*/
src->caps = gst_caps_copy (caps);
}
else
src->caps = NULL;
}
static void
shell_recorder_src_finalize (GObject *object)
{
ShellRecorderSrc *src = SHELL_RECORDER_SRC (object);
g_clear_handle_id (&src->memory_used_update_idle, g_source_remove);
shell_recorder_src_set_caps (src, NULL);
g_queue_free_full (src->queue, (GDestroyNotify) gst_buffer_unref);
g_mutex_clear (&src->mutex);
g_mutex_clear (&src->queue_lock);
g_cond_clear (&src->queue_cond);
G_OBJECT_CLASS (shell_recorder_src_parent_class)->finalize (object);
}
static void
shell_recorder_src_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
ShellRecorderSrc *src = SHELL_RECORDER_SRC (object);
switch (prop_id)
{
case PROP_CAPS:
shell_recorder_src_set_caps (src, gst_value_get_caps (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
shell_recorder_src_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
ShellRecorderSrc *src = SHELL_RECORDER_SRC (object);
switch (prop_id)
{
case PROP_CAPS:
gst_value_set_caps (value, src->caps);
break;
case PROP_MEMORY_USED:
g_mutex_lock (&src->mutex);
g_value_set_uint (value, src->memory_used);
g_mutex_unlock (&src->mutex);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
shell_recorder_src_class_init (ShellRecorderSrcClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
GstBaseSrcClass *base_src_class = GST_BASE_SRC_CLASS (klass);
GstPushSrcClass *push_src_class = GST_PUSH_SRC_CLASS (klass);
static GstStaticPadTemplate src_template =
GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS_ANY);
object_class->finalize = shell_recorder_src_finalize;
object_class->set_property = shell_recorder_src_set_property;
object_class->get_property = shell_recorder_src_get_property;
g_object_class_install_property (object_class,
PROP_CAPS,
g_param_spec_boxed ("caps",
"Caps",
"Fixed GstCaps for the source",
GST_TYPE_CAPS,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class,
PROP_MEMORY_USED,
g_param_spec_uint ("memory-used",
"Memory Used",
"Memory currently used by the queue (in kB)",
0, G_MAXUINT, 0,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&src_template));
gst_element_class_set_details_simple (element_class,
"ShellRecorderSrc",
"Generic/Src",
"Feed screen capture data to a pipeline",
"Owen Taylor <otaylor@redhat.com>");
element_class->send_event = shell_recorder_src_send_event;
base_src_class->negotiate = shell_recorder_src_negotiate;
base_src_class->unlock = shell_recorder_src_unlock;
base_src_class->unlock_stop = shell_recorder_src_unlock_stop;
base_src_class->start = shell_recorder_src_start;
base_src_class->stop = shell_recorder_src_stop;
push_src_class->create = shell_recorder_src_create;
}
/**
* shell_recorder_src_add_buffer:
*
* Adds a buffer to the internal queue to be pushed out at the next opportunity.
* There is no flow control, so arbitrary amounts of memory may be used by
* the buffers on the queue. The buffer contents must match the #GstCaps
* set in the :caps property.
*/
void
shell_recorder_src_add_buffer (ShellRecorderSrc *src,
GstBuffer *buffer)
{
g_return_if_fail (SHELL_IS_RECORDER_SRC (src));
g_return_if_fail (src->caps != NULL);
shell_recorder_src_update_memory_used (src,
(int)(gst_buffer_get_size(buffer) / 1024));
g_mutex_lock (&src->queue_lock);
g_queue_push_tail (src->queue, gst_buffer_ref (buffer));
g_cond_signal (&src->queue_cond);
g_mutex_unlock (&src->queue_lock);
}
/**
* shell_recorder_src_close:
*
* Indicates the end of the input stream. Once all previously added buffers have
* been pushed out an end-of-stream message will be sent.
*/
void
shell_recorder_src_close (ShellRecorderSrc *src)
{
/* We can't send a message to the source immediately or buffers that haven't
* been pushed yet will be discarded. Instead mark ourselves EOS, which will
* make us send an event once everything has been pushed.
*/
g_mutex_lock (&src->queue_lock);
src->eos = TRUE;
g_cond_signal (&src->queue_cond);
g_mutex_unlock (&src->queue_lock);
}
static gboolean
plugin_init (GstPlugin *plugin)
{
gst_element_register(plugin, "shellrecordersrc", GST_RANK_NONE,
SHELL_TYPE_RECORDER_SRC);
return TRUE;
}
/**
* shell_recorder_src_register:
*
* Registers a plugin holding our single element to use privately in
* this application. Can safely be called multiple times.
*/
void
shell_recorder_src_register (void)
{
static gboolean registered = FALSE;
if (registered)
return;
gst_plugin_register_static (GST_VERSION_MAJOR, GST_VERSION_MINOR,
"shellrecorder",
"Plugin for ShellRecorder",
plugin_init,
"0.1",
"LGPL",
"gnome-shell", "gnome-shell", "http://live.gnome.org/GnomeShell");
registered = TRUE;
}

40
src/shell-recorder-src.h Normal file
View File

@@ -0,0 +1,40 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#ifndef __SHELL_RECORDER_SRC_H__
#define __SHELL_RECORDER_SRC_H__
#include <gst/gst.h>
G_BEGIN_DECLS
/**
* ShellRecorderSrc:
*
* shellrecordersrc a custom source element is pretty much like a very
* simple version of the stander GStreamer 'appsrc' element, without
* any of the provisions for seeking, generating data on demand,
* etc. In both cases, the application supplies the buffers and the
* element pushes them into the pipeline. The main reason for not using
* appsrc is that it wasn't a supported element until gstreamer 0.10.22,
* and as of 2009-03, many systems still have 0.10.21.
*/
typedef struct _ShellRecorderSrc ShellRecorderSrc;
typedef struct _ShellRecorderSrcClass ShellRecorderSrcClass;
#define SHELL_TYPE_RECORDER_SRC (shell_recorder_src_get_type ())
#define SHELL_RECORDER_SRC(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SHELL_TYPE_RECORDER_SRC, ShellRecorderSrc))
#define SHELL_RECORDER_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_RECORDER_SRC, ShellRecorderSrcClass))
#define SHELL_IS_RECORDER_SRC(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SHELL_TYPE_RECORDER_SRC))
#define SHELL_IS_RECORDER_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_RECORDER_SRC))
#define SHELL_RECORDER_SRC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_RECORDER_SRC, ShellRecorderSrcClass))
GType shell_recorder_src_get_type (void) G_GNUC_CONST;
void shell_recorder_src_register (void);
void shell_recorder_src_add_buffer (ShellRecorderSrc *src,
GstBuffer *buffer);
void shell_recorder_src_close (ShellRecorderSrc *src);
G_END_DECLS
#endif /* __SHELL_RECORDER_SRC_H__ */

1625
src/shell-recorder.c Normal file

File diff suppressed because it is too large Load Diff

45
src/shell-recorder.h Normal file
View File

@@ -0,0 +1,45 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#ifndef __SHELL_RECORDER_H__
#define __SHELL_RECORDER_H__
#include <clutter/clutter.h>
G_BEGIN_DECLS
/**
* SECTION:shell-recorder
* @short_description: Record from a #ClutterStage
*
* The #ShellRecorder object is used to make recordings ("screencasts")
* of a #ClutterStage. Recording is done via #GStreamer. The default is
* to encode as a Theora movie and write it to a file in the current
* directory named after the date, but the encoding and output can
* be configured.
*/
#define SHELL_TYPE_RECORDER (shell_recorder_get_type ())
G_DECLARE_FINAL_TYPE (ShellRecorder, shell_recorder, SHELL, RECORDER, GObject)
ShellRecorder *shell_recorder_new (ClutterStage *stage);
void shell_recorder_set_framerate (ShellRecorder *recorder,
int framerate);
void shell_recorder_set_file_template (ShellRecorder *recorder,
const char *file_template);
void shell_recorder_set_pipeline (ShellRecorder *recorder,
const char *pipeline);
void shell_recorder_set_draw_cursor (ShellRecorder *recorder,
gboolean draw_cursor);
void shell_recorder_set_area (ShellRecorder *recorder,
int x,
int y,
int width,
int height);
gboolean shell_recorder_record (ShellRecorder *recorder,
char **filename_used);
void shell_recorder_close (ShellRecorder *recorder);
void shell_recorder_pause (ShellRecorder *recorder);
gboolean shell_recorder_is_recording (ShellRecorder *recorder);
G_END_DECLS
#endif /* __SHELL_RECORDER_H__ */

View File

@@ -77,6 +77,61 @@ shell_util_set_hidden_from_pick (ClutterActor *actor,
}
}
/**
* shell_util_get_transformed_allocation:
* @actor: a #ClutterActor
* @box: (out): location to store returned box in stage coordinates
*
* This function is similar to a combination of clutter_actor_get_transformed_position(),
* and clutter_actor_get_transformed_size(), but unlike
* clutter_actor_get_transformed_size(), it always returns a transform
* of the current allocation, while clutter_actor_get_transformed_size() returns
* bad values (the transform of the requested size) if a relayout has been
* queued.
*
* This function is more convenient to use than
* clutter_actor_get_abs_allocation_vertices() if no transformation is in effect
* and also works around limitations in the GJS binding of arrays.
*/
void
shell_util_get_transformed_allocation (ClutterActor *actor,
ClutterActorBox *box)
{
/* Code adapted from clutter-actor.c:
* Copyright 2006, 2007, 2008 OpenedHand Ltd
*/
graphene_point3d_t v[4];
gfloat x_min, x_max, y_min, y_max;
guint i;
g_return_if_fail (CLUTTER_IS_ACTOR (actor));
clutter_actor_get_abs_allocation_vertices (actor, v);
x_min = x_max = v[0].x;
y_min = y_max = v[0].y;
for (i = 1; i < G_N_ELEMENTS (v); ++i)
{
if (v[i].x < x_min)
x_min = v[i].x;
if (v[i].x > x_max)
x_max = v[i].x;
if (v[i].y < y_min)
y_min = v[i].y;
if (v[i].y > y_max)
y_max = v[i].y;
}
box->x1 = x_min;
box->y1 = y_min;
box->x2 = x_max;
box->y2 = y_max;
}
/**
* shell_util_get_week_start:
*
@@ -570,57 +625,6 @@ shell_util_get_uid (void)
return getuid ();
}
typedef struct {
GDBusConnection *connection;
gchar *command;
GCancellable *cancellable;
gulong cancel_id;
guint job_watch;
gchar *job;
} SystemdCall;
static void
shell_util_systemd_call_data_free (SystemdCall *data)
{
if (data->job_watch)
{
g_dbus_connection_signal_unsubscribe (data->connection, data->job_watch);
data->job_watch = 0;
}
if (data->cancellable)
{
g_cancellable_disconnect (data->cancellable, data->cancel_id);
g_clear_object (&data->cancellable);
data->cancel_id = 0;
}
g_clear_object (&data->connection);
g_clear_pointer (&data->job, g_free);
g_clear_pointer (&data->command, g_free);
g_free (data);
}
static void
shell_util_systemd_call_cancelled_cb (GCancellable *cancellable,
GTask *task)
{
SystemdCall *data = g_task_get_task_data (task);
/* Task has returned, but data is not yet free'ed, ignore signal. */
if (g_task_get_completed (task))
return;
/* We are still in the DBus call; it will return the error. */
if (data->job == NULL)
return;
g_task_return_error_if_cancelled (task);
g_object_unref (task);
}
static void
on_systemd_call_cb (GObject *source,
GAsyncResult *res,
@@ -628,143 +632,46 @@ on_systemd_call_cb (GObject *source,
{
g_autoptr (GVariant) reply = NULL;
g_autoptr (GError) error = NULL;
GTask *task = G_TASK (user_data);
SystemdCall *data;
const gchar *command = user_data;
reply = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source),
res, &error);
data = g_task_get_task_data (task);
if (error) {
g_warning ("Could not issue '%s' systemd call", data->command);
g_task_return_error (task, g_steal_pointer (&error));
g_object_unref (task);
return;
}
g_assert (data->job == NULL);
g_variant_get (reply, "(o)", &data->job);
/* And we wait for the JobRemoved notification. */
if (error)
g_warning ("Could not issue '%s' systemd call", command);
}
static void
on_systemd_job_removed_cb (GDBusConnection *connection,
const gchar *sender_name,
const gchar *object_path,
const gchar *interface_name,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data)
static gboolean
shell_util_systemd_call (const char *command,
const char *unit,
const char *mode,
GError **error)
{
GTask *task = G_TASK (user_data);
SystemdCall *data;
guint32 id;
const char *path, *unit, *result;
/* Task has returned, but data is not yet free'ed, ignore signal. */
if (g_task_get_completed (task))
return;
data = g_task_get_task_data (task);
/* No job information yet, ignore. */
if (data->job == NULL)
return;
g_variant_get (parameters, "(u&o&s&s)", &id, &path, &unit, &result);
/* Is it the job we are waiting for? */
if (g_strcmp0 (path, data->job) != 0)
return;
/* Task has completed; return the result of the job */
if (g_strcmp0 (result, "done") == 0)
g_task_return_boolean (task, TRUE);
else
g_task_return_new_error (task,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"Systemd job completed with status \"%s\"",
result);
g_object_unref (task);
}
static void
shell_util_systemd_call (const char *command,
const char *unit,
const char *mode,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr (GTask) task = g_task_new (NULL, cancellable, callback, user_data);
#ifdef HAVE_SYSTEMD
g_autoptr (GDBusConnection) connection = NULL;
GError *error = NULL;
SystemdCall *data;
g_autofree char *self_unit = NULL;
int res;
connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
if (connection == NULL) {
g_task_return_error (task, error);
return;
}
/* We look up the systemd unit that our own process is running in here.
* This way we determine whether the session is managed using systemd.
*/
res = sd_pid_get_user_unit (getpid (), &self_unit);
if (res == -ENODATA)
{
g_task_return_new_error (task,
G_IO_ERROR,
G_IO_ERROR_NOT_SUPPORTED,
"Not systemd managed");
return;
g_debug ("Not systemd-managed, not doing '%s' on '%s'", mode, unit);
return FALSE;
}
else if (res < 0)
{
g_task_return_new_error (task,
G_IO_ERROR,
g_io_error_from_errno (-res),
"Error fetching own systemd unit: %s",
g_strerror (-res));
return;
g_set_error (error,
G_IO_ERROR,
g_io_error_from_errno (-res),
"Error trying to start systemd unit '%s': %s",
unit, g_strerror (-res));
return FALSE;
}
data = g_new0 (SystemdCall, 1);
data->command = g_strdup (command);
data->connection = g_object_ref (connection);
data->job_watch = g_dbus_connection_signal_subscribe (connection,
"org.freedesktop.systemd1",
"org.freedesktop.systemd1.Manager",
"JobRemoved",
"/org/freedesktop/systemd1",
NULL,
G_DBUS_SIGNAL_FLAGS_NONE,
on_systemd_job_removed_cb,
task,
NULL);
g_task_set_task_data (task,
data,
(GDestroyNotify) shell_util_systemd_call_data_free);
connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, error);
if (cancellable)
{
data->cancellable = g_object_ref (cancellable);
data->cancel_id = g_cancellable_connect (cancellable,
G_CALLBACK (shell_util_systemd_call_cancelled_cb),
task,
NULL);
}
if (connection == NULL)
return FALSE;
g_dbus_connection_call (connection,
"org.freedesktop.systemd1",
@@ -773,53 +680,31 @@ shell_util_systemd_call (const char *command,
command,
g_variant_new ("(ss)",
unit, mode),
G_VARIANT_TYPE ("(o)"),
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1, cancellable,
-1, NULL,
on_systemd_call_cb,
g_steal_pointer (&task));
#else /* HAVE_SYSTEMD */
g_task_return_new_error (task,
G_IO_ERROR,
G_IO_ERROR_NOT_SUPPORTED,
"systemd not supported by gnome-shell");
#endif /* !HAVE_SYSTEMD */
}
(gpointer) command);
return TRUE;
#endif /* HAVE_SYSTEMD */
void
shell_util_start_systemd_unit (const char *unit,
const char *mode,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
shell_util_systemd_call ("StartUnit", unit, mode,
cancellable, callback, user_data);
return FALSE;
}
gboolean
shell_util_start_systemd_unit_finish (GAsyncResult *res,
GError **error)
shell_util_start_systemd_unit (const char *unit,
const char *mode,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
void
shell_util_stop_systemd_unit (const char *unit,
const char *mode,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
shell_util_systemd_call ("StopUnit", unit, mode,
cancellable, callback, user_data);
return shell_util_systemd_call ("StartUnit", unit, mode, error);
}
gboolean
shell_util_stop_systemd_unit_finish (GAsyncResult *res,
GError **error)
shell_util_stop_systemd_unit (const char *unit,
const char *mode,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
return shell_util_systemd_call ("StopUnit", unit, mode, error);
}
void

View File

@@ -14,6 +14,9 @@ G_BEGIN_DECLS
void shell_util_set_hidden_from_pick (ClutterActor *actor,
gboolean hidden);
void shell_util_get_transformed_allocation (ClutterActor *actor,
ClutterActorBox *box);
int shell_util_get_week_start (void);
const char *shell_util_translate_time_string (const char *str);
@@ -59,21 +62,12 @@ cairo_surface_t * shell_util_composite_capture_images (ClutterCapture *captures
void shell_util_check_cloexec_fds (void);
void shell_util_start_systemd_unit (const char *unit,
const char *mode,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean shell_util_start_systemd_unit_finish (GAsyncResult *res,
GError **error);
void shell_util_stop_systemd_unit (const char *unit,
const char *mode,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean shell_util_stop_systemd_unit_finish (GAsyncResult *res,
GError **error);
gboolean shell_util_start_systemd_unit (const char *unit,
const char *mode,
GError **error);
gboolean shell_util_stop_systemd_unit (const char *unit,
const char *mode,
GError **error);
void shell_util_sd_notify (void);