mirror of
https://github.com/gryf/wmaker.git
synced 2025-12-18 20:10:29 +01:00
Add a screenshot capture feature
This patch adds a feature to take screenshots directly from Window Maker. Having the feature embeded direclty inside Window Maker allows us to take advantage of how Window Maker is managing and handling Windows. Three new actions can be bound to a key shortcut from WPrefs. The screenshot files are saved in ~/GNUstep/Library/WindowMaker/Screenshots/ dir, with a "screenshot_%Y-%m-%d_at_%H:%M:%S" format followed by the extension. Preferably as a PNG or JPG file if available. Meaning, to work Window Maker via WRaster needs to support at least one of those format. "Capture the entire screen" is quite standard, it takes a screenshot of the whole screen area (even in multiheads env). "Capture a portion of the screen" requires the user to draw a rectangle which will be captured. Those two first are quite straightforward, just taking a live picture of the screen. The last one is "Capture a window" which works in best effort mode, it catures the focused window. As Window Maker by default is not using any compositor (like for example Xcompmgr) it can only dump the content displayed on the screen. If a window is minimized or out of the screen, there is high chance the image will be split or some area greyed in case other windows overlapped it.
This commit is contained in:
committed by
Carlos R. Mafra
parent
6c69dc32a0
commit
d045ffcf7d
@@ -158,7 +158,10 @@ static struct keyOption {
|
||||
{ "RunKey", N_("Run application") },
|
||||
{ "ExitKey", N_("Exit Window Maker") },
|
||||
{ "DockRaiseLowerKey", N_("Raise/Lower Dock") },
|
||||
{ "ClipRaiseLowerKey", N_("Raise/Lower Clip") }
|
||||
{ "ClipRaiseLowerKey", N_("Raise/Lower Clip") },
|
||||
{ "ScreenCaptureKey", N_("Capture the entire screen") },
|
||||
{ "WindowCaptureKey", N_("Capture a window") },
|
||||
{ "PartialCaptureKey", N_("Capture a portion of the screen") }
|
||||
#ifdef XKB_MODELOCK
|
||||
,{ "ToggleKbdModeKey", N_("Toggle keyboard language") }
|
||||
#endif /* XKB_MODELOCK */
|
||||
|
||||
@@ -233,6 +233,9 @@
|
||||
ScreenSwitchKey = None;
|
||||
RunKey = None;
|
||||
ExitKey = None;
|
||||
ScreenCaptureKey = Print;
|
||||
WindowCaptureKey = None;
|
||||
PartialCaptureKey = None;
|
||||
NormalCursor = (builtin, left_ptr);
|
||||
ArrowCursor = (builtin, top_left_arrow);
|
||||
MoveCursor = (builtin, fleur);
|
||||
|
||||
@@ -119,6 +119,7 @@ typedef enum {
|
||||
WCUR_QUESTION,
|
||||
WCUR_TEXT,
|
||||
WCUR_SELECT,
|
||||
WCUR_CAPTURE,
|
||||
WCUR_ROOT,
|
||||
WCUR_EMPTY,
|
||||
|
||||
|
||||
@@ -790,6 +790,12 @@ WDefaultEntry optionList[] = {
|
||||
NULL, getKeybind, setKeyGrab, NULL, NULL},
|
||||
{"ExitKey", "None", (void *)WKBD_EXIT,
|
||||
NULL, getKeybind, setKeyGrab, NULL, NULL},
|
||||
{"ScreenCaptureKey", "None", (void *)WKBD_PRINTS,
|
||||
NULL, getKeybind, setKeyGrab, NULL, NULL},
|
||||
{"WindowCaptureKey", "None", (void *)WKBD_PRINTW,
|
||||
NULL, getKeybind, setKeyGrab, NULL, NULL},
|
||||
{"PartialCaptureKey", "None", (void *)WKBD_PRINTP,
|
||||
NULL, getKeybind, setKeyGrab, NULL, NULL},
|
||||
|
||||
#ifdef KEEP_XKB_LOCK_STATUS
|
||||
{"ToggleKbdModeKey", "None", (void *)WKBD_TOGGLE,
|
||||
@@ -826,6 +832,8 @@ WDefaultEntry optionList[] = {
|
||||
NULL, getCursor, setCursor, NULL, NULL},
|
||||
{"SelectCursor", "(builtin, cross)", (void *)WCUR_SELECT,
|
||||
NULL, getCursor, setCursor, NULL, NULL},
|
||||
{"CaptureCursor", "(builtin, crosshair)", (void *)WCUR_CAPTURE,
|
||||
NULL, getCursor, setCursor, NULL, NULL},
|
||||
{"DialogHistoryLines", "500", NULL,
|
||||
&wPreferences.history_lines, getInt, NULL, NULL, NULL},
|
||||
{"CycleActiveHeadOnly", "NO", NULL,
|
||||
|
||||
18
src/event.c
18
src/event.c
@@ -1863,6 +1863,24 @@ static void handleKeyPress(XEvent * event)
|
||||
break;
|
||||
}
|
||||
|
||||
case WKBD_PRINTS:
|
||||
{
|
||||
ScreenCapture(scr, PRINT_SCREEN);
|
||||
break;
|
||||
}
|
||||
|
||||
case WKBD_PRINTW:
|
||||
{
|
||||
ScreenCapture(scr, PRINT_WINDOW);
|
||||
break;
|
||||
}
|
||||
|
||||
case WKBD_PRINTP:
|
||||
{
|
||||
ScreenCapture(scr, PRINT_PARTIAL);
|
||||
break;
|
||||
}
|
||||
|
||||
case WKBD_NEXTWSLAYER:
|
||||
case WKBD_PREVWSLAYER:
|
||||
{
|
||||
|
||||
@@ -148,6 +148,15 @@ enum {
|
||||
/* open "exit" dialog */
|
||||
WKBD_EXIT,
|
||||
|
||||
/* screen print */
|
||||
WKBD_PRINTS,
|
||||
|
||||
/* window print */
|
||||
WKBD_PRINTW,
|
||||
|
||||
/* partial print */
|
||||
WKBD_PRINTP,
|
||||
|
||||
#ifdef KEEP_XKB_LOCK_STATUS
|
||||
WKBD_TOGGLE,
|
||||
#endif
|
||||
|
||||
250
src/screen.c
250
src/screen.c
@@ -24,6 +24,10 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
@@ -54,10 +58,12 @@
|
||||
#include "geomview.h"
|
||||
#include "wmspec.h"
|
||||
#include "rootmenu.h"
|
||||
#include "misc.h"
|
||||
|
||||
#include "xinerama.h"
|
||||
|
||||
#include <WINGs/WUtil.h>
|
||||
#include <WINGs/WINGsP.h>
|
||||
|
||||
#include "defaults.h"
|
||||
|
||||
@@ -593,6 +599,7 @@ static void createInternalWindows(WScreen * scr)
|
||||
scr->workspace_name =
|
||||
XCreateWindow(dpy, scr->root_win, 0, 0, 10, 10, 0, scr->w_depth,
|
||||
CopyFromParent, scr->w_visual, vmask, &attribs);
|
||||
scr->mini_screenshot_timeout = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1126,3 +1133,246 @@ int wScreenKeepInside(WScreen * scr, int *x, int *y, int width, int height)
|
||||
|
||||
return moved;
|
||||
}
|
||||
|
||||
static XImage *imageCaptureArea(WScreen *scr)
|
||||
{
|
||||
XEvent event;
|
||||
int quit = 0;
|
||||
int xp = -1;
|
||||
int yp = -1;
|
||||
int w = 0, h = 0;
|
||||
int x = xp, y = yp;
|
||||
|
||||
if (XGrabPointer(dpy, scr->root_win, False, ButtonMotionMask
|
||||
| ButtonReleaseMask | ButtonPressMask, GrabModeAsync,
|
||||
GrabModeAsync, None, wPreferences.cursor[WCUR_CAPTURE], CurrentTime) != Success) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
XGrabServer(dpy);
|
||||
|
||||
while (!quit) {
|
||||
WMMaskEvent(dpy, ButtonReleaseMask | PointerMotionMask | ButtonPressMask | KeyPressMask, &event);
|
||||
|
||||
switch (event.type) {
|
||||
case ButtonPress:
|
||||
if (event.xbutton.button == Button1) {
|
||||
xp = event.xbutton.x_root;
|
||||
yp = event.xbutton.y_root;
|
||||
}
|
||||
break;
|
||||
case ButtonRelease:
|
||||
if (event.xbutton.button == Button1) {
|
||||
quit = 1;
|
||||
if (w > 0 && h > 0) {
|
||||
XDrawRectangle(dpy, scr->root_win, scr->frame_gc, x, y, w, h);
|
||||
XUngrabServer(dpy);
|
||||
XUngrabPointer(dpy, CurrentTime);
|
||||
return XGetImage(dpy, scr->root_win, x, y, w, h, AllPlanes, ZPixmap);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MotionNotify:
|
||||
XDrawRectangle(dpy, scr->root_win, scr->frame_gc, x, y, w, h);
|
||||
x = event.xmotion.x_root;
|
||||
if (x < xp) {
|
||||
w = xp - x;
|
||||
} else {
|
||||
w = x - xp;
|
||||
x = xp;
|
||||
}
|
||||
y = event.xmotion.y_root;
|
||||
if (y < yp) {
|
||||
h = yp - y;
|
||||
} else {
|
||||
h = y - yp;
|
||||
y = yp;
|
||||
}
|
||||
XDrawRectangle(dpy, scr->root_win, scr->frame_gc, x, y, w, h);
|
||||
break;
|
||||
case KeyPress:
|
||||
if (W_KeycodeToKeysym(dpy, event.xkey.keycode, 0) == XK_Escape)
|
||||
quit = 1;
|
||||
break;
|
||||
default:
|
||||
WMHandleEvent(&event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
XUngrabServer(dpy);
|
||||
XUngrabPointer(dpy, CurrentTime);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void hideMiniScreenshot(void *data)
|
||||
{
|
||||
WScreen *scr = (WScreen *) data;
|
||||
|
||||
if (time(NULL) < scr->mini_screenshot_timeout) {
|
||||
scr->mini_screenshot_timer = WMAddTimerHandler(WORKSPACE_NAME_FADE_DELAY, hideMiniScreenshot, scr);
|
||||
} else {
|
||||
XWindowAttributes attr;
|
||||
|
||||
WMDeleteTimerHandler(scr->mini_screenshot_timer);
|
||||
if (XGetWindowAttributes(dpy, scr->mini_screenshot, &attr))
|
||||
slide_window(scr->mini_screenshot, attr.x, attr.y, attr.x + attr.width, attr.y);
|
||||
XUnmapWindow(dpy, scr->mini_screenshot);
|
||||
XDestroyWindow(dpy, scr->mini_screenshot);
|
||||
scr->mini_screenshot_timeout = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void showMiniScreenshot(WScreen *scr, RImage *img)
|
||||
{
|
||||
Pixmap pix;
|
||||
int x = scr->scr_width - img->width - 20;
|
||||
int y = scr->scr_height - img->height - 20;
|
||||
|
||||
if (!scr->mini_screenshot_timeout) {
|
||||
Window win;
|
||||
|
||||
win = XCreateSimpleWindow(dpy, scr->root_win, x, y,
|
||||
img->width, img->height, scr->frame_border_width, 0, 0);
|
||||
scr->mini_screenshot = win;
|
||||
}
|
||||
RConvertImage(scr->rcontext, img, &pix);
|
||||
XMapWindow(dpy, scr->mini_screenshot);
|
||||
XCopyArea(dpy, pix, scr->mini_screenshot, scr->rcontext->copy_gc, 0, 0, img->width, img->height, 0, 0);
|
||||
XFlush(dpy);
|
||||
|
||||
scr->mini_screenshot_timeout = time(NULL) + 2;
|
||||
scr->mini_screenshot_timer = WMAddTimerHandler(WORKSPACE_NAME_FADE_DELAY, hideMiniScreenshot, scr);
|
||||
}
|
||||
|
||||
void ScreenCapture(WScreen *scr, int mode)
|
||||
{
|
||||
time_t s;
|
||||
short i = 0;
|
||||
struct tm *tm_info;
|
||||
char index_str[12] = "";
|
||||
char filename_date_part[60];
|
||||
char filename[60];
|
||||
char *filepath;
|
||||
char *screenshot_dir;
|
||||
RImage *img = NULL;
|
||||
RImage *scale_img = NULL;
|
||||
|
||||
#ifdef USE_PNG
|
||||
char *filetype = ".png";
|
||||
#else
|
||||
#ifdef USE_JPEG
|
||||
char *filetype = ".jpg";
|
||||
#else
|
||||
char *filetype = NULL;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (!filetype) {
|
||||
werror(_("Unable to find a proper screenshot image format"));
|
||||
return;
|
||||
}
|
||||
|
||||
screenshot_dir = wstrconcat(wusergnusteppath(), "/Library/WindowMaker/Screenshots/");
|
||||
if (-1 == mkdir(screenshot_dir, 0700) && errno != EEXIST) {
|
||||
wfree(screenshot_dir);
|
||||
werror(_("Unable to create screenshot directory: %s"), strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
s = time(NULL);
|
||||
tm_info = localtime(&s);
|
||||
strftime(filename_date_part, sizeof(filename_date_part), "screenshot_%Y-%m-%d_at_%H:%M:%S", tm_info);
|
||||
strcpy(filename, filename_date_part);
|
||||
|
||||
filepath = wstrconcat(screenshot_dir, strcat(filename, filetype));
|
||||
while (access(filepath, F_OK) == 0 && i < 600) {
|
||||
i++;
|
||||
strcpy(filename, filename_date_part);
|
||||
sprintf(index_str, "_%d", i);
|
||||
strncat(filename, index_str, sizeof(filename) - strlen(filename) - 1);
|
||||
wfree(filepath);
|
||||
filepath = wstrconcat(screenshot_dir, strcat(filename, filetype));
|
||||
}
|
||||
|
||||
/* cannot generate an available filename ?! */
|
||||
if (i == 600) {
|
||||
wfree(filepath);
|
||||
wfree(screenshot_dir);
|
||||
werror(_("Could not generate a free screenshot filename"));
|
||||
return;
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case PRINT_WINDOW:
|
||||
WWindow *wwin = scr->focused_window;
|
||||
if (wwin && !wwin->flags.shaded) {
|
||||
/*
|
||||
* check if hint WM_TAKE_FOCUS is set, if it's the case
|
||||
* we can take screenshot of the out of screen window
|
||||
*/
|
||||
if (wwin->focus_mode >= WFM_LOCALLY_ACTIVE) {
|
||||
img = RCreateImageFromDrawable(scr->rcontext, wwin->client_win, None);
|
||||
}
|
||||
else {
|
||||
/* we will only capture the visible window part */
|
||||
XImage *pimg;
|
||||
int x_crop = 0;
|
||||
int y_crop = 0;
|
||||
int w_crop = wwin->client.width;
|
||||
int h_crop = wwin->client.height;
|
||||
|
||||
if (wwin->client.x > 0)
|
||||
x_crop = wwin->client.x;
|
||||
if (wwin->client.y > 0)
|
||||
y_crop = wwin->client.y;
|
||||
|
||||
if (wwin->client.x + wwin->client.width > scr->scr_width)
|
||||
w_crop = scr->scr_width - wwin->client.x;
|
||||
if (wwin->client.y + wwin->client.height > scr->scr_height)
|
||||
h_crop = scr->scr_height - wwin->client.y;
|
||||
|
||||
pimg = XGetImage(dpy, scr->root_win, x_crop, y_crop,
|
||||
(wwin->client.x > 0)?w_crop:w_crop + wwin->client.x,
|
||||
(wwin->client.y > 0)?h_crop:h_crop + wwin->client.y,
|
||||
AllPlanes, ZPixmap);
|
||||
|
||||
if (pimg) {
|
||||
img = RCreateImageFromXImage(scr->rcontext, pimg, None);
|
||||
XDestroyImage(pimg);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PRINT_PARTIAL:
|
||||
XImage *pimg;
|
||||
|
||||
pimg = imageCaptureArea(scr);
|
||||
if (pimg) {
|
||||
img = RCreateImageFromXImage(scr->rcontext, pimg, None);
|
||||
XDestroyImage(pimg);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* PRINT_SCREEN*/
|
||||
img = RCreateImageFromDrawable(scr->rcontext, scr->root_win, None);
|
||||
}
|
||||
|
||||
if (img) {
|
||||
#ifdef USE_PNG
|
||||
if (RSaveTitledImage(img, filepath, (char *)(filetype + 1), "Screenshot from Window Maker")) {
|
||||
#else
|
||||
if (RSaveTitledImage(img, filepath, (char *)(filetype + 1), "Screenshot from Window Maker")) {
|
||||
#endif
|
||||
scale_img = RSmoothScaleImage(img, scr->scr_width / 10, scr->scr_height / 10);
|
||||
showMiniScreenshot(scr, scale_img);
|
||||
RReleaseImage(scale_img);
|
||||
#ifdef DEBUG
|
||||
wmessage("screenshot filepath: %s", filepath);
|
||||
#endif
|
||||
}
|
||||
RReleaseImage(img);
|
||||
}
|
||||
wfree(filepath);
|
||||
wfree(screenshot_dir);
|
||||
}
|
||||
|
||||
10
src/screen.h
10
src/screen.h
@@ -27,6 +27,9 @@
|
||||
|
||||
#include <WINGs/WUtil.h>
|
||||
|
||||
#define PRINT_SCREEN 1
|
||||
#define PRINT_WINDOW 2
|
||||
#define PRINT_PARTIAL 3
|
||||
|
||||
typedef struct {
|
||||
WMRect *screens;
|
||||
@@ -271,6 +274,11 @@ typedef struct _WScreen {
|
||||
WMHandlerID *workspace_name_timer;
|
||||
struct WorkspaceNameData *workspace_name_data;
|
||||
|
||||
/* mini screenshot data */
|
||||
Window mini_screenshot;
|
||||
time_t mini_screenshot_timeout;
|
||||
WMHandlerID *mini_screenshot_timer;
|
||||
|
||||
/* for raise-delay */
|
||||
WMHandlerID *autoRaiseTimer;
|
||||
Window autoRaiseWindow; /* window that is scheduled to be
|
||||
@@ -314,7 +322,7 @@ void wScreenRestoreState(WScreen *scr);
|
||||
|
||||
int wScreenBringInside(WScreen *scr, int *x, int *y, int width, int height);
|
||||
int wScreenKeepInside(WScreen *scr, int *x, int *y, int width, int height);
|
||||
|
||||
void ScreenCapture(WScreen *scr, int mode);
|
||||
|
||||
/* in startup.c */
|
||||
WScreen *wScreenWithNumber(int i);
|
||||
|
||||
@@ -493,6 +493,7 @@ void StartUp(Bool defaultScreenOnly)
|
||||
wPreferences.cursor[WCUR_QUESTION] = XCreateFontCursor(dpy, XC_question_arrow);
|
||||
wPreferences.cursor[WCUR_TEXT] = XCreateFontCursor(dpy, XC_xterm); /* odd name??? */
|
||||
wPreferences.cursor[WCUR_SELECT] = XCreateFontCursor(dpy, XC_cross);
|
||||
wPreferences.cursor[WCUR_CAPTURE] = XCreateFontCursor(dpy, XC_crosshair);
|
||||
|
||||
Pixmap cur = XCreatePixmap(dpy, DefaultRootWindow(dpy), 16, 16, 1);
|
||||
GC gc = XCreateGC(dpy, cur, 0, NULL);
|
||||
|
||||
Reference in New Issue
Block a user