1 /*$
2  Copyright (C) 2013-2020 Azel.
3 
4  This file is part of AzPainter.
5 
6  AzPainter is free software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  AzPainter is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 $*/
19 
20 /*****************************************
21  * x11_clipboard
22  *****************************************/
23 
24 #include <string.h>
25 
26 #define MINC_X11_ATOM
27 #include "mSysX11.h"
28 
29 #include "mWindowDef.h"
30 #include "x11_clipboard.h"
31 #include "x11_util.h"
32 
33 
34 //-------------------------
35 
36 struct _mX11Clipboard
37 {
38 	int dattype;
39 	void *datbuf;
40 	uint32_t datsize;
41 
42 	Atom *atoms;
43 	int atomNum;
44 
45 	int (*callback)(mX11Clipboard *,mX11Clipboard_cb *);
46 };
47 
48 #define _BASEATOM_NUM  2
49 
50 static int _sendDefaultText(mX11Clipboard *p,mX11Clipboard_cb *dat);
51 
52 //-------------------------
53 
54 
55 //========================
56 // mClipboard.h
57 //========================
58 
59 
60 /** クリップボードからテキスト取得 */
61 
mClipboardGetText(mWidget * wg)62 char *mClipboardGetText(mWidget *wg)
63 {
64 	return mX11ClipboardGetText(MAPP_SYS->clipb, wg->toplevel);
65 }
66 
67 /** クリップボードにテキストセット
68  *
69  * @param size NULL 文字も含めたサイズ */
70 
mClipboardSetText(mWidget * wg,const char * text,int size)71 mBool mClipboardSetText(mWidget *wg,const char *text,int size)
72 {
73 	if(!text || size <= 0) return FALSE;
74 
75 	return mX11ClipboardSetDat(MAPP_SYS->clipb, wg->toplevel,
76 		MX11CLIPB_DATTYPE_TEXT, text, size,
77 		NULL, 0, NULL);
78 }
79 
80 /** 終了後もデータを扱えるように保存 */
81 
mClipboardSave(mWidget * wg)82 mBool mClipboardSave(mWidget *wg)
83 {
84 	return mX11ClipboardSaveManager(MAPP_SYS->clipb, wg->toplevel, 0);
85 }
86 
87 
88 //========================
89 // sub
90 //========================
91 
92 
93 /** アトムリストセット
94  *
95  * @param atoms  NULL でデフォルト */
96 
_setAtomList(mX11Clipboard * p,Atom * atoms,int atom_num)97 static mBool _setAtomList(mX11Clipboard *p,Atom *atoms,int atom_num)
98 {
99 	Atom *pd;
100 
101 	//タイプ別のデフォルトアトム数
102 
103 	if(!atoms)
104 	{
105 		if(p->dattype == MX11CLIPB_DATTYPE_TEXT)
106 			atom_num = 2;
107 		else
108 			return FALSE;
109 	}
110 
111 	//確保
112 
113 	p->atoms = (Atom *)mMalloc(sizeof(Atom) * (atom_num + _BASEATOM_NUM), FALSE);
114 	if(!p->atoms) return FALSE;
115 
116 	p->atomNum = atom_num;
117 
118 	//セット
119 
120 	pd = p->atoms;
121 
122 	if(atoms)
123 	{
124 		memcpy(pd, atoms, sizeof(Atom) * atom_num);
125 		pd += atom_num;
126 	}
127 	else
128 	{
129 		//デフォルト
130 
131 		switch(p->dattype)
132 		{
133 			case MX11CLIPB_DATTYPE_TEXT:
134 				*(pd++) = MX11ATOM(UTF8_STRING);
135 				*(pd++) = MX11ATOM(COMPOUND_TEXT);
136 				break;
137 		}
138 	}
139 
140 	//ベースのアトム
141 
142 	*(pd++) = MX11ATOM(TARGETS);
143 	*(pd++) = MX11ATOM(MULTIPLE);
144 
145 	return TRUE;
146 }
147 
148 
149 //========================
150 
151 
152 /** 解放 */
153 
mX11ClipboardDestroy(mX11Clipboard * p)154 void mX11ClipboardDestroy(mX11Clipboard *p)
155 {
156 	if(p)
157 	{
158 		mX11ClipboardFreeDat(p);
159 		mFree(p);
160 	}
161 }
162 
163 /** データ解放 */
164 
mX11ClipboardFreeDat(mX11Clipboard * p)165 void mX11ClipboardFreeDat(mX11Clipboard *p)
166 {
167 	M_FREE_NULL(p->datbuf);
168 	M_FREE_NULL(p->atoms);
169 
170 	p->dattype = MX11CLIPB_DATTYPE_NONE;
171 }
172 
173 /** 作成 */
174 
mX11ClipboardNew(void)175 mX11Clipboard *mX11ClipboardNew(void)
176 {
177 	return (mX11Clipboard *)mMalloc(sizeof(mX11Clipboard), TRUE);
178 }
179 
180 /** クリップボードにデータをセット */
181 
mX11ClipboardSetDat(mX11Clipboard * p,mWindow * win,int dattype,const void * datbuf,uint32_t datsize,Atom * atom_list,int atom_num,int (* callback)(mX11Clipboard *,mX11Clipboard_cb *))182 mBool mX11ClipboardSetDat(mX11Clipboard *p,
183 	mWindow *win,
184 	int dattype,const void *datbuf,uint32_t datsize,
185 	Atom *atom_list,int atom_num,
186 	int (*callback)(mX11Clipboard *,mX11Clipboard_cb *))
187 {
188 	mX11ClipboardFreeDat(p);
189 
190 	//データコピー
191 
192 	p->datbuf = mMalloc(datsize, FALSE);
193 	if(!p->datbuf) return FALSE;
194 
195 	memcpy(p->datbuf, datbuf, datsize);
196 
197 	//
198 
199 	p->dattype = dattype;
200 	p->datsize = datsize;
201 	p->callback = callback;
202 
203 	//アトムリストセット
204 
205 	if(!_setAtomList(p, atom_list, atom_num))
206 	{
207 		mX11ClipboardFreeDat(p);
208 		return FALSE;
209 	}
210 
211 	//デフォルトコールバックセット
212 
213 	if(!callback)
214 	{
215 		if(dattype == MX11CLIPB_DATTYPE_TEXT)
216 			p->callback = _sendDefaultText;
217 	}
218 
219 	//クリップボード所有権セット
220 
221 	XSetSelectionOwner(XDISP, MX11ATOM(CLIPBOARD), WINDOW_XID(win), MAPP_SYS->timeLast);
222 
223 	return TRUE;
224 }
225 
226 /** クリップボードマネージャにデータを保存
227  *
228  * データはアプリケーション自身が保持しているので、アプリケーションが終了した場合は
229  * クリップボードデータは消えることになる。 @n
230  * アプリケーションが終了した後もクリップボードにデータを置いておきたい場合は、
231  * この関数でクリップボードマネージャの方にデータを保存させる。 @n
232  * @n
233  * [!] 各対応データ形式ごとにデータが保存されるので、コールバック関数が複数回来る。 @n
234  * [!] コールバック呼び出し時は、multiple が 1。
235  *
236  * @param dattype 0 で現在のデータ。それ以外で指定タイプの場合のみ保存。
237  */
238 
mX11ClipboardSaveManager(mX11Clipboard * p,mWindow * win,int dattype)239 mBool mX11ClipboardSaveManager(mX11Clipboard *p,mWindow *win,int dattype)
240 {
241 	Window id;
242 
243 	if(p->dattype == MX11CLIPB_DATTYPE_NONE)
244 		return FALSE;
245 
246 	if(dattype && dattype != p->dattype)
247 		return FALSE;
248 
249 	//マネージャの所有者がいない
250 
251 	if(XGetSelectionOwner(XDISP, MX11ATOM(CLIPBOARD_MANAGER)) == None)
252 		return FALSE;
253 
254 	/* 保存要求
255 	 * [!] 先にプロパティを消しておかないと MULTIPLE で property=0 になる */
256 
257 	id = WINDOW_XID(win);
258 
259 	XDeleteProperty(XDISP, id, MX11ATOM(MLIB_SELECTION));
260 
261 	XSetSelectionOwner(XDISP, MX11ATOM(CLIPBOARD), id, MAPP_SYS->timeLast);
262 
263 	XConvertSelection(XDISP,MX11ATOM(CLIPBOARD_MANAGER),
264 		mX11GetAtom("SAVE_TARGETS"), MX11ATOM(MLIB_SELECTION),
265 		id, MAPP_SYS->timeLast);
266 
267 	XSync(XDISP, False);
268 
269 	return TRUE;
270 }
271 
272 
273 //=============================
274 // データ取得
275 //=============================
276 
277 
278 /** クリップボードからテキスト取得
279  *
280  * @return 確保された文字列のポインタ。mFree() で解放する。 */
281 
mX11ClipboardGetText(mX11Clipboard * p,mWindow * win)282 char *mX11ClipboardGetText(mX11Clipboard *p,mWindow *win)
283 {
284 	Atom atom[3],type;
285 
286 	if(p->dattype == MX11CLIPB_DATTYPE_TEXT)
287 	{
288 		//自身がデータを持っている場合
289 
290 		return mStrdup((char *)p->datbuf);
291 	}
292 	else
293 	{
294 		//------- 他クライアントから
295 
296 		//利用可能なテキストタイプ
297 
298 		atom[0] = MX11ATOM(UTF8_STRING);
299 		atom[1] = MX11ATOM(COMPOUND_TEXT);
300 		atom[2] = XA_STRING;
301 
302 		type = mX11GetSelectionTargetType(WINDOW_XID(win),
303 			MX11ATOM(CLIPBOARD), atom, 3);
304 
305 		if(type == 0) return NULL;
306 
307 		//テキスト取得
308 
309 		if(type == MX11ATOM(COMPOUND_TEXT))
310 		{
311 			return mX11GetSelectionCompoundText(WINDOW_XID(win),
312 				MX11ATOM(CLIPBOARD));
313 		}
314 		else
315 		{
316 			//UTF-8/ASCII
317 
318 			return mX11GetSelectionDat(WINDOW_XID(win),
319 				MX11ATOM(CLIPBOARD), type, TRUE, NULL);
320 		}
321 	}
322 
323 	return NULL;
324 }
325 
326 
327 //=============================
328 // X イベント
329 //=============================
330 
331 
332 /** テキストのデータを送る
333  *
334  * NULL 文字は送らない。 */
335 
_sendDefaultText(mX11Clipboard * p,mX11Clipboard_cb * dat)336 int _sendDefaultText(mX11Clipboard *p,mX11Clipboard_cb *dat)
337 {
338 	if(dat->type == MX11ATOM(COMPOUND_TEXT))
339 	{
340 		mX11SetPropertyCompoundText(dat->winid, dat->prop,
341 			(char *)dat->buf, dat->size - 1);
342 	}
343 	else
344 	{
345 		mX11SetProperty8(dat->winid, dat->prop, dat->type,
346 			dat->buf, dat->size - 1, FALSE);
347 	}
348 
349 	return 1;
350 }
351 
352 /** 指定フォーマットのデータを送る */
353 
_sendDat(mX11Clipboard * p,Window winid,Atom prop,Atom type,mBool multiple)354 static Atom _sendDat(mX11Clipboard *p,
355 	Window winid,Atom prop,Atom type,mBool multiple)
356 {
357 	mX11Clipboard_cb dat;
358 	int i,f;
359 
360 	dat.buf = p->datbuf;
361 	dat.size = p->datsize;
362 	dat.winid = winid;
363 	dat.prop = prop;
364 	dat.type = type;
365 	dat.multiple = multiple;
366 
367 	//指定フォーマットに対応しているか
368 
369 	for(i = 0, f = 0; i < p->atomNum; i++)
370 	{
371 		if(p->atoms[i] == type) { f = 1; break; }
372 	}
373 
374 	if(!f) return 0;
375 
376 	//データ送る
377 
378 	if(p->callback)
379 		f = (p->callback)(p, &dat);
380 	else
381 		f = 0;
382 
383 	return (f)? prop: 0;
384 }
385 
386 /** 複数タイプのデータセット */
387 
_sendMultiple(mX11Clipboard * p,Window id,Atom prop)388 static void _sendMultiple(mX11Clipboard *p,Window id,Atom prop)
389 {
390 	Atom *atoms;
391 	int num,i;
392 
393 	//プロパティとタイプのペアを取得し、各タイプごとにセット
394 
395 	atoms = mX11GetProperty32(id, prop, mX11GetAtom("ATOM_PAIR"), &num);
396 	if(!atoms) return;
397 
398 	for(i = 0; i < num; i += 2)
399 		_sendDat(p, id, atoms[i], atoms[i + 1], TRUE);
400 
401 	mFree(atoms);
402 }
403 
404 /** SelectionRequest イベント時の処理 (selection="CLIPBOARD") */
405 
mX11ClipboardSelectionRequest(mX11Clipboard * p,XSelectionRequestEvent * ev)406 void mX11ClipboardSelectionRequest(mX11Clipboard *p,
407 	XSelectionRequestEvent *ev)
408 {
409 	XSelectionEvent xev;
410 	Atom prop;
411 
412 	prop = ev->property;
413 
414 	if(p->dattype == MX11CLIPB_DATTYPE_NONE)
415 		prop = 0;
416 	else if(ev->target == MX11ATOM(TARGETS))
417 	{
418 		/* 他クライアントからの、対応可能なフォーマットタイプの要求 */
419 
420 		mX11SetPropertyAtom(ev->requestor, ev->property,
421 			p->atoms, p->atomNum + _BASEATOM_NUM);
422 	}
423 	else if(ev->target == MX11ATOM(MULTIPLE))
424 	{
425 		/* クリップボードマネージャへのデータ保存時 */
426 
427 		_sendMultiple(p, ev->requestor, ev->property);
428 	}
429 	else
430 	{
431 		/* 他クライアントからの、指定フォーマットによるデータ要求 */
432 
433 		prop = _sendDat(p, ev->requestor, ev->property, ev->target, FALSE);
434 	}
435 
436 	//------ 返信イベント送信
437 
438 	xev.type       = SelectionNotify;
439 	xev.send_event = 1;
440 	xev.display    = XDISP;
441 	xev.requestor  = ev->requestor;
442 	xev.selection  = ev->selection;
443 	xev.target     = ev->target;
444 	xev.property   = prop;
445 	xev.time       = ev->time;
446 
447 	XSendEvent(xev.display, xev.requestor, False, 0, (XEvent *)&xev);
448 }
449