[gnome] Update gnome extensions

This commit is contained in:
2026-01-13 17:20:07 -05:00
parent 88bae496fd
commit 21fd01aa82
181 changed files with 1145 additions and 7773 deletions

View File

@ -8,9 +8,10 @@
"45",
"46",
"47",
"48"
"48",
"49"
],
"url": "https://github.com/neffo/bing-wallpaper-gnome-extension",
"uuid": "BingWallpaper@ineffable-gmail.com",
"version": 51
"version": 52
}

View File

@ -1,84 +0,0 @@
/*
Copyright (c) 2011-2012, Giovanni Campagna <scampa.giovanni@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the GNOME nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
'use strict';
import Gio from 'gi://Gio';
var debugEnabled = false;
/**
* @param {boolean} d Enable/Disable debug logging
*/
export function setDebugEnabled(d) {
debugEnabled = d;
}
/**
* @param {string} msg the message to log
* @class
*/
export function TalkativeLog(msg) {
if (debugEnabled)
console.log(`[ESC]${msg}`);
}
/**
* Gets the full (semantic) version of this extension.
*
* <p>Note: The actual value is added during build time.
*
* @returns {string} the version
*/
export function getFullVersion() {
return '1.11.1'; // FULL_VERSION
}
/**
* Loads an icon from the extension's subdirectory "images".
*
* @param {Gio.File} extensionDir dir of the extension
* @param {string} name filename of the image
* @returns {Gio.FileIcon} the icon
*/
export function loadIcon(extensionDir, name) {
return new Gio.FileIcon({
file: Gio.File.new_for_path(
getImagePath(extensionDir, name)
),
});
}
/**
* Gets the path to the image from the extension's subdirectory "images".
*
* @param {Gio.File} extensionDir dir of the extension
* @param {string} name filename of the image
* @returns {string} the path
*/
export function getImagePath(extensionDir, name) {
return extensionDir.get_child(`images/${name}`).get_path();
}

View File

@ -1,38 +0,0 @@
'use strict';
/**
* @type {{_display(): Meta_Display, number_of_displays(): int}}
*/
export const DisplayApi = {
/**
* Returns the Wayland display or screen
*
* @returns {Meta.Display}
*/
_display() {
return global.display || global.screen;
},
/**
* @returns {int}
* @public
*/
number_displays() {
return this._display().get_n_monitors();
},
/**
* @param {number} displayIndex the monitor number
* @returns {Meta.Rectangle}
*/
display_geometry_for_index(displayIndex) {
return this._display().get_monitor_geometry(displayIndex);
},
/**
* @param {Meta.Cursor} cursor the new cursor to set
*/
set_cursor(cursor) {
this._display().set_cursor(cursor);
},
};

View File

@ -1,45 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
enable-background="new 0 0 128 128"
height="128px"
id="Layer_1"
version="1.1"
viewBox="0 0 128 128"
width="128px"
xml:space="preserve"
inkscape:version="0.91 r13725"
sodipodi:docname="Icon_Performance.svg"><metadata
id="metadata9"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
id="defs7" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1366"
inkscape:window-height="741"
id="namedview5"
showgrid="false"
inkscape:zoom="1.84375"
inkscape:cx="6.4184127"
inkscape:cy="22.725708"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" /><path
d="m 79.120475,57.28 -7.16142,0 9.95694,-14.93646 c 0.68754,-1.03026 0.75138,-2.35578 0.16716,-3.44862 C 81.497675,37.8025 80.359055,37.12 79.120475,37.12 l -16.8,0 c -1.20582,0 -2.31966,0.64638 -2.9169,1.69302 l -13.44,23.52 c -0.59388,1.04034 -0.59052,2.31672 0.01134,3.35328 0.60018,1.03698 1.70772,1.6737 2.90556,1.6737 l 11.0103,0 -10.5672,18.49302 c -0.83496,1.46034 -0.45948,3.31422 0.8778,4.33776 0.60522,0.46284 1.32384,0.68922 2.03784,0.68922 0.86646,0 1.72578,-0.33138 2.3772,-0.98448 l 26.88,-26.88 c 0.96138,-0.96138 1.24866,-2.40534 0.72828,-3.66198 C 81.704315,58.10026 80.478755,57.28 79.120475,57.28 Z m -26.88,30.24 12.2325,-21.40698 c 0,0 0,-0.31164 0,-0.43302 0,-0.92862 -0.75138,-1.68 -1.68,-1.68 l -13.9125,0 13.44,-23.52 16.8,0 -11.58444,17.3775 c 0,0 -0.38556,0.69552 -0.38556,1.1025 0,0.92862 0.75138,1.68 1.68,1.68 l 10.29,0 -26.88,26.88 z"
id="path3"
style="fill:#030000;fill-opacity:1"
inkscape:connector-curvature="0" /></svg>

Before

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -1,45 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
enable-background="new 0 0 128 128"
height="128px"
id="Layer_1"
version="1.1"
viewBox="0 0 128 128"
width="128px"
xml:space="preserve"
inkscape:version="0.91 r13725"
sodipodi:docname="Icon_Quality.svg"><metadata
id="metadata4236"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
id="defs4234" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1366"
inkscape:window-height="741"
id="namedview4232"
showgrid="false"
inkscape:zoom="1.84375"
inkscape:cx="47.211005"
inkscape:cy="62.915254"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" /><path
d="m 92.8,47.2 c -3.9774,0 -7.2,3.225 -7.2,7.2 0,1.008 0.2112,1.9638 0.5838,2.8362 L 76,61.6 67.4806,46.267 C 69.688,45.0346 71.2,42.7048 71.2,40 c 0,-3.975 -3.2226,-7.2 -7.2,-7.2 -3.9774,0 -7.2,3.225 -7.2,7.2 0,2.7048 1.512,5.0346 3.7194,6.267 L 52,61.6 41.8162,57.2362 C 42.1888,56.3638 42.4,55.408 42.4,54.4 c 0,-3.975 -3.2226,-7.2 -7.2,-7.2 -3.9774,0 -7.2,3.225 -7.2,7.2 0,3.975 3.2226,7.2 7.2,7.2 0.558,0 1.0932,-0.0798 1.617,-0.1968 L 42.4,85.6 l 0,4.8 c 0,2.6532 2.1492,4.8 4.8,4.8 l 33.6,0 c 2.6508,0 4.8,-2.1468 4.8,-4.8 l 0,-4.8 5.5854,-24.1968 C 91.7056,61.525 92.242,61.6 92.8,61.6 c 3.9774,0 7.2,-3.225 7.2,-7.2 0,-3.975 -3.2226,-7.2 -7.2,-7.2 z M 64,37.6 c 1.3242,0 2.4,1.0782 2.4,2.4 0,1.3218 -1.0758,2.4 -2.4,2.4 -1.3242,0 -2.4,-1.0782 -2.4,-2.4 0,-1.3218 1.0758,-2.4 2.4,-2.4 z M 32.8,54.4 c 0,-1.3218 1.0758,-2.4 2.4,-2.4 1.3242,0 2.4,1.0782 2.4,2.4 0,1.3218 -1.0758,2.4 -2.4,2.4 -1.3242,0 -2.4,-1.0782 -2.4,-2.4 z m 48,36 -33.6,0 0,-4.8 33.6,0 0,4.8 z m 0.9798,-9.6 -35.5596,0 -4.2162,-18.2622 8.1048,3.4734 c 0.6138,0.2622 1.2558,0.3888 1.8888,0.3888 1.6968,0 3.3282,-0.9 4.1976,-2.4702 L 64,49.8862 71.8048,63.9298 C 72.6742,65.5 74.3056,66.4 76.0024,66.4 c 0.633,0 1.275,-0.1266 1.8888,-0.3888 L 85.996,62.5378 81.7798,80.8 Z M 92.8,56.8 c -1.3242,0 -2.4,-1.0782 -2.4,-2.4 0,-1.3218 1.0758,-2.4 2.4,-2.4 1.3242,0 2.4,1.0782 2.4,2.4 0,1.3218 -1.0758,2.4 -2.4,2.4 z"
id="path4230"
style="fill:#000000;fill-opacity:1"
inkscape:connector-curvature="0" /></svg>

Before

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -1,99 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64px"
height="64px"
id="svg2993"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="icon_recording.svg">
<defs
id="defs2995" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="8.051626"
inkscape:cx="33.921944"
inkscape:cy="32"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:window-width="1366"
inkscape:window-height="709"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:snap-global="false" />
<metadata
id="metadata2998">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<rect
style="fill:#cccccc;fill-rule:evenodd;stroke:#cccccc;stroke-width:1.49224782000000000;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1"
id="rect3001"
width="33.906902"
height="22.246866"
x="23.587818"
y="30.058556"
rx="6.6889281"
ry="22.246866" />
<g
id="g3832"
style="stroke-width:1.49224782000000000;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;fill:#cccccc;stroke:#cccccc;fill-opacity:1">
<path
sodipodi:end="10.319542"
sodipodi:start="4.0645442"
d="m 16.771536,16.573455 a 5.752316,5.6205831 0 1 1 -0.127895,0.09738 l 3.599241,4.3844 z"
sodipodi:ry="5.6205831"
sodipodi:rx="5.752316"
sodipodi:cy="21.055231"
sodipodi:cx="20.242882"
id="path3773-5"
style="fill:#cccccc;fill-opacity:1;stroke:#cccccc;stroke-width:0.88939973000000005;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
sodipodi:type="arc"
transform="matrix(1.6863671,0,0,1.6693054,18.379885,-17.009556)" />
<path
sodipodi:end="10.319542"
sodipodi:start="4.0645442"
d="m 16.771536,16.573455 a 5.752316,5.6205831 0 1 1 -0.127895,0.09738 l 3.599241,4.3844 z"
sodipodi:ry="5.6205831"
sodipodi:rx="5.752316"
sodipodi:cy="21.055231"
sodipodi:cx="20.242882"
id="path3773-5-9"
style="fill:#cccccc;fill-opacity:1;stroke:#cccccc;stroke-width:0.88939973000000005;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
sodipodi:type="arc"
transform="matrix(1.6863671,0,0,1.6693054,-5.571337,-17.009556)" />
</g>
<path
style="fill:#cccccc;stroke:#cccccc;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;fill-opacity:1"
d="M 19.623366,37.29732 3.4775584,26.243652 3.3533599,57.29328 19.374969,47.357399 z"
id="path3811"
inkscape:connector-curvature="0" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -1,99 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64px"
height="64px"
id="svg2993"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="icon_defaultSel.svg">
<defs
id="defs2995" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="16.669382"
inkscape:cx="33.921944"
inkscape:cy="32"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:window-width="1366"
inkscape:window-height="709"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:snap-global="false" />
<metadata
id="metadata2998">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<rect
style="fill:#f9f9f9;fill-rule:evenodd;stroke:#f9f9f9;stroke-width:1.49224782000000000;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1"
id="rect3001"
width="33.906902"
height="22.246866"
x="23.587818"
y="30.058556"
rx="6.6889281"
ry="22.246866" />
<g
id="g3832"
style="stroke-width:1.49224782000000000;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;fill:#f9f9f9;stroke:#f9f9f9;fill-opacity:1">
<path
sodipodi:end="10.319542"
sodipodi:start="4.0645442"
d="m 16.771536,16.573455 a 5.752316,5.6205831 0 1 1 -0.127895,0.09738 l 3.599241,4.3844 z"
sodipodi:ry="5.6205831"
sodipodi:rx="5.752316"
sodipodi:cy="21.055231"
sodipodi:cx="20.242882"
id="path3773-5"
style="fill:#f9f9f9;fill-opacity:1;stroke:#f9f9f9;stroke-width:0.88939973000000005;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
sodipodi:type="arc"
transform="matrix(1.6863671,0,0,1.6693054,18.379885,-17.009556)" />
<path
sodipodi:end="10.319542"
sodipodi:start="4.0645442"
d="m 16.771536,16.573455 a 5.752316,5.6205831 0 1 1 -0.127895,0.09738 l 3.599241,4.3844 z"
sodipodi:ry="5.6205831"
sodipodi:rx="5.752316"
sodipodi:cy="21.055231"
sodipodi:cx="20.242882"
id="path3773-5-9"
style="fill:#f9f9f9;fill-opacity:1;stroke:#f9f9f9;stroke-width:0.88939973000000005;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
sodipodi:type="arc"
transform="matrix(1.6863671,0,0,1.6693054,-5.571337,-17.009556)" />
</g>
<path
style="fill:#f9f9f9;stroke:#f9f9f9;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;fill-opacity:1"
d="M 19.623366,37.29732 3.4775584,26.243652 3.3533599,57.29328 19.374969,47.357399 z"
id="path3811"
inkscape:connector-curvature="0" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -1,99 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64px"
height="64px"
id="svg2993"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="icon_recordingSel.svg">
<defs
id="defs2995" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="8.051626"
inkscape:cx="33.921944"
inkscape:cy="32"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:window-width="1366"
inkscape:window-height="709"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:snap-global="false" />
<metadata
id="metadata2998">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<rect
style="fill:#ff0000;fill-rule:evenodd;stroke:#ff0000;stroke-width:1.49224782000000000;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3001"
width="33.906902"
height="22.246866"
x="23.587818"
y="30.058556"
rx="6.6889281"
ry="22.246866" />
<g
id="g3832"
style="stroke-width:1.49224782000000000;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;fill:#ff0000;stroke:#ff0000">
<path
sodipodi:end="10.319542"
sodipodi:start="4.0645442"
d="m 16.771536,16.573455 a 5.752316,5.6205831 0 1 1 -0.127895,0.09738 l 3.599241,4.3844 z"
sodipodi:ry="5.6205831"
sodipodi:rx="5.752316"
sodipodi:cy="21.055231"
sodipodi:cx="20.242882"
id="path3773-5"
style="fill:#ff0000;fill-opacity:1;stroke:#ff0000;stroke-width:0.88939973000000005;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
sodipodi:type="arc"
transform="matrix(1.6863671,0,0,1.6693054,18.379885,-17.009556)" />
<path
sodipodi:end="10.319542"
sodipodi:start="4.0645442"
d="m 16.771536,16.573455 a 5.752316,5.6205831 0 1 1 -0.127895,0.09738 l 3.599241,4.3844 z"
sodipodi:ry="5.6205831"
sodipodi:rx="5.752316"
sodipodi:cy="21.055231"
sodipodi:cx="20.242882"
id="path3773-5-9"
style="fill:#ff0000;fill-opacity:1;stroke:#ff0000;stroke-width:0.88939973000000005;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
sodipodi:type="arc"
transform="matrix(1.6863671,0,0,1.6693054,-5.571337,-17.009556)" />
</g>
<path
style="fill:#ff0000;stroke:#ff0000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="M 19.623366,37.29732 3.4775584,26.243652 3.3533599,57.29328 19.374969,47.357399 z"
id="path3811"
inkscape:connector-curvature="0" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -1,99 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64px"
height="64px"
id="svg2993"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="icon_recording.svg">
<defs
id="defs2995" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="8.051626"
inkscape:cx="33.921944"
inkscape:cy="32"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:window-width="1366"
inkscape:window-height="709"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:snap-global="false" />
<metadata
id="metadata2998">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<rect
style="fill:#ff5555;fill-rule:evenodd;stroke:#ff5555;stroke-width:1.49224782000000000;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3001"
width="33.906902"
height="22.246866"
x="23.587818"
y="30.058556"
rx="6.6889281"
ry="22.246866" />
<g
id="g3832"
style="stroke-width:1.49224782000000000;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;fill:#ff5555;stroke:#ff5555">
<path
sodipodi:end="10.319542"
sodipodi:start="4.0645442"
d="m 16.771536,16.573455 a 5.752316,5.6205831 0 1 1 -0.127895,0.09738 l 3.599241,4.3844 z"
sodipodi:ry="5.6205831"
sodipodi:rx="5.752316"
sodipodi:cy="21.055231"
sodipodi:cx="20.242882"
id="path3773-5"
style="fill:#ff5555;fill-opacity:1;stroke:#ff5555;stroke-width:0.88939973000000005;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
sodipodi:type="arc"
transform="matrix(1.6863671,0,0,1.6693054,18.379885,-17.009556)" />
<path
sodipodi:end="10.319542"
sodipodi:start="4.0645442"
d="m 16.771536,16.573455 a 5.752316,5.6205831 0 1 1 -0.127895,0.09738 l 3.599241,4.3844 z"
sodipodi:ry="5.6205831"
sodipodi:rx="5.752316"
sodipodi:cy="21.055231"
sodipodi:cx="20.242882"
id="path3773-5-9"
style="fill:#ff5555;fill-opacity:1;stroke:#ff5555;stroke-width:0.88939973000000005;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
sodipodi:type="arc"
transform="matrix(1.6863671,0,0,1.6693054,-5.571337,-17.009556)" />
</g>
<path
style="fill:#ff5555;stroke:#ff5555;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="M 19.623366,37.29732 3.4775584,26.243652 3.3533599,57.29328 19.374969,47.357399 z"
id="path3811"
inkscape:connector-curvature="0" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -1,15 +0,0 @@
{
"_generated": "Generated by SweetTooth, do not edit",
"description": "This extension simplifies the use of the video recording function integrated in gnome shell, allows quickly to change the various settings of the desktop recording.\n\nSOURCE CODE -> https://github.com/EasyScreenCast/EasyScreenCast\n\nVIDEO -> https://youtu.be/81E9AruraKU\n\n**NOTICE**\nif an error occurs during the update is recommended to reload GNOME Shell (Alt + F2, 'r') and reload the extension's installation page.",
"gettext-domain": "EasyScreenCast@iacopodeenosee.gmail.com",
"name": "EasyScreenCast",
"settings-schema": "org.gnome.shell.extensions.EasyScreenCast",
"shell-version": [
"46",
"47",
"48"
],
"url": "https://github.com/EasyScreenCast/EasyScreenCast",
"uuid": "EasyScreenCast@iacopodeenosee.gmail.com",
"version": 53
}

View File

@ -1,7 +0,0 @@
.options-grid {
margin: 18px;
}
.borderless {
border: none;
}

View File

