Last active
December 8, 2024 14:41
-
-
Save Maltysen/ef86043dcfb9a612074ccb86e70bdac8 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const Gi = imports._gi; | |
import GObject from 'gi://GObject'; | |
import {Extension} from 'resource:///org/gnome/shell/extensions/extension.js'; | |
import * as Main from 'resource:///org/gnome/shell/ui/main.js'; | |
import {_monkey_vfunc_key_press_event, _monkey_onCaptureButtonClicked} | |
from './monkey_functions.js'; | |
export default class ScreenshotExtension extends Extension { | |
// We monkey patch the Main.screenshotUI object to use our | |
// _monkey_vfunc_key_press_event and _monkey_onCaptureButtonClicked | |
// functions which decide whether to save to file or not | |
enable() { | |
Main.screenshotUI._old_vfunc_key_press_event = | |
Main.screenshotUI.vfunc_key_press_event; | |
// According to the first answer in this thread | |
// https://gitlab.gnome.org/GNOME/gjs/-/issues/470 | |
// this is how you alter vfuncs | |
Object.getPrototypeOf(Main.screenshotUI) | |
[Gi.gobject_prototype_symbol][Gi.hook_up_vfunc_symbol] | |
('key_press_event', _monkey_vfunc_key_press_event); | |
GObject.signal_handlers_disconnect_matched( | |
Main.screenshotUI._captureButton, | |
{signalId: 'clicked'}, | |
); | |
Main.screenshotUI._captureButton.connect('clicked', | |
_monkey_onCaptureButtonClicked.bind(Main.screenshotUI) | |
); | |
} | |
disable() { | |
GObject.signal_handlers_disconnect_matched( | |
Main.screenshotUI._captureButton, | |
{signalId: 'clicked'}, | |
); | |
Main.screenshotUI._captureButton.connect('clicked', | |
Main.screenshotUI._onCaptureButtonClicked.bind(Main.screenshotUI) | |
); | |
Object.getPrototypeOf(Main.screenshotUI) | |
[Gi.gobject_prototype_symbol][Gi.hook_up_vfunc_symbol] | |
('key_press_event', Main.screenshotUI._old_vfunc_key_press_event); | |
delete Main.screenshotUI._old_vfunc_key_press_event; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"uuid": "[email protected]", | |
"name": "Screenshot Copy Patch", | |
"description": "Let's you copy screenshots to clipboard without saving them to disk.\n\nEnter/Space/Ctrl-C/click now only copies to clipboard, and Ctrl-S also saves it to the Screenshots directory.", | |
"shell-version": [ "47" ], | |
"url": "https://gist.github.com/Maltysen/ef86043dcfb9a612074ccb86e70bdac8" | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Gio from 'gi://Gio'; | |
import GLib from 'gi://GLib'; | |
import St from 'gi://St'; | |
import Shell from 'gi://Shell'; | |
import Clutter from 'gi://Clutter'; | |
import Cogl from 'gi://Cogl'; | |
import * as Main from 'resource:///org/gnome/shell/ui/main.js'; | |
import * as MessageTray from 'resource:///org/gnome/shell/ui/messageTray.js'; | |
async function _nofile_saveScreenshot() { | |
if (this._selectionButton.checked || this._screenButton.checked) { | |
const content = this._stageScreenshot.get_content(); | |
if (!content) | |
return; // Failed to capture the screenshot for some reason. | |
const texture = content.get_texture(); | |
const geometry = this._getSelectedGeometry(true); | |
let cursorTexture = this._cursor.content?.get_texture(); | |
if (!this._cursor.visible) | |
cursorTexture = null; | |
try { | |
await _nofile_CaptureScreenshot( | |
texture, geometry, this._scale, | |
{ | |
texture: cursorTexture ?? null, | |
x: this._cursor.x * this._scale, | |
y: this._cursor.y * this._scale, | |
scale: this._cursorScale, | |
} | |
); | |
} catch (e) { | |
logError(e, 'Error capturing screenshot'); | |
} | |
} else if (this._windowButton.checked) { | |
const window = | |
this._windowSelectors.flatMap(selector => selector.windows()) | |
.find(win => win.checked); | |
if (!window) | |
return; | |
const content = window.windowContent; | |
if (!content) | |
return; | |
const texture = content.get_texture(); | |
let cursorTexture = window.getCursorTexture()?.get_texture(); | |
if (!this._cursor.visible) | |
cursorTexture = null; | |
try { | |
await _nofile_CaptureScreenshot( | |
texture, | |
null, | |
window.bufferScale, | |
{ | |
texture: cursorTexture ?? null, | |
x: window.cursorPoint.x * window.bufferScale, | |
y: window.cursorPoint.y * window.bufferScale, | |
scale: this._cursorScale, | |
} | |
); | |
} catch (e) { | |
logError(e, 'Error capturing screenshot'); | |
} | |
} | |
// We *did* take a screenshot, but it wasn't stored | |
this.emit('screenshot-taken', | |
Gio.File.new_for_path('/dev/null')); | |
} | |
async function _nofile_CaptureScreenshot(texture, geometry, scale, cursor) { | |
const stream = Gio.MemoryOutputStream.new_resizable(); | |
const [x, y, w, h] = geometry ?? [0, 0, -1, -1]; | |
if (cursor === null) | |
cursor = {texture: null, x: 0, y: 0, scale: 1}; | |
global.display.get_sound_player().play_from_theme( | |
'screen-capture', _('Screenshot taken'), null); | |
const pixbuf = await Shell.Screenshot.composite_to_stream( | |
texture, | |
x, y, w, h, | |
scale, | |
cursor.texture, cursor.x, cursor.y, cursor.scale, | |
stream | |
); | |
stream.close(null); | |
_nofile_StoreScreenshot(stream.steal_as_bytes(), pixbuf); | |
} | |
function _nofile_StoreScreenshot(bytes, pixbuf) { | |
const clipboard = St.Clipboard.get_default(); | |
clipboard.set_content(St.ClipboardType.CLIPBOARD, 'image/png', bytes); | |
const pixels = pixbuf.read_pixel_bytes(); | |
const content = | |
St.ImageContent.new_with_preferred_size(pixbuf.width, pixbuf.height); | |
content.set_bytes( | |
pixels, | |
Cogl.PixelFormat.RGBA_8888, | |
pixbuf.width, | |
pixbuf.height, | |
pixbuf.rowstride | |
); | |
// Show a notification. | |
const source = new MessageTray.Source({ | |
// Translators: notification source name. | |
title: _('Screenshot'), | |
iconName: 'screenshot-recorded-symbolic', | |
}); | |
const notification = new MessageTray.Notification({ | |
source, | |
// Translators: notification title. | |
title: _('Screenshot copied'), | |
// Translators: notification body when a screenshot was captured. | |
body: _('You can paste the image from the clipboard.'), | |
datetime: GLib.DateTime.new_now_local(), | |
gicon: content, | |
isTransient: true, | |
}); | |
Main.messageTray.add(source); | |
source.addNotification(notification); | |
} | |
export function _monkey_onCaptureButtonClicked() { | |
if (this._shotButton.checked) { | |
_nofile_saveScreenshot.call(this).catch(logError); | |
this.close(); | |
} else { | |
// Screencast closes the UI on its own. | |
this._startScreencast(); | |
} | |
} | |
// All the existing keybinds copy to clipboard, but I've | |
// added ctrl-S to do the original functionality | |
export function _monkey_vfunc_key_press_event(event) { | |
const symbol = event.get_key_symbol(); | |
if (symbol === Clutter.KEY_Return || symbol === Clutter.KEY_space || | |
symbol === Clutter.KEY_KP_Enter || symbol === Clutter.KEY_ISO_Enter || | |
((event.get_state() & Clutter.ModifierType.CONTROL_MASK) && | |
(symbol === Clutter.KEY_c || symbol === Clutter.KEY_C))) { | |
_monkey_onCaptureButtonClicked.call(this); | |
return Clutter.EVENT_STOP; | |
} | |
if ((event.get_state() & Clutter.ModifierType.CONTROL_MASK) && | |
(symbol === Clutter.KEY_s || symbol === Clutter.KEY_S)) { | |
this._onCaptureButtonClicked(); | |
return Clutter.EVENT_STOP; | |
} | |
return this._old_vfunc_key_press_event(event); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment