summaryrefslogtreecommitdiffstats
path: root/Python/modsupport.c
blob: 12f3f17b487011561c13568f2aaa3e23eda89ca7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545

/* Module support implementation */

#include "Python.h"

#ifdef MPW /* MPW pushes 'extended' for float and double types with varargs */
typedef extended va_double;
#else
typedef double va_double;
#endif

/* Package context -- the full module name for package imports */
char *_Py_PackageContext = NULL;

/* Py_InitModule4() parameters:
   - name is the module name
   - methods is the list of top-level functions
   - doc is the documentation string
   - passthrough is passed as self to functions defined in the module
   - api_version is the value of PYTHON_API_VERSION at the time the
     module was compiled

   Return value is a borrowed reference to the module object; or NULL
   if an error occurred (in Python 1.4 and before, errors were fatal).
   Errors may still leak memory.
*/

static char api_version_warning[] =
"Python C API version mismatch for module %.100s:\
 This Python has API version %d, module %.100s has version %d.";

PyObject *
Py_InitModule4(char *name, PyMethodDef *methods, char *doc,
	       PyObject *passthrough, int module_api_version)
{
	PyObject *m, *d, *v, *n;
	PyMethodDef *ml;
	if (!Py_IsInitialized())
	    Py_FatalError("Interpreter not initialized (version mismatch?)");
	if (module_api_version != PYTHON_API_VERSION) {
		char message[512];
		PyOS_snprintf(message, sizeof(message), 
			      api_version_warning, name, 
			      PYTHON_API_VERSION, name, 
			      module_api_version);
		if (PyErr_Warn(PyExc_RuntimeWarning, message)) 
			return NULL;
	}
	/* Make sure name is fully qualified.

	   This is a bit of a hack: when the shared library is loaded,
	   the module name is "package.module", but the module calls
	   Py_InitModule*() with just "module" for the name.  The shared
	   library loader squirrels away the true name of the module in
	   _Py_PackageContext, and Py_InitModule*() will substitute this
	   (if the name actually matches).
	*/
	if (_Py_PackageContext != NULL) {
		char *p = strrchr(_Py_PackageContext, '.');
		if (p != NULL && strcmp(name, p+1) == 0) {
			name = _Py_PackageContext;
			_Py_PackageContext = NULL;
		}
	}
	if ((m = PyImport_AddModule(name)) == NULL)
		return NULL;
	d = PyModule_GetDict(m);
	if (methods != NULL) {
		n = PyString_FromString(name);
		if (n == NULL)
			return NULL;
		for (ml = methods; ml->ml_name != NULL; ml++) {
			if ((ml->ml_flags & METH_CLASS) ||
			    (ml->ml_flags & METH_STATIC)) {
				PyErr_SetString(PyExc_ValueError,
						"module functions cannot set"
						" METH_CLASS or METH_STATIC");
				return NULL;
			}
			v = PyCFunction_NewEx(ml, passthrough, n);
			if (v == NULL)
				return NULL;
			if (PyDict_SetItemString(d, ml->ml_name, v) != 0) {
				Py_DECREF(v);
				return NULL;
			}
			Py_DECREF(v);
		}
	}
	if (doc != NULL) {
		v = PyString_FromString(doc);
		if (v == NULL || PyDict_SetItemString(d, "__doc__", v) != 0) {
			Py_XDECREF(v);
			return NULL;
		}
		Py_DECREF(v);
	}
	return m;
}


/* Helper for mkvalue() to scan the length of a format */

static int
countformat(char *format, int endchar)
{
	int count = 0;
	int level = 0;
	while (level > 0 || *format != endchar) {
		switch (*format) {
		case '\0':
			/* Premature end */
			PyErr_SetString(PyExc_SystemError,
					"unmatched paren in format");
			return -1;
		case '(':
		case '[':
		case '{':
			if (level == 0)
				count++;
			level++;
			break;
		case ')':
		case ']':
		case '}':
			level--;
			break;
		case '#':
		case '&':
		case ',':
		case ':':
		case ' ':
		case '\t':
			break;
		default:
			if (level == 0)
				count++;
		}
		format++;
	}
	return count;
}


/* Generic function to create a value -- the inverse of getargs() */
/* After an original idea and first implementation by Steven Miale */

static PyObject *do_mktuple(char**, va_list *, int, int);
static PyObject *do_mklist(char**, va_list *, int, int);
static PyObject *do_mkdict(char**, va_list *, int, int);
static PyObject *do_mkvalue(char**, va_list *);


static PyObject *
do_mkdict(char **p_format, va_list *p_va, int endchar, int n)
{
	PyObject *d;
	int i;
	if (n < 0)
		return NULL;
	if ((d = PyDict_New()) == NULL)
		return NULL;
	for (i = 0; i < n; i+= 2) {
		PyObject *k, *v;
		int err;
		k = do_mkvalue(p_format, p_va);
		if (k == NULL) {
			Py_DECREF(d);
			return NULL;
		}
		v = do_mkvalue(p_format, p_va);
		if (v == NULL) {
			Py_DECREF(k);
			Py_DECREF(d);
			return NULL;
		}
		err = PyDict_SetItem(d, k, v);
		Py_DECREF(k);
		Py_DECREF(v);
		if (err < 0) {
			Py_DECREF(d);
			return NULL;
		}
	}
	if (d != NULL && **p_format != endchar) {
		Py_DECREF(d);
		d = NULL;
		PyErr_SetString(PyExc_SystemError,
				"Unmatched paren in format");
	}
	else if (endchar)
		++*p_format;
	return d;
}

static PyObject *
do_mklist(char **p_format, va_list *p_va, int endchar, int n)
{
	PyObject *v;
	int i;
	if (n < 0)
		return NULL;
	if ((v = PyList_New(n)) == NULL)
		return NULL;
	for (i = 0; i < n; i++) {
		PyObject *w = do_mkvalue(p_format, p_va);
		if (w == NULL) {
			Py_DECREF(v);
			return NULL;
		}
		PyList_SetItem(v, i, w);
	}
	if (v != NULL && **p_format != endchar) {
		Py_DECREF(v);
		v = NULL;
		PyErr_SetString(PyExc_SystemError,
				"Unmatched paren in format");
	}
	else if (endchar)
		++*p_format;
	return v;
}

#ifdef Py_USING_UNICODE
static int
_ustrlen(Py_UNICODE *u)
{
	int i = 0;
	Py_UNICODE *v = u;
	while (*v != 0) { i++; v++; } 
	return i;
}
#endif

static PyObject *
do_mktuple(char **p_format, va_list *p_va, int endchar, int n)
{
	PyObject *v;
	int i;
	if (n < 0)
		return NULL;
	if ((v = PyTuple_New(n)) == NULL)
		return NULL;
	for (i = 0; i < n; i++) {
		PyObject *w = do_mkvalue(p_format, p_va);
		if (w == NULL) {
			Py_DECREF(v);
			return NULL;
		}
		PyTuple_SetItem(v, i, w);
	}
	if (v != NULL && **p_format != endchar) {
		Py_DECREF(v);
		v = NULL;
		PyErr_SetString(PyExc_SystemError,
				"Unmatched paren in format");
	}
	else if (endchar)
		++*p_format;
	return v;
}

static PyObject *
do_mkvalue(char **p_format, va_list *p_va)
{
	for (;;) {
		switch (*(*p_format)++) {
		case '(':
			return do_mktuple(p_format, p_va, ')',
					  countformat(*p_format, ')'));

		case '[':
			return do_mklist(p_format, p_va, ']',
					 countformat(*p_format, ']'));

		case '{':
			return do_mkdict(p_format, p_va, '}',
					 countformat(*p_format, '}'));

		case 'b':
		case 'B':
		case 'h':
		case 'i':
			return PyInt_FromLong((long)va_arg(*p_va, int));
			
		case 'H':
			return PyInt_FromLong((long)va_arg(*p_va, unsigned int));

		case 'l':
			return PyInt_FromLong((long)va_arg(*p_va, long));

		case 'k':
			return PyInt_FromLong((long)va_arg(*p_va, unsigned long));

#ifdef HAVE_LONG_LONG
		case 'L':
			return PyLong_FromLongLong((PY_LONG_LONG)va_arg(*p_va, PY_LONG_LONG));

		case 'K':
			return PyLong_FromLongLong((PY_LONG_LONG)va_arg(*p_va, unsigned PY_LONG_LONG));
#endif
#ifdef Py_USING_UNICODE
		case 'u':
		{
			PyObject *v;
			Py_UNICODE *u = va_arg(*p_va, Py_UNICODE *);
			int n;
			if (**p_format == '#') {
				++*p_format;
				n = va_arg(*p_va, int);
			}
			else
				n = -1;
			if (u == NULL) {
				v = Py_None;
				Py_INCREF(v);
			}
			else {
				if (n < 0)
					n = _ustrlen(u);
				v = PyUnicode_FromUnicode(u, n);
			}
			return v;
		}
#endif
		case 'f':
		case 'd':
			return PyFloat_FromDouble(
				(double)va_arg(*p_va, va_double));

#ifndef WITHOUT_COMPLEX
		case 'D':
			return PyComplex_FromCComplex(
				*((Py_complex *)va_arg(*p_va, Py_complex *)));
#endif /* WITHOUT_COMPLEX */

		case 'c':
		{
			char p[1];
			p[0] = va_arg(*p_va, int);
			return PyString_FromStringAndSize(p, 1);
		}

		case 's':
		case 'z':
		{
			PyObject *v;
			char *str = va_arg(*p_va, char *);
			int n;
			if (**p_format == '#') {
				++*p_format;
				n = va_arg(*p_va, int);
			}
			else
				n = -1;
			if (str == NULL) {
				v = Py_None;
				Py_INCREF(v);
			}
			else {
				if (n < 0) {
					size_t m = strlen(str);
					if (m > INT_MAX) {
						PyErr_SetString(PyExc_OverflowError,
							"string too long for Python string");
						return NULL;
					}
					n = (int)m;
				}
				v = PyString_FromStringAndSize(str, n);
			}
			return v;
		}

		case 'N':
		case 'S':
		case 'O':
		if (**p_format == '&') {
			typedef PyObject *(*converter)(void *);
			converter func = va_arg(*p_va, converter);
			void *arg = va_arg(*p_va, void *);
			++*p_format;
			return (*func)(arg);
		}
		else {
			PyObject *v;
			v = va_arg(*p_va, PyObject *);
			if (v != NULL) {
				if (*(*p_format - 1) != 'N')
					Py_INCREF(v);
			}
			else if (!PyErr_Occurred())
				/* If a NULL was passed
				 * because a call that should
				 * have constructed a value
				 * failed, that's OK, and we
				 * pass the error on; but if
				 * no error occurred it's not
				 * clear that the caller knew
				 * what she was doing. */
				PyErr_SetString(PyExc_SystemError,
					"NULL object passed to Py_BuildValue");
			return v;
		}

		case ':':
		case ',':
		case ' ':
		case '\t':
			break;

		default:
			PyErr_SetString(PyExc_SystemError,
				"bad format char passed to Py_BuildValue");
			return NULL;

		}
	}
}


PyObject *
Py_BuildValue(char *format, ...)
{
	va_list va;
	PyObject* retval;
	va_start(va, format);
	retval = Py_VaBuildValue(format, va);
	va_end(va);
	return retval;
}

PyObject *
Py_VaBuildValue(char *format, va_list va)
{
	char *f = format;
	int n = countformat(f, '\0');
	va_list lva;

#ifdef VA_LIST_IS_ARRAY
	memcpy(lva, va, sizeof(va_list));
#else
#ifdef __va_copy
	__va_copy(lva, va);
#else
	lva = va;
#endif
#endif

	if (n < 0)
		return NULL;
	if (n == 0) {
		Py_INCREF(Py_None);
		return Py_None;
	}
	if (n == 1)
		return do_mkvalue(&f, &lva);
	return do_mktuple(&f, &lva, '\0', n);
}