@ -1,200 +0,0 @@
<schemalist>
<schema id="org.gnome.shell.extensions.EasyScreenCast" path="/org/gnome/shell/extensions/EasyScreenCast/">
<key name="show-notify-alert" type="b">
<default>true</default>
<summary>Enable show notify and alert message</summary>
<description>Enable show notify and alert message</description>
</key>
<key name="show-area-rec" type="b">
<default>false</default>
<summary>Enable show area screencast during recording</summary>
<description>Enable show area screencast during recording</description>
</key>
<key name="input-audio-source" type="i">
<default>0</default>
<summary>select input audio source</summary>
<description>select input audio source</description>
</key>
<key name="active-custom-gsp" type="b">
<default>false</default>
<summary>Active custom GStreamer Pipeline</summary>
<description>Active custom GStreamer Pipeline</description>
</key>
<key name="delay-time" type="i">
<default>0</default>
<summary>Time in seconds fot delay recording</summary>
<description>Time in seconds fot delay recording</description>
</key>
<key name="verbose-debug" type="b">
<default>false</default>
<summary>Enable verbose debug</summary>
<description>Enable verbose debug</description>
</key>
<key name="x-pos" type="i">
<default>0</default>
<summary>X position of screencasting</summary>
<description>X position of screencasting</description>
</key>
<key name="y-pos" type="i">
<default>0</default>
<summary>Y position of screencasting</summary>
<description>Y position of screencasting</description>
</key>
<key name="width-rec" type="i">
<default>600</default>
<summary>width of screencasting</summary>
<description>width of screencasting</description>
</key>
<key name="height-rec" type="i">
<default>400</default>
<summary>height of screencasting</summary>
<description>height of screencasting</description>
</key>
<key name="status-indicators" type="i">
<default>1</default>
<summary>active status indicators</summary>
<description>active status indicators</description>
</key>
<key name="draw-cursor" type="b">
<default>true</default>
<summary>draw the cursor into screencast</summary>
<description>draw the cursor into screencast</description>
</key>
<key name="fps" type="i">
<default>30</default>
<summary>fps of screencasting</summary>
<description>fps of screencasting</description>
</key>
<key name="pipeline" type="s">
<default>'vp9enc min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux'</default>
<summary>gstreamer pipeline of screencasting</summary>
<description>gstreamer pipeline of screencasting</description>
</key>
<key name="area-screen" type="i">
<default>0</default>
<summary>select area of screencast</summary>
<description>select area of screencast</description>
</key>
<key name="file-name" type="s">
<default>'Screencast_%d_%t'</default>
<summary>file name template of screencast</summary>
<description>file name template of screencast</description>
</key>
<key name="file-folder" type="s">
<default>''</default>
<summary>file folder of screencast</summary>
<description>file folder of screencast</description>
</key>
<key name="execute-pre-cmd" type="b">
<default>false</default>
<summary>enable execute pre command</summary>
<description>enable execute pre command</description>
</key>
<key name="pre-cmd" type="s">
<default>''</default>
<summary>command to execute</summary>
<description>command to execute</description>
</key>
<key name="execute-post-cmd" type="b">
<default>false</default>
<summary>enable execute post command</summary>
<description>enable execute post command</description>
</key>
<key name="post-cmd" type="s">
<default>'xdg-open _fpath'</default>
<summary>command to execute</summary>
<description>command to execute</description>
</key>
<key name="shortcut-key" type="as">
<default>
<![CDATA[['<Super>E']]]>
</default>
</key>
<key name="active-shortcut" type="b">
<default>false</default>
<summary>Enable keyboard shortcut</summary>
<description>Enable keyboard shortcut</description>
</key>
<key name="file-container" type="i">
<default>0</default>
<summary>file screencast container</summary>
<description>file screencast container</description>
</key>
<key name="file-resolution-type" type="i">
<default>-1</default>
<summary>file screencast resolution type</summary>
<description>file screencast resolution type [native(-1),preset(0-99),custom(99)]</description>
</key>
<key name="file-resolution-kar" type="b">
<default>true</default>
<summary>file screencast resolution Keep Aspect Ratio</summary>
<description>file screencast resolution Keep Aspect Ratio (add borders)</description>
</key>
<key name="file-resolution-width" type="i">
<default>0</default>
<summary>file screencast resolution width</summary>
<description>file screencast resolution width</description>
</key>
<key name="file-resolution-height" type="i">
<default>0</default>
<summary>file screencast resolution height</summary>
<description>file screencast resolution height</description>
</key>
<key name="quality-index" type="i">
<default>1</default>
<summary>store the quality index option</summary>
<description>store the quality index option</description>
</key>
<key name="device-webcam-index" type="i">
<default>0</default>
<summary>select webcam device</summary>
<description>select webcam device</description>
</key>
<key name="device-webcam" type="s">
<default>""</default>
<summary>selected webcam device path</summary>
<description>selected webcam device path</description>
</key>
<key name="quality-webcam" type="s">
<default>""</default>
<summary>select webcam quality</summary>
<description>select webcam quality</description>
</key>
<key name="width-webcam" type="i">
<default>20</default>
<summary>width webcam</summary>
<description>width webcam</description>
</key>
<key name="height-webcam" type="i">
<default>10</default>
<summary>height webcam</summary>
<description>height webcam</description>
</key>
<key name="type-unit-webcam" type="i">
<default>0</default>
<summary>type of unit of measure webcam</summary>
<description>type of unit of measure</description>
</key>
<key name="margin-x-webcam" type="i">
<default>0</default>
<summary>margin x from border webcam</summary>
<description>margin x webcam</description>
</key>
<key name="margin-y-webcam" type="i">
<default>0</default>
<summary>margin y from border webcam</summary>
<description>margin y from border webcam</description>
</key>
<key name="alpha-channel-webcam" type="d">
<default>0.75</default>
<range min="0.05" max="1.00"/>
<summary>alpha channel webcam</summary>
<description>aplha channel webcam</description>
</key>
<key name="corner-position-webcam" type="i">
<default>0</default>
<summary>the corner position of webcam</summary>
<description>the corner position of webcam</description>
</key>
</schema>
</schemalist>

View File

@ -1,508 +0,0 @@
/*
The MIT License (MIT)
Copyright (c) 2013 otto.allmendinger@gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
'use strict';
import GObject from 'gi://GObject';
import Meta from 'gi://Meta';
import Clutter from 'gi://Clutter';
import St from 'gi://St';
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
import * as Signals from 'resource:///org/gnome/shell/misc/signals.js';
import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js';
import * as Lib from './convenience.js';
import * as Ext from './extension.js';
import * as UtilNotify from './utilnotify.js';
import {DisplayApi} from './display_module.js';
/**
* @type {Capture}
*/
class Capture extends Signals.EventEmitter {
constructor() {
super();
Lib.TalkativeLog('-£-capture selection init');
this._mouseDown = false;
this.monitor = Main.layoutManager.focusMonitor;
this._areaSelection = new St.Widget({
name: 'area-selection',
style_class: 'area-selection',
visible: 'true',
reactive: 'true',
x: -10,
y: -10,
});
Main.uiGroup.add_child(this._areaSelection);
this._areaResolution = new St.Label({
style_class: 'area-resolution',
text: '',
});
this._areaResolution.opacity = 255;
this._areaResolution.set_position(0, 0);
Main.uiGroup.add_child(this._areaResolution);
this._grab = Main.pushModal(this._areaSelection);
if (this._grab) {
this._signalCapturedEvent = this._areaSelection.connect(
'captured-event',
this._onCaptureEvent.bind(this)
);
this._setCaptureCursor();
} else {
Lib.TalkativeLog('-£-Main.pushModal() === false');
}
}
/**
* @private
*/
_setDefaultCursor() {
DisplayApi.set_cursor(Meta.Cursor.DEFAULT);
}
/**
* @private
*/
_setCaptureCursor() {
DisplayApi.set_cursor(Meta.Cursor.CROSSHAIR);
}
/**
* @param {Clutter.Actor} actor the actor that received the event
* @param {Clutter.Event} event a Clutter.Event
* @private
*/
_onCaptureEvent(actor, event) {
if (event.type() === Clutter.EventType.KEY_PRESS) {
if (event.get_key_symbol() === Clutter.KEY_Escape)
this._stop();
}
this.emit('captured-event', event);
}
/**
* Draws a on-screen rectangle showing the area that will be captured by screen cast.
*
* @param {object} rect rectangle
* @param {number} rect.x left position in pixels
* @param {number} rect.y top position in pixels
* @param {number} rect.w width in pixels
* @param {number} rect.h height in pixels
* @param {boolean} showResolution whether to display the size of the selected area
*/
drawSelection({x, y, w, h}, showResolution) {
this._areaSelection.set_position(x, y);
this._areaSelection.set_size(w, h);
if (showResolution && w > 100 && h > 50) {
this._areaResolution.set_text(`${w} X ${h}`);
this._areaResolution.set_position(
x + (w / 2 - this._areaResolution.width / 2),
y + (h / 2 - this._areaResolution.height / 2)
);
} else {
this._areaResolution.set_position(0, 0);
this._areaResolution.set_text('');
}
}
/**
* Clear drawing selection
*/
clearSelection() {
this.drawSelection(
{
x: -10,
y: -10,
w: 0,
h: 0,
},
false
);
}
/**
* @private
*/
_stop() {
Lib.TalkativeLog('-£-capture selection stop');
this._areaSelection.disconnect(this._signalCapturedEvent);
this._setDefaultCursor();
Main.uiGroup.remove_child(this._areaSelection);
Main.popModal(this._grab);
Main.uiGroup.remove_child(this._areaResolution);
this._areaSelection.destroy();
this.emit('stop');
}
_saveRect(x, y, h, w) {
Lib.TalkativeLog(`-£-selection x:${x} y:${y} height:${h} width:${w}`);
Ext.Indicator.saveSelectedRect(x, y, h, w);
Ext.Indicator._doDelayAction();
}
toString() {
return this.GTypeName;
}
}
export class SelectionArea extends Signals.EventEmitter {
constructor() {
super();
Lib.TalkativeLog('-£-area selection init');
this._mouseDown = false;
this._capture = new Capture();
this._capture.connect('captured-event', this._onEvent.bind(this));
this._capture.connect('stop', () => {
this._ctrlNotify.resetAlert();
this.emit('stop');
});
this._ctrlNotify = new UtilNotify.NotifyManager();
this._ctrlNotify.createAlert(
_('Select an area for recording or press [ESC] to abort')
);
}
/**
* @param {Clutter.actor} capture the actor the captured the event
* @param {Clutter.Event} event a Clutter.Event
* @private
*/
_onEvent(capture, event) {
let type = event.type();
let [x, y] = global.get_pointer();
if (type === Clutter.EventType.BUTTON_PRESS) {
[this._startX, this._startY] = [x, y];
this._mouseDown = true;
} else if (this._mouseDown) {
let rect = _getRectangle(this._startX, this._startY, x, y);
if (type === Clutter.EventType.MOTION) {
this._capture.drawSelection(rect, true);
} else if (type === Clutter.EventType.BUTTON_RELEASE) {
this._capture._stop();
Lib.TalkativeLog(`-£-area x: ${rect.x} y: ${rect.y} height: ${rect.h} width: ${rect.w}`);
this._capture._saveRect(rect.x, rect.y, rect.h, rect.w);
}
}
}
toString() {
return this.GTypeName;
}
}
export class SelectionWindow extends Signals.EventEmitter {
constructor() {
super();
Lib.TalkativeLog('-£-window selection init');
this._windows = global.get_window_actors();
this._capture = new Capture();
this._capture.connect('captured-event', this._onEvent.bind(this));
this._capture.connect('stop', () => {
this._ctrlNotify.resetAlert();
this.emit('stop');
});
this._ctrlNotify = new UtilNotify.NotifyManager();
this._ctrlNotify.createAlert(
_('Select a window for recording or press [ESC] to abort')
);
}
/**
* @param {Clutter.Actor} capture the actor the captured the event
* @param {Clutter.Event} event a Clutter.Event
* @private
*/
_onEvent(capture, event) {
let type = event.type();
let [x, y] = global.get_pointer();
this._selectedWindow = _selectWindow(this._windows, x, y);
if (this._selectedWindow)
this._highlightWindow(this._selectedWindow);
else
this._clearHighlight();
if (type === Clutter.EventType.BUTTON_PRESS) {
if (this._selectedWindow) {
this._capture._stop();
let maxHeight = global.screen_height;
let maxWidth = global.screen_width;
Lib.TalkativeLog(`-£-global screen area H: ${maxHeight} W: ${maxWidth}`);
let [w, h] = this._selectedWindow.get_size();
let [wx, wy] = this._selectedWindow.get_position();
Lib.TalkativeLog(`-£-windows pre wx: ${wx} wy: ${wy} height: ${h} width: ${w}`);
if (wx < 0)
wx = 0;
if (wy < 0)
wy = 0;
if (wx + w > maxWidth)
w = maxWidth - wx;
if (wy + h > maxHeight)
h = maxHeight - wy;
Lib.TalkativeLog(`-£-windows post wx: ${wx} wy: ${wy} height: ${h} width: ${w}`);
this._capture._saveRect(wx, wy, h, w);
}
}
}
/**
* @param {Clutter.Actor} win the window to highlight
* @private
*/
_highlightWindow(win) {
let rect = _getWindowRectangle(win);
Lib.TalkativeLog(`-£-window highlight on, pos/meas: x:${rect.x} y:${rect.y} w:${rect.w} h:${rect.h}`);
this._capture.drawSelection(rect, false);
}
/**
* @private
*/
_clearHighlight() {
Lib.TalkativeLog('-£-window highlight off');
this._capture.clearSelection();
}
toString() {
return this.GTypeName;
}
}
export class SelectionDesktop extends Signals.EventEmitter {
constructor() {
super();
Lib.TalkativeLog('-£-desktop selection init');
const displayCount = DisplayApi.number_displays();
Lib.TalkativeLog(`-£-Number of monitor ${displayCount}`);
for (let i = 0; i < displayCount; i++) {
let tmpM = DisplayApi.display_geometry_for_index(i);
Lib.TalkativeLog(`-£-monitor ${i} geometry x=${tmpM.x} y=${tmpM.y} w=${tmpM.width} h=${tmpM.height}`);
}
this._capture = new Capture();
this._capture.connect('captured-event', this._onEvent.bind(this));
this._capture.connect('stop', () => {
this._ctrlNotify.resetAlert();
this.emit('stop');
});
this._ctrlNotify = new UtilNotify.NotifyManager();
this._ctrlNotify.createAlert(
_('Select a desktop for recording or press [ESC] to abort')
);
}
/**
* @param {Clutter.Actor} capture the actor that captured the event
* @param {Clutter.Event} event a Clutter.Event
* @private
*/
_onEvent(capture, event) {
let type = event.type();
if (type === Clutter.EventType.BUTTON_PRESS) {
this._capture._stop();
let tmpM = Main.layoutManager.currentMonitor;
let x = tmpM.x;
let y = tmpM.y;
let height = tmpM.height;
let width = tmpM.width;
Lib.TalkativeLog(`-£-desktop x: ${x} y: ${y} height: ${height} width: ${width}`);
this._capture._saveRect(x, y, height, width);
}
}
toString() {
return this.GTypeName;
}
}
export class AreaRecording extends GObject.Object {
constructor() {
super();
Lib.TalkativeLog('-£-area recording init');
this._areaRecording = new St.Widget({
name: 'area-recording',
style_class: 'area-recording',
visible: 'true',
reactive: 'false',
x: -10,
y: -10,
});
let [recX, recY, recW, recH] = Ext.Indicator.getSelectedRect();
let tmpH = Main.layoutManager.currentMonitor.height;
let tmpW = Main.layoutManager.currentMonitor.width;
Main.uiGroup.add_child(this._areaRecording);
Main.overview.connect('showing', () => {
Lib.TalkativeLog('-£-overview opening');
Main.uiGroup.remove_child(this._areaRecording);
});
Main.overview.connect('hidden', () => {
Lib.TalkativeLog('-£-overview closed');
Main.uiGroup.add_child(this._areaRecording);
});
if (recX + recW <= tmpW - 5 && recY + recH <= tmpH - 5)
this.drawArea(recX - 2, recY - 2, recW + 4, recH + 4);
}
/**
* @param {number} x left position
* @param {number} y top position
* @param {number} w width
* @param {number} h height
*/
drawArea(x, y, w, h) {
Lib.TalkativeLog('-£-draw area recording');
this._visible = true;
this._areaRecording.set_position(x, y);
this._areaRecording.set_size(w, h);
}
/**
* Clears the drawing area
*/
clearArea() {
Lib.TalkativeLog('-£-hide area recording');
this._visible = false;
this.drawArea(-10, -10, 0, 0);
}
/**
* @returns {boolean}
*/
isVisible() {
return this._visible;
}
toString() {
return this.GTypeName;
}
}
/**
* @param {number} x1 left position
* @param {number} y1 top position
* @param {number} x2 right position
* @param {number} y2 bottom position
* @returns {{x: number, y: number, w: number, h: number}}
*/
function _getRectangle(x1, y1, x2, y2) {
return {
x: Math.min(x1, x2),
y: Math.min(y1, y2),
w: Math.abs(x1 - x2),
h: Math.abs(y1 - y2),
};
}
/**
* @param {Clutter.Actor} win a Clutter.Actor
* @returns {{x: number, y: number, w: number, h: number}}
*/
function _getWindowRectangle(win) {
let [tw, th] = win.get_size();
let [tx, ty] = win.get_position();
return {
x: tx,
y: ty,
w: tw,
h: th,
};
}
/**
* @param {Array(Clutter.Actor)} windows all windows on the display
* @param {number} x left position
* @param {number} y top position
* @returns {Clutter.Actor}
*/
function _selectWindow(windows, x, y) {
let filtered = windows.filter(win => {
if (
win !== undefined &&
win.visible &&
typeof win.get_meta_window === 'function'
) {
Lib.TalkativeLog(`-£-selectWin x:${x} y:${y}`);
let [w, h] = win.get_size();
let [wx, wy] = win.get_position();
Lib.TalkativeLog(`-£-selectWin w:${w} h:${h} wx:${wx} wy:${wy}`);
return wx <= x && wy <= y && wx + w >= x && wy + h >= y;
} else {
return false;
}
});
filtered.sort((a, b) => {
return (
a.get_meta_window().get_layer() <= b.get_meta_window().get_layer()
);
});
return filtered[0];
}

