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