PyObject *
PyEval_CallFunction(PyObject *obj, char *format, ...)
{
	va_list vargs;
	PyObject *args;
	PyObject *res;

	va_start(vargs, format);

	args = Py_VaBuildValue(format, vargs);
	va_end(vargs);

	if (args == NULL)
		return NULL;

	res = PyEval_CallObject(obj, args);
	Py_DECREF(args);

	return res;
}


PyObject *
PyEval_CallMethod(PyObject *obj, char *methodname, char *format, ...)
{
	va_list vargs;
	PyObject *meth;
	PyObject *args;
	PyObject *res;

	meth = PyObject_GetAttrString(obj, methodname);
	if (meth == NULL)
		return NULL;

	va_start(vargs, format);

	args = Py_VaBuildValue(format, vargs);
	va_end(vargs);

	if (args == NULL) {
		Py_DECREF(meth);
		return NULL;
	}

	res = PyEval_CallObject(meth, args);
	Py_DECREF(meth);
	Py_DECREF(args);

	return res;
}

int
PyModule_AddObject(PyObject *m, char *name, PyObject *o)
{
	PyObject *dict;
	if (!PyModule_Check(m) || o == NULL) {
		PyErr_SetString(PyExc_TypeError,
			    "PyModule_AddObject() needs module as first arg");
		return -1;
	}
	dict = PyModule_GetDict(m);
	if (dict == NULL) {
		/* Internal error -- modules must have a dict! */
		PyErr_Format(PyExc_SystemError, "module '%s' has no __dict__",
			     PyModule_GetName(m));
		return -1;
	}
	if (PyDict_SetItemString(dict, name, o))
		return -1;
	Py_DECREF(o);
	return 0;
}

int 
PyModule_AddIntConstant(PyObject *m, char *name, long value)
{
	return PyModule_AddObject(m, name, PyInt_FromLong(value));
}