View File

@ -1,146 +0,0 @@
/*
Copyright (C) 2013 Borsato Ivano
The JavaScript code in this page is free software: you can
redistribute it and/or modify it under the terms of the GNU
General Public License (GNU GPL) as published by the Free Software
Foundation, either version 3 of the License, or (at your option)
any later version. The code is distributed WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU GPL for more details.
*/
'use strict';
import GObject from 'gi://GObject';
// setting keys
export const INPUT_AUDIO_SOURCE_SETTING_KEY = 'input-audio-source';
export const ACTIVE_POST_CMD_SETTING_KEY = 'execute-post-cmd';
export const POST_CMD_SETTING_KEY = 'post-cmd';
export const ACTIVE_PRE_CMD_SETTING_KEY = 'execute-pre-cmd';
export const PRE_CMD_SETTING_KEY = 'pre-cmd';
export const ACTIVE_CUSTOM_GSP_SETTING_KEY = 'active-custom-gsp';
export const ACTIVE_SHORTCUT_SETTING_KEY = 'active-shortcut';
export const SHORTCUT_KEY_SETTING_KEY = 'shortcut-key';
export const TIME_DELAY_SETTING_KEY = 'delay-time';
export const SHOW_NOTIFY_ALERT_SETTING_KEY = 'show-notify-alert';
export const SHOW_AREA_REC_SETTING_KEY = 'show-area-rec';
export const VERBOSE_DEBUG_SETTING_KEY = 'verbose-debug';
export const PIPELINE_REC_SETTING_KEY = 'pipeline';
export const FPS_SETTING_KEY = 'fps';
export const STATUS_INDICATORS_SETTING_KEY = 'status-indicators';
export const X_POS_SETTING_KEY = 'x-pos';
export const Y_POS_SETTING_KEY = 'y-pos';
export const WIDTH_SETTING_KEY = 'width-rec';
export const HEIGHT_SETTING_KEY = 'height-rec';
export const DRAW_CURSOR_SETTING_KEY = 'draw-cursor';
export const AREA_SCREEN_SETTING_KEY = 'area-screen';
export const FILE_NAME_SETTING_KEY = 'file-name';
export const FILE_FOLDER_SETTING_KEY = 'file-folder';
export const FILE_CONTAINER_SETTING_KEY = 'file-container';
export const FILE_RESOLUTION_TYPE_SETTING_KEY = 'file-resolution-type';
export const FILE_RESOLUTION_KAR_SETTING_KEY = 'file-resolution-kar';
export const FILE_RESOLUTION_WIDTH_SETTING_KEY = 'file-resolution-width';
export const FILE_RESOLUTION_HEIGHT_SETTING_KEY = 'file-resolution-height';
export const QUALITY_SETTING_KEY = 'quality-index';
export const DEVICE_INDEX_WEBCAM_SETTING_KEY = 'device-webcam-index';
export const DEVICE_WEBCAM_SETTING_KEY = 'device-webcam';
export const QUALITY_WEBCAM_SETTING_KEY = 'quality-webcam';
export const WIDTH_WEBCAM_SETTING_KEY = 'width-webcam';
export const HEIGHT_WEBCAM_SETTING_KEY = 'height-webcam';
export const TYPE_UNIT_WEBCAM_SETTING_KEY = 'type-unit-webcam';
export const MARGIN_X_WEBCAM_SETTING_KEY = 'margin-x-webcam';
export const MARGIN_Y_WEBCAM_SETTING_KEY = 'margin-y-webcam';
export const ALPHA_CHANNEL_WEBCAM_SETTING_KEY = 'alpha-channel-webcam';
export const CORNER_POSITION_WEBCAM_SETTING_KEY = 'corner-position-webcam';
// shortcut tree view columns
export const SHORTCUT_COLUMN_KEY = 0;
export const SHORTCUT_COLUMN_MODS = 1;
export const Settings = GObject.registerClass(class EasyScreenCastSettings extends GObject.Object {
constructor(settings) {
super();
this._settings = settings;
}
/**
* getter option
*
* @param {string} type value type of the option. one of 'b', 'i', 's', 'd', 'as'
* @param {string} key option key
* @returns {string}
*/
getOption(type, key) {
switch (type) {
case 'b':
return this._settings.get_boolean(key);
case 'i':
return this._settings.get_int(key);
case 's':
return this._settings.get_string(key);
case 'd':
return this._settings.get_double(key);
case 'as':
return this._settings.get_strv(key);
}
return '';
}
/**
* setter option
*
* @param {string} key option key
* @param {boolean|number|string|double|object} option option value
* @returns {string} empty string if successful, 'ERROR' otherwise
*/
setOption(key, option) {
switch (typeof option) {
case 'boolean':
this._settings.set_boolean(key, option);
break;
case 'number':
this._settings.set_int(key, option);
break;
case 'string':
this._settings.set_string(key, option);
break;
case 'double':
this._settings.set_double(key, option);
break;
case 'object':
this._settings.set_strv(key, option);
break;
default:
return 'ERROR';
}
return '';
}
destroy() {
if (this._settings)
this._settings = null;
}
});
/**
* get a standard gsp pipeline
*
* @param {boolean} audio with or without audio
* @returns {string}
*/
export function getGSPstd(audio) {
// TODO update gsp
if (audio)
return 'queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! videorate ! vp8enc min_quantizer=0 max_quantizer=5 cpu-used=3 deadline=1000000 threads=%T ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! mux. pulsesrc ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! audioconvert ! vorbisenc ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! mux. webmmux name=mux ';
else
return 'vp9enc min_quantizer=0 max_quantizer=5 cpu-used=3 deadline=1000000 threads=%T ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! webmmux';
}

View File

@ -1,36 +0,0 @@
.RecordAction-label {
font-size: 15px;
font-weight: bold;
}
.area-selection {
background-color: rgba(255, 0, 0, 0.5);
border: 2px solid rgba(255, 0, 0, 1);
}
.area-recording {
border-style: dashed;
border: 2px rgba(255, 0, 0, 1);
}
.alert-msg {
font-size: 40px;
font-weight: bold;
color: #ffffff;
background-color: rgba(10, 10, 10, 0.7);
border-radius: 5px;
padding: .5em;
}
.area-resolution {
font-size: 20px;
font-weight: bold;
color: #ffffff;
background-color: rgba(0, 0, 0, 0.0);
}
.time-label {
font-size: 14px;
padding-right: 5px;
color: #ff0000;
}

View File

@ -1,257 +0,0 @@
/*
Copyright (C) 2013 Borsato Ivano
The JavaScript code in this page is free software: you can
redistribute it and/or modify it under the terms of the GNU
General Public License (GNU GPL) as published by the Free Software
Foundation, either version 3 of the License, or (at your option)
any later version. The code is distributed WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU GPL for more details.
*/
'use strict';
import GObject from 'gi://GObject';
import GLib from 'gi://GLib';
import * as Lib from './convenience.js';
/*
DELAY TIMER
*/
let DelaySec = 0;
let timerDelayId = null;
let CallbackFuncDelay = null;
let ElapsedSec;
/**
* @type {TimerDelay}
*/
export const TimerDelay = GObject.registerClass({
GTypeName: 'EasyScreenCast_TimerDelay',
}, class TimerDelay extends GObject.Object {
/**
* Create a new timer
*
* @param {number} delay delay in seconds
* @param {Function} callback callback function that is called after delay seconds (without arguments)
* @param {*} scope scope for the callback
*/
constructor(delay, callback, scope) {
super();
if (isNaN(delay)) {
Lib.TalkativeLog(`-%-delay is NOT a number :${delay}`);
} else {
Lib.TalkativeLog(`-%-init TimerDelay called - sec : ${delay}`);
DelaySec = delay;
ElapsedSec = 1;
this.setCallback(callback);
this.Scope = scope;
}
}
/**
* Set the callback-function
*
* @param {Function} callback callback function that is called after delay seconds (without arguments)
*/
setCallback(callback) {
Lib.TalkativeLog('-%-setcallback TimerDelay called');
if (
callback === undefined ||
callback === null ||
typeof callback !== 'function'
)
throw TypeError("'callback' needs to be a function.");
CallbackFuncDelay = callback;
}
/**
* Set the delay time
*
* @param {number} delay delay in seconds
*/
setDelay(delay) {
Lib.TalkativeLog(`-%-setdelay TimerDelay called: ${delay}`);
DelaySec = delay;
}
/**
* Start or restart a new timer
*/
begin() {
Lib.TalkativeLog('-%-start TimerDelay called');
this.stop();
timerDelayId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 1000, () =>
this._callbackInternal()
);
}
/**
* Stop the current timer
*/
stop() {
Lib.TalkativeLog('-%-stop TimerDelay called');
if (timerDelayId !== null) {
if (GLib.source_remove(timerDelayId)) {
timerDelayId = null;
ElapsedSec = 1;
}
}
}
/**
* A convenient way to restart the timer.
*/
restart() {
this.stop();
this.begin();
}
/**
* The internal callback-function.
*
* @private
* @returns {boolean}
*/
_callbackInternal() {
Lib.TalkativeLog(`-%-internalFunction TimerDelay called | Sec = ${ElapsedSec} Sec delay = ${DelaySec}`);
if (ElapsedSec >= DelaySec) {
CallbackFuncDelay.apply(this.Scope, []);
ElapsedSec = 1;
return false;
} else {
ElapsedSec++;
return true;
}
}
});
/*
COUNTING TIMER
*/
let timerCountingId = null;
let CallbackFuncCounting = null;
let isRunning = false;
let secpassed = 0;
/**
* @type {TimerCounting}
*/
export const TimerCounting = GObject.registerClass({
GTypeName: 'EasyScreenCast_TimerCounting',
}, class TimerCounting extends GObject.Object {
/**
* Callback for the counting timer.
*
* @callback TimerCounting~callback
* @param {number} count seconds passed
* @param {boolean} alertEnd whether the timer is ending
*/
/**
* Create a new timer
*
* @param {TimerCounting~callback} callback callback function that is called every second
* @param {EasyScreenCast_Indicator} scope scope for the callback function. This is also used to updateTimeLabel.
*/
constructor(callback, scope) {
super();
Lib.TalkativeLog('-%-init TimerCounting called');
this.setCallback(callback);
secpassed = 0;
this.Scope = scope;
}
/**
* Set the callback-function
*
* @param {TimerCounting~callback} callback callback function that is called every second
*/
setCallback(callback) {
Lib.TalkativeLog('-%-setcallback TimerCounting called');
if (
callback === undefined ||
callback === null ||
typeof callback !== 'function'
)
throw TypeError("'callback' needs to be a function.");
CallbackFuncCounting = callback;
}
/**
* Start or restart a new timer
*/
begin() {
Lib.TalkativeLog('-%-start TimerCounting called');
if (isRunning)
this.stop();
isRunning = true;
timerCountingId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 1000, () =>
this._callbackInternal()
);
}
/**
* Stop the current timer
*/
stop() {
Lib.TalkativeLog('-%-stop TimerCounting called');
isRunning = false;
if (timerCountingId !== null && GLib.source_remove(timerCountingId))
timerCountingId = null;
}
/**
* A convenient way to stop timer
*/
halt() {
isRunning = false;
}
/**
* The internal callback-function. Calls a function that handles
* the desktop notifications and one that sets the time label next
* to the icon.
*
* @private
* @returns {boolean}
*/
_callbackInternal() {
if (isRunning === false) {
Lib.TalkativeLog('-%-finish TimerCounting ');
CallbackFuncCounting.apply(this.Scope, [secpassed, true]);
secpassed = 0;
this.stop();
this.Scope.updateTimeLabel('');
return false;
} else {
secpassed++;
Lib.TalkativeLog(`-%-continued TimerCounting | sec: ${secpassed}`);
CallbackFuncCounting.apply(this.Scope, [secpassed, false]);
this.Scope.updateTimeLabel(secpassed);
return true;
}
}
});

View File

@ -1,319 +0,0 @@
/*
Copyright (C) 2015 Borsato Ivano
The JavaScript code in this page is free software: you can
redistribute it and/or modify it under the terms of the GNU
General Public License (GNU GPL) as published by the Free Software
Foundation, either version 3 of the License, or (at your option)
any later version. The code is distributed WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU GPL for more details.
*/
'use strict';
import GObject from 'gi://GObject';
import GIRepository from 'gi://GIRepository';
GIRepository.Repository.prepend_search_path('/usr/lib/gnome-shell');
GIRepository.Repository.prepend_library_path('/usr/lib/gnome-shell');
GIRepository.Repository.prepend_search_path('/usr/lib64/gnome-shell');
GIRepository.Repository.prepend_library_path('/usr/lib64/gnome-shell');
import Gvc from 'gi://Gvc';
import * as Lib from './convenience.js';
import * as Settings from './settings.js';
import * as Ext from './extension.js';
/**
* @type {MixerAudio}
*/
export const MixerAudio = GObject.registerClass({
GTypeName: 'EasyScreenCast_MixerAudio',
}, class MixerAudio extends GObject.Object {
constructor() {
super();
Lib.TalkativeLog('-#-mixer constructor');
this._mixerControl = this._createMixerControl();
this._isConnected = true;
this._mixerControl.connect('state-changed', () =>
this._onChangeStatePAC()
);
// more log for debug
if (Lib.debugEnabled) {
this._mixerControl.connect('stream_added', (control, id) => {
this._onStreamAdd(control, id);
});
this._mixerControl.connect('stream_removed', (control, id) => {
this._onStreamRemove(control, id);
});
}
}
/**
* @returns {Gvc.MixerControl}
* @private
*/
_createMixerControl() {
Lib.TalkativeLog('-#-mixer create');
// https://gitlab.gnome.org/GNOME/libgnome-volume-control
var _mixerTmp = new Gvc.MixerControl({
name: 'ESC Mixer Control',
});
_mixerTmp.open();
return _mixerTmp;
}
_onChangeStatePAC() {
Lib.TalkativeLog('-#-mixer state changed');
switch (this._mixerControl.get_state()) {
case Gvc.MixerControlState.CLOSED:
Lib.TalkativeLog('-#-Mixer close');
this._isConnected = false;
break;
case Gvc.MixerControlState.CONNECTING:
Lib.TalkativeLog('-#-Mixer connecting');
this._isConnected = false;
break;
case Gvc.MixerControlState.FAILED:
Lib.TalkativeLog('-#-Mixer failed');
this._isConnected = false;
break;
case Gvc.MixerControlState.READY:
Lib.TalkativeLog('-#-Mixer ready');
this._isConnected = true;
// more log for debug
if (Lib.debugEnabled)
this._getInfoPA();
break;
default:
Lib.TalkativeLog('-#-Mixer UNK');
this._isConnected = false;
break;
}
}
/**
* Gets a list of input audio sources.
*
* @returns {Array}
*/
getListInputAudio() {
Lib.TalkativeLog('-#-get list input audio');
if (this._isConnected) {
var arrayTmp = [];
var tmpSinks = this._mixerControl.get_sinks();
Lib.TalkativeLog(`-#-Mixer sink -> ${tmpSinks.length}`);
for (let x in tmpSinks) {
Lib.TalkativeLog(`-#-sink index: ${tmpSinks[x].index}`);
Lib.TalkativeLog(`-#-sink name: ${tmpSinks[x].name}`);
Lib.TalkativeLog(
`-#-sink description: ${tmpSinks[x].description}`
);
Lib.TalkativeLog(`-#-sink port: ${tmpSinks[x].port}`);
arrayTmp.push({
desc: tmpSinks[x].description,
name: tmpSinks[x].name,
port: tmpSinks[x].port,
sortable: true,
resizeable: true,
});
}
var tmpSources = this._mixerControl.get_sources();
Lib.TalkativeLog(`-#-Mixer sources -> ${tmpSources.length}`);
for (let x in tmpSources) {
Lib.TalkativeLog(`-#-source index: ${tmpSources[x].index}`);
Lib.TalkativeLog(`-#-source name: ${tmpSources[x].name}`);
Lib.TalkativeLog(
`-#-source description: ${tmpSources[x].description}`
);
Lib.TalkativeLog(`-#-source port: ${tmpSources[x].port}`);
arrayTmp.push({
desc: tmpSources[x].description,
name: tmpSources[x].name,
port: tmpSources[x].port,
sortable: true,
resizeable: true,
});
}
Lib.TalkativeLog(`-#-MIXER SOURCE TOT -> ${arrayTmp.length}`);
return arrayTmp;
} else {
Lib.TalkativeLog('-#-Error lib pulse NOT present or NOT respond');
}
return [];
}
/**
* @returns {string}
*/
getAudioSource() {
Lib.TalkativeLog('-#-get source audio choosen');
var arrtmp = this.getListInputAudio();
var index = Ext.Indicator.getSettings().getOption('i', Settings.INPUT_AUDIO_SOURCE_SETTING_KEY) - 2;
if (index >= 0 && index < arrtmp.length) {
return arrtmp[index].name;
} else {
Lib.TalkativeLog('-#-ERROR, audio source missing');
Ext.Indicator.getSettings().setOption(Settings.INPUT_AUDIO_SOURCE_SETTING_KEY, 0);
return '';
}
}
/**
* @param {Gvc.MixerControl} control the mixer control to which the stream was added
* @param {number} id the stream id
* @private
*/
_onStreamAdd(control, id) {
Lib.TalkativeLog(`-#-mixer stream add - ID: ${id}`);
var streamTmp = control.lookup_stream_id(id);
if (
streamTmp.name === 'GNOME Shell' &&
streamTmp.description === 'Record Stream'
) {
Lib.TalkativeLog('-#-stream gnome recorder captured');
Lib.TalkativeLog(`-#-stream index: ${streamTmp.index}`);
Lib.TalkativeLog(`-#-stream card index: ${streamTmp.card_index}`);
Lib.TalkativeLog(`-#-application_ID: ${streamTmp.application_id}`);
Lib.TalkativeLog(`-#-stream name: ${streamTmp.name}`);
Lib.TalkativeLog(`-#-stream icon: ${streamTmp.icon_name}`);
Lib.TalkativeLog(`-#-stream description: ${streamTmp.description}`);
Lib.TalkativeLog(`-#-stream port: ${streamTmp.port}`);
}
}
/**
* @param {Gvc.MixerControl} control the mixer control from where the stream was removed
* @param {number} id stream id
* @private
*/
_onStreamRemove(control, id) {
Lib.TalkativeLog(`-#-mixer stream remove - ID: ${id}`);
// note: the stream has been already removed, so
// control.lookup_stream_id(id) won't return anything
}
/**
* @private
*/
_getInfoPA() {
var tmp = this._mixerControl.get_cards();
Lib.TalkativeLog(`#-# mixer cards -> ${tmp.length}`);
for (let x in tmp) {
Lib.TalkativeLog(`-#-card index: ${tmp[x].index}`);
Lib.TalkativeLog(`-#-card name: ${tmp[x].name}`);
Lib.TalkativeLog(`-#-card icon: ${tmp[x].icon_name}`);
Lib.TalkativeLog(`-#-card profile: ${tmp[x].profile}`);
Lib.TalkativeLog(`-#-card human profile: ${tmp[x].human_profile}`);
}
tmp = this._mixerControl.get_sources();
Lib.TalkativeLog(`#-# mixer sources -> ${tmp.length}`);
for (let x in tmp) {
Lib.TalkativeLog(`-#-source index: ${tmp[x].index}`);
Lib.TalkativeLog(`-#-application_ID: ${tmp[x].application_id}`);
Lib.TalkativeLog(`-#-source name: ${tmp[x].name}`);
Lib.TalkativeLog(`-#-source icon: ${tmp[x].icon_name}`);
Lib.TalkativeLog(`-#-source description: ${tmp[x].description}`);
Lib.TalkativeLog(`-#-source port: ${tmp[x].port}`);
}
tmp = this._mixerControl.get_source_outputs();
Lib.TalkativeLog(`#-# mixer source output -> ${tmp.length}`);
for (let x in tmp) {
Lib.TalkativeLog(`-#-sourceouput index: ${tmp[x].index}`);
Lib.TalkativeLog(`-#-application_ID: ${tmp[x].application_id}`);
Lib.TalkativeLog(`-#-sourceouput name: ${tmp[x].name}`);
Lib.TalkativeLog(`-#-sourceoutput icon: ${tmp[x].icon_name}`);
Lib.TalkativeLog(
`-#-sourceoutput description: ${tmp[x].description}`
);
Lib.TalkativeLog(`-#-sourceoutput port: ${tmp[x].port}`);
}
tmp = this._mixerControl.get_sinks();
Lib.TalkativeLog(`#-# mixer sink -> ${tmp.length}`);
for (let x in tmp) {
Lib.TalkativeLog(`-#-sink index: ${tmp[x].index}`);
Lib.TalkativeLog(`-#-application_ID: ${tmp[x].application_id}`);
Lib.TalkativeLog(`-#-sink name: ${tmp[x].name}`);
Lib.TalkativeLog(`-#-sink icon: ${tmp[x].icon_name}`);
Lib.TalkativeLog(`-#-sink description: ${tmp[x].description}`);
Lib.TalkativeLog(`-#-sink port: ${tmp[x].port}`);
}
tmp = this._mixerControl.get_sink_inputs();
Lib.TalkativeLog(`#-# mixer sink input -> ${tmp.length}`);
for (let x in tmp) {
Lib.TalkativeLog(`-#-sink input index: ${tmp[x].index}`);
Lib.TalkativeLog(`-#-application_ID: ${tmp[x].application_id}`);
Lib.TalkativeLog(`-#-sink input name: ${tmp[x].name}`);
Lib.TalkativeLog(`-#-sink input icon: ${tmp[x].icon_name}`);
Lib.TalkativeLog(
`-#-sink input description: ${tmp[x].description}`
);
Lib.TalkativeLog(`-#-sink input port: ${tmp[x].port}`);
}
tmp = this._mixerControl.get_streams();
Lib.TalkativeLog(`#-# mixer stream -> ${tmp.length}`);
for (let x in tmp) {
Lib.TalkativeLog(`-#-STREAM index: ${tmp[x].index}`);
Lib.TalkativeLog(`-#-application_ID: ${tmp[x].application_id}`);
Lib.TalkativeLog(`-#-stream name: ${tmp[x].name}`);
Lib.TalkativeLog(`-#-stream icon: ${tmp[x].icon_name}`);
Lib.TalkativeLog(`-#-stream description: ${tmp[x].description}`);
var tmp1 = tmp[x].get_ports();
for (let y in tmp1) {
Lib.TalkativeLog(`-##-stream port number: ${y}`);
Lib.TalkativeLog(`-##-stream port name: ${tmp1[y].port}`);
Lib.TalkativeLog(
`-##-stream port human name: ${tmp1[y].human_port}`
);
Lib.TalkativeLog(
`-##-stream port priority: ${tmp1[y].priority}`
);
}
}
}
/**
* @returns {boolean}
*/
checkAudio() {
Lib.TalkativeLog(`-#-check GVC lib presence: ${this._isConnected}`);
return this._isConnected;
}
/**
* Destroy mixer control
*/
destroy() {
if (this._mixerControl) {
this._mixerControl.close();
this._mixerControl = null;
}
}
});

