screenshot-ui: Add cursor capturing option
The cursor texture, scale and position is captured separately and overlaid on top of the preview, and on top of the final screenshot image. This allows toggling it on and off post-factum. Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1954>
This commit is contained in:
parent
639a346c1e
commit
941774b786
@ -44,5 +44,6 @@
|
|||||||
<file alias="icons/scalable/status/screenshot-ui-area-symbolic.svg">screenshot-ui-area-symbolic.svg</file>
|
<file alias="icons/scalable/status/screenshot-ui-area-symbolic.svg">screenshot-ui-area-symbolic.svg</file>
|
||||||
<file alias="icons/scalable/status/screenshot-ui-display-symbolic.svg">screenshot-ui-display-symbolic.svg</file>
|
<file alias="icons/scalable/status/screenshot-ui-display-symbolic.svg">screenshot-ui-display-symbolic.svg</file>
|
||||||
<file alias="icons/scalable/status/screenshot-ui-window-symbolic.svg">screenshot-ui-window-symbolic.svg</file>
|
<file alias="icons/scalable/status/screenshot-ui-window-symbolic.svg">screenshot-ui-window-symbolic.svg</file>
|
||||||
|
<file alias="icons/scalable/status/select-mode-symbolic.svg">select-mode-symbolic.svg</file>
|
||||||
</gresource>
|
</gresource>
|
||||||
</gresources>
|
</gresources>
|
||||||
|
@ -70,6 +70,19 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.screenshot-ui-show-pointer-button {
|
||||||
|
padding: $base_padding * 2;
|
||||||
|
border-radius: 99px;
|
||||||
|
background-color: $hover_bg_color;
|
||||||
|
&:hover, &:focus { background-color: lighten($hover_bg_color, 5%); }
|
||||||
|
&:active { background-color: $active_bg_color; }
|
||||||
|
&:checked { background-color: darken($hover_bg_color, 10%); }
|
||||||
|
|
||||||
|
StIcon {
|
||||||
|
icon-size: $base_icon_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.screenshot-ui-area-indicator-shade {
|
.screenshot-ui-area-indicator-shade {
|
||||||
background-color: rgba(0, 0, 0, .3);
|
background-color: rgba(0, 0, 0, .3);
|
||||||
}
|
}
|
||||||
|
2
data/theme/select-mode-symbolic.svg
Normal file
2
data/theme/select-mode-symbolic.svg
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><path d="m 2.953125 1.074219 l 2.417969 13.210937 l 3.238281 -2.398437 l 2.054687 2.648437 c 1.03125 1.433594 3.148438 -0.210937 2.011719 -1.5625 l -2.015625 -2.59375 l 2.984375 -2.175781 z m 0 0" fill="#2e3436"/></svg>
|
After Width: | Height: | Size: 346 B |
@ -726,6 +726,10 @@ class UIWindowSelectorWindow extends St.Button {
|
|||||||
y_align: Clutter.ActorAlign.CENTER,
|
y_align: Clutter.ActorAlign.CENTER,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this._cursor = null;
|
||||||
|
this._cursorPoint = { x: 0, y: 0 };
|
||||||
|
this._shouldShowCursor = actor.get_children().some(c => c.has_pointer);
|
||||||
|
|
||||||
this.connect('destroy', this._onDestroy.bind(this));
|
this.connect('destroy', this._onDestroy.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -753,6 +757,13 @@ class UIWindowSelectorWindow extends St.Button {
|
|||||||
return [0, 0];
|
return [0, 0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get cursorPoint() {
|
||||||
|
return {
|
||||||
|
x: this._cursorPoint.x + this._boundingBox.x - this._bufferRect.x,
|
||||||
|
y: this._cursorPoint.y + this._boundingBox.y - this._bufferRect.y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
get bufferScale() {
|
get bufferScale() {
|
||||||
return this._bufferScale;
|
return this._bufferScale;
|
||||||
}
|
}
|
||||||
@ -768,6 +779,12 @@ class UIWindowSelectorWindow extends St.Button {
|
|||||||
this.remove_child(this._border);
|
this.remove_child(this._border);
|
||||||
this._border.destroy();
|
this._border.destroy();
|
||||||
this._border = null;
|
this._border = null;
|
||||||
|
|
||||||
|
if (this._cursor) {
|
||||||
|
this.remove_child(this._cursor);
|
||||||
|
this._cursor.destroy();
|
||||||
|
this._cursor = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vfunc_allocate(box) {
|
vfunc_allocate(box) {
|
||||||
@ -799,6 +816,53 @@ class UIWindowSelectorWindow extends St.Button {
|
|||||||
windowH * yScale / this._bufferScale
|
windowH * yScale / this._bufferScale
|
||||||
);
|
);
|
||||||
this._actor.allocate(actorBox);
|
this._actor.allocate(actorBox);
|
||||||
|
|
||||||
|
// Allocate the cursor if we have one.
|
||||||
|
if (!this._cursor)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let [, , w, h] = this._cursor.get_preferred_size();
|
||||||
|
w *= this._cursorScale;
|
||||||
|
h *= this._cursorScale;
|
||||||
|
|
||||||
|
const cursorBox = new Clutter.ActorBox({
|
||||||
|
x1: this._cursorPoint.x,
|
||||||
|
y1: this._cursorPoint.y,
|
||||||
|
x2: this._cursorPoint.x + w,
|
||||||
|
y2: this._cursorPoint.y + h,
|
||||||
|
});
|
||||||
|
cursorBox.x1 *= xScale;
|
||||||
|
cursorBox.x2 *= xScale;
|
||||||
|
cursorBox.y1 *= yScale;
|
||||||
|
cursorBox.y2 *= yScale;
|
||||||
|
|
||||||
|
this._cursor.allocate(cursorBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
addCursorTexture(content, point, scale) {
|
||||||
|
if (!this._shouldShowCursor)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Add the cursor.
|
||||||
|
this._cursor = new St.Widget({
|
||||||
|
content,
|
||||||
|
request_mode: Clutter.RequestMode.CONTENT_SIZE,
|
||||||
|
});
|
||||||
|
|
||||||
|
this._cursorPoint = {
|
||||||
|
x: point.x - this._boundingBox.x,
|
||||||
|
y: point.y - this._boundingBox.y,
|
||||||
|
};
|
||||||
|
this._cursorScale = scale;
|
||||||
|
|
||||||
|
this.insert_child_below(this._cursor, this._border);
|
||||||
|
}
|
||||||
|
|
||||||
|
setCursorVisible(visible) {
|
||||||
|
if (!this._cursor)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._cursor.visible = visible;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -898,6 +962,9 @@ class ScreenshotUI extends St.Widget {
|
|||||||
}));
|
}));
|
||||||
this._stageScreenshotContainer.add_child(this._stageScreenshot);
|
this._stageScreenshotContainer.add_child(this._stageScreenshot);
|
||||||
|
|
||||||
|
this._cursor = new St.Widget();
|
||||||
|
this._stageScreenshotContainer.add_child(this._cursor);
|
||||||
|
|
||||||
this._openingCoroutineInProgress = false;
|
this._openingCoroutineInProgress = false;
|
||||||
this._grabHelper = new GrabHelper.GrabHelper(this, {
|
this._grabHelper = new GrabHelper.GrabHelper(this, {
|
||||||
actionMode: Shell.ActionMode.POPUP,
|
actionMode: Shell.ActionMode.POPUP,
|
||||||
@ -1007,6 +1074,30 @@ class ScreenshotUI extends St.Widget {
|
|||||||
this._onCaptureButtonClicked.bind(this));
|
this._onCaptureButtonClicked.bind(this));
|
||||||
this._bottomRowContainer.add_child(this._captureButton);
|
this._bottomRowContainer.add_child(this._captureButton);
|
||||||
|
|
||||||
|
this._showPointerButtonContainer = new St.BoxLayout({
|
||||||
|
x_align: Clutter.ActorAlign.END,
|
||||||
|
x_expand: true,
|
||||||
|
});
|
||||||
|
this._bottomRowContainer.add_child(this._showPointerButtonContainer);
|
||||||
|
|
||||||
|
this._showPointerButton = new St.Button({
|
||||||
|
style_class: 'screenshot-ui-show-pointer-button',
|
||||||
|
toggle_mode: true,
|
||||||
|
});
|
||||||
|
this._showPointerButton.set_child(new St.Icon({ icon_name: 'select-mode-symbolic' }));
|
||||||
|
this._showPointerButtonContainer.add_child(this._showPointerButton);
|
||||||
|
|
||||||
|
this._showPointerButton.connect('notify::checked', () => {
|
||||||
|
const state = this._showPointerButton.checked;
|
||||||
|
this._cursor.visible = state;
|
||||||
|
|
||||||
|
const windows =
|
||||||
|
this._windowSelectors.flatMap(selector => selector.windows());
|
||||||
|
for (const window of windows)
|
||||||
|
window.setCursorVisible(state);
|
||||||
|
});
|
||||||
|
this._cursor.visible = false;
|
||||||
|
|
||||||
this._monitorBins = [];
|
this._monitorBins = [];
|
||||||
this._windowSelectors = [];
|
this._windowSelectors = [];
|
||||||
this._rebuildMonitorBins();
|
this._rebuildMonitorBins();
|
||||||
@ -1131,11 +1222,28 @@ class ScreenshotUI extends St.Widget {
|
|||||||
|
|
||||||
this._openingCoroutineInProgress = true;
|
this._openingCoroutineInProgress = true;
|
||||||
try {
|
try {
|
||||||
const [content, scale] =
|
const [content, scale, cursorContent, cursorPoint, cursorScale] =
|
||||||
await this._shooter.screenshot_stage_to_content();
|
await this._shooter.screenshot_stage_to_content();
|
||||||
this._stageScreenshot.set_content(content);
|
this._stageScreenshot.set_content(content);
|
||||||
this._scale = scale;
|
this._scale = scale;
|
||||||
|
|
||||||
|
if (cursorContent !== null) {
|
||||||
|
this._cursor.set_content(cursorContent);
|
||||||
|
this._cursor.set_position(cursorPoint.x, cursorPoint.y);
|
||||||
|
|
||||||
|
let [, w, h] = cursorContent.get_preferred_size();
|
||||||
|
w *= cursorScale;
|
||||||
|
h *= cursorScale;
|
||||||
|
this._cursor.set_size(w, h);
|
||||||
|
|
||||||
|
this._cursorScale = cursorScale;
|
||||||
|
|
||||||
|
for (const window of windows) {
|
||||||
|
window.addCursorTexture(cursorContent, cursorPoint, cursorScale);
|
||||||
|
window.setCursorVisible(this._showPointerButton.checked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this._stageScreenshotContainer.show();
|
this._stageScreenshotContainer.show();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log('Error capturing screenshot: %s'.format(e.message));
|
log('Error capturing screenshot: %s'.format(e.message));
|
||||||
@ -1183,6 +1291,7 @@ class ScreenshotUI extends St.Widget {
|
|||||||
this._stageScreenshotContainer.hide();
|
this._stageScreenshotContainer.hide();
|
||||||
|
|
||||||
this._stageScreenshot.set_content(null);
|
this._stageScreenshot.set_content(null);
|
||||||
|
this._cursor.set_content(null);
|
||||||
|
|
||||||
this._areaSelector.reset();
|
this._areaSelector.reset();
|
||||||
for (const selector of this._windowSelectors)
|
for (const selector of this._windowSelectors)
|
||||||
@ -1338,9 +1447,18 @@ class ScreenshotUI extends St.Widget {
|
|||||||
|
|
||||||
const [x, y, w, h] = this._getSelectedGeometry();
|
const [x, y, w, h] = this._getSelectedGeometry();
|
||||||
|
|
||||||
|
let cursorTexture = this._cursor.content?.get_texture();
|
||||||
|
if (!this._cursor.visible)
|
||||||
|
cursorTexture = null;
|
||||||
|
|
||||||
Shell.Screenshot.composite_to_stream(
|
Shell.Screenshot.composite_to_stream(
|
||||||
texture,
|
texture,
|
||||||
x, y, w, h,
|
x, y, w, h,
|
||||||
|
this._scale,
|
||||||
|
cursorTexture ?? null,
|
||||||
|
this._cursor.x * this._scale,
|
||||||
|
this._cursor.y * this._scale,
|
||||||
|
this._cursorScale,
|
||||||
stream
|
stream
|
||||||
).then(() => {
|
).then(() => {
|
||||||
stream.close(null);
|
stream.close(null);
|
||||||
@ -1370,9 +1488,18 @@ class ScreenshotUI extends St.Widget {
|
|||||||
const texture = content.get_texture();
|
const texture = content.get_texture();
|
||||||
const stream = Gio.MemoryOutputStream.new_resizable();
|
const stream = Gio.MemoryOutputStream.new_resizable();
|
||||||
|
|
||||||
|
let cursorTexture = this._cursor.content?.get_texture();
|
||||||
|
if (!this._cursor.visible)
|
||||||
|
cursorTexture = null;
|
||||||
|
|
||||||
Shell.Screenshot.composite_to_stream(
|
Shell.Screenshot.composite_to_stream(
|
||||||
texture,
|
texture,
|
||||||
0, 0, -1, -1,
|
0, 0, -1, -1,
|
||||||
|
window.bufferScale,
|
||||||
|
cursorTexture ?? null,
|
||||||
|
window.cursorPoint.x * window.bufferScale,
|
||||||
|
window.cursorPoint.y * window.bufferScale,
|
||||||
|
this._cursorScale,
|
||||||
stream
|
stream
|
||||||
).then(() => {
|
).then(() => {
|
||||||
stream.close(null);
|
stream.close(null);
|
||||||
@ -1416,6 +1543,11 @@ class ScreenshotUI extends St.Widget {
|
|||||||
return Clutter.EVENT_STOP;
|
return Clutter.EVENT_STOP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (symbol === Clutter.KEY_p || symbol === Clutter.KEY_P) {
|
||||||
|
this._showPointerButton.checked = !this._showPointerButton.checked;
|
||||||
|
return Clutter.EVENT_STOP;
|
||||||
|
}
|
||||||
|
|
||||||
if (symbol === Clutter.KEY_Left || symbol === Clutter.KEY_Right ||
|
if (symbol === Clutter.KEY_Left || symbol === Clutter.KEY_Right ||
|
||||||
symbol === Clutter.KEY_Up || symbol === Clutter.KEY_Down) {
|
symbol === Clutter.KEY_Up || symbol === Clutter.KEY_Down) {
|
||||||
let direction;
|
let direction;
|
||||||
|
@ -59,6 +59,9 @@ struct _ShellScreenshotPrivate
|
|||||||
gboolean include_frame;
|
gboolean include_frame;
|
||||||
|
|
||||||
float scale;
|
float scale;
|
||||||
|
ClutterContent *cursor_content;
|
||||||
|
graphene_point_t cursor_point;
|
||||||
|
float cursor_scale;
|
||||||
};
|
};
|
||||||
|
|
||||||
G_DEFINE_TYPE_WITH_PRIVATE (ShellScreenshot, shell_screenshot, G_TYPE_OBJECT);
|
G_DEFINE_TYPE_WITH_PRIVATE (ShellScreenshot, shell_screenshot, G_TYPE_OBJECT);
|
||||||
@ -305,6 +308,9 @@ grab_screenshot_content (ShellScreenshot *screenshot,
|
|||||||
float scale;
|
float scale;
|
||||||
g_autoptr (GError) error = NULL;
|
g_autoptr (GError) error = NULL;
|
||||||
g_autoptr (ClutterContent) content = NULL;
|
g_autoptr (ClutterContent) content = NULL;
|
||||||
|
MetaCursorTracker *tracker;
|
||||||
|
CoglTexture *cursor_texture;
|
||||||
|
int cursor_hot_x, cursor_hot_y;
|
||||||
|
|
||||||
display = shell_global_get_display (priv->global);
|
display = shell_global_get_display (priv->global);
|
||||||
meta_display_get_size (display, &width, &height);
|
meta_display_get_size (display, &width, &height);
|
||||||
@ -333,6 +339,80 @@ grab_screenshot_content (ShellScreenshot *screenshot,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tracker = meta_cursor_tracker_get_for_display (display);
|
||||||
|
cursor_texture = meta_cursor_tracker_get_sprite (tracker);
|
||||||
|
|
||||||
|
// If the cursor is invisible, the texture is NULL.
|
||||||
|
if (cursor_texture)
|
||||||
|
{
|
||||||
|
unsigned int width, height;
|
||||||
|
CoglContext *ctx;
|
||||||
|
CoglPipeline *pipeline;
|
||||||
|
CoglTexture2D *texture;
|
||||||
|
CoglOffscreen *offscreen;
|
||||||
|
ClutterStageView *view;
|
||||||
|
|
||||||
|
// Copy the texture to prevent it from changing shortly after.
|
||||||
|
width = cogl_texture_get_width (cursor_texture);
|
||||||
|
height = cogl_texture_get_height (cursor_texture);
|
||||||
|
|
||||||
|
ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
|
||||||
|
|
||||||
|
texture = cogl_texture_2d_new_with_size (ctx, width, height);
|
||||||
|
offscreen = cogl_offscreen_new_with_texture (texture);
|
||||||
|
cogl_framebuffer_clear4f (COGL_FRAMEBUFFER (offscreen),
|
||||||
|
COGL_BUFFER_BIT_COLOR,
|
||||||
|
0, 0, 0, 0);
|
||||||
|
|
||||||
|
pipeline = cogl_pipeline_new (ctx);
|
||||||
|
cogl_pipeline_set_layer_texture (pipeline, 0, cursor_texture);
|
||||||
|
|
||||||
|
cogl_framebuffer_draw_textured_rectangle (COGL_FRAMEBUFFER (offscreen),
|
||||||
|
pipeline,
|
||||||
|
-1, 1, 1, -1,
|
||||||
|
0, 0, 1, 1);
|
||||||
|
cogl_object_unref (pipeline);
|
||||||
|
g_object_unref (offscreen);
|
||||||
|
|
||||||
|
priv->cursor_content =
|
||||||
|
clutter_texture_content_new_from_texture (texture, NULL);
|
||||||
|
cogl_object_unref (texture);
|
||||||
|
|
||||||
|
priv->cursor_scale = meta_cursor_tracker_get_scale (tracker);
|
||||||
|
|
||||||
|
meta_cursor_tracker_get_pointer (tracker, &priv->cursor_point, NULL);
|
||||||
|
|
||||||
|
view = clutter_stage_get_view_at (stage,
|
||||||
|
priv->cursor_point.x,
|
||||||
|
priv->cursor_point.y);
|
||||||
|
|
||||||
|
meta_cursor_tracker_get_hot (tracker, &cursor_hot_x, &cursor_hot_y);
|
||||||
|
priv->cursor_point.x -= cursor_hot_x * priv->cursor_scale;
|
||||||
|
priv->cursor_point.y -= cursor_hot_y * priv->cursor_scale;
|
||||||
|
|
||||||
|
// Align the coordinates to the pixel grid the same way it's done in
|
||||||
|
// MetaCursorRenderer.
|
||||||
|
if (view)
|
||||||
|
{
|
||||||
|
cairo_rectangle_int_t view_layout;
|
||||||
|
float view_scale;
|
||||||
|
|
||||||
|
clutter_stage_view_get_layout (view, &view_layout);
|
||||||
|
view_scale = clutter_stage_view_get_scale (view);
|
||||||
|
|
||||||
|
priv->cursor_point.x -= view_layout.x;
|
||||||
|
priv->cursor_point.y -= view_layout.y;
|
||||||
|
|
||||||
|
priv->cursor_point.x =
|
||||||
|
floorf (priv->cursor_point.x * view_scale) / view_scale;
|
||||||
|
priv->cursor_point.y =
|
||||||
|
floorf (priv->cursor_point.y * view_scale) / view_scale;
|
||||||
|
|
||||||
|
priv->cursor_point.x += view_layout.x;
|
||||||
|
priv->cursor_point.y += view_layout.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
g_task_return_pointer (result, g_steal_pointer (&content), g_object_unref);
|
g_task_return_pointer (result, g_steal_pointer (&content), g_object_unref);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -610,6 +690,10 @@ shell_screenshot_screenshot_stage_to_content (ShellScreenshot *screenshot,
|
|||||||
* @screenshot: the #ShellScreenshot
|
* @screenshot: the #ShellScreenshot
|
||||||
* @result: the #GAsyncResult that was provided to the callback
|
* @result: the #GAsyncResult that was provided to the callback
|
||||||
* @scale: (out) (optional): location to store the content scale
|
* @scale: (out) (optional): location to store the content scale
|
||||||
|
* @cursor_content: (out) (optional): location to store the cursor content
|
||||||
|
* @cursor_point: (out) (optional): location to store the point at which to
|
||||||
|
* draw the cursor content
|
||||||
|
* @cursor_scale: (out) (optional): location to store the cursor scale
|
||||||
* @error: #GError for error reporting
|
* @error: #GError for error reporting
|
||||||
*
|
*
|
||||||
* Finish the asynchronous operation started by
|
* Finish the asynchronous operation started by
|
||||||
@ -619,10 +703,13 @@ shell_screenshot_screenshot_stage_to_content (ShellScreenshot *screenshot,
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
ClutterContent *
|
ClutterContent *
|
||||||
shell_screenshot_screenshot_stage_to_content_finish (ShellScreenshot *screenshot,
|
shell_screenshot_screenshot_stage_to_content_finish (ShellScreenshot *screenshot,
|
||||||
GAsyncResult *result,
|
GAsyncResult *result,
|
||||||
float *scale,
|
float *scale,
|
||||||
GError **error)
|
ClutterContent **cursor_content,
|
||||||
|
graphene_point_t *cursor_point,
|
||||||
|
float *cursor_scale,
|
||||||
|
GError **error)
|
||||||
{
|
{
|
||||||
ShellScreenshotPrivate *priv = screenshot->priv;
|
ShellScreenshotPrivate *priv = screenshot->priv;
|
||||||
ClutterContent *content;
|
ClutterContent *content;
|
||||||
@ -640,6 +727,17 @@ shell_screenshot_screenshot_stage_to_content_finish (ShellScreenshot *screensho
|
|||||||
if (scale)
|
if (scale)
|
||||||
*scale = priv->scale;
|
*scale = priv->scale;
|
||||||
|
|
||||||
|
if (cursor_content)
|
||||||
|
*cursor_content = g_steal_pointer (&priv->cursor_content);
|
||||||
|
else
|
||||||
|
g_clear_pointer (&priv->cursor_content, g_object_unref);
|
||||||
|
|
||||||
|
if (cursor_point)
|
||||||
|
*cursor_point = priv->cursor_point;
|
||||||
|
|
||||||
|
if (cursor_scale)
|
||||||
|
*cursor_scale = priv->cursor_scale;
|
||||||
|
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -973,6 +1071,13 @@ composite_to_stream_on_png_saved (GObject *source,
|
|||||||
* @y: y coordinate of the rectangle
|
* @y: y coordinate of the rectangle
|
||||||
* @width: width of the rectangle, or -1 to use the full texture
|
* @width: width of the rectangle, or -1 to use the full texture
|
||||||
* @height: height of the rectangle, or -1 to use the full texture
|
* @height: height of the rectangle, or -1 to use the full texture
|
||||||
|
* @scale: scale of the source texture
|
||||||
|
* @cursor: (nullable): the cursor texture
|
||||||
|
* @cursor_x: x coordinate to put the cursor texture at, relative to the full
|
||||||
|
* source texture
|
||||||
|
* @cursor_y: y coordinate to put the cursor texture at, relative to the full
|
||||||
|
* source texture
|
||||||
|
* @cursor_scale: scale of the cursor texture
|
||||||
* @stream: the stream to write the PNG image into
|
* @stream: the stream to write the PNG image into
|
||||||
* @callback: (scope async): function to call returning success or failure
|
* @callback: (scope async): function to call returning success or failure
|
||||||
* @user_data: the data to pass to callback function
|
* @user_data: the data to pass to callback function
|
||||||
@ -987,6 +1092,11 @@ shell_screenshot_composite_to_stream (CoglTexture *texture,
|
|||||||
int y,
|
int y,
|
||||||
int width,
|
int width,
|
||||||
int height,
|
int height,
|
||||||
|
float scale,
|
||||||
|
CoglTexture *cursor,
|
||||||
|
int cursor_x,
|
||||||
|
int cursor_y,
|
||||||
|
float cursor_scale,
|
||||||
GOutputStream *stream,
|
GOutputStream *stream,
|
||||||
GAsyncReadyCallback callback,
|
GAsyncReadyCallback callback,
|
||||||
gpointer user_data)
|
gpointer user_data)
|
||||||
@ -994,6 +1104,8 @@ shell_screenshot_composite_to_stream (CoglTexture *texture,
|
|||||||
CoglContext *ctx;
|
CoglContext *ctx;
|
||||||
CoglTexture *sub_texture;
|
CoglTexture *sub_texture;
|
||||||
cairo_surface_t *surface;
|
cairo_surface_t *surface;
|
||||||
|
cairo_surface_t *cursor_surface;
|
||||||
|
cairo_t *cr;
|
||||||
g_autoptr (GTask) task = NULL;
|
g_autoptr (GTask) task = NULL;
|
||||||
g_autoptr (GdkPixbuf) pixbuf = NULL;
|
g_autoptr (GdkPixbuf) pixbuf = NULL;
|
||||||
g_autofree char *creation_time = NULL;
|
g_autofree char *creation_time = NULL;
|
||||||
@ -1024,6 +1136,34 @@ shell_screenshot_composite_to_stream (CoglTexture *texture,
|
|||||||
|
|
||||||
cogl_object_unref (sub_texture);
|
cogl_object_unref (sub_texture);
|
||||||
|
|
||||||
|
cairo_surface_set_device_scale (surface, scale, scale);
|
||||||
|
|
||||||
|
if (cursor != NULL)
|
||||||
|
{
|
||||||
|
// Paint the cursor on top.
|
||||||
|
cursor_surface =
|
||||||
|
cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
|
||||||
|
cogl_texture_get_width (cursor),
|
||||||
|
cogl_texture_get_height (cursor));
|
||||||
|
cogl_texture_get_data (cursor, CLUTTER_CAIRO_FORMAT_ARGB32,
|
||||||
|
cairo_image_surface_get_stride (cursor_surface),
|
||||||
|
cairo_image_surface_get_data (cursor_surface));
|
||||||
|
cairo_surface_mark_dirty (cursor_surface);
|
||||||
|
|
||||||
|
cairo_surface_set_device_scale (cursor_surface,
|
||||||
|
1 / cursor_scale,
|
||||||
|
1 / cursor_scale);
|
||||||
|
|
||||||
|
cr = cairo_create (surface);
|
||||||
|
cairo_set_source_surface (cr, cursor_surface,
|
||||||
|
(cursor_x - x) / scale,
|
||||||
|
(cursor_y - y) / scale);
|
||||||
|
cairo_paint (cr);
|
||||||
|
cairo_destroy (cr);
|
||||||
|
|
||||||
|
cairo_surface_destroy (cursor_surface);
|
||||||
|
}
|
||||||
|
|
||||||
// Save to an image.
|
// Save to an image.
|
||||||
pixbuf = gdk_pixbuf_get_from_surface (surface,
|
pixbuf = gdk_pixbuf_get_from_surface (surface,
|
||||||
0, 0,
|
0, 0,
|
||||||
|
@ -53,10 +53,13 @@ gboolean shell_screenshot_screenshot_finish (ShellScreenshot *screensho
|
|||||||
void shell_screenshot_screenshot_stage_to_content (ShellScreenshot *screenshot,
|
void shell_screenshot_screenshot_stage_to_content (ShellScreenshot *screenshot,
|
||||||
GAsyncReadyCallback callback,
|
GAsyncReadyCallback callback,
|
||||||
gpointer user_data);
|
gpointer user_data);
|
||||||
ClutterContent *shell_screenshot_screenshot_stage_to_content_finish (ShellScreenshot *screenshot,
|
ClutterContent *shell_screenshot_screenshot_stage_to_content_finish (ShellScreenshot *screenshot,
|
||||||
GAsyncResult *result,
|
GAsyncResult *result,
|
||||||
float *scale,
|
float *scale,
|
||||||
GError **error);
|
ClutterContent **cursor_content,
|
||||||
|
graphene_point_t *cursor_point,
|
||||||
|
float *cursor_scale,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
void shell_screenshot_pick_color (ShellScreenshot *screenshot,
|
void shell_screenshot_pick_color (ShellScreenshot *screenshot,
|
||||||
int x,
|
int x,
|
||||||
@ -73,6 +76,11 @@ void shell_screenshot_composite_to_stream (CoglTexture *texture,
|
|||||||
int y,
|
int y,
|
||||||
int width,
|
int width,
|
||||||
int height,
|
int height,
|
||||||
|
float scale,
|
||||||
|
CoglTexture *cursor,
|
||||||
|
int cursor_x,
|
||||||
|
int cursor_y,
|
||||||
|
float cursor_scale,
|
||||||
GOutputStream *stream,
|
GOutputStream *stream,
|
||||||
GAsyncReadyCallback callback,
|
GAsyncReadyCallback callback,
|
||||||
gpointer user_data);
|
gpointer user_data);
|
||||||
|
Loading…
Reference in New Issue
Block a user