int 
PyModule_AddStringConstant(PyObject *m, char *name, char *value)
{
	return PyModule_AddObject(m, name, PyString_FromString(value));
}
tr->faces[i].ftFont); } /* *--------------------------------------------------------------------------- * * GetTkFontAttributes -- * Fill in TkFontAttributes from an XftFont. */ static void GetTkFontAttributes( XftFont *ftFont, TkFontAttributes *faPtr) { const char *family = "Unknown"; const char *const *familyPtr = &family; int weight, slant, pxsize; double size, ptsize; (void) XftPatternGetString(ftFont->pattern, XFT_FAMILY, 0, familyPtr); if (XftPatternGetDouble(ftFont->pattern, XFT_SIZE, 0, &ptsize) == XftResultMatch) { size = ptsize; } else if (XftPatternGetDouble(ftFont->pattern, XFT_PIXEL_SIZE, 0, &ptsize) == XftResultMatch) { size = -ptsize; } else if (XftPatternGetInteger(ftFont->pattern, XFT_PIXEL_SIZE, 0, &pxsize) == XftResultMatch) { size = (double)-pxsize; } else { size = 12.0; } if (XftPatternGetInteger(ftFont->pattern, XFT_WEIGHT, 0, &weight) != XftResultMatch) { weight = XFT_WEIGHT_MEDIUM; } if (XftPatternGetInteger(ftFont->pattern, XFT_SLANT, 0, &slant) != XftResultMatch) { slant = XFT_SLANT_ROMAN; } #if DEBUG_FONTSEL printf("family %s size %d weight %d slant %d\n", family, (int)size, weight, slant); #endif /* DEBUG_FONTSEL */ faPtr->family = Tk_GetUid(family); faPtr->size = size; faPtr->weight = (weight > XFT_WEIGHT_MEDIUM) ? TK_FW_BOLD : TK_FW_NORMAL; faPtr->slant = (slant > XFT_SLANT_ROMAN) ? TK_FS_ITALIC : TK_FS_ROMAN; faPtr->underline = 0; faPtr->overstrike = 0; } /* *--------------------------------------------------------------------------- * * GetTkFontMetrics -- * Fill in TkFontMetrics from an XftFont. */ static void GetTkFontMetrics( XftFont *ftFont, TkFontMetrics *fmPtr) { int spacing; if (XftPatternGetInteger(ftFont->pattern, XFT_SPACING, 0, &spacing) != XftResultMatch) { spacing = XFT_PROPORTIONAL; } fmPtr->ascent = ftFont->ascent; fmPtr->descent = ftFont->descent; fmPtr->maxWidth = ftFont->max_advance_width; fmPtr->fixed = spacing != XFT_PROPORTIONAL; } /* *--------------------------------------------------------------------------- * * InitFont -- * * Initializes the fields of a UnixFtFont structure. If fontPtr is NULL, * also allocates a new UnixFtFont. * * Results: * On error, frees fontPtr and returns NULL, otherwise returns fontPtr. * *--------------------------------------------------------------------------- */ static UnixFtFont * InitFont( Tk_Window tkwin, FcPattern *pattern, UnixFtFont *fontPtr) { FcFontSet *set; FcCharSet *charset; FcResult result; XftFont *ftFont; int i, iWidth; if (!fontPtr) { fontPtr = ckalloc(sizeof(UnixFtFont)); } FcConfigSubstitute(0, pattern, FcMatchPattern); XftDefaultSubstitute(Tk_Display(tkwin), Tk_ScreenNumber(tkwin), pattern); /* * Generate the list of fonts */ set = FcFontSort(0, pattern, FcTrue, NULL, &result); if (!set) { ckfree(fontPtr); return NULL; } fontPtr->fontset = set; fontPtr->pattern = pattern; fontPtr->faces = ckalloc(set->nfont * sizeof(UnixFtFace)); fontPtr->nfaces = set->nfont; /* * Fill in information about each returned font */ for (i = 0; i < set->nfont; i++) { fontPtr->faces[i].ftFont = 0; fontPtr->faces[i].ft0Font = 0; fontPtr->faces[i].source = set->fonts[i]; if (FcPatternGetCharSet(set->fonts[i], FC_CHARSET, 0, &charset) == FcResultMatch) { fontPtr->faces[i].charset = FcCharSetCopy(charset); } else { fontPtr->faces[i].charset = 0; } fontPtr->faces[i].angle = 0.0; } fontPtr->display = Tk_Display(tkwin); fontPtr->screen = Tk_ScreenNumber(tkwin); fontPtr->ftDraw = 0; fontPtr->ncolors = 0; fontPtr->firstColor = -1; /* * Fill in platform-specific fields of TkFont. */ ftFont = GetFont(fontPtr, 0, 0.0); fontPtr->font.fid = XLoadFont(Tk_Display(tkwin), "fixed"); GetTkFontAttributes(ftFont, &fontPtr->font.fa); GetTkFontMetrics(ftFont, &fontPtr->font.fm); /* * Fontconfig can't report any information about the position or thickness * of underlines or overstrikes. Thus, we use some defaults that are * hacked around from backup defaults in tkUnixFont.c, which are in turn * based on recommendations in the X manual. The comments from that file * leading to these computations were: * * If the XA_UNDERLINE_POSITION property does not exist, the X manual * recommends using half the descent. * * If the XA_UNDERLINE_THICKNESS property does not exist, the X * manual recommends using the width of the stem on a capital letter. * I don't know of a way to get the stem width of a letter, so guess * and use 1/3 the width of a capital I. * * Note that nothing corresponding to *either* property is reported by * Fontconfig at all. [Bug 1961455] */ { TkFont *fPtr = &fontPtr->font; fPtr->underlinePos = fPtr->fm.descent / 2; Tk_MeasureChars((Tk_Font) fPtr, "I", 1, -1, 0, &iWidth); fPtr->underlineHeight = iWidth / 3; if (fPtr->underlineHeight == 0) { fPtr->underlineHeight = 1; } if (fPtr->underlineHeight + fPtr->underlinePos > fPtr->fm.descent) { fPtr->underlineHeight = fPtr->fm.descent - fPtr->underlinePos; if (fPtr->underlineHeight == 0) { fPtr->underlinePos--; fPtr->underlineHeight = 1; } } } return fontPtr; } static void FinishedWithFont( UnixFtFont *fontPtr) { Display *display = fontPtr->display; int i; Tk_ErrorHandler handler = Tk_CreateErrorHandler(display, -1, -1, -1, NULL, NULL); for (i = 0; i < fontPtr->nfaces; i++) { if (fontPtr->faces[i].ftFont) { XftFontClose(fontPtr->display, fontPtr->faces[i].ftFont); } if (fontPtr->faces[i].ft0Font) { XftFontClose(fontPtr->display, fontPtr->faces[i].ft0Font); } if (fontPtr->faces[i].charset) { FcCharSetDestroy(fontPtr->faces[i].charset); } } if (fontPtr->faces) { ckfree(fontPtr->faces); } if (fontPtr->pattern) { FcPatternDestroy(fontPtr->pattern); } if (fontPtr->ftDraw) { XftDrawDestroy(fontPtr->ftDraw); } if (fontPtr->font.fid) { XUnloadFont(fontPtr->display, fontPtr->font.fid); } if (fontPtr->fontset) { FcFontSetDestroy(fontPtr->fontset); } Tk_DeleteErrorHandler(handler); } TkFont * TkpGetNativeFont( Tk_Window tkwin, /* For display where font will be used. */ const char *name) /* Platform-specific font name. */ { UnixFtFont *fontPtr; FcPattern *pattern; #if DEBUG_FONTSEL printf("TkpGetNativeFont %s\n", name); #endif /* DEBUG_FONTSEL */ pattern = XftXlfdParse(name, FcFalse, FcFalse); if (!pattern) { return NULL; } /* * Should also try: pattern = FcNameParse(name); but generic/tkFont.c * expects TkpGetNativeFont() to only work on XLFD names under Unix. */ fontPtr = InitFont(tkwin, pattern, NULL); if (!fontPtr) { FcPatternDestroy(pattern); return NULL; } return &fontPtr->font; } TkFont * TkpGetFontFromAttributes( TkFont *tkFontPtr, /* If non-NULL, store the information in this * existing TkFont structure, rather than * allocating a new structure to hold the * font; the existing contents of the font * will be released. If NULL, a new TkFont * structure is allocated. */ Tk_Window tkwin, /* For display where font will be used. */ const TkFontAttributes *faPtr) /* Set of attributes to match. */ { XftPattern *pattern; int weight, slant; UnixFtFont *fontPtr; #if DEBUG_FONTSEL printf("TkpGetFontFromAttributes %s-%d %d %d\n", faPtr->family, faPtr->size, faPtr->weight, faPtr->slant); #endif /* DEBUG_FONTSEL */ pattern = XftPatternCreate(); if (faPtr->family) { XftPatternAddString(pattern, XFT_FAMILY, faPtr->family); } if (faPtr->size > 0.0) { XftPatternAddDouble(pattern, XFT_SIZE, faPtr->size); } else if (faPtr->size < 0.0) { XftPatternAddDouble(pattern, XFT_SIZE, TkFontGetPoints(tkwin, faPtr->size)); } else { XftPatternAddDouble(pattern, XFT_SIZE, 12.0); } switch (faPtr->weight) { case TK_FW_NORMAL: default: weight = XFT_WEIGHT_MEDIUM; break; case TK_FW_BOLD: weight = XFT_WEIGHT_BOLD; break; } XftPatternAddInteger(pattern, XFT_WEIGHT, weight); switch (faPtr->slant) { case TK_FS_ROMAN: default: slant = XFT_SLANT_ROMAN; break; case TK_FS_ITALIC: slant = XFT_SLANT_ITALIC; break; case TK_FS_OBLIQUE: slant = XFT_SLANT_OBLIQUE; break; } XftPatternAddInteger(pattern, XFT_SLANT, slant); fontPtr = (UnixFtFont *) tkFontPtr; if (fontPtr != NULL) { FinishedWithFont(fontPtr); } fontPtr = InitFont(tkwin, pattern, fontPtr); /* * Hack to work around issues with weird issues with Xft/Xrender * connection. For details, see comp.lang.tcl thread starting from * <adcc99ed-c73e-4efc-bb5d-e57a57a051e8@l35g2000pra.googlegroups.com> */ if (!fontPtr) { XftPatternAddBool(pattern, XFT_RENDER, FcFalse); fontPtr = InitFont(tkwin, pattern, fontPtr); } if (!fontPtr) { FcPatternDestroy(pattern); return NULL; } fontPtr->font.fa.underline = faPtr->underline; fontPtr->font.fa.overstrike = faPtr->overstrike; return &fontPtr->font; } void TkpDeleteFont( TkFont *tkFontPtr) /* Token of font to be deleted. */ { UnixFtFont *fontPtr = (UnixFtFont *) tkFontPtr; FinishedWithFont(fontPtr); /* XXX tkUnixFont.c doesn't free tkFontPtr... */ } /* *--------------------------------------------------------------------------- * * TkpGetFontFamilies -- * * Return information about the font families that are available on the * display of the given window. * * Results: * Modifies interp's result object to hold a list of all the available * font families. * *--------------------------------------------------------------------------- */ void TkpGetFontFamilies( Tcl_Interp *interp, /* Interp to hold result. */ Tk_Window tkwin) /* For display to query. */ { Tcl_Obj *resultPtr; XftFontSet *list; int i; resultPtr = Tcl_NewListObj(0, NULL); list = XftListFonts(Tk_Display(tkwin), Tk_ScreenNumber(tkwin), (char *) 0, /* pattern elements */ XFT_FAMILY, (char*) 0); /* fields */ for (i = 0; i < list->nfont; i++) { char *family, **familyPtr = &family; if (XftPatternGetString(list->fonts[i], XFT_FAMILY, 0, familyPtr) == XftResultMatch) { Tcl_Obj *strPtr = Tcl_NewStringObj(family, -1); Tcl_ListObjAppendElement(NULL, resultPtr, strPtr); } } XftFontSetDestroy(list); Tcl_SetObjResult(interp, resultPtr); } /* *------------------------------------------------------------------------- * * TkpGetSubFonts -- * * Called by [testfont subfonts] in the Tk testing package. * * Results: * Sets interp's result to a list of the faces used by tkfont * *------------------------------------------------------------------------- */ void TkpGetSubFonts( Tcl_Interp *interp, Tk_Font tkfont) { Tcl_Obj *objv[3], *listPtr, *resultPtr; UnixFtFont *fontPtr = (UnixFtFont *) tkfont; FcPattern *pattern; const char *family = "Unknown"; const char *const *familyPtr = &family; const char *foundry = "Unknown"; const char *const *foundryPtr = &foundry; const char *encoding = "Unknown"; const char *const *encodingPtr = &encoding; int i; resultPtr = Tcl_NewListObj(0, NULL); for (i = 0; i < fontPtr->nfaces ; ++i) { pattern = FcFontRenderPrepare(0, fontPtr->pattern, fontPtr->faces[i].source); XftPatternGetString(pattern, XFT_FAMILY, 0, familyPtr); XftPatternGetString(pattern, XFT_FOUNDRY, 0, foundryPtr); XftPatternGetString(pattern, XFT_ENCODING, 0, encodingPtr); objv[0] = Tcl_NewStringObj(family, -1); objv[1] = Tcl_NewStringObj(foundry, -1); objv[2] = Tcl_NewStringObj(encoding, -1); listPtr = Tcl_NewListObj(3, objv); Tcl_ListObjAppendElement(NULL, resultPtr, listPtr); } Tcl_SetObjResult(interp, resultPtr); } /* *---------------------------------------------------------------------- * * TkpGetFontAttrsForChar -- * * Retrieve the font attributes of the actual font used to render a given * character. * *---------------------------------------------------------------------- */ void TkpGetFontAttrsForChar( Tk_Window tkwin, /* Window on the font's display */ Tk_Font tkfont, /* Font to query */ int c, /* Character of interest */ TkFontAttributes *faPtr) /* Output: Font attributes */ { UnixFtFont *fontPtr = (UnixFtFont *) tkfont; /* Structure describing the logical font */ FcChar32 ucs4 = (FcChar32) c; /* UCS-4 character to map */ XftFont *ftFont = GetFont(fontPtr, ucs4, 0.0); /* Actual font used to render the character */ GetTkFontAttributes(ftFont, faPtr); faPtr->underline = fontPtr->font.fa.underline; faPtr->overstrike = fontPtr->font.fa.overstrike; } int Tk_MeasureChars( Tk_Font tkfont, /* Font in which characters will be drawn. */ const char *source, /* UTF-8 string to be displayed. Need not be * '\0' terminated. */ int numBytes, /* Maximum number of bytes to consider from * source string. */ int maxLength, /* If >= 0, maxLength specifies the longest * permissible line length in pixels; don't * consider any character that would cross * this x-position. If < 0, then line length * is unbounded and the flags argument is * ignored. */ int flags, /* Various flag bits OR-ed together: * TK_PARTIAL_OK means include the last char * which only partially fit on this line. * TK_WHOLE_WORDS means stop on a word * boundary, if possible. TK_AT_LEAST_ONE * means return at least one character even if * no characters fit. */ int *lengthPtr) /* Filled with x-location just after the * terminating character. */ { UnixFtFont *fontPtr = (UnixFtFont *) tkfont; XftFont *ftFont; FcChar32 c; XGlyphInfo extents; int clen, curX, newX, curByte, newByte, sawNonSpace; int termByte = 0, termX = 0; #if DEBUG_FONTSEL char string[256]; int len = 0; #endif /* DEBUG_FONTSEL */ curX = 0; curByte = 0; sawNonSpace = 0; while (numBytes > 0) { int unichar; clen = TkUtfToUniChar(source, &unichar); c = (FcChar32) unichar; if (clen <= 0) { /* * This can't happen (but see #1185640) */ *lengthPtr = curX; return curByte; } source += clen; numBytes -= clen; if (c < 256 && isspace(c)) { /* I18N: ??? */ if (sawNonSpace) { termByte = curByte; termX = curX; sawNonSpace = 0; } } else { sawNonSpace = 1; } #if DEBUG_FONTSEL string[len++] = (char) c; #endif /* DEBUG_FONTSEL */ ftFont = GetFont(fontPtr, c, 0.0); XftTextExtents32(fontPtr->display, ftFont, &c, 1, &extents); newX = curX + extents.xOff; newByte = curByte + clen; if (maxLength >= 0 && newX > maxLength) { if (flags & TK_PARTIAL_OK || (flags & TK_AT_LEAST_ONE && curByte == 0)) { curX = newX; curByte = newByte; } else if (flags & TK_WHOLE_WORDS && termX != 0) { curX = termX; curByte = termByte; } break; } curX = newX; curByte = newByte; } #if DEBUG_FONTSEL string[len] = '\0'; printf("MeasureChars %s length %d bytes %d\n", string, curX, curByte); #endif /* DEBUG_FONTSEL */ *lengthPtr = curX; return curByte; } int TkpMeasureCharsInContext( Tk_Font tkfont, const char *source, int numBytes, int rangeStart, int rangeLength, int maxLength, int flags, int *lengthPtr) { (void) numBytes; /*unused*/ return Tk_MeasureChars(tkfont, source + rangeStart, rangeLength, maxLength, flags, lengthPtr); } /* *---------------------------------------------------------------------- * * LookUpColor -- * * Convert a pixel value to an XftColor. This can be slow due to the * need to call XQueryColor, which involves a server round-trip. To * avoid that, a least-recently-used cache of up to MAX_CACHED_COLORS * is kept, in the form of a linked list. The returned color is moved * to the front of the list, so repeatedly asking for the same one * should be fast. * * Results: * A pointer to the XftColor structure for the requested color is * returned. * * Side effects: * The converted color is stored in a cache in the UnixFtFont structure. The cache * can hold at most MAX_CACHED_COLORS colors. If no more slots are available, the least * recently used color is replaced with the new one. *---------------------------------------------------------------------- */ static XftColor * LookUpColor(Display *display, /* Display to lookup colors on */ UnixFtFont *fontPtr, /* Font to search for cached colors */ unsigned long pixel) /* Pixel value to translate to XftColor */ { int i, last = -1, last2 = -1; XColor xcolor; for (i = fontPtr->firstColor; i >= 0; last2 = last, last = i, i = fontPtr->colors[i].next) { if (pixel == fontPtr->colors[i].color.pixel) { /* * Color found in cache. Move it to the front of the list and return it. */ if (last >= 0) { fontPtr->colors[last].next = fontPtr->colors[i].next; fontPtr->colors[i].next = fontPtr->firstColor; fontPtr->firstColor = i; } return &fontPtr->colors[i].color; } } /* * Color wasn't found, so it needs to be added to the cache. * If a spare slot is available, it can be put there. If not, last * will now point to the least recently used color, so replace that one. */ if (fontPtr->ncolors < MAX_CACHED_COLORS) { last2 = -1; last = fontPtr->ncolors++; } /* * Translate the pixel value to a color. Needs a server round-trip. */ xcolor.pixel = pixel; XQueryColor(display, DefaultColormap(display, fontPtr->screen), &xcolor); fontPtr->colors[last].color.color.red = xcolor.red; fontPtr->colors[last].color.color.green = xcolor.green; fontPtr->colors[last].color.color.blue = xcolor.blue; fontPtr->colors[last].color.color.alpha = 0xffff; fontPtr->colors[last].color.pixel = pixel; /* * Put at the front of the list. */ if (last2 >= 0) { fontPtr->colors[last2].next = fontPtr->colors[last].next; } fontPtr->colors[last].next = fontPtr->firstColor; fontPtr->firstColor = last; return &fontPtr->colors[last].color; } #define NUM_SPEC 1024 void Tk_DrawChars( Display *display, /* Display on which to draw. */ Drawable drawable, /* Window or pixmap in which to draw. */ GC gc, /* Graphics context for drawing characters. */ Tk_Font tkfont, /* Font in which characters will be drawn; * must be the same as font used in GC. */ const char *source, /* UTF-8 string to be displayed. Need not be * '\0' terminated. All Tk meta-characters * (tabs, control characters, and newlines) * should be stripped out of the string that * is passed to this function. If they are not * stripped out, they will be displayed as * regular printing characters. */ int numBytes, /* Number of bytes in string. */ int x, int y) /* Coordinates at which to place origin of * string when drawing. */ { const int maxCoord = 0x7FFF;/* Xft coordinates are 16 bit values */ UnixFtFont *fontPtr = (UnixFtFont *) tkfont; XGCValues values; XftColor *xftcolor; int clen, nspec, xStart = x; XftGlyphFontSpec specs[NUM_SPEC]; XGlyphInfo metrics; ThreadSpecificData *tsdPtr = (ThreadSpecificData *) Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); if (fontPtr->ftDraw == 0) { #if DEBUG_FONTSEL printf("Switch to drawable 0x%x\n", drawable); #endif /* DEBUG_FONTSEL */ fontPtr->ftDraw = XftDrawCreate(display, drawable, DefaultVisual(display, fontPtr->screen), DefaultColormap(display, fontPtr->screen)); } else { Tk_ErrorHandler handler = Tk_CreateErrorHandler(display, -1, -1, -1, NULL, NULL); XftDrawChange(fontPtr->ftDraw, drawable); Tk_DeleteErrorHandler(handler); } XGetGCValues(display, gc, GCForeground, &values); xftcolor = LookUpColor(display, fontPtr, values.foreground); if (tsdPtr->clipRegion != None) { XftDrawSetClip(fontPtr->ftDraw, tsdPtr->clipRegion); } nspec = 0; while (numBytes > 0 && x <= maxCoord && y <= maxCoord) { XftFont *ftFont; FcChar32 c; clen = FcUtf8ToUcs4((FcChar8 *) source, &c, numBytes); if (clen <= 0) { /* * This should not happen, but it can. */ goto doUnderlineStrikeout; } source += clen; numBytes -= clen; ftFont = GetFont(fontPtr, c, 0.0); if (ftFont) { specs[nspec].font = ftFont; specs[nspec].glyph = XftCharIndex(fontPtr->display, ftFont, c); specs[nspec].x = x; specs[nspec].y = y; XftGlyphExtents(fontPtr->display, ftFont, &specs[nspec].glyph, 1, &metrics); x += metrics.xOff; y += metrics.yOff; nspec++; if (nspec == NUM_SPEC) { XftDrawGlyphFontSpec(fontPtr->ftDraw, xftcolor, specs, nspec); nspec = 0; } } } if (nspec) { XftDrawGlyphFontSpec(fontPtr->ftDraw, xftcolor, specs, nspec); } doUnderlineStrikeout: if (tsdPtr->clipRegion != None) { XftDrawSetClip(fontPtr->ftDraw, None); } if (fontPtr->font.fa.underline != 0) { XFillRectangle(display, drawable, gc, xStart, y + fontPtr->font.underlinePos, (unsigned) (x - xStart), (unsigned) fontPtr->font.underlineHeight); } if (fontPtr->font.fa.overstrike != 0) { y -= fontPtr->font.fm.descent + (fontPtr->font.fm.ascent) / 10; XFillRectangle(display, drawable, gc, xStart, y, (unsigned) (x - xStart), (unsigned) fontPtr->font.underlineHeight); } } /* *--------------------------------------------------------------------------- * * TkDrawAngledChars -- * * Draw some characters at an angle. This would be simple code, except * Xft has bugs with cumulative errors in character positioning which are * caused by trying to perform all calculations internally with integers. * So we have to do the work ourselves with floating-point math. * * Results: * None. * * Side effects: * Target drawable is updated. * *--------------------------------------------------------------------------- */ void TkDrawAngledChars( Display *display, /* Display on which to draw. */ Drawable drawable, /* Window or pixmap in which to draw. */ GC gc, /* Graphics context for drawing characters. */ Tk_Font tkfont, /* Font in which characters will be drawn; * must be the same as font used in GC. */ const char *source, /* UTF-8 string to be displayed. Need not be * '\0' terminated. All Tk meta-characters * (tabs, control characters, and newlines) * should be stripped out of the string that * is passed to this function. If they are not * stripped out, they will be displayed as * regular printing characters. */ int numBytes, /* Number of bytes in string. */ double x, double y, /* Coordinates at which to place origin of * string when drawing. */ double angle) /* What angle to put text at, in degrees. */ { const int maxCoord = 0x7FFF;/* Xft coordinates are 16 bit values */ const int minCoord = -1000; /* Should be good enough... */ UnixFtFont *fontPtr = (UnixFtFont *) tkfont; XGCValues values; XftColor *xftcolor; int xStart = x, yStart = y; ThreadSpecificData *tsdPtr = (ThreadSpecificData *) Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); #ifdef XFT_HAS_FIXED_ROTATED_PLACEMENT int clen, nglyph; FT_UInt glyphs[NUM_SPEC]; XGlyphInfo metrics; XftFont *currentFtFont; int originX, originY; if (fontPtr->ftDraw == 0) { #if DEBUG_FONTSEL printf("Switch to drawable 0x%x\n", drawable); #endif /* DEBUG_FONTSEL */ fontPtr->ftDraw = XftDrawCreate(display, drawable, DefaultVisual(display, fontPtr->screen), DefaultColormap(display, fontPtr->screen)); } else { Tk_ErrorHandler handler = Tk_CreateErrorHandler(display, -1, -1, -1, NULL, NULL); XftDrawChange(fontPtr->ftDraw, drawable); Tk_DeleteErrorHandler(handler); } XGetGCValues(display, gc, GCForeground, &values); xftcolor = LookUpColor(display, fontPtr, values.foreground); if (tsdPtr->clipRegion != None) { XftDrawSetClip(fontPtr->ftDraw, tsdPtr->clipRegion); } nglyph = 0; currentFtFont = NULL; originX = originY = 0; /* lint */ while (numBytes > 0 && x <= maxCoord && x >= minCoord && y <= maxCoord && y >= minCoord) { XftFont *ftFont; FcChar32 c; clen = FcUtf8ToUcs4((FcChar8 *) source, &c, numBytes); if (clen <= 0) { /* * This should not happen, but it can. */ goto doUnderlineStrikeout; } source += clen; numBytes -= clen; ftFont = GetFont(fontPtr, c, angle); if (!ftFont) { continue; } if (ftFont != currentFtFont || nglyph == NUM_SPEC) { if (nglyph) { /* * We pass multiple glyphs at once to enable the code to * perform better rendering of sub-pixel inter-glyph spacing. * If only the current Xft implementation could make use of * this information... but we'll be ready when it does! */ XftDrawGlyphs(fontPtr->ftDraw, xftcolor, currentFtFont, originX, originY, glyphs, nglyph); } originX = ROUND16(x); originY = ROUND16(y); if (nglyph) { XftGlyphExtents(fontPtr->display, currentFtFont, glyphs, nglyph, &metrics); nglyph = 0; x += metrics.xOff; y += metrics.yOff; } currentFtFont = ftFont; } glyphs[nglyph++] = XftCharIndex(fontPtr->display, ftFont, c); } if (nglyph) { XftDrawGlyphs(fontPtr->ftDraw, xftcolor, currentFtFont, originX, originY, glyphs, nglyph); } #else /* !XFT_HAS_FIXED_ROTATED_PLACEMENT */ int clen, nspec; XftGlyphFontSpec specs[NUM_SPEC]; XGlyphInfo metrics; double sinA = sin(angle * PI/180.0), cosA = cos(angle * PI/180.0); if (fontPtr->ftDraw == 0) { #if DEBUG_FONTSEL printf("Switch to drawable 0x%x\n", drawable); #endif /* DEBUG_FONTSEL */ fontPtr->ftDraw = XftDrawCreate(display, drawable, DefaultVisual(display, fontPtr->screen), DefaultColormap(display, fontPtr->screen)); } else { Tk_ErrorHandler handler = Tk_CreateErrorHandler(display, -1, -1, -1, NULL, NULL); XftDrawChange(fontPtr->ftDraw, drawable); Tk_DeleteErrorHandler(handler); } XGetGCValues(display, gc, GCForeground, &values); xftcolor = LookUpColor(display, fontPtr, values.foreground); if (tsdPtr->clipRegion != None) { XftDrawSetClip(fontPtr->ftDraw, tsdPtr->clipRegion); } nspec = 0; while (numBytes > 0 && x <= maxCoord && x >= minCoord && y <= maxCoord && y >= minCoord) { XftFont *ftFont, *ft0Font; FcChar32 c; clen = FcUtf8ToUcs4((FcChar8 *) source, &c, numBytes); if (clen <= 0) { /* * This should not happen, but it can. */ goto doUnderlineStrikeout; } source += clen; numBytes -= clen; ftFont = GetFont(fontPtr, c, angle); ft0Font = GetFont(fontPtr, c, 0.0); if (ftFont && ft0Font) { specs[nspec].font = ftFont; specs[nspec].glyph = XftCharIndex(fontPtr->display, ftFont, c); specs[nspec].x = ROUND16(x); specs[nspec].y = ROUND16(y); XftGlyphExtents(fontPtr->display, ft0Font, &specs[nspec].glyph, 1, &metrics); x += metrics.xOff*cosA + metrics.yOff*sinA; y += metrics.yOff*cosA - metrics.xOff*sinA; nspec++; if (nspec == NUM_SPEC) { XftDrawGlyphFontSpec(fontPtr->ftDraw, xftcolor, specs, nspec); nspec = 0; } } } if (nspec) { XftDrawGlyphFontSpec(fontPtr->ftDraw, xftcolor, specs, nspec); } #endif /* XFT_HAS_FIXED_ROTATED_PLACEMENT */ doUnderlineStrikeout: if (tsdPtr->clipRegion != None) { XftDrawSetClip(fontPtr->ftDraw, None); } if (fontPtr->font.fa.underline || fontPtr->font.fa.overstrike) { XPoint points[5]; double width = (x - xStart) * cosA + (yStart - y) * sinA; double barHeight = fontPtr->font.underlineHeight; double dy = fontPtr->font.underlinePos; if (fontPtr->font.fa.underline != 0) { if (fontPtr->font.underlineHeight == 1) { dy++; } points[0].x = xStart + ROUND16(dy*sinA); points[0].y = yStart + ROUND16(dy*cosA); points[1].x = xStart + ROUND16(dy*sinA + width*cosA); points[1].y = yStart + ROUND16(dy*cosA - width*sinA); if (fontPtr->font.underlineHeight == 1) { XDrawLines(display, drawable, gc, points, 2, CoordModeOrigin); } else { points[2].x = xStart + ROUND16(dy*sinA + width*cosA + barHeight*sinA); points[2].y = yStart + ROUND16(dy*cosA - width*sinA + barHeight*cosA); points[3].x = xStart + ROUND16(dy*sinA + barHeight*sinA); points[3].y = yStart + ROUND16(dy*cosA + barHeight*cosA); points[4].x = points[0].x; points[4].y = points[0].y; XFillPolygon(display, drawable, gc, points, 5, Complex, CoordModeOrigin); XDrawLines(display, drawable, gc, points, 5, CoordModeOrigin); } } if (fontPtr->font.fa.overstrike != 0) { dy = -fontPtr->font.fm.descent - (fontPtr->font.fm.ascent) / 10; points[0].x = xStart + ROUND16(dy*sinA); points[0].y = yStart + ROUND16(dy*cosA); points[1].x = xStart + ROUND16(dy*sinA + width*cosA); points[1].y = yStart + ROUND16(dy*cosA - width*sinA); if (fontPtr->font.underlineHeight == 1) { XDrawLines(display, drawable, gc, points, 2, CoordModeOrigin); } else { points[2].x = xStart + ROUND16(dy*sinA + width*cosA + barHeight*sinA); points[2].y = yStart + ROUND16(dy*cosA - width*sinA + barHeight*cosA); points[3].x = xStart + ROUND16(dy*sinA + barHeight*sinA); points[3].y = yStart + ROUND16(dy*cosA + barHeight*cosA); points[4].x = points[0].x; points[4].y = points[0].y; XFillPolygon(display, drawable, gc, points, 5, Complex, CoordModeOrigin); XDrawLines(display, drawable, gc, points, 5, CoordModeOrigin); } } } } void TkUnixSetXftClipRegion( TkRegion clipRegion) /* The clipping region to install. */ { ThreadSpecificData *tsdPtr = (ThreadSpecificData *) Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); tsdPtr->clipRegion = (Region) clipRegion; } /* * Local Variables: * c-basic-offset: 4 * fill-column: 78 * End: */