View File

@ -1,264 +0,0 @@
/*
Copyright (C) 2017 Borsato Ivano
The JavaScript code in this page is free software: you can
redistribute it and/or modify it under the terms of the GNU
General Public License (GNU GPL) as published by the Free Software
Foundation, either version 3 of the License, or (at your option)
any later version. The code is distributed WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU GPL for more details.
*/
'use strict';
import GObject from 'gi://GObject';
import GLib from 'gi://GLib';
import Gio from 'gi://Gio';
import * as Lib from './convenience.js';
/**
* @type {ExecuteStuff}
*/
export const ExecuteStuff = GObject.registerClass({
GTypeName: 'EasyScreenCast_ExecuteStuff',
}, class ExecuteStuff extends GObject.Object {
/**
* @param {EasyScreenCastSettingsWidget|EasyScreenCastIndicator} scope the scope for executing callback methods
* @private
*/
constructor(scope) {
super();
Lib.TalkativeLog(`-¶-init scope:${scope}`);
this.Scope = scope;
this.Callback = null;
}
/**
* @param {string} cmd the command to be executed
* @returns {*[]}
* @private
*/
_parseCmd(cmd) {
let successP, argv;
try {
[successP, argv] = GLib.shell_parse_argv(cmd);
} catch (err) {
Lib.TalkativeLog('-¶-ERROR PARSE');
successP = false;
}
if (successP) {
Lib.TalkativeLog(`-¶-parse: ${successP} argv: ${argv}`);
return [successP, argv];
} else {
return [successP, null];
}
}
/**
* Result callback.
*
* @callback ExecuteStuff~resultCallback
* @param {boolean} result whether the executed command exited successfully or not
* @param {string} stdout (optional) output of the result, if it was executed synchronously
*/
/**
* Line output callback for asynchronous commands.
*
* @callback ExecuteStuff~lineCallback
* @param {string} line a single line of output
*/
/**
* @param {string} cmd the command to be executed
* @param {boolean} sync execute synchronously (wait for return) or fork a process
* @param {ExecuteStuff~resultCallback} resCb callback after the command is finished
* @param {ExecuteStuff~lineCallback} lineCb callback for stdout when command is executed asynchronosly
* @class
*/
Execute(cmd, sync, resCb, lineCb) {
Lib.TalkativeLog(`-¶-execute: ${cmd}`);
this.CommandString = cmd;
if (
resCb === undefined &&
resCb === null &&
typeof resCb !== 'function'
) {
Lib.TalkativeLog('-¶-resCallback NEED to be a function');
this.Callback = null;
} else {
this.Callback = resCb;
}
if (sync === true) {
Lib.TalkativeLog('-¶-sync execute (wait for return)');
this._syncCmd(this.CommandString);
} else {
Lib.TalkativeLog('-¶-async execute (fork process)');
if (
lineCb === undefined &&
lineCb === null &&
typeof lineCb !== 'function'
) {
Lib.TalkativeLog('-¶-lineCallback NEED to be a function');
this.lineCallback = null;
} else {
this.lineCallback = lineCb;
}
this._asyncCmd(this.CommandString);
}
}
/**
* @param {string} cmd the command to be executed
* @returns {*}
* @class
*/
Spawn(cmd) {
let [successP, argv] = this._parseCmd(cmd);
if (successP) {
let successS, pid;
try {
[successS, pid] = GLib.spawn_async(
null,
argv,
null,
GLib.SpawnFlags.SEARCH_PATH |
GLib.SpawnFlags.DO_NOT_REAP_CHILD,
null
);
} catch (err) {
Lib.TalkativeLog(
`-¶-ERROR SPAWN err:${err.message.toString()}`
);
successS = false;
}
if (successS) {
Lib.TalkativeLog(`-¶-spawn: ${successS} pid: ${pid}`);
return true;
} else {
Lib.TalkativeLog('-¶-spawn ERROR');
return null;
}
}
return null;
}
/**
* @param {string} cmd the command to be executed
* @private
*/
_syncCmd(cmd) {
let [successP, argv] = this._parseCmd(cmd);
let decoder = new TextDecoder();
if (successP) {
Lib.TalkativeLog(`-¶-argv: ${argv}`);
let successS, stdOut, stdErr, exit;
try {
[successS, stdOut, stdErr, exit] = GLib.spawn_sync(
null,
argv,
null,
GLib.SpawnFlags.SEARCH_PATH,
() => {}
);
} catch (err) {
Lib.TalkativeLog('-¶-ERROR SPAWN');
successS = false;
}
if (successS) {
Lib.TalkativeLog(`-¶-argv: ${argv}`);
Lib.TalkativeLog(`-¶-stdOut: ${decoder.decode(stdOut)}`);
Lib.TalkativeLog(`-¶-stdErr: ${decoder.decode(stdErr)}`);
Lib.TalkativeLog(`-¶-exit: ${exit}`);
Lib.TalkativeLog('-¶-exe RC');
if (this.Callback !== null) {
this.Callback.apply(this.Scope, [
true,
decoder.decode(stdOut),
]);
}
} else {
Lib.TalkativeLog(`-¶-ERROR exe WC - exit status: ${exit}`);
if (this.Callback !== null)
this.Callback.apply(this.Scope, [false]);
}
}
}
/**
* @param {string} cmd the command to be executed
* @private
*/
_asyncCmd(cmd) {
let [successP, argv] = this._parseCmd(cmd);
let decoder = new TextDecoder();
if (successP) {
Lib.TalkativeLog(`-¶-argv: ${argv}`);
let successS, pid, stdIn, stdOut, stdErr;
try {
[
successS,
pid,
stdIn,
stdOut,
stdErr,
] = GLib.spawn_async_with_pipes(
null, // working directory
argv, // argv
null, // envp
GLib.SpawnFlags.SEARCH_PATH, // flags
() => {} // child_setup
);
} catch (err) {
Lib.TalkativeLog('-¶-ERROR SPAWN');
successS = false;
}
if (successS) {
Lib.TalkativeLog(`-¶-argv: ${argv}`);
Lib.TalkativeLog(`-¶-pid: ${pid}`);
Lib.TalkativeLog(`-¶-stdIn: ${stdIn}`);
Lib.TalkativeLog(`-¶-stdOut: ${stdOut}`);
Lib.TalkativeLog(`-¶-stdErr: ${stdErr}`);
let outReader = new Gio.DataInputStream({
base_stream: new Gio.UnixInputStream({
fd: stdOut,
}),
});
let inWriter = new Gio.DataOutputStream({
base_stream: new Gio.UnixOutputStream({
fd: stdIn,
}),
});
inWriter.close(null);
let [out] = outReader.read_line(null);
while (out !== null) {
if (this.lineCallback !== null)
this.lineCallback.apply(this.Scope, [decoder.decode(out)]);
[out] = outReader.read_line(null);
}
if (this.Callback !== null) {
Lib.TalkativeLog('-¶-exe RC');
this.Callback.apply(this.Scope, [true]);
}
} else {
Lib.TalkativeLog('-¶-ERROR exe WC');
if (this.Callback !== null)
this.Callback.apply(this.Scope, [false]);
}
}
}
});

View File

@ -1,735 +0,0 @@
/*
Copyright (C) 2016 Borsato Ivano
The JavaScript code in this page is free software: you can
redistribute it and/or modify it under the terms of the GNU
General Public License (GNU GPL) as published by the Free Software
Foundation, either version 3 of the License, or (at your option)
any later version. The code is distributed WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU GPL for more details.
*/
'use strict';
import * as Lib from './convenience.js';
import * as Settings from './settings.js';
// CONST GSP - base
const SCREEN =
'_SCREENCAST_RES_ _ENCODER_VIDEO_ ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! _CONTAINER_';
// CONST GSP - base plus sound
const SCREEN_SOUND =
'queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! _SCREENCAST_RES_ _ENCODER_VIDEO_ ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! mux. pulsesrc ! audioconvert ! _ENCODER_AUDIO_ ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! mux. _CONTAINER_ name=mux ';
// CONST GSP - base plus webcam
const SCREEN_WEBCAM =
'queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! videomixer name=mix _WEBCAM_OPT_ ! videoconvert ! _SCREENCAST_RES_ _ENCODER_VIDEO_ ! mux. v4l2src _WEBCAM_DEV_ ! _WEBCAM_CAP_ ! videoscale ! video/x-raw, width=_WEBCAM_W_, height=_WEBCAM_H_, add-borders=false ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! mix. _CONTAINER_ name=mux';
// CONST GSP - base plus sound and webcam stream
const SCREEN_WEBCAM_SOUND =
'queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! videomixer name=mix _WEBCAM_OPT_ ! videoconvert ! _SCREENCAST_RES_ _ENCODER_VIDEO_ ! mux. v4l2src _WEBCAM_DEV_ ! _WEBCAM_CAP_ ! videoscale ! video/x-raw, width=_WEBCAM_W_, height=_WEBCAM_H_, add-borders=false ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! mix. pulsesrc ! audioconvert ! _ENCODER_AUDIO_ ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! mux. _CONTAINER_ name=mux';
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
// CONST CONTAINER - WebM
const webmVP8 = {
fileExt: '.webm',
nameGSP: 'webmmux',
quality: [
{
// quality level 0
fps: 15,
vq:
'vp8enc min_quantizer=13 max_quantizer=20 cpu-used=5 deadline=1000000 sharpness=2 target-bitrate=10000 threads=%T',
aq: 'vorbisenc',
descr:
'FPS: 15 \nVideo -> VP8 Encoder:\n-min_quantizer=13\n-max_quantizer=20\n-cpu-used=5\n-deadline=1000000\n-sharpness=2\n-target-bitrate=10000\nAudio -> Vorbis Encoder',
},
{
// quality level 1
fps: 30,
vq:
'vp8enc min_quantizer=4 max_quantizer=13 cpu-used=2 deadline=500000 sharpness=0 target-bitrate=15000 threads=%T',
aq: 'vorbisenc',
descr:
'FPS: 30 \nVideo -> VP8 Encoder:\n-min_quantizer=4\n-max_quantizer=13\n-cpu-used=2\n-deadline=500000\n-sharpness=0\n-target-bitrate=15000\nAudio -> Vorbis Encoder',
},
{
// quality level 2
fps: 30,
vq:
'vp8enc min_quantizer=0 max_quantizer=7 cpu-used=1 deadline=500000 sharpness=0 target-bitrate=25000 threads=%T',
aq: 'vorbisenc',
descr:
'FPS: 30 \nVideo -> VP8 Encoder:\n-min_quantizer=0\n-max_quantizer=7\n-cpu-used=1\n-deadline=500000\n-sharpness=0\n-target-bitrate=25000\nAudio -> Vorbis Encoder',
},
{
// quality level 3
fps: 60,
vq:
'vp8enc min_quantizer=0 max_quantizer=0 cpu-used=0 deadline=100000 sharpness=0 target-bitrate=40000 threads=%T',
aq: 'vorbisenc',
descr:
'FPS: 60 \nVideo -> VP8 Encoder:\n-min_quantizer=0\n-max_quantizer=0\n-cpu-used=0\n-deadline=100000\n-sharpness=0\n-target-bitrate=40000\nAudio -> Vorbis Encoder',
},
],
};
const webmVP9 = {
fileExt: '.webm',
nameGSP: 'webmmux',
quality: [
{
// quality level 0
fps: 15,
vq:
'vp9enc min_quantizer=13 max_quantizer=20 cpu-used=5 deadline=1000000 sharpness=2 target-bitrate=10000 threads=%T',
aq: 'vorbisenc',
descr:
'FPS: 15 \nVideo -> VP9 Encoder:\n-min_quantizer=13\n-max_quantizer=20\n-cpu-used=5\n-deadline=1000000\n-sharpness=2\n-target-bitrate=10000\nAudio -> Vorbis Encoder',
},
{
// quality level 1
fps: 30,
vq:
'vp9enc min_quantizer=4 max_quantizer=13 cpu-used=2 deadline=500000 sharpness=0 target-bitrate=15000 threads=%T',
aq: 'vorbisenc',
descr:
'FPS: 30 \nVideo -> VP9 Encoder:\n-min_quantizer=4\n-max_quantizer=13\n-cpu-used=2\n-deadline=500000\n-sharpness=0\n-target-bitrate=15000\nAudio -> Vorbis Encoder',
},
{
// quality level 2
fps: 30,
vq:
'vp9enc min_quantizer=0 max_quantizer=7 cpu-used=1 deadline=500000 sharpness=0 target-bitrate=25000 threads=%T',
aq: 'vorbisenc',
descr:
'FPS: 30 \nVideo -> VP9 Encoder:\n-min_quantizer=0\n-max_quantizer=7\n-cpu-used=1\n-deadline=500000\n-sharpness=0\n-target-bitrate=25000\nAudio -> Vorbis Encoder',
},
{
// quality level 3
fps: 60,
vq:
'vp9enc min_quantizer=0 max_quantizer=0 cpu-used=0 deadline=100000 sharpness=0 target-bitrate=40000 threads=%T',
aq: 'vorbisenc',
descr:
'FPS: 60 \nVideo -> VP9 Encoder:\n-min_quantizer=0\n-max_quantizer=0\n-cpu-used=0\n-deadline=100000\n-sharpness=0\n-target-bitrate=40000\nAudio -> Vorbis Encoder',
},
],
};
// CONST CONTAINER - Mp4
const mp4 = {
fileExt: '.mp4',
nameGSP: 'mp4mux',
quality: [
{
// quality level 0
fps: 15,
vq:
'x264enc psy-tune="none" speed-preset="superfast" subme=1 qp-min=28 qp-max=40 threads=%T',
aq: 'lamemp3enc',
descr:
'FPS: 15 \nVideo -> x264enc Encoder:\n-psy-tune="none"\n-speed-preset="superfast"\n-subme=1\n-qp-min=28\n-qp-max=40\nAudio -> Mp3 Encoder',
},
{
// quality level 1
fps: 30,
vq:
'x264enc psy-tune="animation" speed-preset="fast" subme=5 qp-min=18 qp-max=28 threads=%T',
aq: 'lamemp3enc',
descr:
'FPS: 30 \nVideo -> x264enc Encoder:\n-psy-tune="animation"\n-speed-preset="fast"\n-subme=5\n-qp-min=18\n-qp-max=28\nAudio -> Mp3 Encoder',
},
{
// quality level 2
fps: 30,
vq:
'x264enc psy-tune="animation" speed-preset="medium" subme=8 qp-min=10 qp-max=18 threads=%T',
aq: 'lamemp3enc',
descr:
'FPS: 30 \nVideo -> x264enc Encoder:\n-psy-tune="animation"\n-speed-preset="medium"\n-subme=8\n-qp-min=10\n-qp-max=18\nAudio -> Mp3 Encoder',
},
{
// quality level 3
fps: 60,
vq:
'x264enc psy-tune="film" speed-preset="slower" subme=10 qp-min=0 qp-max=10 threads=%T',
aq: 'lamemp3enc',
descr:
'FPS: 60 \nVideo -> x264enc Encoder:\n-psy-tune="film"\n-speed-preset="slower"\n-subme=10\n-qp-min=0\n-qp-max=10\nAudio -> Mp3 Encoder',
},
],
};
const mp4Aac = {
fileExt: '.mp4',
nameGSP: 'mp4mux',
quality: [
{
// quality level 0
fps: 15,
vq:
'x264enc psy-tune="none" speed-preset="superfast" subme=1 qp-min=28 qp-max=40 threads=%T',
aq: 'avenc_aac',
descr:
'FPS: 15 \nVideo -> x264enc Encoder:\n-psy-tune="none"\n-speed-preset="superfast"\n-subme=1\n-qp-min=28\n-qp-max=40\nAudio -> AAC Encoder',
},
{
// quality level 1
fps: 30,
vq:
'x264enc psy-tune="animation" speed-preset="fast" subme=5 qp-min=18 qp-max=28 threads=%T',
aq: 'avenc_aac',
descr:
'FPS: 30 \nVideo -> x264enc Encoder:\n-psy-tune="animation"\n-speed-preset="fast"\n-subme=5\n-qp-min=18\n-qp-max=28\nAudio -> AAC Encoder',
},
{
// quality level 2
fps: 30,
vq:
'x264enc psy-tune="animation" speed-preset="medium" subme=8 qp-min=10 qp-max=18 threads=%T',
aq: 'avenc_aac',
descr:
'FPS: 30 \nVideo -> x264enc Encoder:\n-psy-tune="animation"\n-speed-preset="medium"\n-subme=8\n-qp-min=10\n-qp-max=18\nAudio -> AAC Encoder',
},
{
// quality level 3
fps: 60,
vq:
'x264enc psy-tune="film" speed-preset="slower" subme=10 qp-min=0 qp-max=10 threads=%T',
aq: 'avenc_aac',
descr:
'FPS: 60 \nVideo -> x264enc Encoder:\n-psy-tune="film"\n-speed-preset="slower"\n-subme=10\n-qp-min=0\n-qp-max=10\nAudio -> AAC Encoder',
},
],
};
// CONST CONTAINER - Mkv
const mkv = {
fileExt: '.mkv',
nameGSP: 'matroskamux',
quality: [
{
// quality level 0
fps: 15,
vq:
'x264enc psy-tune="none" speed-preset="superfast" subme=1 qp-min=28 qp-max=40 threads=%T',
aq: 'flacenc',
descr:
'FPS: 15 \nVideo -> x264enc Encoder:\n-psy-tune="none"\n-speed-preset="superfast"\n-subme=1\n-qp-min=28\n-qp-max=40\nAudio -> Flac Encoder',
},
{
// quality level 1
fps: 30,
vq:
'x264enc psy-tune="animation" speed-preset="fast" subme=5 qp-min=18 qp-max=28 threads=%T',
aq: 'flacenc',
descr:
'FPS: 30 \nVideo -> x264enc Encoder:\n-psy-tune="animation"\n-speed-preset="fast"\n-subme=5\n-qp-min=18\n-qp-max=28\nAudio -> Flac Encoder',
},
{
// quality level 2
fps: 30,
vq:
'x264enc psy-tune="animation" speed-preset="medium" subme=8 qp-min=10 qp-max=18 threads=%T',
aq: 'flacenc',
descr:
'FPS: 30 \nVideo -> x264enc Encoder:\n-psy-tune="animation"\n-speed-preset="medium"\n-subme=8\n-qp-min=10\n-qp-max=18\nAudio -> Flac Encoder',
},
{
// quality level 3
fps: 60,
vq:
'x264enc psy-tune="film" speed-preset="slower" subme=10 qp-min=0 qp-max=10 threads=%T',
aq: 'flacenc',
descr:
'FPS: 60 \nVideo -> x264enc Encoder:\n-psy-tune="film"\n-speed-preset="slower"\n-subme=10\n-qp-min=0\n-qp-max=10\nAudio -> Flac Encoder',
},
],
};
// CONST CONTAINER - Ogg
const ogg = {
fileExt: '.ogg',
nameGSP: 'oggmux',
quality: [
{
// quality level 0
fps: 15,
vq:
'theoraenc speed-level=3 vp3-compatible=true quality=10 bitrate=10000',
aq: 'opusenc',
descr:
'FPS: 15 \nVideo -> Theora Encoder:\n-speed-level=3\n-vp3-compatible=true\n-quality=10\n-bitrate=10000\nAudio -> Opus Encoder',
},
{
// quality level 1
fps: 30,
vq:
'theoraenc speed-level=1 vp3-compatible=false quality=30 bitrate=25000',
aq: 'opusenc',
descr:
'FPS: 30 \nVideo -> Theora Encoder:\n-speed-level=1\n-vp3-compatible=false\n-quality=30\n-bitrate=25000\nAudio -> Opus Encoder',
},
{
// quality level 2
fps: 30,
vq:
'theoraenc speed-level=0 vp3-compatible=false quality=50 bitrate=50000',
aq: 'opusenc',
descr:
'FPS: 30 \nVideo -> Theora Encoder:\n-speed-level=0\n-vp3-compatible=false\n-quality=50\n-bitrate=50000\nAudio -> Opus Encoder',
},
{
// quality level 3
fps: 60,
vq:
'theoraenc speed-level=0 vp3-compatible=false quality=60 bitrate=100000',
aq: 'opusenc',
descr:
'FPS: 60 \nVideo -> Theora Encoder\n-speed-level=0\n-vp3-compatible=false\n-quality=60\n-bitrate=100000\nAudio -> Opus Encoder',
},
],
};
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
// CONST RESOLUTION
const RESOLUTION = [
// NATIVE SCREENCAST RESOLUTION
'',
// PRESET/CUSTOM SCREENCAST RESOLUTION
'videoscale ! video/x-raw, width=_RES_WIDTH_, height=_RES_HEIGHT_, add-borders=_RES_KAR_ ! ',
];
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
// VAR ARRAY CONTAINER
// see cbt_FileContainer in *.glade files for the combo box
const CONTAINER = [webmVP8, webmVP9, mp4, mkv, ogg, mp4Aac];
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
/**
* Compose GSP
*
* @param {EasyScreenCastSettings} settings the extension's settings
* @param {MixerAudio} mixer the mixer
*
* @returns {string}
*/
export function composeGSP(settings, mixer) {
Lib.TalkativeLog('-§-COMPOSE GSP');
let tmpGSP = '';
// retrieve options
let deviceWebcam = settings.getOption('s', Settings.DEVICE_WEBCAM_SETTING_KEY);
let deviceAudio = settings.getOption('i', Settings.INPUT_AUDIO_SOURCE_SETTING_KEY);
let qualityGSP = settings.getOption('i', Settings.QUALITY_SETTING_KEY);
let qualityWebcam = settings.getOption('s', Settings.QUALITY_WEBCAM_SETTING_KEY);
let resolutionType = settings.getOption('i', Settings.FILE_RESOLUTION_TYPE_SETTING_KEY);
let resolutionKAR = settings.getOption('b', Settings.FILE_RESOLUTION_KAR_SETTING_KEY);
let resolutionHeight = settings.getOption('i', Settings.FILE_RESOLUTION_HEIGHT_SETTING_KEY);
let resolutionWidth = settings.getOption('i', Settings.FILE_RESOLUTION_WIDTH_SETTING_KEY);
let container = settings.getOption('i', Settings.FILE_CONTAINER_SETTING_KEY);
Lib.TalkativeLog(
`-§-get option||devW: ${deviceWebcam}||devA: ${deviceAudio}||Qgsp: ${qualityGSP}||Qwc: ${qualityWebcam}||Res: ${resolutionType}||Cont: ${container}`
);
if (deviceWebcam !== '') {
switch (deviceAudio) {
case 0:
Lib.TalkativeLog('-§- SCREEN-WEBCAM');
tmpGSP = SCREEN_WEBCAM;
// replace WEBCAM_DEVICE/WEBCAM_CAPS
tmpGSP = _replaceWebcam(
tmpGSP,
deviceWebcam,
qualityWebcam,
settings
);
break;
case 1:
Lib.TalkativeLog('-§-SCREEN-WEBCAM-AUDIO(d)');
tmpGSP = SCREEN_WEBCAM_SOUND;
// replace WEBCAM_DEVICE/WEBCAM_CAPS/ENCODER-AUDIO
tmpGSP = _replaceAudio(
_replaceWebcam(tmpGSP, deviceWebcam, qualityWebcam, settings),
true,
container,
qualityGSP,
mixer
);
break;
default:
Lib.TalkativeLog('-§-SCREEN-WEBCAM-AUDIO');
tmpGSP = SCREEN_WEBCAM_SOUND;
// replace WEBCAM_DEVICE/WEBCAM_CAPS/ENCODER-AUDIO/AUDIO_DEVICE
tmpGSP = _replaceAudio(
_replaceWebcam(tmpGSP, deviceWebcam, qualityWebcam, settings),
false,
container,
qualityGSP,
mixer
);
}
} else {
switch (deviceAudio) {
case 0:
Lib.TalkativeLog('-§-SCREEN');
tmpGSP = SCREEN;
break;
case 1:
Lib.TalkativeLog('-§-SCREEN-AUDIO(d)');
tmpGSP = SCREEN_SOUND;
// replace ENCODER-AUDIO
tmpGSP = _replaceAudio(
tmpGSP,
true,
container,
qualityGSP,
mixer
);
break;
default:
Lib.TalkativeLog('-§-SCREEN-AUDIO');
tmpGSP = SCREEN_SOUND;
// replace ENCODER-AUDIO/AUDIO_DEVICE
tmpGSP = _replaceAudio(
tmpGSP,
false,
container,
qualityGSP,
mixer
);
}
}
// compose resolution string
var resolution = _composeResolution(
resolutionType,
resolutionHeight,
resolutionWidth,
resolutionKAR
);
// replace RESOLUTION/ENCODER-VIDEO/CONTAINER
var mapObj = {
_SCREENCAST_RES_: resolution,
_ENCODER_VIDEO_: CONTAINER[container].quality[qualityGSP].vq,
_CONTAINER_: CONTAINER[container].nameGSP,
};
tmpGSP = tmpGSP.replace(
/_SCREENCAST_RES_|_ENCODER_VIDEO_|_CONTAINER_/gi,
match => {
return mapObj[match];
}
);
Lib.TalkativeLog(`-§-final GSP :${tmpGSP}`);
return tmpGSP;
}
/**
* replace audio
*
* @param {string} gspRA input pipeline to be modified
* @param {boolean} defaultAudio whether to use the default audio device
* @param {int} ConTMP selected output container. Used to determine correct audio encoder
* @param {int} QGSPtmp quality setting
* @param {MixerAudio} mixer audio mixer
* @returns {string} pipeline with audio
*/
function _replaceAudio(gspRA, defaultAudio, ConTMP, QGSPtmp, mixer) {
Lib.TalkativeLog(`-§-replace audio default->${defaultAudio}`);
// replace device/encoder
var aq = CONTAINER[ConTMP].quality[QGSPtmp].aq;
Lib.TalkativeLog(`-§-pipeline pre-audio:${gspRA} aq:${aq}`);
var audioPipeline;
if (defaultAudio) {
Lib.TalkativeLog('-§-default audio source');
audioPipeline = gspRA.replace(/_ENCODER_AUDIO_/gi, aq);
} else {
var audiosource = mixer.getAudioSource();
if (audiosource === undefined) {
Lib.TalkativeLog('-§-failure combination of array audio sources');
audioPipeline = gspRA.replace(/_ENCODER_AUDIO_/gi, aq);
} else {
Lib.TalkativeLog('-§-correct audio source assignment');
if (audiosource.indexOf('output') !== -1)
audiosource += '.monitor';
var reDev = `pulsesrc device="${audiosource}"`;
var mapObj = {
pulsesrc: reDev,
_ENCODER_AUDIO_: aq,
};
audioPipeline = gspRA.replace(
/pulsesrc|_ENCODER_AUDIO_/gi,
match => {
return mapObj[match];
}
);
}
}
Lib.TalkativeLog(`-§-pipeline post-audio:${audioPipeline}`);
return audioPipeline;
}
/**
* replace webcam
*
* @param {string} gspRW input pipeline to be modified
* @param {string} device webcam device file (e.g. /dev/video0)
* @param {string} caps quality options
* @param {EasyScreenCastSettings} settings the extension's settings
* @returns {string} pipeline with webcam settings
*/
function _replaceWebcam(gspRW, device, caps, settings) {
Lib.TalkativeLog(`-§-replace webcam -> ${device} caps: ${caps}`);
// replace device/caps
var reDev = `device=${device}`;
var reWCopt = _composeWebCamOption(settings);
var [reWCwidth, reWCheight] = _getWebCamDimension(settings);
Lib.TalkativeLog(`-§-pipeline pre-webcam:${gspRW}`);
var mapObj = {
_WEBCAM_DEV_: reDev,
_WEBCAM_CAP_: caps,
_WEBCAM_OPT_: reWCopt,
_WEBCAM_W_: reWCwidth,
_WEBCAM_H_: reWCheight,
};
var webcamPipeline = gspRW.replace(
/_WEBCAM_DEV_|_WEBCAM_CAP_|_WEBCAM_OPT_|_WEBCAM_W_|_WEBCAM_H_/gi,
match => {
return mapObj[match];
}
);
Lib.TalkativeLog(`-§-pipeline post-webcam:${webcamPipeline}`);
return webcamPipeline;
}
/**
* replace resolution
*
* @param {int} tmpRes resolution type: native/custom
* @param {int} h custom height
* @param {int} w custom width
* @param {boolean} kar whether to keep aspect ratio
* @returns {string} pipeline part for scaling resolution
*/
function _composeResolution(tmpRes, h, w, kar) {
Lib.TalkativeLog(`-§-resolution option: ${tmpRes}`);
var strRes = RESOLUTION[0];
var mapObj = {};
switch (tmpRes) {
case -1:
break;
case 999:
mapObj = {
_RES_KAR_: kar ? 'true' : 'false',
_RES_HEIGHT_: h,
_RES_WIDTH_: w,
};
strRes = RESOLUTION[1].replace(
/_RES_KAR_|_RES_HEIGHT_|_RES_WIDTH_/gi,
match => {
return mapObj[match];
}
);
break;
default:
mapObj = {
_RES_KAR_: 'true',
_RES_HEIGHT_: h,
_RES_WIDTH_: w,
};
strRes = RESOLUTION[1].replace(
/_RES_KAR_|_RES_WIDTH_|_RES_HEIGHT_/gi,
match => {
return mapObj[match];
}
);
}
Lib.TalkativeLog(`-§-compose resolution: ${strRes}`);
return strRes;
}
/**
* compose option webcam position
*
* @param {EasyScreenCastSettings} settings the extension's settings
*
* @returns {string}
*/
function _composeWebCamOption(settings) {
Lib.TalkativeLog('-§-compose webcam option');
// retrieve option webcam
var webcamAlpha = settings.getOption('d', Settings.ALPHA_CHANNEL_WEBCAM_SETTING_KEY);
var webcamMarginX = settings.getOption('i', Settings.MARGIN_X_WEBCAM_SETTING_KEY);
var webcamMarginY = settings.getOption('i', Settings.MARGIN_Y_WEBCAM_SETTING_KEY);
var webcamCornerPosition = settings.getOption('i', Settings.CORNER_POSITION_WEBCAM_SETTING_KEY);
var [webcamWidth, webcamHeight, screenWidth, screenHeight] = _getWebCamDimension(settings);
var posX = 0;
var posY = 0;
Lib.TalkativeLog(
`-§-alpha=${webcamAlpha} |marX=${webcamMarginX} |marY=${webcamMarginY} |corner=${webcamCornerPosition}`
);
// corner top-left
posX = webcamMarginX;
posY = webcamMarginY;
switch (webcamCornerPosition) {
case 0:
// corner bottom-right
posX = Math.floor(screenWidth - (webcamWidth + webcamMarginX));
posY = Math.floor(screenHeight - (webcamHeight + webcamMarginY));
break;
case 1:
// corner bottom-left
posX = webcamMarginX;
posY = Math.floor(screenHeight - (webcamHeight + webcamMarginY));
break;
case 2:
// corner top-right
posX = Math.floor(screenWidth - (webcamWidth + webcamMarginX));
posY = webcamMarginY;
break;
default:
}
// check valid position
if ((posX < 0 || posX > screenWidth) && (posY < 0 || posY > screenHeight)) {
Lib.TalkativeLog('-§-NOT valid position');
posX = 0;
posY = 0;
}
var tmpWCopt =
`sink_0::alpha=1 sink_1::alpha=${
webcamAlpha
} sink_1::xpos=${
posX
} sink_1::ypos=${
posY
} `;
Lib.TalkativeLog(`-§-posX=${posX} |posY=${posY}`);
Lib.TalkativeLog(`-§-webcam option=${tmpWCopt}`);
return tmpWCopt;
}
/**
* retrieve dimension webcam
*
* @param {EasyScreenCastSettings} settings the extension's settings
*
* @returns {*[]} array with webcam width,height,screen-width,screen-height
*/
function _getWebCamDimension(settings) {
Lib.TalkativeLog('-§-get webcam dimension');
var webcamWidth = settings.getOption('i', Settings.WIDTH_WEBCAM_SETTING_KEY);
var webcamHeight = settings.getOption('i', Settings.HEIGHT_WEBCAM_SETTING_KEY);
var webcamUnit = settings.getOption('i', Settings.TYPE_UNIT_WEBCAM_SETTING_KEY);
var screenWidth = settings.getOption('i', Settings.WIDTH_SETTING_KEY);
var screenHeight = settings.getOption('i', Settings.HEIGHT_SETTING_KEY);
if (settings.getOption('i', Settings.AREA_SCREEN_SETTING_KEY) === 0) {
screenWidth = global.screen_width;
screenHeight = global.screen_height;
}
Lib.TalkativeLog(
`-§-WC w=${webcamWidth} WC h=${webcamHeight} WCtype=${webcamUnit} screen W=${screenWidth} screen H=${screenHeight}`
);
if (webcamUnit === 0) {
webcamWidth = Math.floor((screenWidth * webcamWidth) / 100);
webcamHeight = Math.floor((screenHeight * webcamHeight) / 100);
}
Lib.TalkativeLog(`-§-after percentage WCw=${webcamWidth} WCh=${webcamHeight}`);
return [webcamWidth, webcamHeight, screenWidth, screenHeight];
}
/**
* get description
*
* @param {int} quality selected quality
* @param {int} container selected container format
* @returns {string}
*/
export function getDescr(quality, container) {
Lib.TalkativeLog(`-§-get description Q-> ${quality} C-> ${container}`);
return CONTAINER[container].quality[quality].descr;
}
/**
* get fps
*
* @param {int} quality selected quality
* @param {int} container selected container format
* @returns {number}
*/
export function getFps(quality, container) {
Lib.TalkativeLog(`-§-get fps Q-> ${quality} C-> ${container}`);
return CONTAINER[container].quality[quality].fps;
}
/**
* get file extension
*
* @param {int} container selected container format
* @returns {string}
*/
export function getFileExtension(container) {
Lib.TalkativeLog(`-§-get file extension C-> ${container}`);
return CONTAINER[container].fileExt;
}

