- add description placeholders to all CMDx definitions - swap arguments to qe_split_window() and do_split_window() - pass iteration count based on universal argument to do_up_down(), do_left_right(), do_word_right(), do_kill_word(), do_forward_paragraph(), do_kill_paragraph(), do_c_forward_conditional(), do_forward_block(), do_kill_block(), - remove do_backward_paragraph() - add int eb_prev_paragraph(EditBuffer *b, int offset); - dont call command on 0 universal prefix
2051 lines
60 KiB
C
2051 lines
60 KiB
C
/*
|
|
* X11 handling for QEmacs
|
|
*
|
|
* Copyright (c) 2000-2003 Fabrice Bellard.
|
|
* Copyright (c) 2002-2020 Charlie Gordon.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "qe.h"
|
|
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <signal.h>
|
|
#include <X11/Xlib.h>
|
|
#include <X11/Xutil.h>
|
|
#include <X11/keysym.h>
|
|
#include <X11/Xatom.h>
|
|
#include <sys/ipc.h>
|
|
#include <sys/shm.h>
|
|
#ifdef CONFIG_XSHM
|
|
#include <X11/extensions/XShm.h>
|
|
#endif
|
|
#ifdef CONFIG_XFT
|
|
#include <X11/extensions/Xrender.h>
|
|
#include <X11/Xft/Xft.h>
|
|
#endif
|
|
#ifdef CONFIG_XV
|
|
#include <X11/extensions/Xv.h>
|
|
#include <X11/extensions/Xvlib.h>
|
|
#endif
|
|
|
|
//#define CONFIG_DOUBLE_BUFFER 1
|
|
|
|
/* NOTE: XFT code is currently broken */
|
|
|
|
static QEFont *x11_dpy_open_font(QEditScreen *s, int style, int size);
|
|
static void x11_dpy_close_font(QEditScreen *s, QEFont **fontp);
|
|
#ifdef CONFIG_XV
|
|
static void xv_init(QEditScreen *s);
|
|
#endif
|
|
static void x11_handle_event(void *opaque);
|
|
|
|
typedef struct X11State {
|
|
Display *display;
|
|
int xscreen;
|
|
Window window;
|
|
Atom wm_delete_window;
|
|
GC gc, gc_pixmap;
|
|
XWindowAttributes attr;
|
|
int event_mask;
|
|
int screen_width, screen_height;
|
|
int last_window_width, last_window_height, last_window_x, last_window_y;
|
|
XIM xim; /* X11 input method */
|
|
XIC xic; /* X11 input context */
|
|
Pixmap dbuffer;
|
|
#ifdef CONFIG_XSHM
|
|
int shm_use;
|
|
#endif
|
|
#ifdef CONFIG_XFT
|
|
XftDraw *renderDraw;
|
|
#endif
|
|
#ifdef CONFIG_XV
|
|
unsigned int xv_nb_adaptors;
|
|
int xv_nb_formats, xv_port, xv_format, xv_open_count;
|
|
XvAdaptorInfo *xv_ai;
|
|
XvImageFormatValues *xv_fo;
|
|
#endif
|
|
|
|
#ifdef CONFIG_DOUBLE_BUFFER
|
|
/* update region to speed up double buffer flush */
|
|
#define UPDATE_MAX_REGIONS 3
|
|
#define UPDATE_DMIN 64
|
|
int update_nb;
|
|
CSSRect update_rects[UPDATE_MAX_REGIONS];
|
|
#endif
|
|
|
|
int visual_depth;
|
|
|
|
} X11State;
|
|
|
|
/* global variables set from the command line */
|
|
static const char *display_str;
|
|
static const char *geometry_str;
|
|
static int font_ptsize;
|
|
|
|
static const char * const default_x11_fonts[NB_FONT_FAMILIES] = {
|
|
#ifdef CONFIG_XFT
|
|
"mono",
|
|
#else
|
|
"fixed,unifont",
|
|
#endif
|
|
"times,unifont",
|
|
"helvetica,unifont",
|
|
};
|
|
|
|
#ifdef CONFIG_DOUBLE_BUFFER
|
|
static void update_reset(X11State *xs)
|
|
{
|
|
int i;
|
|
for (i = 0; i < UPDATE_MAX_REGIONS; i++)
|
|
css_set_rect(xs->update_rects + i, 0, 0, 0, 0);
|
|
xs->update_nb = 0;
|
|
}
|
|
|
|
static inline int rect_dist(CSSRect *r1, CSSRect *r2)
|
|
{
|
|
int dx, dy;
|
|
|
|
if (r2->x2 < r1->x1)
|
|
dx = r1->x1 - r2->x2;
|
|
else if (r2->x1 > r1->x2)
|
|
dx = r2->x1 - r1->x2;
|
|
else
|
|
dx = 0;
|
|
|
|
if (r2->y2 < r1->y1)
|
|
dy = r1->y1 - r2->y2;
|
|
else if (r2->y1 > r1->y2)
|
|
dy = r2->y1 - r1->y2;
|
|
else
|
|
dy = 0;
|
|
if (dy > dx)
|
|
dx = dy;
|
|
return dx;
|
|
}
|
|
|
|
static void update_rect(X11State *xs, int x1, int y1, int x2, int y2)
|
|
{
|
|
CSSRect r, *r1, *r2;
|
|
int i, d, dmin;
|
|
|
|
css_set_rect(&r, x1, y1, x2, y2);
|
|
if (css_is_null_rect(&r))
|
|
return;
|
|
|
|
/* find closest rectangle */
|
|
dmin = INT_MAX;
|
|
r2 = xs->update_rects;
|
|
r1 = NULL;
|
|
for (i = 0; i < xs->update_nb; i++) {
|
|
d = rect_dist(r2, &r);
|
|
if (d < dmin) {
|
|
dmin = d;
|
|
r1 = r2;
|
|
}
|
|
r2++;
|
|
}
|
|
if (dmin < UPDATE_DMIN || xs->update_nb == UPDATE_MAX_REGIONS) {
|
|
css_union_rect(r1, &r);
|
|
} else {
|
|
*r2 = r;
|
|
xs->update_nb++;
|
|
}
|
|
}
|
|
|
|
#else
|
|
static inline void update_rect(X11State *xs,
|
|
qe__unused__ int x1, qe__unused__ int y1,
|
|
qe__unused__ int x2, qe__unused__ int y2)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
static int x11_dpy_probe(void)
|
|
{
|
|
char *dpy;
|
|
|
|
if (force_tty)
|
|
return 0;
|
|
|
|
/* if no env variable DISPLAY, we do not use x11 */
|
|
dpy = getenv("DISPLAY");
|
|
if (dpy == NULL || dpy[0] == '\0')
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
static int x11_dpy_init(QEditScreen *s, int w, int h)
|
|
{
|
|
XSizeHints hint;
|
|
int xsize, ysize;
|
|
XSetWindowAttributes xwa;
|
|
int font_xsize, font_ysize, fd;
|
|
unsigned int fg, bg;
|
|
const char *p;
|
|
QEFont *font;
|
|
QEStyleDef default_style;
|
|
XGCValues gc_val;
|
|
X11State *xs = qe_mallocz(X11State);
|
|
|
|
s->priv_data = xs;
|
|
s->media = CSS_MEDIA_SCREEN;
|
|
|
|
if (!display_str)
|
|
display_str = "";
|
|
|
|
xs->display = XOpenDisplay(display_str);
|
|
if (xs->display == NULL) {
|
|
fprintf(stderr, "Could not open X11 display - exiting.\n");
|
|
return -1;
|
|
}
|
|
xs->xscreen = DefaultScreen(xs->display);
|
|
|
|
bg = BlackPixel(xs->display, xs->xscreen);
|
|
fg = WhitePixel(xs->display, xs->xscreen);
|
|
xs->screen_width = DisplayWidth(xs->display, xs->xscreen);
|
|
xs->screen_height = DisplayHeight(xs->display, xs->xscreen);
|
|
|
|
/* At this point, we should be able to ask for metrics */
|
|
if (font_ptsize)
|
|
qe_styles[0].font_size = font_ptsize;
|
|
get_style(NULL, &default_style, 0);
|
|
font = x11_dpy_open_font(s, default_style.font_style,
|
|
default_style.font_size);
|
|
if (!font) {
|
|
fprintf(stderr, "Could not open default font\n");
|
|
exit(1);
|
|
}
|
|
font_ysize = font->ascent + font->descent;
|
|
font_xsize = glyph_width(s, font, 'x');
|
|
x11_dpy_close_font(s, &font);
|
|
|
|
if (w > 0 && h > 0) {
|
|
xsize = w;
|
|
ysize = h;
|
|
} else {
|
|
xsize = 128 * font_xsize;
|
|
ysize = 50 * font_ysize;
|
|
|
|
if (geometry_str) {
|
|
p = geometry_str;
|
|
xsize = strtol(p, (char **)&p, 0);
|
|
if (*p == 'x')
|
|
p++;
|
|
ysize = strtol(p, (char **)&p, 0);
|
|
|
|
if (xsize <= 0 || ysize <=0) {
|
|
fprintf(stderr, "Invalid geometry '%s'\n", geometry_str);
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
s->width = xsize;
|
|
s->height = ysize;
|
|
s->charset = &charset_utf8;
|
|
|
|
s->clip_x1 = 0;
|
|
s->clip_y1 = 0;
|
|
s->clip_x2 = s->width;
|
|
s->clip_y2 = s->height;
|
|
/* Fill in hint structure */
|
|
|
|
hint.x = 0;
|
|
hint.y = 0;
|
|
hint.width = xsize;
|
|
hint.height = ysize;
|
|
hint.flags = PPosition | PSize;
|
|
|
|
/* Make the window */
|
|
xs->window = XCreateSimpleWindow(xs->display,
|
|
DefaultRootWindow(xs->display),
|
|
hint.x, hint.y,
|
|
hint.width, hint.height,
|
|
4, fg, bg);
|
|
/* Enable backing store */
|
|
xwa.backing_store = Always;
|
|
XChangeWindowAttributes(xs->display, xs->window, CWBackingStore, &xwa);
|
|
|
|
XSelectInput(xs->display, xs->window, StructureNotifyMask);
|
|
|
|
/* Tell other applications about this window */
|
|
|
|
XSetStandardProperties(xs->display, xs->window,
|
|
"qemacs", "qemacs",
|
|
None, NULL, 0, &hint);
|
|
|
|
/* Map window. */
|
|
|
|
XMapWindow(xs->display, xs->window);
|
|
|
|
/* Wait for map. */
|
|
while (1) {
|
|
XEvent xev;
|
|
XNextEvent(xs->display, &xev);
|
|
if (xev.type == MapNotify && xev.xmap.event == xs->window)
|
|
break;
|
|
}
|
|
xs->event_mask = KeyPressMask | ButtonPressMask | ButtonReleaseMask |
|
|
ButtonMotionMask | ExposureMask | StructureNotifyMask;
|
|
XSelectInput(xs->display, xs->window, xs->event_mask);
|
|
|
|
XGetWindowAttributes(xs->display, xs->window, &xs->attr);
|
|
|
|
/* see if we can bypass the X color functions */
|
|
xs->visual_depth = 0;
|
|
if (xs->attr.visual->class == TrueColor) {
|
|
XVisualInfo *vinfo, templ;
|
|
int n;
|
|
templ.visualid = xs->attr.visual->visualid;
|
|
vinfo = XGetVisualInfo(xs->display, VisualIDMask, &templ, &n);
|
|
if (vinfo) {
|
|
xs->visual_depth = vinfo->depth;
|
|
}
|
|
}
|
|
|
|
xs->xim = XOpenIM(xs->display, NULL, NULL, NULL);
|
|
xs->xic = XCreateIC(xs->xim, XNInputStyle,
|
|
XIMPreeditNothing | XIMStatusNothing,
|
|
XNClientWindow, xs->window,
|
|
XNFocusWindow, xs->window,
|
|
NULL);
|
|
|
|
xs->wm_delete_window = XInternAtom(xs->display, "WM_DELETE_WINDOW", False);
|
|
XSetWMProtocols(xs->display, xs->window, &xs->wm_delete_window, 1);
|
|
|
|
xs->gc = XCreateGC(xs->display, xs->window, 0, NULL);
|
|
#ifdef CONFIG_XFT
|
|
xs->renderDraw = XftDrawCreate(xs->display, xs->window, xs->attr.visual,
|
|
DefaultColormap(xs->display, xs->xscreen));
|
|
#endif
|
|
/* double buffer handling */
|
|
xs->gc_pixmap = XCreateGC(xs->display, xs->window, 0, NULL);
|
|
gc_val.graphics_exposures = 0;
|
|
XChangeGC(xs->display, xs->gc_pixmap, GCGraphicsExposures, &gc_val);
|
|
XSetForeground(xs->display, xs->gc, BlackPixel(xs->display, xs->xscreen));
|
|
#ifdef CONFIG_DOUBLE_BUFFER
|
|
xs->dbuffer = XCreatePixmap(xs->display, xs->window, xsize, ysize, xs->attr.depth);
|
|
/* erase pixmap */
|
|
XFillRectangle(xs->display, xs->dbuffer, xs->gc, 0, 0, xsize, ysize);
|
|
update_reset(xs);
|
|
#else
|
|
xs->dbuffer = xs->window;
|
|
#endif
|
|
|
|
#ifdef CONFIG_XSHM
|
|
/* shm extension usable ? */
|
|
p = XDisplayName(display_str);
|
|
strstart(p, "unix:", &p);
|
|
strstart(p, "localhost:", &p);
|
|
xs->shm_use = 0;
|
|
/* Check if display is local and XShm available */
|
|
if ((*p == ':') && XShmQueryExtension(xs->display))
|
|
xs->shm_use = 1;
|
|
#endif
|
|
|
|
/* compute bitmap format */
|
|
switch (xs->visual_depth) {
|
|
case 15:
|
|
s->bitmap_format = QEBITMAP_FORMAT_RGB555;
|
|
break;
|
|
default:
|
|
case 16:
|
|
s->bitmap_format = QEBITMAP_FORMAT_RGB565;
|
|
break;
|
|
case 24:
|
|
s->bitmap_format = QEBITMAP_FORMAT_RGB24;
|
|
break;
|
|
case 32:
|
|
s->bitmap_format = QEBITMAP_FORMAT_RGBA32;
|
|
break;
|
|
}
|
|
s->video_format = s->bitmap_format;
|
|
|
|
#ifdef CONFIG_XV
|
|
xv_init(s);
|
|
#endif
|
|
fd = ConnectionNumber(xs->display);
|
|
set_read_handler(fd, x11_handle_event, s);
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_XV
|
|
static void xv_init(QEditScreen *s)
|
|
{
|
|
X11State *xs = s->priv_data;
|
|
int i;
|
|
XvPortID xv_p;
|
|
|
|
xs->xv_port = 0; /* zero means no XV port found */
|
|
if (XvQueryAdaptors(xs->display,
|
|
DefaultRootWindow(xs->display),
|
|
&xs->xv_nb_adaptors, &xs->xv_ai) != Success)
|
|
return;
|
|
|
|
for (i = 0; i < (int)xs->xv_nb_adaptors; i++) {
|
|
if ((xs->xv_ai[i].type & XvInputMask) &&
|
|
(xs->xv_ai[i].type & XvImageMask)) {
|
|
for (xv_p = xs->xv_ai[i].base_id;
|
|
xv_p < (XvPortID)(xs->xv_ai[i].base_id + xs->xv_ai[i].num_ports);
|
|
xv_p++) {
|
|
if (!XvGrabPort(xs->display, xv_p, CurrentTime)) {
|
|
xs->xv_port = xv_p;
|
|
goto found;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
found:
|
|
|
|
xs->xv_fo = XvListImageFormats(xs->display, xs->xv_port, &xs->xv_nb_formats);
|
|
for (i = 0; i < xs->xv_nb_formats; i++) {
|
|
XvImageFormatValues *fo = &xs->xv_fo[i];
|
|
#if 0
|
|
printf("Xvideo image format: 0x%x (%c%c%c%c) %s\n",
|
|
fo->id,
|
|
(fo->id) & 0xff,
|
|
(fo->id >> 8) & 0xff,
|
|
(fo->id >> 16) & 0xff,
|
|
(fo->id >> 24) & 0xff,
|
|
(fo->format == XvPacked) ? "packed" : "planar");
|
|
#endif
|
|
/* search YUV420P format */
|
|
if (fo->id == 0x32315659 && fo->format == XvPlanar) {
|
|
xs->xv_format = fo->id;
|
|
break;
|
|
}
|
|
}
|
|
/* if no format found, then release port */
|
|
if (i == xs->xv_nb_formats) {
|
|
XvUngrabPort(xs->display, xv_p, CurrentTime);
|
|
xs->xv_port = 0;
|
|
} else {
|
|
s->video_format = QEBITMAP_FORMAT_YUV420P;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void x11_dpy_close(QEditScreen *s)
|
|
{
|
|
X11State *xs = s->priv_data;
|
|
|
|
#ifdef CONFIG_DOUBLE_BUFFER
|
|
XFreePixmap(xs->display, xs->dbuffer);
|
|
#endif
|
|
XCloseDisplay(xs->display);
|
|
qe_free(&s->priv_data);
|
|
}
|
|
|
|
static int x11_term_resize(QEditScreen *s, int w, int h)
|
|
{
|
|
if (s->width == w && s->height == h)
|
|
return 0;
|
|
|
|
s->width = w;
|
|
s->height = h;
|
|
|
|
#ifdef CONFIG_DOUBLE_BUFFER
|
|
{
|
|
X11State *xs = s->priv_data;
|
|
|
|
/* resize double buffer */
|
|
XFreePixmap(xs->display, xs->dbuffer);
|
|
xs->dbuffer = XCreatePixmap(xs->display, xs->window, w, h, xs->attr.depth);
|
|
}
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
static unsigned long get_x11_color(X11State *xs, QEColor color)
|
|
{
|
|
unsigned int r = (color >> 16) & 0xff;
|
|
unsigned int g = (color >> 8) & 0xff;
|
|
unsigned int b = (color >> 0) & 0xff;
|
|
XColor col;
|
|
|
|
switch (xs->visual_depth) {
|
|
case 15:
|
|
return ((((r) >> 3) << 10) | (((g) >> 3) << 5) | ((b) >> 3));
|
|
case 16:
|
|
return ((((r) >> 3) << 11) | (((g) >> 2) << 5) | ((b) >> 3));
|
|
case 24:
|
|
case 32:
|
|
return (r << 16) | (g << 8) | b;
|
|
default:
|
|
/* NOTE: the color is never freed */
|
|
/* XXX: should do allocation ourself ? */
|
|
col.red = r << 8;
|
|
col.green = g << 8;
|
|
col.blue = b << 8;
|
|
XAllocColor(xs->display, xs->attr.colormap, &col);
|
|
return col.pixel;
|
|
}
|
|
}
|
|
|
|
/* Print the unicode string 'str' with baseline at position (x,y). The
|
|
higher bits of each char may contain attributes. */
|
|
#ifdef CONFIG_XFT
|
|
|
|
static void x11_dpy_fill_rectangle(QEditScreen *s,
|
|
int x1, int y1, int w, int h, QEColor color)
|
|
{
|
|
X11State *xs = s->priv_data;
|
|
XftColor col;
|
|
int r, g, b, a;
|
|
|
|
a = (color >> 24) & 0xff;
|
|
r = (color >> 16) & 0xff;
|
|
g = (color >> 8) & 0xff;
|
|
b = (color >> 0) & 0xff;
|
|
/* not exact, but faster */
|
|
col.color.red = r << 8;
|
|
col.color.green = g << 8;
|
|
col.color.blue = b << 8;
|
|
col.color.alpha = a << 8;
|
|
col.pixel = get_x11_color(xs, color);
|
|
XftDrawRect(xs->renderDraw, &col, x1, y1, w, h);
|
|
}
|
|
|
|
static void x11_dpy_xor_rectangle(QEditScreen *s,
|
|
int x1, int y1, int w, int h, QEColor color)
|
|
{
|
|
X11State *xs = s->priv_data;
|
|
unsigned long fg;
|
|
|
|
fg = get_x11_color(xs, QE_RGB(255, 255, 255));
|
|
XSetForeground(xs->display, xs->gc, fg);
|
|
XSetFunction(xs->display, xs->gc, GXxor);
|
|
XFillRectangle(xs->display, xs->dbuffer, xs->gc, x1, y1, w, h);
|
|
XSetFunction(xs->display, xs->gc, GXcopy);
|
|
}
|
|
|
|
static QEFont *x11_dpy_open_font(QEditScreen *s, int style, int size)
|
|
{
|
|
X11State *xs = s->priv_data;
|
|
const char *family;
|
|
int weight, slant;
|
|
XftFont *renderFont;
|
|
QEFont *font;
|
|
|
|
font = qe_mallocz(QEFont);
|
|
if (!font)
|
|
return NULL;
|
|
|
|
switch (style & QE_FONT_FAMILY_MASK) {
|
|
default:
|
|
case QE_FONT_FAMILY_FIXED:
|
|
family = font_family_str;
|
|
break;
|
|
case QE_FONT_FAMILY_SANS:
|
|
family = "sans";
|
|
break;
|
|
case QE_FONT_FAMILY_SERIF:
|
|
family = "serif";
|
|
break;
|
|
}
|
|
weight = XFT_WEIGHT_MEDIUM;
|
|
if (style & QE_FONT_STYLE_BOLD)
|
|
weight = XFT_WEIGHT_BOLD;
|
|
slant = XFT_SLANT_ROMAN;
|
|
if (style & QE_FONT_STYLE_ITALIC)
|
|
slant = XFT_SLANT_ITALIC;
|
|
|
|
renderFont = XftFontOpen(xs->display, xs->xscreen,
|
|
XFT_FAMILY, XftTypeString, family,
|
|
XFT_SIZE, XftTypeInteger, size,
|
|
XFT_WEIGHT, XftTypeInteger, weight,
|
|
XFT_SLANT, XftTypeInteger, slant,
|
|
0);
|
|
if (!renderFont) {
|
|
/* CG: don't know if this can happen, should try fallback? */
|
|
qe_free(&font);
|
|
return NULL;
|
|
}
|
|
font->ascent = renderFont->ascent;
|
|
font->descent = renderFont->descent;
|
|
font->priv_data = renderFont;
|
|
return font;
|
|
}
|
|
|
|
static void x11_dpy_close_font(QEditScreen *s, QEFont **fontp)
|
|
{
|
|
X11State *xs = s->priv_data;
|
|
QEFont *font = *fontp;
|
|
|
|
if (font) {
|
|
XftFont *renderFont = font->priv_data;
|
|
|
|
XftFontClose(xs->display, renderFont);
|
|
/* Clear structure to force crash if font is still used after
|
|
* close_font.
|
|
*/
|
|
memset(font, 0, sizeof(*font));
|
|
qe_free(fontp);
|
|
}
|
|
}
|
|
|
|
static int x11_term_glyph_width(QEditScreen *s, QEFont *font, unsigned int cc)
|
|
{
|
|
X11State *xs = s->priv_data;
|
|
XftFont *renderFont = font->priv_data;
|
|
XGlyphInfo gi;
|
|
|
|
XftTextExtents32(xs->display, renderFont, &cc, 1, &gi);
|
|
return gi.xOff;
|
|
}
|
|
|
|
static void x11_dpy_draw_text(QEditScreen *s, QEFont *font,
|
|
int x, int y, const unsigned int *str, int len,
|
|
QEColor color)
|
|
{
|
|
X11State *xs = s->priv_data;
|
|
XftFont *renderFont = font->priv_data;
|
|
XftColor col;
|
|
int a = (color >> 24) & 0xff;
|
|
int r = (color >> 16) & 0xff;
|
|
int g = (color >> 8) & 0xff;
|
|
int b = (color >> 0) & 0xff;
|
|
|
|
/* not exact, but faster */
|
|
col.color.red = r << 8;
|
|
col.color.green = g << 8;
|
|
col.color.blue = b << 8;
|
|
col.color.alpha = a << 8;
|
|
col.pixel = get_x11_color(xs, color);
|
|
|
|
XftDrawString32(xs->renderDraw, &col, renderFont, x, y,
|
|
(XftChar32 *)str, len);
|
|
}
|
|
|
|
#else
|
|
|
|
static void x11_dpy_fill_rectangle(QEditScreen *s,
|
|
int x1, int y1, int w, int h, QEColor color)
|
|
{
|
|
X11State *xs = s->priv_data;
|
|
unsigned long xcolor;
|
|
|
|
update_rect(xs, x1, y1, x1 + w, y1 + h);
|
|
|
|
xcolor = get_x11_color(xs, color);
|
|
XSetForeground(xs->display, xs->gc, xcolor);
|
|
XFillRectangle(xs->display, xs->dbuffer, xs->gc, x1, y1, w, h);
|
|
}
|
|
|
|
static void x11_dpy_xor_rectangle(QEditScreen *s,
|
|
int x1, int y1, int w, int h, QEColor color)
|
|
{
|
|
X11State *xs = s->priv_data;
|
|
int fg;
|
|
|
|
update_rect(xs, x1, y1, x1 + w, y1 + h);
|
|
|
|
fg = WhitePixel(xs->display, xs->xscreen);
|
|
XSetForeground(xs->display, xs->gc, fg);
|
|
XSetFunction(xs->display, xs->gc, GXxor);
|
|
XFillRectangle(xs->display, xs->dbuffer, xs->gc, x1, y1, w, h);
|
|
XSetFunction(xs->display, xs->gc, GXcopy);
|
|
}
|
|
|
|
static void get_entry(char *buf, int buf_size, const char **pp)
|
|
{
|
|
const char *p, *r;
|
|
int len;
|
|
|
|
r = *pp;
|
|
p = strchr(r, '-');
|
|
if (!p) {
|
|
if (buf)
|
|
buf[0] = '\0';
|
|
} else {
|
|
if (buf) {
|
|
len = p - r;
|
|
if (len >= buf_size)
|
|
len = buf_size - 1;
|
|
memcpy(buf, r, len);
|
|
buf[len] = '\0';
|
|
}
|
|
*pp = p + 1;
|
|
}
|
|
}
|
|
|
|
|
|
static QEFont *x11_dpy_open_font(QEditScreen *s, int style, int size)
|
|
{
|
|
QEmacsState *qs = &qe_state;
|
|
X11State *xs = s->priv_data;
|
|
char family[128];
|
|
const char *family_list, *p1;
|
|
XFontStruct *xfont;
|
|
QEFont *font;
|
|
char buf[512];
|
|
int count, found, dist, dist_min, i, size1, font_index, font_fallback;
|
|
char **list;
|
|
const char *p;
|
|
|
|
font = qe_mallocz(QEFont);
|
|
if (!font)
|
|
return NULL;
|
|
|
|
/* get font name */
|
|
font_index = ((style & QE_FONT_FAMILY_MASK) >> QE_FONT_FAMILY_SHIFT) - 1;
|
|
if ((unsigned)font_index >= NB_FONT_FAMILIES)
|
|
font_index = 0; /* fixed font is default */
|
|
family_list = qs->system_fonts[font_index];
|
|
if (family_list[0] == '\0')
|
|
family_list = default_x11_fonts[font_index];
|
|
|
|
/* take the nth font number in family list */
|
|
font_fallback = (style & QE_FONT_FAMILY_FALLBACK_MASK) >> QE_FONT_FAMILY_FALLBACK_SHIFT;
|
|
p = family_list;
|
|
for (i = 0; i < font_fallback; i++) {
|
|
p = strchr(p, ',');
|
|
if (!p) {
|
|
/* no font found */
|
|
qe_free(&font);
|
|
return NULL;
|
|
}
|
|
p++;
|
|
}
|
|
p1 = strchr(p, ',');
|
|
if (!p1)
|
|
pstrcpy(family, sizeof(family), p);
|
|
else
|
|
pstrncpy(family, sizeof(family), p, p1 - p);
|
|
#if 0
|
|
for (i = 0; i < 2; i++) {
|
|
char buf1[32];
|
|
if (i == 0)
|
|
snprintf(buf1, sizeof(buf1), "%d", size * 10);
|
|
else
|
|
pstrcpy(buf1, sizeof(buf1), "*");
|
|
snprintf(buf, sizeof(buf),
|
|
"-*-%s-*-*-*-*-*-%s-*-*-*-*-*-*",
|
|
family, buf1);
|
|
list = XListFonts(xs->display, buf, 256, &count);
|
|
if (count > 0)
|
|
break;
|
|
}
|
|
if (i == 2)
|
|
goto fail;
|
|
#else
|
|
/* note: we do not want the X server to scale the font (usually
|
|
ugly result), so we do not ask a given size */
|
|
snprintf(buf, sizeof(buf),
|
|
"-*-%s-*-*-*-*-*-%s-*-*-*-*-*-*",
|
|
family, "*");
|
|
list = XListFonts(xs->display, buf, 256, &count);
|
|
if (count == 0)
|
|
goto fail;
|
|
#endif
|
|
/* iterate thru each font and select closer one */
|
|
found = 0;
|
|
dist_min = INT_MAX;
|
|
for (i = 0; i < count; i++) {
|
|
dist = 0;
|
|
p = list[i] + 1;
|
|
get_entry(NULL, 0, &p);
|
|
get_entry(NULL, 0, &p); /* family */
|
|
get_entry(buf, sizeof(buf), &p); /* weight */
|
|
if (!((strequal(buf, "bold") && (style & QE_FONT_STYLE_BOLD)) ||
|
|
(strequal(buf, "medium") && !(style & QE_FONT_STYLE_BOLD))))
|
|
dist += 3;
|
|
get_entry(buf, sizeof(buf), &p); /* slant */
|
|
if (!((strequal(buf, "o") && (style & QE_FONT_STYLE_ITALIC)) ||
|
|
(strequal(buf, "i") && (style & QE_FONT_STYLE_ITALIC)) ||
|
|
(strequal(buf, "r") && !(style & QE_FONT_STYLE_ITALIC))))
|
|
dist += 3;
|
|
get_entry(NULL, 0, &p); /* swidth */
|
|
get_entry(NULL, 0, &p); /* adstyle */
|
|
get_entry(NULL, 0, &p); /* pixsize */
|
|
get_entry(buf, sizeof(buf), &p); /* size */
|
|
size1 = atoi(buf) / 10;
|
|
dist += abs(size - size1) * 3;
|
|
get_entry(NULL, 0, &p); /* pixsize */
|
|
get_entry(NULL, 0, &p); /* pixsize */
|
|
get_entry(NULL, 0, &p); /* pixsize */
|
|
get_entry(NULL, 0, &p); /* pixsize */
|
|
/* always favor unicode encoding */
|
|
if (!strequal(p, "iso10646-1"))
|
|
dist += 3;
|
|
|
|
if (dist < dist_min) {
|
|
found = i;
|
|
dist_min = dist;
|
|
}
|
|
}
|
|
|
|
xfont = XLoadQueryFont(xs->display, list[found]);
|
|
if (!xfont)
|
|
goto fail;
|
|
XFreeFontNames(list);
|
|
|
|
font->ascent = xfont->ascent;
|
|
font->descent = xfont->descent;
|
|
font->priv_data = xfont;
|
|
return font;
|
|
fail:
|
|
XFreeFontNames(list);
|
|
qe_free(&font);
|
|
return NULL;
|
|
}
|
|
|
|
static void x11_dpy_close_font(QEditScreen *s, QEFont **fontp)
|
|
{
|
|
X11State *xs = s->priv_data;
|
|
|
|
if (*fontp) {
|
|
QEFont *font = *fontp;
|
|
XFontStruct *xfont = font->priv_data;
|
|
|
|
XFreeFont(xs->display, xfont);
|
|
/* Clear structure to force crash if font is still used after
|
|
* close_font.
|
|
*/
|
|
memset(font, 0, sizeof(*font));
|
|
qe_free(fontp);
|
|
}
|
|
}
|
|
|
|
/* get a char struct associated to a char. Return NULL if no glyph
|
|
associated. */
|
|
static XCharStruct *get_char_struct(QEFont *font, int cc)
|
|
{
|
|
XFontStruct *xfont = font->priv_data;
|
|
int b1, b2;
|
|
XCharStruct *cs;
|
|
|
|
if (!xfont)
|
|
return NULL;
|
|
|
|
if (xfont->min_byte1 == 0 && xfont->max_byte1 == 0) {
|
|
if (cc > (int)xfont->max_char_or_byte2)
|
|
return NULL;
|
|
cc -= xfont->min_char_or_byte2;
|
|
if (cc < 0)
|
|
return NULL;
|
|
} else {
|
|
b1 = (cc >> 8) & 0xff;
|
|
b2 = cc & 0xff;
|
|
if (b1 > (int)xfont->max_byte1)
|
|
return NULL;
|
|
b1 -= xfont->min_byte1;
|
|
if (b1 < 0)
|
|
return NULL;
|
|
if (b2 > (int)xfont->max_char_or_byte2)
|
|
return NULL;
|
|
b2 -= xfont->min_char_or_byte2;
|
|
if (b2 < 0)
|
|
return NULL;
|
|
cc = b1 * (xfont->max_char_or_byte2 -
|
|
xfont->min_char_or_byte2 + 1) + b2;
|
|
}
|
|
cs = xfont->per_char;
|
|
if (!cs)
|
|
return &xfont->min_bounds; /* all char have same metrics */
|
|
cs += cc;
|
|
/* fast test for non existant char */
|
|
if (cs->width == 0 &&
|
|
(cs->ascent | cs->descent | cs->rbearing | cs->lbearing) == 0) {
|
|
return NULL;
|
|
} else {
|
|
return cs;
|
|
}
|
|
}
|
|
|
|
static XCharStruct *handle_fallback(QEditScreen *s, QEFont **out_font,
|
|
QEFont *font, unsigned int cc)
|
|
{
|
|
XFontStruct *xfont;
|
|
XCharStruct *cs;
|
|
int fallback_count;
|
|
QEFont *font1;
|
|
|
|
/* fallback case */
|
|
for (fallback_count = 1; fallback_count < 5; fallback_count++) {
|
|
font1 = select_font(s, font->style |
|
|
(fallback_count << QE_FONT_FAMILY_FALLBACK_SHIFT),
|
|
font->size);
|
|
if (!font1)
|
|
break;
|
|
cs = get_char_struct(font1, cc);
|
|
if (cs) {
|
|
*out_font = font1;
|
|
return cs;
|
|
}
|
|
release_font(s, font1);
|
|
}
|
|
|
|
/* really no glyph : use default char in current font */
|
|
/* Should have half-width and full-width default char patterns */
|
|
xfont = font->priv_data;
|
|
cs = get_char_struct(font, xfont->default_char);
|
|
*out_font = lock_font(s, font);
|
|
return cs;
|
|
}
|
|
|
|
static void x11_dpy_text_metrics(QEditScreen *s, QEFont *font,
|
|
QECharMetrics *metrics,
|
|
const unsigned int *str, int len)
|
|
{
|
|
QEFont *font1;
|
|
XCharStruct *cs;
|
|
int i, x;
|
|
unsigned int cc;
|
|
|
|
metrics->font_ascent = font->ascent;
|
|
metrics->font_descent = font->descent;
|
|
x = 0;
|
|
for (i = 0; i < len; i++) {
|
|
cc = str[i];
|
|
cs = get_char_struct(font, cc);
|
|
if (cs) {
|
|
/* most common case */
|
|
x += cs->width;
|
|
} else {
|
|
/* no glyph: use a fallback font */
|
|
cs = handle_fallback(s, &font1, font, cc);
|
|
if (cs) {
|
|
x += cs->width;
|
|
metrics->font_ascent = max(metrics->font_ascent, font1->ascent);
|
|
metrics->font_descent = max(metrics->font_descent, font1->descent);
|
|
}
|
|
release_font(s, font1);
|
|
}
|
|
}
|
|
metrics->width = x;
|
|
}
|
|
|
|
static void x11_dpy_draw_text(QEditScreen *s, QEFont *font,
|
|
int x1, int y, const unsigned int *str, int len,
|
|
QEColor color)
|
|
{
|
|
X11State *xs = s->priv_data;
|
|
XFontStruct *xfont;
|
|
QEFont *font1, *last_font;
|
|
XCharStruct *cs;
|
|
#ifdef __GNUC__
|
|
XChar2b x11_str[len];
|
|
#else
|
|
XChar2b x11_str[LINE_MAX_SIZE];
|
|
#endif
|
|
XChar2b *q;
|
|
int i, l, x, x_start;
|
|
unsigned int cc;
|
|
unsigned long xcolor;
|
|
|
|
xcolor = get_x11_color(xs, color);
|
|
XSetForeground(xs->display, xs->gc, xcolor);
|
|
q = x11_str;
|
|
i = 0;
|
|
x = x1;
|
|
x_start = x;
|
|
last_font = font;
|
|
while (i < len) {
|
|
cc = str[i++];
|
|
cs = get_char_struct(font, cc);
|
|
if (cs) {
|
|
font1 = font; /* most common case */
|
|
} else {
|
|
/* complicated case: fallback */
|
|
cs = handle_fallback(s, &font1, font, cc);
|
|
if (!cs) {
|
|
/* still no char: use default glyph */
|
|
xfont = font->priv_data;
|
|
cc = xfont->default_char;
|
|
}
|
|
}
|
|
/* flush previous chars if font change needed */
|
|
if (font1 != last_font && q > x11_str) {
|
|
xfont = last_font->priv_data;
|
|
l = q - x11_str;
|
|
XSetFont(xs->display, xs->gc, xfont->fid);
|
|
XDrawString16(xs->display, xs->dbuffer, xs->gc, x_start, y, x11_str, l);
|
|
update_rect(xs, x_start, y - last_font->ascent, x, y + last_font->descent);
|
|
x_start = x;
|
|
q = x11_str;
|
|
}
|
|
last_font = font1;
|
|
/* XXX: invalid conversion from UCS4 to UCS2 */
|
|
if (cc >= 0xFFFF)
|
|
cc = 0xFFFD;
|
|
q->byte1 = (cc >> 8) & 0xff;
|
|
q->byte2 = (cc) & 0xff;
|
|
q++;
|
|
x += cs->width;
|
|
}
|
|
if (q > x11_str) {
|
|
/* flush remaining chars (more common case) */
|
|
xfont = last_font->priv_data;
|
|
l = q - x11_str;
|
|
XSetFont(xs->display, xs->gc, xfont->fid);
|
|
XDrawString16(xs->display, xs->dbuffer, xs->gc, x_start, y, x11_str, l);
|
|
update_rect(xs, x_start, y - last_font->ascent, x, y + last_font->descent);
|
|
}
|
|
/* underline synthesis */
|
|
if (font->style & (QE_FONT_STYLE_UNDERLINE | QE_FONT_STYLE_LINE_THROUGH)) {
|
|
int dy, h, w;
|
|
h = (font->descent + 2) / 4;
|
|
if (h < 1)
|
|
h = 1;
|
|
w = x - x1;
|
|
if (font->style & QE_FONT_STYLE_UNDERLINE) {
|
|
dy = (font->descent + 1) / 3;
|
|
XFillRectangle(xs->display, xs->dbuffer, xs->gc, x1, y + dy, w, h);
|
|
}
|
|
if (font->style & QE_FONT_STYLE_LINE_THROUGH) {
|
|
dy = -(font->ascent / 2 - 1);
|
|
XFillRectangle(xs->display, xs->dbuffer, xs->gc, x1, y + dy, w, h);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void x11_dpy_set_clip(QEditScreen *s, int x, int y, int w, int h)
|
|
{
|
|
X11State *xs = s->priv_data;
|
|
XRectangle rect;
|
|
|
|
rect.x = x;
|
|
rect.y = y;
|
|
rect.width = w;
|
|
rect.height = h;
|
|
XSetClipRectangles(xs->display, xs->gc, 0, 0, &rect, 1, YXSorted);
|
|
}
|
|
|
|
static void x11_dpy_flush(QEditScreen *s)
|
|
{
|
|
X11State *xs = s->priv_data;
|
|
#ifdef CONFIG_DOUBLE_BUFFER
|
|
CSSRect *r;
|
|
int i, w, h;
|
|
|
|
r = xs->update_rects;
|
|
for (i = 0; i < xs->update_nb; i++) {
|
|
if (r->x1 < 0)
|
|
r->x1 = 0;
|
|
if (r->x2 > s->width)
|
|
r->x2 = s->width;
|
|
if (r->y1 < 0)
|
|
r->y1 = 0;
|
|
if (r->y2 > s->height)
|
|
r->y2 = s->height;
|
|
w = r->x2 - r->x1;
|
|
h = r->y2 - r->y1;
|
|
if (w > 0 && h > 0) {
|
|
XCopyArea(xs->display, xs->dbuffer, xs->window, xs->gc_pixmap,
|
|
r->x1, r->y1, w, h, r->x1, r->y1);
|
|
#if 0
|
|
XSetForeground(xs->display, xs->gc_pixmap, 0xffff);
|
|
XDrawRectangle(xs->display, xs->window, xs->gc_pixmap, r->x1, r->y1,
|
|
w - 1, h - 1);
|
|
#endif
|
|
}
|
|
r++;
|
|
}
|
|
#endif
|
|
/* XXX: update cursor? */
|
|
XFlush(xs->display);
|
|
#ifdef CONFIG_DOUBLE_BUFFER
|
|
update_reset(xs);
|
|
#endif
|
|
}
|
|
|
|
static void x11_dpy_full_screen(QEditScreen *s, int full_screen)
|
|
{
|
|
X11State *xs = s->priv_data;
|
|
XWindowAttributes attr1;
|
|
Window win;
|
|
|
|
XGetWindowAttributes(xs->display, xs->window, &attr1);
|
|
if (full_screen) {
|
|
if (attr1.width != xs->screen_width || attr1.height != xs->screen_height) {
|
|
/* store current window position and size */
|
|
XTranslateCoordinates(xs->display, xs->window, attr1.root, 0, 0,
|
|
&xs->last_window_x, &xs->last_window_y, &win);
|
|
xs->last_window_width = attr1.width;
|
|
xs->last_window_height = attr1.height;
|
|
XMoveResizeWindow(xs->display, xs->window,
|
|
0, 0, xs->screen_width, xs->screen_height);
|
|
}
|
|
} else {
|
|
if (xs->last_window_width) {
|
|
XMoveResizeWindow(xs->display, xs->window,
|
|
xs->last_window_x, xs->last_window_y,
|
|
xs->last_window_width, xs->last_window_height);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void x11_dpy_selection_activate(QEditScreen *s)
|
|
{
|
|
X11State *xs = s->priv_data;
|
|
|
|
/* own selection from now */
|
|
XSetSelectionOwner(xs->display, XA_PRIMARY, xs->window, CurrentTime);
|
|
}
|
|
|
|
static Bool test_event(qe__unused__ Display *dpy, XEvent *ev,
|
|
qe__unused__ char *arg)
|
|
{
|
|
return (ev->type == SelectionNotify);
|
|
}
|
|
|
|
/* request the selection from the GUI and put it in a new yank buffer
|
|
if needed */
|
|
static void x11_dpy_selection_request(QEditScreen *s)
|
|
{
|
|
X11State *xs = s->priv_data;
|
|
QEmacsState *qs = &qe_state;
|
|
Window w;
|
|
Atom prop;
|
|
Atom utf8;
|
|
long nread;
|
|
unsigned char *data;
|
|
Atom actual_type;
|
|
int actual_fmt;
|
|
unsigned long nitems, bytes_after;
|
|
EditBuffer *b;
|
|
XEvent xev;
|
|
|
|
w = XGetSelectionOwner(xs->display, XA_PRIMARY);
|
|
if (w == None || w == xs->window)
|
|
return; /* qemacs can use its own selection */
|
|
|
|
/* use X11 selection (Will be pasted when receiving
|
|
SelectionNotify event) */
|
|
prop = XInternAtom(xs->display, "VT_SELECTION", False);
|
|
utf8 = XInternAtom(xs->display, "UTF8_STRING", False);
|
|
XConvertSelection(xs->display, XA_PRIMARY, utf8,
|
|
prop, xs->window, CurrentTime);
|
|
|
|
/* XXX: add timeout too if the target application is not well
|
|
educated */
|
|
XIfEvent(xs->display, &xev, test_event, NULL);
|
|
|
|
w = xev.xselection.requestor;
|
|
prop = xev.xselection.property;
|
|
|
|
/* copy GUI selection a new yank buffer */
|
|
b = new_yank_buffer(qs, NULL);
|
|
eb_set_charset(b, &charset_utf8, EOL_UNIX);
|
|
|
|
nread = 0;
|
|
for (;;) {
|
|
if ((XGetWindowProperty(xs->display, w, prop,
|
|
nread/4, 4096, True,
|
|
AnyPropertyType, &actual_type, &actual_fmt,
|
|
&nitems, &bytes_after,
|
|
&data) != Success) ||
|
|
(actual_type != utf8)) {
|
|
XFree(data);
|
|
break;
|
|
}
|
|
|
|
eb_write(b, nread, data, nitems);
|
|
|
|
nread += nitems;
|
|
XFree(data);
|
|
|
|
if (bytes_after == 0)
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* send qemacs selection to requestor */
|
|
static void selection_send(X11State *xs, XSelectionRequestEvent *rq)
|
|
{
|
|
static Atom xa_targets = None;
|
|
static Atom xa_formats[] = { None, None, None, None };
|
|
QEmacsState *qs = &qe_state;
|
|
unsigned char *buf;
|
|
XEvent ev;
|
|
EditBuffer *b;
|
|
|
|
if (xa_targets == None)
|
|
xa_targets = XInternAtom(xs->display, "TARGETS", False);
|
|
if (xa_formats[0] == None) {
|
|
xa_formats[0] = XInternAtom(xs->display, "UTF8_STRING", False);
|
|
xa_formats[1] = XInternAtom(xs->display, "text/plain;charset=UTF-8", False);
|
|
xa_formats[2] = XInternAtom(xs->display, "text/plain;charset=utf-8", False);
|
|
xa_formats[3] = XA_STRING;
|
|
}
|
|
|
|
ev.xselection.type = SelectionNotify;
|
|
ev.xselection.property = None;
|
|
ev.xselection.display = rq->display;
|
|
ev.xselection.requestor = rq->requestor;
|
|
ev.xselection.selection = rq->selection;
|
|
ev.xselection.target = rq->target;
|
|
ev.xselection.time = rq->time;
|
|
|
|
if (rq->target == xa_targets) {
|
|
unsigned int target_list[1 + countof(xa_formats)];
|
|
int i;
|
|
|
|
/* indicate which are supported types */
|
|
target_list[0] = xa_targets;
|
|
for (i = 0; i < countof(xa_formats); i++)
|
|
target_list[i + 1] = xa_formats[i];
|
|
|
|
XChangeProperty(xs->display, rq->requestor, rq->property,
|
|
xa_targets, 8*sizeof(target_list[0]), PropModeReplace,
|
|
(unsigned char *)target_list,
|
|
countof(target_list));
|
|
} else
|
|
if (rq->target == XA_STRING) {
|
|
/* XXX: charset is ignored! */
|
|
|
|
/* get qemacs yank buffer */
|
|
b = qs->yank_buffers[qs->yank_current];
|
|
if (!b)
|
|
return;
|
|
buf = qe_malloc_array(unsigned char, b->total_size);
|
|
if (!buf)
|
|
return;
|
|
eb_read(b, 0, buf, b->total_size);
|
|
|
|
XChangeProperty(xs->display, rq->requestor, rq->property,
|
|
XA_STRING, 8, PropModeReplace,
|
|
buf, b->total_size);
|
|
qe_free(&buf);
|
|
} else
|
|
if (rq->target == xa_formats[0]
|
|
|| rq->target == xa_formats[1]
|
|
|| rq->target == xa_formats[2]) {
|
|
int len, size;
|
|
|
|
/* get qemacs yank buffer */
|
|
b = qs->yank_buffers[qs->yank_current];
|
|
if (!b)
|
|
return;
|
|
|
|
/* Get buffer contents encoded in utf-8-unix */
|
|
size = eb_get_content_size(b) + 1;
|
|
buf = qe_malloc_array(unsigned char, size);
|
|
if (!buf)
|
|
return;
|
|
len = eb_get_contents(b, (char *)buf, size);
|
|
|
|
XChangeProperty(xs->display, rq->requestor, rq->property,
|
|
rq->target, 8, PropModeReplace,
|
|
buf, len);
|
|
qe_free(&buf);
|
|
}
|
|
ev.xselection.property = rq->property;
|
|
XSendEvent(xs->display, rq->requestor, False, 0, &ev);
|
|
}
|
|
|
|
/* fast test to see if the user pressed a key or a mouse button */
|
|
static int x11_dpy_is_user_input_pending(QEditScreen *s)
|
|
{
|
|
X11State *xs = s->priv_data;
|
|
XEvent xev;
|
|
|
|
if (XCheckMaskEvent(xs->display, KeyPressMask | ButtonPressMask, &xev)) {
|
|
XPutBackEvent(xs->display, &xev);
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
typedef struct QExposeRegion QExposeRegion;
|
|
struct QExposeRegion {
|
|
int pending;
|
|
/* simplistic single rectangle region */
|
|
int x0, y0, x1, y1;
|
|
};
|
|
|
|
static void qe_expose_reset(QEditScreen *s, QExposeRegion *rgn)
|
|
{
|
|
rgn->pending = 0;
|
|
rgn->x0 = rgn->y0 = INT_MAX;
|
|
rgn->x1 = rgn->y1 = INT_MIN;
|
|
}
|
|
|
|
static void qe_expose_set(QEditScreen *s, QExposeRegion *rgn,
|
|
int x, int y, int width, int height)
|
|
{
|
|
rgn->pending = 1;
|
|
rgn->x0 = x;
|
|
rgn->y0 = y;
|
|
rgn->x1 = x + width;
|
|
rgn->y1 = y + height;
|
|
}
|
|
|
|
static void qe_expose_add(QEditScreen *s, QExposeRegion *rgn,
|
|
int x, int y, int width, int height)
|
|
{
|
|
rgn->pending++;
|
|
if (rgn->x0 > x)
|
|
rgn->x0 = x;
|
|
if (rgn->y0 > y)
|
|
rgn->y0 = y;
|
|
if (rgn->x1 < x + width)
|
|
rgn->x1 = x + width;
|
|
if (rgn->y1 > y + height)
|
|
rgn->y1 = y + height;
|
|
}
|
|
|
|
static void qe_expose_flush(QEditScreen *s, QExposeRegion *rgn)
|
|
{
|
|
if (rgn->pending) {
|
|
QEEvent ev1, *ev = &ev1;
|
|
|
|
/* Ignore expose region */
|
|
ev->expose_event.type = QE_EXPOSE_EVENT;
|
|
qe_handle_event(ev);
|
|
qe_expose_reset(s, rgn);
|
|
}
|
|
}
|
|
|
|
/* called when an X event happens. dispatch events to qe_handle_event() */
|
|
static void x11_handle_event(void *opaque)
|
|
{
|
|
QEditScreen *s = opaque;
|
|
X11State *xs = s->priv_data;
|
|
char buf[16];
|
|
XEvent xev;
|
|
KeySym keysym;
|
|
int shift, ctrl, meta, len, key;
|
|
QEEvent ev1, *ev = &ev1;
|
|
QExposeRegion rgn1, *rgn = &rgn1;
|
|
|
|
qe_expose_reset(s, rgn);
|
|
|
|
while (XPending(xs->display)) {
|
|
XNextEvent(xs->display, &xev);
|
|
switch (xev.type) {
|
|
case ClientMessage:
|
|
if ((Atom)xev.xclient.data.l[0] == xs->wm_delete_window) {
|
|
// cancel pending operation
|
|
ev->key_event.type = QE_KEY_EVENT;
|
|
ev->key_event.key = KEY_CTRL('g');
|
|
qe_handle_event(ev);
|
|
|
|
// simulate C-x C-c
|
|
ev->key_event.type = QE_KEY_EVENT;
|
|
ev->key_event.key = KEY_CTRL('x');
|
|
qe_handle_event(ev);
|
|
ev->key_event.type = QE_KEY_EVENT;
|
|
ev->key_event.key = KEY_CTRL('c');
|
|
qe_handle_event(ev);
|
|
}
|
|
break;
|
|
case ConfigureNotify:
|
|
if (x11_term_resize(s, xev.xconfigure.width, xev.xconfigure.height)) {
|
|
qe_expose_set(s, rgn, 0, 0, s->width, s->height);
|
|
}
|
|
break;
|
|
|
|
case Expose:
|
|
{
|
|
XExposeEvent *xe = &xev.xexpose;
|
|
|
|
qe_expose_add(s, rgn, xe->x, xe->y, xe->width, xe->height);
|
|
}
|
|
break;
|
|
|
|
case ButtonPress:
|
|
case ButtonRelease:
|
|
{
|
|
XButtonEvent *xe = &xev.xbutton;
|
|
|
|
if (xev.type == ButtonPress)
|
|
ev->button_event.type = QE_BUTTON_PRESS_EVENT;
|
|
else
|
|
ev->button_event.type = QE_BUTTON_RELEASE_EVENT;
|
|
|
|
ev->button_event.x = xe->x;
|
|
ev->button_event.y = xe->y;
|
|
switch (xe->button) {
|
|
case Button1:
|
|
ev->button_event.button = QE_BUTTON_LEFT;
|
|
break;
|
|
case Button2:
|
|
ev->button_event.button = QE_BUTTON_MIDDLE;
|
|
break;
|
|
case Button3:
|
|
ev->button_event.button = QE_BUTTON_RIGHT;
|
|
break;
|
|
case Button4:
|
|
ev->button_event.button = QE_WHEEL_UP;
|
|
break;
|
|
case Button5:
|
|
ev->button_event.button = QE_WHEEL_DOWN;
|
|
break;
|
|
default:
|
|
continue;
|
|
}
|
|
qe_expose_flush(s, rgn);
|
|
qe_handle_event(ev);
|
|
}
|
|
break;
|
|
case MotionNotify:
|
|
{
|
|
XMotionEvent *xe = &xev.xmotion;
|
|
ev->button_event.type = QE_MOTION_EVENT;
|
|
ev->button_event.x = xe->x;
|
|
ev->button_event.y = xe->y;
|
|
qe_expose_flush(s, rgn);
|
|
qe_handle_event(ev);
|
|
}
|
|
break;
|
|
/* selection handling */
|
|
case SelectionClear:
|
|
{
|
|
/* ask qemacs to stop visual notification of selection */
|
|
ev->type = QE_SELECTION_CLEAR_EVENT;
|
|
qe_expose_flush(s, rgn);
|
|
qe_handle_event(ev);
|
|
}
|
|
break;
|
|
case SelectionRequest:
|
|
qe_expose_flush(s, rgn);
|
|
selection_send(xs, &xev.xselectionrequest);
|
|
break;
|
|
case KeyPress:
|
|
#ifdef X_HAVE_UTF8_STRING
|
|
/* only present since XFree 4.0.2 */
|
|
{
|
|
Status status;
|
|
len = Xutf8LookupString(xs->xic, &xev.xkey, buf, sizeof(buf),
|
|
&keysym, &status);
|
|
}
|
|
#else
|
|
{
|
|
static XComposeStatus status;
|
|
len = XLookupString(&xev.xkey, buf, sizeof(buf),
|
|
&keysym, &status);
|
|
}
|
|
#endif
|
|
shift = (xev.xkey.state & ShiftMask);
|
|
ctrl = (xev.xkey.state & ControlMask);
|
|
meta = (xev.xkey.state & Mod1Mask);
|
|
#ifdef CONFIG_DARWIN
|
|
/* Also interpret Darwin's Command key as Meta */
|
|
meta |= (xev.xkey.state & Mod2Mask);
|
|
#endif
|
|
#if 0
|
|
fprintf(stderr, "keysym=%lx state=%lx%s%s%s len=%d buf[0]='\\x%02x'\n",
|
|
(long)keysym, (long)xev.xkey.state,
|
|
shift ? " shft" : "",
|
|
ctrl ? " ctrl" : "",
|
|
meta ? " meta" : "",
|
|
len, buf[0]);
|
|
#endif
|
|
if (meta) {
|
|
switch (keysym) {
|
|
case XK_BackSpace:
|
|
key = KEY_META(KEY_DEL);
|
|
goto got_key;
|
|
default:
|
|
if (len > 0) {
|
|
key = KEY_META(buf[0] & 0xff);
|
|
goto got_key;
|
|
}
|
|
if (keysym >= ' ' && keysym <= '~') {
|
|
key = KEY_META(' ') + keysym - ' ';
|
|
goto got_key;
|
|
}
|
|
break;
|
|
}
|
|
} else
|
|
if (shift) {
|
|
switch (keysym) {
|
|
case XK_ISO_Left_Tab:
|
|
key = KEY_SHIFT_TAB;
|
|
goto got_key;
|
|
default:
|
|
if (len > 0) {
|
|
key = buf[0] & 0xff;
|
|
goto got_key;
|
|
}
|
|
break;
|
|
}
|
|
} else
|
|
if (ctrl) {
|
|
switch (keysym) {
|
|
case XK_Right:
|
|
key = KEY_CTRL_RIGHT;
|
|
goto got_key;
|
|
case XK_Left:
|
|
key = KEY_CTRL_LEFT;
|
|
goto got_key;
|
|
case XK_Home:
|
|
key = KEY_CTRL_HOME;
|
|
goto got_key;
|
|
case XK_End:
|
|
key = KEY_CTRL_END;
|
|
goto got_key;
|
|
default:
|
|
if (len > 0) {
|
|
key = buf[0] & 0xff;
|
|
goto got_key;
|
|
}
|
|
break;
|
|
}
|
|
} else {
|
|
switch (keysym) {
|
|
case XK_F1: key = KEY_F1; goto got_key;
|
|
case XK_F2: key = KEY_F2; goto got_key;
|
|
case XK_F3: key = KEY_F3; goto got_key;
|
|
case XK_F4: key = KEY_F4; goto got_key;
|
|
case XK_F5: key = KEY_F5; goto got_key;
|
|
case XK_F6: key = KEY_F6; goto got_key;
|
|
case XK_F7: key = KEY_F7; goto got_key;
|
|
case XK_F8: key = KEY_F8; goto got_key;
|
|
case XK_F9: key = KEY_F9; goto got_key;
|
|
case XK_F10: key = KEY_F10; goto got_key;
|
|
case XK_F11: key = KEY_F11; goto got_key;
|
|
case XK_F13: key = KEY_F13; goto got_key;
|
|
case XK_F14: key = KEY_F14; goto got_key;
|
|
case XK_F15: key = KEY_F15; goto got_key;
|
|
case XK_F16: key = KEY_F16; goto got_key;
|
|
case XK_F17: key = KEY_F17; goto got_key;
|
|
case XK_F18: key = KEY_F18; goto got_key;
|
|
case XK_F19: key = KEY_F19; goto got_key;
|
|
case XK_F20: key = KEY_F20; goto got_key;
|
|
case XK_Up: key = KEY_UP; goto got_key;
|
|
case XK_Down: key = KEY_DOWN; goto got_key;
|
|
case XK_Right: key = KEY_RIGHT; goto got_key;
|
|
case XK_Left: key = KEY_LEFT; goto got_key;
|
|
case XK_BackSpace: key = KEY_DEL; goto got_key;
|
|
case XK_Insert: key = KEY_INSERT; goto got_key;
|
|
case XK_Delete: key = KEY_DELETE; goto got_key;
|
|
case XK_Home: key = KEY_HOME; goto got_key;
|
|
case XK_End: key = KEY_END; goto got_key;
|
|
case XK_Prior: key = KEY_PAGEUP; goto got_key;
|
|
case XK_Next: key = KEY_PAGEDOWN; goto got_key;
|
|
case XK_ISO_Left_Tab: key = KEY_SHIFT_TAB; goto got_key;
|
|
default:
|
|
if (len > 0) {
|
|
#ifdef X_HAVE_UTF8_STRING
|
|
{
|
|
const char *p = buf;
|
|
buf[len] = '\0';
|
|
key = utf8_decode(&p);
|
|
}
|
|
#else
|
|
key = buf[0] & 0xff;
|
|
#endif
|
|
got_key:
|
|
ev->key_event.type = QE_KEY_EVENT;
|
|
ev->key_event.key = key;
|
|
qe_expose_flush(s, rgn);
|
|
qe_handle_event(ev);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
qe_expose_flush(s, rgn);
|
|
}
|
|
|
|
/* bitmap handling */
|
|
|
|
enum X11BitmapType {
|
|
BMP_PIXMAP,
|
|
BMP_XIMAGE,
|
|
#ifdef CONFIG_XSHM
|
|
BMP_XSHMIMAGE,
|
|
#endif
|
|
#ifdef CONFIG_XV
|
|
BMP_XVIMAGE,
|
|
#ifdef CONFIG_XSHM
|
|
BMP_XVSHMIMAGE,
|
|
#endif
|
|
#endif
|
|
};
|
|
|
|
typedef struct X11Bitmap {
|
|
enum X11BitmapType type;
|
|
union {
|
|
Pixmap pixmap;
|
|
XImage *ximage;
|
|
#ifdef CONFIG_XV
|
|
XvImage *xvimage;
|
|
#endif
|
|
} u;
|
|
#ifdef CONFIG_XSHM
|
|
XShmSegmentInfo *shm_info;
|
|
#endif
|
|
int x_lock, y_lock; /* destination for locking */
|
|
XImage *ximage_lock;
|
|
} X11Bitmap;
|
|
|
|
static int x11_dpy_bmp_alloc(QEditScreen *s, QEBitmap *b)
|
|
{
|
|
X11State *xs = s->priv_data;
|
|
X11Bitmap *xb;
|
|
|
|
xb = qe_mallocz(X11Bitmap);
|
|
if (!xb)
|
|
return -1;
|
|
b->priv_data = xb;
|
|
/* choose bitmap type to optimize communication with x server and
|
|
performances */
|
|
if (b->flags & QEBITMAP_FLAG_VIDEO) {
|
|
#if defined(CONFIG_XV)
|
|
if (xs->xv_port != 0 && xs->xv_open_count == 0) {
|
|
#ifdef CONFIG_XSHM
|
|
if (xs->shm_use)
|
|
xb->type = BMP_XVSHMIMAGE;
|
|
else
|
|
#endif
|
|
xb->type = BMP_XVIMAGE;
|
|
b->format = s->video_format;
|
|
xs->xv_open_count++;
|
|
} else
|
|
#endif
|
|
{
|
|
#ifdef CONFIG_XSHM
|
|
if (xs->shm_use)
|
|
xb->type = BMP_XSHMIMAGE;
|
|
else
|
|
#endif
|
|
xb->type = BMP_XIMAGE;
|
|
b->format = s->bitmap_format;
|
|
}
|
|
} else {
|
|
xb->type = BMP_PIXMAP;
|
|
b->format = s->bitmap_format;
|
|
}
|
|
|
|
switch (xb->type) {
|
|
default:
|
|
case BMP_PIXMAP:
|
|
xb->u.pixmap = XCreatePixmap(xs->display, xs->window,
|
|
b->width, b->height, xs->attr.depth);
|
|
if (!xb->u.pixmap)
|
|
goto fail;
|
|
break;
|
|
case BMP_XIMAGE:
|
|
{
|
|
XImage *ximage;
|
|
ximage = XCreateImage(xs->display, None, xs->attr.depth, ZPixmap, 0,
|
|
NULL, b->width, b->height, 8, 0);
|
|
ximage->data = qe_malloc_array(char, b->height * ximage->bytes_per_line);
|
|
xb->u.ximage = ximage;
|
|
}
|
|
break;
|
|
#ifdef CONFIG_XSHM
|
|
case BMP_XSHMIMAGE:
|
|
{
|
|
XImage *ximage;
|
|
XShmSegmentInfo *shm_info;
|
|
|
|
/* XXX: error testing */
|
|
shm_info = qe_mallocz(XShmSegmentInfo);
|
|
ximage = XShmCreateImage(xs->display, None, xs->attr.depth, ZPixmap, NULL,
|
|
shm_info, b->width, b->height);
|
|
shm_info->shmid = shmget(IPC_PRIVATE,
|
|
b->height * ximage->bytes_per_line,
|
|
IPC_CREAT | 0777);
|
|
ximage->data = shmat(shm_info->shmid, 0, 0);
|
|
shm_info->shmaddr = ximage->data;
|
|
shm_info->readOnly = False;
|
|
|
|
XShmAttach(xs->display, shm_info);
|
|
XSync(xs->display, False);
|
|
|
|
/* the shared memory will be automatically deleted */
|
|
shmctl(shm_info->shmid, IPC_RMID, 0);
|
|
xb->shm_info = shm_info;
|
|
xb->u.ximage = ximage;
|
|
}
|
|
break;
|
|
#endif
|
|
#ifdef CONFIG_XV
|
|
case BMP_XVIMAGE:
|
|
{
|
|
XvImage *xvimage;
|
|
xvimage = XvCreateImage(xs->display, xs->xv_port, xs->xv_format, 0,
|
|
b->width, b->height);
|
|
xvimage->data = qe_malloc_array(char, xvimage->data_size);
|
|
xb->u.xvimage = xvimage;
|
|
}
|
|
break;
|
|
#ifdef CONFIG_XSHM
|
|
case BMP_XVSHMIMAGE:
|
|
{
|
|
XvImage *xvimage;
|
|
XShmSegmentInfo *shm_info;
|
|
|
|
shm_info = qe_mallocz(XShmSegmentInfo);
|
|
xvimage = XvShmCreateImage(xs->display, xs->xv_port, xs->xv_format, 0,
|
|
b->width, b->height, shm_info);
|
|
shm_info->shmid = shmget(IPC_PRIVATE,
|
|
xvimage->data_size,
|
|
IPC_CREAT | 0777);
|
|
xvimage->data = shmat(shm_info->shmid, 0, 0);
|
|
shm_info->shmaddr = xvimage->data;
|
|
shm_info->readOnly = False;
|
|
|
|
XShmAttach(xs->display, shm_info);
|
|
XSync(xs->display, False);
|
|
|
|
/* the shared memory will be automatically deleted */
|
|
shmctl(shm_info->shmid, IPC_RMID, 0);
|
|
xb->shm_info = shm_info;
|
|
xb->u.xvimage = xvimage;
|
|
}
|
|
break;
|
|
#endif
|
|
#endif
|
|
}
|
|
return 0;
|
|
fail:
|
|
qe_free(&xb);
|
|
return -1;
|
|
}
|
|
|
|
static void x11_dpy_bmp_free(QEditScreen *s, QEBitmap *b)
|
|
{
|
|
X11State *xs = s->priv_data;
|
|
X11Bitmap *xb = b->priv_data;
|
|
|
|
switch (xb->type) {
|
|
case BMP_PIXMAP:
|
|
XFreePixmap(xs->display, xb->u.pixmap);
|
|
break;
|
|
case BMP_XIMAGE:
|
|
/* NOTE: also frees the ximage data */
|
|
XDestroyImage(xb->u.ximage);
|
|
break;
|
|
#ifdef CONFIG_XSHM
|
|
case BMP_XSHMIMAGE:
|
|
XShmDetach(xs->display, xb->shm_info);
|
|
XDestroyImage(xb->u.ximage);
|
|
shmdt(xb->shm_info->shmaddr);
|
|
qe_free(&xb->shm_info);
|
|
break;
|
|
#endif
|
|
#ifdef CONFIG_XV
|
|
case BMP_XVIMAGE:
|
|
qe_free(&xb->u.xvimage->data);
|
|
XFree(xb->u.xvimage);
|
|
xs->xv_open_count--;
|
|
break;
|
|
#ifdef CONFIG_XSHM
|
|
case BMP_XVSHMIMAGE:
|
|
XShmDetach(xs->display, xb->shm_info);
|
|
XFree(xb->u.xvimage);
|
|
shmdt(xb->shm_info->shmaddr);
|
|
qe_free(&xb->shm_info);
|
|
xs->xv_open_count--;
|
|
break;
|
|
#endif
|
|
#endif
|
|
}
|
|
qe_free(&b->priv_data);
|
|
}
|
|
|
|
static void x11_dpy_bmp_draw(QEditScreen *s, QEBitmap *b,
|
|
int dst_x, int dst_y, int dst_w, int dst_h,
|
|
qe__unused__ int offset_x, qe__unused__ int offset_y,
|
|
qe__unused__ int flags)
|
|
{
|
|
X11State *xs = s->priv_data;
|
|
X11Bitmap *xb = b->priv_data;
|
|
|
|
/* XXX: handle clipping ? */
|
|
update_rect(xs, dst_x, dst_y, dst_x + dst_w, dst_y + dst_h);
|
|
|
|
switch (xb->type) {
|
|
case BMP_PIXMAP:
|
|
XCopyArea(xs->display, xb->u.pixmap, xs->dbuffer, xs->gc,
|
|
0, 0, b->width, b->height, dst_x, dst_y);
|
|
break;
|
|
case BMP_XIMAGE:
|
|
XPutImage(xs->display, xs->dbuffer, xs->gc,
|
|
xb->u.ximage, 0, 0, dst_x, dst_y,
|
|
b->width, b->height);
|
|
break;
|
|
|
|
#ifdef CONFIG_XSHM
|
|
case BMP_XSHMIMAGE:
|
|
XShmPutImage(xs->display, xs->dbuffer, xs->gc,
|
|
xb->u.ximage, 0, 0, dst_x, dst_y,
|
|
b->width, b->height, False);
|
|
break;
|
|
#endif
|
|
#ifdef CONFIG_XV
|
|
case BMP_XVIMAGE:
|
|
XvPutImage(xs->display, xs->xv_port, xs->window, xs->gc, xb->u.xvimage,
|
|
0, 0, b->width, b->height,
|
|
dst_x, dst_y, dst_w, dst_h);
|
|
break;
|
|
#ifdef CONFIG_XSHM
|
|
case BMP_XVSHMIMAGE:
|
|
XvShmPutImage(xs->display, xs->xv_port, xs->window, xs->gc, xb->u.xvimage,
|
|
0, 0, b->width, b->height,
|
|
dst_x, dst_y, dst_w, dst_h, False);
|
|
break;
|
|
#endif
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static void x11_dpy_bmp_lock(QEditScreen *s, QEBitmap *b, QEPicture *pict,
|
|
int x1, int y1, int w1, int h1)
|
|
{
|
|
X11State *xs = s->priv_data;
|
|
X11Bitmap *xb = b->priv_data;
|
|
|
|
pict->width = w1;
|
|
pict->height = h1;
|
|
pict->format = b->format;
|
|
switch (xb->type) {
|
|
case BMP_PIXMAP:
|
|
{
|
|
XImage *ximage;
|
|
ximage = XCreateImage(xs->display, None, xs->attr.depth, ZPixmap, 0,
|
|
NULL, w1, h1, 8, 0);
|
|
ximage->data = qe_malloc_array(char, h1 * ximage->bytes_per_line);
|
|
pict->data[0] = (unsigned char *)ximage->data;
|
|
pict->linesize[0] = ximage->bytes_per_line;
|
|
xb->ximage_lock = ximage;
|
|
xb->x_lock = x1;
|
|
xb->y_lock = y1;
|
|
if (ximage->bits_per_pixel == 32) {
|
|
/* adjust format from QEBITMAP_FORMAT_RGB24 to PIX_FMT_BGR0
|
|
on little endian architectures */
|
|
pict->format = QEBITMAP_FORMAT_RGBA32;
|
|
}
|
|
}
|
|
break;
|
|
case BMP_XIMAGE:
|
|
#ifdef CONFIG_XSHM
|
|
case BMP_XSHMIMAGE:
|
|
#endif
|
|
{
|
|
int bpp = (xb->u.ximage->bits_per_pixel + 7) >> 3;
|
|
pict->data[0] = (unsigned char *)xb->u.ximage->data +
|
|
y1 * xb->u.ximage->bytes_per_line + x1 * bpp;
|
|
pict->linesize[0] = xb->u.ximage->bytes_per_line;
|
|
if (bpp == 4) {
|
|
/* adjust format from QEBITMAP_FORMAT_RGB24 to PIX_FMT_BGR0
|
|
on little endian architectures */
|
|
pict->format = QEBITMAP_FORMAT_RGBA32;
|
|
}
|
|
}
|
|
break;
|
|
#ifdef CONFIG_XV
|
|
case BMP_XVIMAGE:
|
|
#ifdef CONFIG_XSHM
|
|
case BMP_XVSHMIMAGE:
|
|
#endif
|
|
/* XXX: only YUV420P is handled yet */
|
|
{
|
|
XvImage *xvimage = xb->u.xvimage;
|
|
int i, xx, yy, j;
|
|
for (i = 0; i < 3; i++) {
|
|
xx = x1;
|
|
yy = y1;
|
|
j = i;
|
|
if (i >= 1) {
|
|
xx >>= 1;
|
|
yy >>= 1;
|
|
j = 3 - i; /* don't know why they inverted Cb and Cr! */
|
|
}
|
|
pict->data[j] = (unsigned char *)xvimage->data +
|
|
xvimage->offsets[i] +
|
|
yy * xvimage->pitches[i] + xx;
|
|
pict->linesize[j] = xvimage->pitches[i];
|
|
}
|
|
}
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static void x11_dpy_bmp_unlock(QEditScreen *s, QEBitmap *b)
|
|
{
|
|
X11State *xs = s->priv_data;
|
|
X11Bitmap *xb = b->priv_data;
|
|
|
|
switch (xb->type) {
|
|
case BMP_PIXMAP:
|
|
XPutImage(xs->display, xb->u.pixmap, xs->gc_pixmap, xb->ximage_lock,
|
|
0, 0, xb->x_lock, xb->y_lock,
|
|
xb->ximage_lock->width, xb->ximage_lock->height);
|
|
/* NOTE: also frees the ximage data */
|
|
XDestroyImage(xb->ximage_lock);
|
|
xb->ximage_lock = NULL;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int x11_dpy_draw_picture(QEditScreen *s,
|
|
int dst_x, int dst_y, int dst_w, int dst_h,
|
|
const QEPicture *ip0,
|
|
int src_x, int src_y, int src_w, int src_h,
|
|
int flags)
|
|
{
|
|
X11State *xs = s->priv_data;
|
|
XImage *im;
|
|
int status = 0, depth;
|
|
const QEPicture *ip = ip0;
|
|
QEPicture *ip1 = NULL;
|
|
|
|
/* Only support 32-bit true color X11 displays */
|
|
/* XXX: should enumerate visuals? */
|
|
depth = DefaultDepth(xs->display, xs->xscreen);
|
|
if (depth != 24)
|
|
return 1;
|
|
|
|
if (ip->format != QEBITMAP_FORMAT_RGBA32
|
|
|| !(src_w == dst_w && src_h == dst_h)) {
|
|
ip1 = qe_create_picture(dst_w, dst_h, QEBITMAP_FORMAT_RGBA32, 0);
|
|
if (!ip1)
|
|
return 2;
|
|
/* Convert and/or resize picture */
|
|
if (qe_picture_copy(ip1, 0, 0, dst_w, dst_h,
|
|
ip0, src_x, src_y, src_w, src_h, flags)) {
|
|
qe_free_picture(&ip1);
|
|
return 3;
|
|
}
|
|
ip = ip1;
|
|
src_x = src_y = 0;
|
|
src_w = dst_w;
|
|
src_h = dst_h;
|
|
}
|
|
|
|
im = XCreateImage(xs->display, xs->attr.visual, depth, ZPixmap, 0,
|
|
NULL, dst_w, dst_h, 32, ip->linesize[0] / 4);
|
|
if (im == NULL) {
|
|
qe_free_picture(&ip1);
|
|
return 4;
|
|
}
|
|
if (im->bits_per_pixel != 32) {
|
|
XDestroyImage(im);
|
|
qe_free_picture(&ip1);
|
|
return 5;
|
|
}
|
|
|
|
if (ip->format == QEBITMAP_FORMAT_RGBA32) {
|
|
im->data = (char *)(ip->data[0] + src_y * ip->linesize[0] + src_x * 4);
|
|
im->bytes_per_line = ip->linesize[0];
|
|
update_rect(xs, dst_x, dst_y, dst_x + dst_w, dst_y + dst_h);
|
|
status = XPutImage(xs->display, xs->dbuffer, xs->gc, im,
|
|
0, 0, dst_x, dst_y, dst_w, dst_h);
|
|
XFlush(xs->display);
|
|
} else {
|
|
status = 6;
|
|
}
|
|
|
|
im->data = NULL;
|
|
XDestroyImage(im);
|
|
qe_free_picture(&ip1);
|
|
return status;
|
|
}
|
|
|
|
static QEDisplay x11_dpy = {
|
|
"x11", 1, 1,
|
|
x11_dpy_probe,
|
|
x11_dpy_init,
|
|
x11_dpy_close,
|
|
x11_dpy_flush,
|
|
x11_dpy_is_user_input_pending,
|
|
x11_dpy_fill_rectangle,
|
|
x11_dpy_xor_rectangle,
|
|
x11_dpy_open_font,
|
|
x11_dpy_close_font,
|
|
x11_dpy_text_metrics,
|
|
x11_dpy_draw_text,
|
|
x11_dpy_set_clip,
|
|
x11_dpy_selection_activate,
|
|
x11_dpy_selection_request,
|
|
NULL, /* dpy_invalidate */
|
|
NULL, /* dpy_cursor_at */
|
|
x11_dpy_bmp_alloc,
|
|
x11_dpy_bmp_free,
|
|
x11_dpy_bmp_draw,
|
|
x11_dpy_bmp_lock,
|
|
x11_dpy_bmp_unlock,
|
|
x11_dpy_draw_picture,
|
|
x11_dpy_full_screen,
|
|
NULL, /* dpy_describe */
|
|
NULL, /* next */
|
|
};
|
|
|
|
static void x11_list_fonts(EditState *s, int argval)
|
|
{
|
|
QEmacsState *qs = s->qe_state;
|
|
QEditScreen *screen = qs->screen;
|
|
X11State *xs = screen->priv_data;
|
|
char buf[80];
|
|
EditBuffer *b;
|
|
int i, count;
|
|
char **list;
|
|
|
|
b = new_help_buffer();
|
|
if (!b)
|
|
return;
|
|
|
|
if (argval == NO_ARG) {
|
|
snprintf(buf, sizeof(buf), "-*-*-*-*-*-*-*-*-*-*-*-*-iso10646-1");
|
|
} else {
|
|
snprintf(buf, sizeof(buf), "-*-*-*-*-*-*-*-%d-*-*-*-*-iso10646-1", argval);
|
|
}
|
|
list = XListFonts(xs->display, buf, 20000, &count);
|
|
|
|
eb_printf(b, "\n%d entries\n\n", count);
|
|
|
|
for (i = 0; i < count; i++) {
|
|
eb_printf(b, "%d: %s\n", i, list[i]);
|
|
}
|
|
XFreeFontNames(list);
|
|
|
|
b->flags |= BF_READONLY;
|
|
show_popup(s, b, "X11 Font list");
|
|
}
|
|
|
|
static CmdDef x11_commands[] = {
|
|
|
|
CMD2( KEY_CTRLH('f'), KEY_CTRLH(KEY_CTRL('F')),
|
|
"x11-list-fonts", x11_list_fonts, ESi, "p", "")
|
|
|
|
CMD_DEF_END,
|
|
};
|
|
|
|
static CmdLineOptionDef cmd_options[] = {
|
|
CMD_LINE_STRING("d", "display", "DISPLAY", &display_str,
|
|
"set X11 display"),
|
|
CMD_LINE_STRING("g", "geometry", "WxH", &geometry_str,
|
|
"set X11 display size"),
|
|
CMD_LINE_INT("fs", "font-size", "ptsize", &font_ptsize,
|
|
"set default font size"),
|
|
CMD_LINE_LINK()
|
|
};
|
|
|
|
static int x11_init(void)
|
|
{
|
|
qe_register_cmd_line_options(cmd_options);
|
|
qe_register_cmd_table(x11_commands, NULL);
|
|
if (force_tty)
|
|
return 0;
|
|
return qe_register_display(&x11_dpy);
|
|
}
|
|
|
|
qe_module_init(x11_init);
|