[gnome] Update gnome extensions
@ -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
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
@ -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);
|
||||
},
|
||||
};
|
||||
|
Before Width: | Height: | Size: 4.2 KiB |
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
.options-grid {
|
||||
margin: 18px;
|
||||
}
|
||||
|
||||
.borderless {
|
||||
border: none;
|
||||
}
|
||||
@ -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>
|
||||
@ -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];
|
||||
}
|
||||
@ -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';
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -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;
|
||||
}
|
||||
});
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -11,9 +11,10 @@
|
||||
"45",
|
||||
"46",
|
||||
"47",
|
||||
"48"
|
||||
"48",
|
||||
"49"
|
||||
],
|
||||
"url": "https://github.com/corecoding/Vitals",
|
||||
"uuid": "Vitals@CoreCoding.com",
|
||||
"version": 71
|
||||
"version": 73
|
||||
}
|
||||
@ -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');
|
||||
|
||||
@ -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 |
@ -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 |
@ -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
|
||||
}
|
||||
@ -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());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -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"
|
||||
}
|
||||
@ -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;
|
||||
});
|
||||
|
||||
@ -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
|
||||
}
|
||||
@ -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" },
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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()) {
|
||||
|
||||