View File

@ -1,145 +0,0 @@
/*
Copyright (C) 2016 Borsato Ivano
The JavaScript code in this page is free software: you can
redistribute it and/or modify it under the terms of the GNU
General Public License (GNU GPL) as published by the Free Software
Foundation, either version 3 of the License, or (at your option)
any later version. The code is distributed WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU GPL for more details.
*/
'use strict';
import GObject from 'gi://GObject';
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
// https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/ui/messageTray.js
import * as MessageTray from 'resource:///org/gnome/shell/ui/messageTray.js';
import St from 'gi://St';
import * as Lib from './convenience.js';
import * as Settings from './settings.js';
import * as Ext from './extension.js';
import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js';
/**
* @type {NotifyManager}
*/
export const NotifyManager = GObject.registerClass({
GTypeName: 'EasyScreenCast_NotifyManager',
}, class NotifyManager extends GObject.Object {
/**
* Create a notify manager
*/
constructor() {
super();
Lib.TalkativeLog('-°-init notify manager');
this._alertWidget = null;
}
/**
* create notify
*
* @param {string} msg the title
* @param {Gio.FileIcon} icon the icon
* @param {boolean} sound whether to play a sound
* @returns {MessageTray.Notification}
*/
createNotify(msg, icon, sound) {
Lib.TalkativeLog(`-°-create notify :${msg}`);
var source = new MessageTray.Source({
title: _('EasyScreenCast'),
});
var notify = new MessageTray.Notification({
source,
title: msg,
body: null,
gicon: icon,
isTransient: false,
resident: true,
});
Main.messageTray.add(source);
source.addNotification(notify);
if (sound)
notify.playSound();
return notify;
}
/**
* update notify
*
* @param {MessageTray.Notification} notify the already existing notification to update
* @param {string} msg the title
* @param {Gio.FileIcon} icon the icon
* @param {boolean} sound whether to play a sound
*/
updateNotify(notify, msg, icon, sound) {
Lib.TalkativeLog('-°-update notify');
notify.set({
title: msg,
body: null,
gicon: icon,
});
if (sound)
notify.playSound();
}
/**
* create alert
*
* @param {string} msg the message
*/
createAlert(msg) {
Lib.TalkativeLog(`-°-show alert tweener : ${msg}`);
if (Ext.Indicator.getSettings().getOption('b', Settings.SHOW_NOTIFY_ALERT_SETTING_KEY)) {
var monitor = Main.layoutManager.focusMonitor;
this.resetAlert();
this._alertWidget = new St.Label({
style_class: 'alert-msg',
opacity: 255,
text: msg,
});
Main.uiGroup.add_child(this._alertWidget);
this._alertWidget.set_position(
Math.floor(monitor.width / 2 - this._alertWidget.width / 2),
Math.floor(monitor.height / 2 - this._alertWidget.height / 2)
);
Lib.TalkativeLog(`-°-show alert tweener : opacity=${this._alertWidget.opacity}`);
// see org/gnome/shell/ui/environment.js#_easeActor
// TODO: for some reason, no transition is created, so the onComplete
// callback is called _immediately_
/*
import Clutter from 'gi://Clutter';
this._alertWidget.ease({
opacity: 0,
duration: 400,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => {
Lib.TalkativeLog(`-°-show alert tweener completed: opacity=${this._alertWidget.opacity}`);
Main.uiGroup.remove_child(this._alertWidget);
this._alertWidget = null;
},
});
*/
}
}
resetAlert() {
if (this._alertWidget !== null) {
this._alertWidget.hide();
Main.uiGroup.remove_child(this._alertWidget);
this._alertWidget = null;
}
}
});

View File

@ -1,214 +0,0 @@
/*
Copyright (C) 2013 Borsato Ivano
The JavaScript code in this page is free software: you can
redistribute it and/or modify it under the terms of the GNU
General Public License (GNU GPL) as published by the Free Software
Foundation, either version 3 of the License, or (at your option)
any later version. The code is distributed WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU GPL for more details.
*/
'use strict';
import GObject from 'gi://GObject';
import Gio from 'gi://Gio';
import GLib from 'gi://GLib';
import {loadInterfaceXML} from 'resource:///org/gnome/shell/misc/dbusUtils.js';
const ScreencastIface = loadInterfaceXML('org.gnome.Shell.Screencast');
import * as Lib from './convenience.js';
import * as Settings from './settings.js';
import * as Selection from './selection.js';
import * as UtilGSP from './utilgsp.js';
import * as Ext from './extension.js';
/**
* @type {CaptureVideo}
*/
export const CaptureVideo = GObject.registerClass({
GTypeName: 'EasyScreenCast_CaptureVideo',
}, class CaptureVideo extends GObject.Object {
/**
* Create a video recorder
*/
constructor() {
super();
Lib.TalkativeLog('-&-init recorder');
this.AreaSelected = null;
// connect to d-bus service
const ScreenCastProxy = Gio.DBusProxy.makeProxyWrapper(ScreencastIface);
this._screenCastService = new ScreenCastProxy(
Gio.DBus.session,
'org.gnome.Shell.Screencast',
'/org/gnome/Shell/Screencast',
(proxy, error) => {
if (error)
Lib.TalkativeLog(`-&-ERROR(d-bus proxy connected) - ${error.message}`);
else
Lib.TalkativeLog('-&-d-bus proxy connected');
}
);
}
/**
* start recording
*/
start() {
Lib.TalkativeLog('-&-start video recording');
this.recordingActive = false;
let fileExt = UtilGSP.getFileExtension(
Ext.Indicator.getSettings().getOption('i', Settings.FILE_CONTAINER_SETTING_KEY)
);
// prepare variable for screencast
let fileRec = Ext.Indicator.getSettings().getOption('s', Settings.FILE_NAME_SETTING_KEY);
let folderRec = '';
if (Ext.Indicator.getSettings().getOption('s', Settings.FILE_FOLDER_SETTING_KEY) !== '')
folderRec = Ext.Indicator.getSettings().getOption('s', Settings.FILE_FOLDER_SETTING_KEY);
let pipelineRec = '';
if (Ext.Indicator.getSettings().getOption('b', Settings.ACTIVE_CUSTOM_GSP_SETTING_KEY)) {
pipelineRec = Ext.Indicator.getSettings().getOption(
's',
Settings.PIPELINE_REC_SETTING_KEY
);
} else {
// compose GSP
pipelineRec = UtilGSP.composeGSP(Ext.Indicator.getSettings(), Ext.Indicator.CtrlAudio);
}
Lib.TalkativeLog(`-&-file template : ${fileRec}`);
fileRec = this._generateFileName(fileRec);
Lib.TalkativeLog(`-&-file final : ${fileRec}`);
const completeFileRecPath = folderRec !== ''
? `${folderRec}/${fileRec}`
: fileRec;
Lib.TalkativeLog(`-&-file rec path complete : ${completeFileRecPath}${fileExt}`);
// prefix with a videoconvert element
// see DEFAULT_PIPELINE in https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/dbusServices/screencast/screencastService.js#L26
// this videoconvert element was added always previously (< Gnome 40) and needs to be added now explicitly
// https://gitlab.gnome.org/GNOME/gnome-shell/-/commit/51bf7ec17617a9ed056dd563afdb98e17da07373
pipelineRec = `videoconvert chroma-mode=GST_VIDEO_CHROMA_MODE_NONE dither=GST_VIDEO_DITHER_NONE matrix-mode=GST_VIDEO_MATRIX_MODE_OUTPUT_ONLY n-threads=%T ! queue ! ${pipelineRec}`;
Lib.TalkativeLog(`-&-pipeline : pipeline: ${pipelineRec}`);
var optionsRec = {
'draw-cursor': new GLib.Variant(
'b',
Ext.Indicator.getSettings().getOption('b', Settings.DRAW_CURSOR_SETTING_KEY)
),
framerate: new GLib.Variant(
'i',
Ext.Indicator.getSettings().getOption('i', Settings.FPS_SETTING_KEY)
),
pipeline: new GLib.Variant('s', pipelineRec),
};
if (Ext.Indicator.getSettings().getOption('i', Settings.AREA_SCREEN_SETTING_KEY) === 0) {
this._screenCastService.ScreencastRemote(
completeFileRecPath,
optionsRec,
(result, error) => {
if (error) {
Lib.TalkativeLog(`-&-ERROR(screencast execute) - ${error.message}`);
this.stop();
Ext.Indicator.doRecResult(false);
} else {
Lib.TalkativeLog(`-&-screencast execute - ${result[0]} - ${result[1]}`);
let resultingFilePath = result[1];
if (resultingFilePath.endsWith('.undefined')) {
resultingFilePath = resultingFilePath.substring(0, resultingFilePath.length - '.undefined'.length);
resultingFilePath = `${resultingFilePath}${fileExt}`;
}
this._originalFilePath = result[1];
this._filePathWithExtension = resultingFilePath;
Ext.Indicator.doRecResult(result[0], resultingFilePath);
}
}
);
} else {
this._screenCastService.ScreencastAreaRemote(
Ext.Indicator.getSettings().getOption('i', Settings.X_POS_SETTING_KEY),
Ext.Indicator.getSettings().getOption('i', Settings.Y_POS_SETTING_KEY),
Ext.Indicator.getSettings().getOption('i', Settings.WIDTH_SETTING_KEY),
Ext.Indicator.getSettings().getOption('i', Settings.HEIGHT_SETTING_KEY),
completeFileRecPath,
optionsRec,
(result, error) => {
if (error) {
Lib.TalkativeLog(`-&-ERROR(screencast execute) - ${error.message}`);
this.stop();
Ext.Indicator.doRecResult(false);
} else {
Lib.TalkativeLog(`-&-screencast execute - ${result[0]} - ${result[1]}`);
// draw area recording
if (Ext.Indicator.getSettings().getOption('b', Settings.SHOW_AREA_REC_SETTING_KEY))
this.AreaSelected = new Selection.AreaRecording();
let resultingFilePath = result[1];
if (resultingFilePath.endsWith('.undefined')) {
resultingFilePath = resultingFilePath.substring(0, resultingFilePath.length - '.undefined'.length);
resultingFilePath = `${resultingFilePath}${fileExt}`;
}
this._originalFilePath = result[1];
this._filePathWithExtension = resultingFilePath;
Ext.Indicator.doRecResult(result[0], resultingFilePath);
}
}
);
}
}
/**
* Stop recording
*
* @returns {boolean}
*/
stop() {
Lib.TalkativeLog('-&-stop video recording');
this._screenCastService.StopScreencastRemote((result, error) => {
if (error) {
Lib.TalkativeLog(`-&-ERROR(screencast stop) - ${error.message}`);
return false;
} else {
Lib.TalkativeLog(`-&-screencast stop - ${result[0]}`);
// rename the file...
Lib.TalkativeLog(`-&-screencast: rename ${this._originalFilePath} to ${this._filePathWithExtension}`);
const sourceFile = Gio.File.new_for_path(this._originalFilePath);
const destFile = Gio.File.new_for_path(this._filePathWithExtension);
sourceFile.move(destFile, 0, null, null);
}
// clear area recording
if (this.AreaSelected !== null && this.AreaSelected.isVisible())
this.AreaSelected.clearArea();
return true;
});
}
// without file extension
_generateFileName(template) {
template = template.replaceAll('%d', '%0x').replaceAll('%t', '%0X');
const datetime = GLib.DateTime.new_now_local();
let result = datetime.format(template);
result = result.replaceAll(' ', '_'); // remove white space
result = result.replaceAll('/', '_'); // remove path separators
return result;
}
});

View File

@ -1,326 +0,0 @@
/*
Copyright (C) 2015 Borsato Ivano
The JavaScript code in this page is free software: you can
redistribute it and/or modify it under the terms of the GNU
General Public License (GNU GPL) as published by the Free Software
Foundation, either version 3 of the License, or (at your option)
any later version. The code is distributed WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU GPL for more details.
*/
'use strict';
import GObject from 'gi://GObject';
import GLib from 'gi://GLib';
import Gst from 'gi://Gst?version=1.0';
import * as Lib from './convenience.js';
export const HelperWebcam = GObject.registerClass({
GTypeName: 'EasyScreenCast_HelperWebcam',
}, class HelperWebcam extends GObject.Object {
/**
* Create a device monitor inputvideo
*
* @param {string} unspecifiedWebcamText localized text for "Unspecified Webcam"
*/
constructor(unspecifiedWebcamText) {
super();
this._unspecified_webcam_text = unspecifiedWebcamText;
Lib.TalkativeLog('-@-init webcam');
var [result, _] = Gst.init_check(null);
Lib.TalkativeLog(`-@-gstreamer init result: ${result}`);
if (!result) {
Lib.TalkativeLog('-@-gstreamer init failed');
throw new Error('GStreamer init failed');
}
// get gstreamer lib version
var [M, m, micro, nano] = Gst.version();
Lib.TalkativeLog(
`-@-gstreamer version: ${M}.${m}.${micro}.${nano}`
);
if (M === 1 && m >= 8) {
// gstreamer version equal or higher 1.8
this.deviceMonitor = new Gst.DeviceMonitor({
show_all: true,
});
} else {
// gstreamer version lower 1.8
this.deviceMonitor = new Gst.DeviceMonitor();
}
// get gstreamer plugin avaiable
let registry = new Gst.Registry();
let listPI = registry.get_plugin_list();
Lib.TalkativeLog(`-@-plugin list: ${listPI.length}`);
for (var ind in listPI) {
Lib.TalkativeLog(
`-@-plugin name: ${
listPI[ind].get_name()
} Pfilename: ${
listPI[ind].get_filename()
} Pdesc: ${
listPI[ind].get_description()
} Pversion: ${
listPI[ind].get_version()
} Pload: ${
listPI[ind].is_loaded()}`
);
}
// create device monitor
if (this.deviceMonitor !== null && this.deviceMonitor !== undefined) {
Lib.TalkativeLog('-@-device monitor created');
this.dmBus = this.deviceMonitor.get_bus();
if (this.dmBus !== null && this.dmBus !== undefined) {
Lib.TalkativeLog('-@-dbus created');
this.dmBus.add_watch(GLib.PRIORITY_DEFAULT, this._getMsg.bind(this));
let caps = Gst.Caps.new_empty_simple('video/x-raw');
this.deviceMonitor.add_filter('Video/Source', caps);
this.startMonitor();
// update device and caps
this.refreshAllInputVideo();
} else {
Lib.TalkativeLog('-@-ERROR dbus creation');
}
} else {
Lib.TalkativeLog('-@-ERROR device monitor creation');
}
}
/**
* Callback for the DeviceMonitor watcher
*
* @param {Gst.Bus} bus the DeviceMonitor Bus of gstreamer
* @param {Gst.Message} message the message
* @returns {boolean}
*/
_getMsg(bus, message) {
Lib.TalkativeLog('-@-event getmsg');
switch (message.type) {
case Gst.MessageType.DEVICE_ADDED:
Lib.TalkativeLog('Device added');
// update device and caps
this.refreshAllInputVideo();
break;
case Gst.MessageType.DEVICE_REMOVED:
Lib.TalkativeLog('Device removed');
// update device and caps
this.refreshAllInputVideo();
break;
default:
Lib.TalkativeLog('Device UNK');
break;
}
return GLib.SOURCE_CONTINUE;
}
/**
* refresh all devices info
*/
refreshAllInputVideo() {
Lib.TalkativeLog('-@-refresh all video input');
this._listDevices = this.getDevicesIV();
// compose devices array
this._listCaps = [];
for (var index in this._listDevices) {
this._listCaps[index] = this.getCapsForIV(this._listDevices[index].caps);
Lib.TalkativeLog(`-@-webcam /dev/video${index} name: ${this._listDevices[index].display_name}`);
Lib.TalkativeLog(`-@-caps available: ${this._listCaps[index].length}`);
Lib.TalkativeLog(`-@-ListCaps[${index}]: ${this._listCaps[index]}`);
}
}
/**
* get caps from device.
* A single capability might look like: <code>video/x-raw, format=(string)YUY2, width=(int)640, height=(int)480, pixel-aspect-ratio=(fraction)1/1, framerate=(fraction)30/1</code>
* This encodes a single capability (fixed), but there might also be capabilities which represent options, e.g.
* <code>video/x-raw, format=(string)YUY2, width=(int)640, height=(int)480, pixel-aspect-ratio=(fraction)1/1, framerate=(fraction) { 30/1, 25/1, 20/1 }</code>.
* <br>
* The code here will take always the first option and unroll the options for framerate.
*
* @param {Gst.Caps} tmpCaps capabilities of a device
* @returns {string[]}
*/
getCapsForIV(tmpCaps) {
Lib.TalkativeLog('-@-get all caps from a input video');
Lib.TalkativeLog(`-@-caps available before filtering for video/x-raw: ${tmpCaps.get_size()}`);
let cleanCaps = [];
for (let i = 0; i < tmpCaps.get_size(); i++) {
let capsStructure = tmpCaps.get_structure(i);
// only consider "video/x-raw"
if (capsStructure.get_name() === 'video/x-raw') {
Lib.TalkativeLog(`-@-cap : ${i} : original : ${capsStructure.to_string()}`);
let tmpStr = 'video/x-raw';
let result, number, fraction;
result = capsStructure.get_string('format');
if (result !== null)
tmpStr += `, format=(string)${result}`;
[result, number] = capsStructure.get_int('width');
if (result === true)
tmpStr += `, width=(int)${number}`;
[result, number] = capsStructure.get_int('height');
if (result === true)
tmpStr += `, height=(int)${number}`;
[result, number, fraction] = capsStructure.get_fraction('pixel-aspect-ratio');
if (result === true)
tmpStr += `, pixel-aspect-ratio=(fraction)${number}/${fraction}`;
if (capsStructure.has_field('framerate')) {
[result, number, fraction] = capsStructure.get_fraction('framerate');
if (result === true) {
// a single framerate
this._addAndLogCapability(cleanCaps, i, `${tmpStr}, framerate=(fraction)${number}/${fraction}`);
} else {
// multiple framerates
// unfortunately GstValueList is not supported in this gjs-binding
// "Error: Don't know how to convert GType GstValueList to JavaScript object"
// -> capsStructure.get_value('framerate') <- won't work
// -> capsStructure.get_list('framerate') <- only returns the numerator of the fraction
//
// therefore manually parsing the framerate values from the string representation
let framerates = capsStructure.to_string();
framerates = framerates.substring(framerates.indexOf('framerate=(fraction){') + 21);
framerates = framerates.substring(0, framerates.indexOf('}'));
framerates.split(',').forEach(element => {
let [numerator, denominator] = element.split('/', 2);
this._addAndLogCapability(cleanCaps, i, `${tmpStr}, framerate=(fraction)${numerator.trim()}/${denominator.trim()}`);
});
}
} else {
// no framerate at all
this._addAndLogCapability(cleanCaps, i, tmpStr);
}
} else {
Lib.TalkativeLog(`-@-cap : ${i} : skipped : ${capsStructure.to_string()}`);
}
}
return cleanCaps;
}
/**
* Adds the capability str to the array caps, if it is not already there. Avoids duplicates.
*
* @param {Array} caps the list of capabilities
* @param {int} originalIndex index of the original capabilities list from the device
* @param {string} str the capability string to add
*/
_addAndLogCapability(caps, originalIndex, str) {
if (caps.indexOf(str) === -1) {
caps.push(str);
Lib.TalkativeLog(`-@-cap : ${originalIndex} : added cap : ${str}`);
} else {
Lib.TalkativeLog(`-@-cap : ${originalIndex} : ignore duplicated cap : ${str}`);
}
}
/**
* get devices IV
*
* @returns {Gst.Device[]}
*/
getDevicesIV() {
Lib.TalkativeLog('-@-get devices');
var list = this.deviceMonitor.get_devices();
Lib.TalkativeLog(`-@-devices number: ${list.length}`);
// Note:
// Although the computer may have just one webcam connected to
// it, more than one GstDevice may be listed and all pointing to
// the same video device (for example /dev/video0. Each
// GstDevice is supposed to be used with a specific source, for
// example, a pipewiresrc or a v4l2src. For now, we are only
// using v4l2src.
// See also: Gst.DeviceMonitor.get_providers: pipewiredeviceprovider,decklinkdeviceprovider,v4l2deviceprovider
// CLI: "/usr/bin/gst-device-monitor-1.0 Video/Source"
//
// So, here we filter the devices, that have a device.path property, which
// means, these are only v4l2 devices
var filtered = list.filter(device => {
let props = device.get_properties();
let hasDevice = props != null && props.get_string('device.path') !== null;
if (props != null)
props.free();
return hasDevice;
});
Lib.TalkativeLog(`-@-devices number after filtering for v4l2: ${filtered.length}`);
return filtered;
}
/**
* get array name devices IV
*
* @returns {Array}
*/
getNameDevices() {
Lib.TalkativeLog('-@-get name devices');
let tmpArray = [];
for (var index in this._listDevices) {
var wcName = this._unspecified_webcam_text;
if (this._listDevices[index].display_name !== '')
wcName = this._listDevices[index].display_name;
tmpArray.push(wcName);
}
Lib.TalkativeLog(`-@-list devices name: ${tmpArray}`);
return tmpArray;
}
/**
* get array caps
*
* @param {int} index device
* @returns {string[]}
*/
getListCapsDevice(index) {
const tmpArray = this._listCaps[index];
Lib.TalkativeLog(`-@-list caps of device: ${tmpArray}`);
return tmpArray;
}
/**
* start listening
*/
startMonitor() {
Lib.TalkativeLog('-@-start video devicemonitor');
this.deviceMonitor.start();
}
/**
* Stop listening
*/
stopMonitor() {
Lib.TalkativeLog('-@-stop video devicemonitor');
this.disconnectSourceBus();
this.deviceMonitor.stop();
}
/**
* disconect bus
*/
disconnectSourceBus() {
if (this.dmBusId) {
this.dmBus.disconnect(this.dmBusId);
this.dmBusId = 0;
}
}
});

View File

@ -11,9 +11,10 @@
"45",
"46",
"47",
"48"
"48",
"49"
],
"url": "https://github.com/corecoding/Vitals",
"uuid": "Vitals@CoreCoding.com",
"version": 71
"version": 73
}

View File

@ -230,16 +230,20 @@ export const Sensors = GObject.registerClass({
let hertz = (sum / freqs.length) * 1000 * 1000;
this._returnValue(callback, 'Frequency', hertz, 'processor', 'hertz');
//let max_hertz = Math.getMaxOfArray(freqs) * 1000 * 1000;
//this._returnValue(callback, 'Boost', max_hertz, 'processor', 'hertz');
let max_hertz = freqs.reduce((a, b) => Math.max(a, b)) * 1000 * 1000;
this._returnValue(callback, 'Max frequency', max_hertz, 'processor', 'hertz');
let min_hertz = freqs.reduce((a, b) => Math.min(a, b)) * 1000 * 1000;
this._returnValue(callback, 'Min frequency', min_hertz, 'processor', 'hertz');
}).catch(err => { });
// if frequency scaling is enabled, cpu-freq reports
} else if (Object.values(this._last_processor['speed']).length > 0) {
let sum = this._last_processor['speed'].reduce((a, b) => a + b);
let hertz = (sum / this._last_processor['speed'].length) * 1000;
this._returnValue(callback, 'Frequency', hertz, 'processor', 'hertz');
//let max_hertz = Math.getMaxOfArray(this._last_processor['speed']) * 1000;
//this._returnValue(callback, 'Boost', max_hertz, 'processor', 'hertz');
let max_hertz = this._last_processor['speed'].reduce((a, b) => Math.max(a, b)) * 1000;
this._returnValue(callback, 'Max frequency', max_hertz, 'processor', 'hertz');
let min_hertz = this._last_processor['speed'].reduce((a, b) => Math.min(a, b)) * 1000;
this._returnValue(callback, 'Min frequency', min_hertz, 'processor', 'hertz');
}
}
@ -455,7 +459,7 @@ export const Sensors = GObject.registerClass({
this._returnValue(callback, 'Energy (now)', output['ENERGY_NOW'], 'battery', 'watt-hour');
}
if ('ENERGY_FULL' in output && 'ENERGY_NOW' in output && 'POWER_NOW' in output && output['POWER_NOW'] > 0 && 'STATUS' in output && (output['STATUS'] == 'Charging' || output['STATUS'] == 'Discharging')) {
if ('ENERGY_FULL' in output && 'ENERGY_NOW' in output && 'POWER_NOW' in output && output['POWER_NOW'] !== 0 && 'STATUS' in output && (output['STATUS'] == 'Charging' || output['STATUS'] == 'Discharging')) {
let timeLeft = 0;
@ -463,7 +467,7 @@ export const Sensors = GObject.registerClass({
if (output['STATUS'] == 'Charging') {
timeLeft = ((output['ENERGY_FULL'] - output['ENERGY_NOW']) / output['POWER_NOW']);
} else {
timeLeft = (output['ENERGY_NOW'] / output['POWER_NOW']);
timeLeft = (output['ENERGY_NOW'] / Math.abs(output['POWER_NOW']));
}
// don't process Infinity values
@ -644,7 +648,7 @@ export const Sensors = GObject.registerClass({
if(vendor === "0x1002") {
// read GPU usage and create group lebel for card
new FileModule.File('/sys/class/drm/card'+i+'/device/gpu_busy_percent').read().then(value => {
// create group
// create group
this._returnGpuValue(callback, 'Graphics', parseInt(value) * 0.01, typeName + '-group', 'percent');
this._returnGpuValue(callback, 'Vendor', "AMD", typeName, 'string');
this._returnGpuValue(callback, 'Usage', parseInt(value) * 0.01, typeName, 'percent');

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 16 16"
version="1.1"
id="svg2"
sodipodi:docname="custom-icons-symbolic.svg"
inkscape:version="1.4 (1:1.4+202410161351+e7c3feb100)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs2" />
<sodipodi:namedview
id="namedview2"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="31.404053"
inkscape:cx="7.4671891"
inkscape:cy="8.9001252"
inkscape:window-width="1860"
inkscape:window-height="1011"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" />
<g
fill="#222222"
id="g3"
transform="translate(-0.16009403)">
<path
d="m 5.207031,1.292969 c -0.390625,-0.390625 -1.023437,-0.390625 -1.414062,0 l -2.5,2.5 c -0.390625,0.390625 -0.390625,1.023437 0,1.414062 l 2.5,2.5 c 0.390625,0.390625 1.023437,0.390625 1.414062,0 l 2.496094,-2.5 c 0.390625,-0.390625 0.390625,-1.023437 0,-1.414062 z m 0,0"
id="path1-3" />
<path
d="m 10,12 c 0,1.65625 -1.34375,3 -3,3 -1.660156,0 -3,-1.34375 -3,-3 0,-1.65625 1.339844,-3 3,-3 1.65625,0 3,1.34375 3,3 z m 0,0"
id="path2-6" />
<path
d="M 11.945312,2.003906 C 11.605469,2.019531 11.300781,2.207031 11.132812,2.5 L 8.8125,6.5 C 8.425781,7.164062 8.90625,8 9.675781,8 h 4.644531 c 0.769532,0 1.25,-0.835938 0.863282,-1.5 l -2.320313,-4 C 12.675781,2.171875 12.320312,1.980469 11.945312,2.003906 Z m 0,0"
id="path3" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path d="M12.5 2A2.5 2.5 0 0 0 10 4.5 2.5 2.5 0 0 0 12.5 7 2.5 2.5 0 0 0 15 4.5 2.5 2.5 0 0 0 12.5 2zm0 1A1.5 1.5 0 0 1 14 4.5 1.5 1.5 0 0 1 12.5 6 1.5 1.5 0 0 1 11 4.5 1.5 1.5 0 0 1 12.5 3zm-9 5A2.5 2.5 0 0 0 1 10.5 2.5 2.5 0 0 0 3.5 13a2.5 2.5 0 0 0 2.443-2H15v-1H5.95A2.5 2.5 0 0 0 3.5 8zm0 1A1.5 1.5 0 0 1 5 10.5 1.5 1.5 0 0 1 3.5 12 1.5 1.5 0 0 1 2 10.5 1.5 1.5 0 0 1 3.5 9z" style="fill:gray;stroke-width:.70228"/><path d="M-9 4h8v1h-8z" style="fill:gray;fill-opacity:1;stroke-width:1.05153;opacity:.5" transform="scale(-1 1)"/></svg>

After

Width:  |  Height:  |  Size: 600 B

View File

@ -8,9 +8,10 @@
"45",
"46",
"47",
"48"
"48",
"49"
],
"url": "https://github.com/ubuntu/gnome-shell-extension-appindicator",
"uuid": "appindicatorsupport@rgcjonas.gmail.com",
"version": 60
"version": 61
}

View File

@ -0,0 +1,240 @@
/* exported GeneralPage*/
import Adw from 'gi://Adw';
import Gtk from 'gi://Gtk';
import GObject from 'gi://GObject';
import Gio from 'gi://Gio';
import GLib from 'gi://GLib';
import {gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
const IconData = GObject.registerClass({
GTypeName: 'IconData',
Properties: {
'id': GObject.ParamSpec.string(
'id',
'Id',
'A read and write string property',
GObject.ParamFlags.READWRITE,
''
),
'name': GObject.ParamSpec.string(
'name',
'Name',
'A read and write string property',
GObject.ParamFlags.READWRITE,
''
),
'at-name': GObject.ParamSpec.string(
'at-name',
'At Name',
'A read and write string property',
GObject.ParamFlags.READWRITE,
''
),
},
}, class IconData extends GObject.Object {
constructor(props = {}) {
super(props);
}
get id() {
if (this._id === 'undefined')
this._id = null;
return this._id;
}
set id(value) {
if (this.id === value)
return;
this._id = value;
this.notify('id');
}
get name() {
if (this._name === 'undefined')
this._name = null;
return this._name;
}
set name(value) {
if (this.name === value)
return;
this._name = value;
this.notify('name');
}
get atName() {
if (this._atName === 'undefined')
this._atName = null;
return this._atName;
}
set atName(value) {
if (this.atName === value)
return;
this._atName = value;
this.notify('at-name');
}
});
export var CustomIconPage = GObject.registerClass(
class AppIndicatorCustomIconPage extends Adw.PreferencesPage {
_init(settings, settingsKey) {
super._init({
title: _('Custom Icons'),
icon_name: 'custom-icons-symbolic',
name: 'Custom Icons Page',
});
this._settings = settings;
this._settingsKey = settingsKey;
this.group = new Adw.PreferencesGroup();
this.container = new Gtk.Box({
halign: Gtk.Align.FILL,
hexpand: true,
orientation: Gtk.Orientation.VERTICAL,
});
this.listStore = new Gio.ListStore({
item_type: IconData,
});
const initList = this._settings.get_value(this._settingsKey.CUSTOM_ICONS).deep_unpack();
initList.forEach(pair => {
let data = new IconData({
id: pair[0],
name: pair[1],
atName: pair[2],
});
this.listStore.append(data);
});
this.listStore.append(new IconData({id: '', name: '', atName: ''}));
this.selectionModel = new Gtk.SingleSelection({
model: this.listStore,
});
this.columnView = new Gtk.ColumnView({
hexpand: true,
model: this.selectionModel,
reorderable: false,
});
const columnTitles = [
_('Indicator ID'),
_('Icon Name'),
_('Attention Icon Name'),
];
const indicatorIdColumn = new Gtk.ColumnViewColumn({
title: columnTitles[0],
expand: true,
});
const iconNameColumn = new Gtk.ColumnViewColumn({
title: columnTitles[1],
expand: true,
});
const attentionIconNameColumn = new Gtk.ColumnViewColumn({
title: columnTitles[2],
expand: true,
});
const factoryId = new Gtk.SignalListItemFactory();
factoryId.connect('setup', (_widget, item) => {
const label = new Gtk.EditableLabel({text: ''});
item.set_child(label);
});
factoryId.connect('bind', (_widget, item) => {
const label = item.get_child();
const data = item.get_item();
label.set_text(data.id);
label.bind_property('text', data, 'id', GObject.BindingFlags.SYNC_CREATE);
label.connect('changed', () => this._autoSave(item));
});
const factoryName = new Gtk.SignalListItemFactory();
factoryName.connect('setup', (_widget, item) => {
const label = new Gtk.EditableLabel({text: ''});
item.set_child(label);
});
factoryName.connect('bind', (_widget, item) => {
const label = item.get_child();
const data = item.get_item();
label.set_text(data.name);
label.bind_property('text', data, 'name', GObject.BindingFlags.SYNC_CREATE);
label.connect('changed', () => this._autoSave(item));
});
const factoryItName = new Gtk.SignalListItemFactory();
factoryItName.connect('setup', (_widget, item) => {
const label = new Gtk.EditableLabel({text: ''});
item.set_child(label);
});
factoryItName.connect('bind', (_widget, item) => {
const label = item.get_child();
const data = item.get_item();
label.set_text(data.atName);
label.bind_property('text', data, 'at-name', GObject.BindingFlags.SYNC_CREATE);
label.connect('changed', () => this._autoSave(item));
});
indicatorIdColumn.set_factory(factoryId);
iconNameColumn.set_factory(factoryName);
attentionIconNameColumn.set_factory(factoryItName);
this.columnView.append_column(indicatorIdColumn);
this.columnView.append_column(iconNameColumn);
this.columnView.append_column(attentionIconNameColumn);
this.container.append(this.columnView);
this.group.add(this.container);
this.add(this.group);
}
_autoSave(item) {
const storeLength = this.listStore.get_n_items();
const newStore = [];
for (let i = 0; i < storeLength; i++) {
const currentItem = this.listStore.get_item(i);
const id = currentItem.id;
const name = currentItem.name;
const atName = currentItem.atName;
if (id && name)
newStore.push([id, name, atName || '']);
}
this._settings.set_value(this._settingsKey.CUSTOM_ICONS,
new GLib.Variant('a(sss)', newStore));
const id = item.get_item().id;
const name = item.get_item().name;
/* dynamic new entry*/
if (storeLength === 1 && (id || name))
this.listStore.append(new IconData({id: '', name: '', atName: ''}));
if (storeLength > 1) {
if ((id || name) && item.get_position() >= storeLength - 1)
this.listStore.append(new IconData({id: '', name: '', atName: ''}));
}
}
_autoDelete(item) {
const storeLength = this.listStore.get_n_items();
const id = item.get_item().id;
const name = item.get_item().name;
if (storeLength > 1 && !id && !name &&
item.get_position() < storeLength - 1)
this.listStore.remove(item.get_position());
}
});

View File

@ -0,0 +1,141 @@
/* exported GeneralPage*/
import Adw from 'gi://Adw';
import Gtk from 'gi://Gtk';
import GObject from 'gi://GObject';
import {gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
export var GeneralPage = GObject.registerClass(
class AppIndicatorGeneralPage extends Adw.PreferencesPage {
_init(settings, settingsKey) {
super._init({
title: _('General'),
icon_name: 'general-preferences-symbolic',
name: 'General Page',
});
this._settings = settings;
this._settingsKey = settingsKey;
this.group = new Adw.PreferencesGroup(); // no title since only single group
const legacyTraySwitch = new Adw.SwitchRow({
title: _('Enable Legacy Tray Icons support'),
subtitle: _('Add X11 legacy tray icons to the panel area'),
active: this._settings.get_boolean(this._settingsKey.LEGACY_TRAY_ENABLED),
});
legacyTraySwitch.connect('notify::active', widget =>
this._settings.set_boolean(this._settingsKey.LEGACY_TRAY_ENABLED,
widget.get_active()));
this.group.add(legacyTraySwitch);
this._createSpinRow({
title: _('Opacity'),
settingsKey: this._settingsKey.ICON_OPACITY,
from: 0,
to: 255,
step: 1,
round: true,
});
this._createSpinRow({
title: _('Desaturation'),
settingsKey: this._settingsKey.ICON_SATURATION,
from: 0.0,
to: 1.0,
step: 0.1,
});
this._createSpinRow({
title: _('Brightness'),
settingsKey: this._settingsKey.ICON_BRIGHTNESS,
from: -1.0,
to: 1.0,
step: 0.1,
});
this._createSpinRow({
title: _('Contrast'),
settingsKey: this._settingsKey.ICON_CONTRAST,
from: -1.0,
to: 1.0,
step: 0.1,
});
this._createSpinRow({
title: _('Icon Size'),
settingsKey: this._settingsKey.ICON_SIZE,
from: 0,
to: 96,
step: 2,
round: true,
});
const alignmentList = new Gtk.StringList();
const comboItems = [
{pos: 'center', label: _('Center')},
{pos: 'left', label: _('Left')},
{pos: 'right', label: _('Right')},
];
comboItems.forEach(item => alignmentList.append(item.label));
const combo = new Adw.ComboRow({
title: _('Tray Horizontal Alignment'),
model: alignmentList,
});
const trayPos = this._settings.get_string(this._settingsKey.TRAY_POS);
combo.set_selected(comboItems.findIndex(item => trayPos === item.pos));
combo.connect('notify::selected', widget => this._settings.set_string(
this._settingsKey.TRAY_POS, comboItems[widget.get_selected()].pos));
this.group.add(combo);
this.add(this.group);
}
_createSpinRow(args) {
let title = args.title || 'Default Title';
let subtitle = args.subtitle || null;
let settingsKey = args.settingsKey || '';
let from = args.from || 0;
let to = args.to || 100;
let step = args.step || 1;
let round = args.round || false;
let spin = Adw.SpinRow.new_with_range(from, to, step);
spin.title = title;
if (subtitle !== null)
spin.subtitle = subtitle;
if (round)
spin.set_value(this._settings.get_int(settingsKey));
else
spin.set_value(this._settings.get_int(settingsKey));
spin.connect('input', widget => {
if (round)
this._settings.set_int(settingsKey, parseInt(widget.get_value(), 10));
else
this._settings.set_double(settingsKey, widget.get_value());
return false;
});
spin.connect('output', widget => {
if (round)
this._settings.set_int(settingsKey, parseInt(widget.get_value(), 10));
else
this._settings.set_double(settingsKey, widget.get_value());
return false;
});
this.group.add(spin);
}
});

View File

@ -11,10 +11,10 @@
"original-author": "mi-jan-sena@proton.me",
"settings-schema": "org.gnome.shell.extensions.auto-activities",
"shell-version": [
"48"
"49"
],
"url": "https://github.com/CleoMenezesJr/auto-activities",
"uuid": "auto-activities@CleoMenezesJr.github.io",
"version": 15,
"version-name": "48.0"
"version": 16,
"version-name": "49.0"
}

View File

@ -1,4 +1,5 @@
import St from 'gi://St';
import GLib from 'gi://GLib';
import Meta from 'gi://Meta';
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
@ -83,23 +84,34 @@ export const PanelBlur = class PanelBlur {
}
blur_dtp_panels() {
// FIXME when Dash to Panel changes its size, it seems it creates new
// panels; but I can't get to delete old widgets
// blur every panel found
global.dashToPanel.panels.forEach(p => {
this.maybe_blur_panel(p.panel);
// Defer the blurring to the next idle cycle.
// This is crucial to ensure the panel actors have been allocated their
// final size and position by the compositor, avoiding race conditions
// during extension startup.
GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => {
if (!global.dashToPanel?.panels) {
return GLib.SOURCE_REMOVE;
}
this._log("Blurring Dash to Panel panels after idle.");
// blur every panel found
global.dashToPanel.panels.forEach(p => {
this.maybe_blur_panel(p.panel);
});
// if main panel is not included in the previous panels, blur it
if (
!global.dashToPanel.panels
.map(p => p.panel)
.includes(Main.panel)
&&
this.settings.dash_to_panel.BLUR_ORIGINAL_PANEL
)
this.maybe_blur_panel(Main.panel);
return GLib.SOURCE_REMOVE;
});
// if main panel is not included in the previous panels, blur it
if (
!global.dashToPanel.panels
.map(p => p.panel)
.includes(Main.panel)
&&
this.settings.dash_to_panel.BLUR_ORIGINAL_PANEL
)
this.maybe_blur_panel(Main.panel);
};
/// Blur a panel only if it is not already blurred (contained in the list)
@ -438,11 +450,15 @@ export const PanelBlur = class PanelBlur {
let same_monitor = actors.monitor.index == window_monitor_i;
let window_vertical_pos = meta_window.get_frame_rect().y;
let window_vertical_bottom = window_vertical_pos + meta_window.get_frame_rect().height;
// if so, and if in the same monitor, then it overlaps
if (same_monitor
&&
window_vertical_pos < panel_bottom + 5 * scale
// check if panel is on top
((panel_top === 0 && window_vertical_pos < panel_bottom + 5 * scale) ||
// check if panel is at the bottom
(panel_top > 0 && window_vertical_bottom > panel_top - 5 * scale))
)
window_overlap_panel = true;
});

View File

@ -18,9 +18,10 @@
"shell-version": [
"46",
"47",
"48"
"48",
"49"
],
"url": "https://github.com/aunetx/blur-my-shell",
"uuid": "blur-my-shell@aunetx",
"version": 68
"version": 70
}

View File

@ -8,8 +8,8 @@
{ "wmClass": "jetbrains-datagrip", "wmTitle": "splash", "mode": "float" },
{ "wmClass": "jetbrains-rubymine", "wmTitle": "splash", "mode": "float" },
{ "wmClass": "jetbrains-idea", "wmTitle": "splash", "mode": "float" },
{ "wmClass": "Com.github.amezin.ddterm", "mode": "float" },
{ "wmClass": "Com.github.donadigo.eddy", "mode": "float" },
{ "wmClass": "com.github.amezin.ddterm", "mode": "float" },
{ "wmClass": "com.github.donadigo.eddy", "mode": "float" },
{ "wmClass": "Conky", "mode": "float" },
{ "wmClass": "Gnome-initial-setup", "mode": "float" },
{ "wmClass": "org.gnome.Calculator", "mode": "float" },

View File

@ -831,6 +831,7 @@ export class Tree extends Node {
let metaWindow = next.nodeValue;
if (!metaWindow) return null;
const previousMetaWindow = this.extWm.focusMetaWindow;
if (metaWindow.minimized) {
next = this.focus(next, direction);
} else {
@ -838,14 +839,18 @@ export class Tree extends Node {
metaWindow.focus(global.display.get_current_time());
metaWindow.activate(global.display.get_current_time());
const monitorArea = metaWindow.get_work_area_current_monitor();
const ptr = this.extWm.getPointer();
const pointerInside = Utils.rectContainsPoint(monitorArea, [ptr[0], ptr[1]]);
const monitorChanged =
!!previousMetaWindow &&
previousMetaWindow.get_monitor &&
previousMetaWindow.get_monitor() !== metaWindow.get_monitor();
if (this.settings.get_boolean("move-pointer-focus-enabled")) {
this.extWm.movePointerWith(next);
} else {
let monitorArea = metaWindow.get_work_area_current_monitor();
let ptr = this.extWm.getPointer();
if (!Utils.rectContainsPoint(monitorArea, [ptr[0], ptr[1]])) {
this.extWm.movePointerWith(next);
}
} else if (!pointerInside) {
this.extWm.movePointerWith(next, { force: monitorChanged });
}
}
return next;

View File

@ -67,8 +67,7 @@ export class WindowManager extends GObject.Object {
this.prefsTitle = `Forge ${_("Settings")} - ${
!production ? "DEV" : `${PACKAGE_VERSION}-${ext.metadata.version}`
}`;
this.windowProps = this.ext.configMgr.windowProps;
this.windowProps.overrides = this.windowProps.overrides.filter((override) => !override.wmId);
this.reloadWindowOverrides();
this._kbd = this.ext.keybindings;
this._tree = new Tree(this);
this.eventQueue = new Queue();
@ -98,11 +97,12 @@ export class WindowManager extends GObject.Object {
}
addFloatOverride(metaWindow, withWmId) {
let overrides = this.windowProps.overrides;
let currentProps = this.ext.configMgr.windowProps;
let overrides = currentProps.overrides;
let wmClass = metaWindow.get_wm_class();
let wmId = metaWindow.get_id();
for (let override in overrides) {
for (let override of overrides) {
// if the window is already floating
if (override.wmClass === wmClass && override.mode === "float" && !override.wmTitle) return;
}
@ -111,12 +111,15 @@ export class WindowManager extends GObject.Object {
wmId: withWmId ? wmId : undefined,
mode: "float",
});
this.windowProps.overrides = overrides;
this.ext.configMgr.windowProps = this.windowProps;
// Save the updated overrides back to the ConfigManager
currentProps.overrides = overrides;
this.ext.configMgr.windowProps = currentProps;
}
removeFloatOverride(metaWindow, withWmId) {
let overrides = this.windowProps.overrides;
let currentProps = this.ext.configMgr.windowProps;
let overrides = currentProps.overrides;
let wmClass = metaWindow.get_wm_class();
let wmId = metaWindow.get_id();
overrides = overrides.filter(
@ -129,8 +132,9 @@ export class WindowManager extends GObject.Object {
)
);
this.windowProps.overrides = overrides;
this.ext.configMgr.windowProps = this.windowProps;
// Save the updated overrides back to the ConfigManager
currentProps.overrides = overrides;
this.ext.configMgr.windowProps = currentProps;
}
toggleFloatingMode(action, metaWindow) {
@ -285,6 +289,11 @@ export class WindowManager extends GObject.Object {
settings.connect("changed", (_, settingName) => {
switch (settingName) {
case "window-overrides-reload-trigger":
// Reload window overrides when triggered by preferences
// This prevents the main extension from overwriting changes made by preferences
this.reloadWindowOverrides();
break;
case "focus-border-toggle":
this.renderTree(settingName);
break;
@ -951,9 +960,16 @@ export class WindowManager extends GObject.Object {
move(metaWindow, rect) {
if (!metaWindow) return;
if (metaWindow.grabbed) return;
metaWindow.unmaximize(Meta.MaximizeFlags.HORIZONTAL);
metaWindow.unmaximize(Meta.MaximizeFlags.VERTICAL);
metaWindow.unmaximize(Meta.MaximizeFlags.BOTH);
try {
// GNOME 49+
metaWindow.set_unmaximize_flags(Meta.MaximizeFlags.BOTH);
metaWindow.unmaximize();
} catch (e) {
// pre-49 fallback
metaWindow.unmaximize(Meta.MaximizeFlags.HORIZONTAL);
metaWindow.unmaximize(Meta.MaximizeFlags.VERTICAL);
metaWindow.unmaximize(Meta.MaximizeFlags.BOTH);
}
let windowActor = metaWindow.get_compositor_private();
if (!windowActor) return;
@ -1235,7 +1251,13 @@ export class WindowManager extends GObject.Object {
let tilingModeEnabled = this.ext.settings.get_boolean("tiling-mode-enabled");
let gap = this.calculateGaps(nodeWindow);
let maximized = () => {
return metaWindow.get_maximized() === 3 || metaWindow.is_fullscreen() || gap === 0;
try {
// GNOME 49+
return metaWindow.is_maximized() || metaWindow.is_fullscreen() || gap === 0;
} catch (e) {
// pre-49 fallback
return metaWindow.get_maximized() === 3 || metaWindow.is_fullscreen() || gap === 0;
}
};
let monitorCount = global.display.get_n_monitors();
let tiledChildren = this.tree.getTiledChildren(nodeWindow.parentNode.childNodes);
@ -1287,7 +1309,18 @@ export class WindowManager extends GObject.Object {
}
}
if (gap === 0 || metaWindow.get_maximized() === 1 || metaWindow.get_maximized() === 2) {
if (
gap === 0 ||
(() => {
try {
// GNOME 49+
return metaWindow.is_maximized();
} catch (e) {
// pre-49 fallback
return metaWindow.get_maximized() === 1 || metaWindow.get_maximized() === 2;
}
})()
) {
inset = 0;
}
@ -1506,9 +1539,16 @@ export class WindowManager extends GObject.Object {
{
name: "window-create-queue",
callback: () => {
metaWindow.unmaximize(Meta.MaximizeFlags.HORIZONTAL);
metaWindow.unmaximize(Meta.MaximizeFlags.VERTICAL);
metaWindow.unmaximize(Meta.MaximizeFlags.BOTH);
try {
// GNOME 49+
metaWindow.set_unmaximize_flags(Meta.MaximizeFlags.BOTH);
metaWindow.unmaximize();
} catch (e) {
// pre-49 fallback
metaWindow.unmaximize(Meta.MaximizeFlags.HORIZONTAL);
metaWindow.unmaximize(Meta.MaximizeFlags.VERTICAL);
metaWindow.unmaximize(Meta.MaximizeFlags.BOTH);
}
this.renderTree("window-create", true);
},
},
@ -1727,7 +1767,17 @@ export class WindowManager extends GObject.Object {
this._handleMoving(focusNodeWindow);
}
} else {
if (focusMetaWindow.get_maximized() === 0) {
if (
(() => {
try {
// GNOME 49+
return !focusMetaWindow.is_maximized();
} catch (e) {
// pre-49 fallback
return focusMetaWindow.get_maximized() === 0;
}
})()
) {
this.renderTree(from);
}
}
@ -1765,9 +1815,18 @@ export class WindowManager extends GObject.Object {
let monWsNoMaxWindows = activeWsNode.getNodeByType(NODE_TYPES.MONITOR).filter((monitor) => {
return (
monitor.getNodeByType(NODE_TYPES.WINDOW).filter((w) => {
return (
w.nodeValue.get_maximized() === Meta.MaximizeFlags.BOTH || w.nodeValue.is_fullscreen()
);
return (() => {
try {
// GNOME 49+
return w.nodeValue.is_maximized() || w.nodeValue.is_fullscreen();
} catch (e) {
// pre-49 fallback
return (
w.nodeValue.get_maximized() === Meta.MaximizeFlags.BOTH ||
w.nodeValue.is_fullscreen()
);
}
})();
}).length === 0
);
});
@ -1814,9 +1873,10 @@ export class WindowManager extends GObject.Object {
* This is useful for making sure that Forge calculates the attachNode
* properly
*/
movePointerWith(nodeWindow) {
movePointerWith(nodeWindow, { force = false } = {}) {
if (!nodeWindow || !nodeWindow._data) return;
if (this.ext.settings.get_boolean("move-pointer-focus-enabled")) {
const shouldWarp = force || this.ext.settings.get_boolean("move-pointer-focus-enabled");
if (shouldWarp) {
this.storePointerLastPosition(this.lastFocusedWindow);
if (this.canMovePointerInsideNodeWindow(nodeWindow)) {
this.warpPointerToNodeWindow(nodeWindow);
@ -2410,7 +2470,17 @@ export class WindowManager extends GObject.Object {
}
this._grabCleanup(focusNodeWindow);
if (focusMetaWindow.get_maximized() === 0) {
if (
(() => {
try {
// GNOME 49+
return !focusMetaWindow.is_maximized();
} catch (e) {
// pre-49 fallback
return focusMetaWindow.get_maximized() === 0;
}
})()
) {
this.renderTree("grab-op-end");
}
@ -2708,6 +2778,20 @@ export class WindowManager extends GObject.Object {
return `mo${display.get_current_monitor()}`;
}
/**
* Reload window overrides from the configuration file
* This is called when the preferences page modifies the overrides
*/
reloadWindowOverrides() {
// Get fresh data from the ConfigManager
const freshProps = this.ext.configMgr.windowProps;
if (freshProps) {
this.windowProps = freshProps;
this.windowProps.overrides = this.windowProps.overrides.filter((override) => !override.wmId);
Logger.info(`Reloaded ${this.windowProps.overrides.length} window overrides from file`);
}
}
floatAllWindows() {
this.tree.getNodeByType(NODE_TYPES.WINDOW).forEach((w) => {
if (w.isFloat()) {

Some files were not shown because too many files have changed in this diff Show More