1 module pango.utils; 2 3 import glib; 4 import gobject; 5 6 import std.traits; 7 import std.conv; 8 import std..string; 9 import std.range; 10 import core.memory; 11 12 13 package enum Transfer { 14 Full, 15 None, 16 } 17 18 19 private GQuark gobjAssocQuark; 20 21 static this() { 22 gobjAssocQuark = g_quark_from_string(toStringz("D_GObject")); 23 } 24 25 26 27 DObjT getDObject(DObjT, GObjT)(GObjT * obj, Transfer transfer) 28 { 29 static if ( is(DObjT : D_GObject) ) 30 { 31 if (!obj) return null; 32 33 auto p = g_object_get_qdata(cast(GObject*)obj, gobjAssocQuark); 34 if (p) return cast(DObjT)(cast(D_GObject)p); 35 return new DObjT(obj, transfer); 36 } 37 else static if ( is(DObjT == class) ) 38 { 39 if (!obj) return null; 40 41 return new DObjT(obj, transfer); 42 } 43 else 44 { 45 if (!obj) throw new Exception("tentative to construct obj from null ptr"); 46 47 return DObjT(obj, transfer); 48 } 49 } 50 51 52 D_GObject getExistingDObject(GObjT, bool ThrowErrorIfNot=true) (GObjT * obj, Transfer transfer) 53 { 54 if (!obj) return null; 55 56 auto p = g_object_get_qdata(cast(GObject*)obj, gobjAssocQuark); 57 if (!p) { 58 static if (ThrowErrorIfNot) throw new Error("could not retrieve \"existing\" object"); 59 else return null; 60 } 61 return cast(D_GObject)p; 62 63 } 64 65 66 abstract class D_GObject 67 { 68 private GObject *gobjPtr_; 69 private bool isGCProtected; 70 71 protected inout(GObject)* gobjPtr() inout { return gobjPtr_; } 72 73 74 protected this(GObject* ptr, Transfer transfer) { 75 gobjPtr_ = ptr; 76 if (transfer == Transfer.None) g_object_ref(gobjPtr_); 77 78 g_object_set_qdata_full(gobjPtr_, gobjAssocQuark, 79 cast(gpointer)this, &destroyNotification); 80 g_object_add_toggle_ref(gobjPtr_, &toggleNotification, cast(gpointer)this); 81 82 if (gobjPtr_.ref_count > 1) protectFromGC(); 83 } 84 85 86 ~this() { 87 g_object_steal_qdata(gobjPtr_, gobjAssocQuark); 88 exposeToGC(); 89 g_object_unref(gobjPtr_); 90 } 91 92 invariant() { 93 assert(gobjPtr_ !is null); 94 } 95 96 97 private 98 { 99 100 void protectFromGC() { 101 if (!isGCProtected) { 102 GC.addRoot(cast(void*)this); 103 GC.setAttr(cast(void*)this, GC.BlkAttr.NO_MOVE); 104 isGCProtected = true; 105 } 106 } 107 108 void exposeToGC() { 109 if (isGCProtected) { 110 GC.removeRoot(cast(void*)this); 111 GC.clrAttr(cast(void*)this, GC.BlkAttr.NO_MOVE); 112 isGCProtected = false; 113 } 114 } 115 } 116 } 117 118 119 mixin template GObjectHolder(GDerivedT) 120 { 121 alias NativePtrT = GDerivedT*; 122 123 @property inout(GDerivedT)* nativePtr() inout { return cast(inout(GDerivedT)*) gobjPtr; } 124 } 125 126 127 private { 128 129 extern (C) 130 void toggleNotification(gpointer data, 131 GObject *object, 132 gboolean is_last_ref) 133 { 134 D_GObject obj = cast(D_GObject)data; 135 if (is_last_ref) { 136 obj.exposeToGC(); 137 } 138 else { 139 obj.protectFromGC(); 140 } 141 } 142 143 144 extern (C) 145 void destroyNotification(gpointer data) { 146 D_GObject obj = cast(D_GObject)data; 147 obj.exposeToGC(); 148 } 149 150 } 151 152 153 154 mixin template NativePtrHolder(PangoCType, alias FreeFunc) 155 { 156 private { 157 PangoCType *nativePtr_; 158 Transfer transfer_; 159 160 void initialize(PangoCType *ptr, Transfer transfer) { 161 nativePtr_ = ptr; 162 transfer_ = transfer; 163 } 164 165 invariant() { 166 assert(nativePtr_ !is null); 167 } 168 } 169 170 alias NativePtrT = PangoCType*; 171 172 ~this() { 173 if (transfer_ == Transfer.Full) FreeFunc(nativePtr_); 174 } 175 176 pure @property inout(PangoCType)* nativePtr() inout { return nativePtr_; } 177 178 } 179 180 181 mixin template RefCountThat(PangoCType, alias FreeFunc) 182 { 183 import std.typecons; 184 185 private { 186 187 struct Payload 188 { 189 PangoCType *_payload; 190 191 this(PangoCType *ptr) 192 { 193 _payload = ptr; 194 } 195 ~this() 196 { 197 if(_payload) 198 { 199 FreeFunc(_payload); 200 _payload = null; 201 } 202 } 203 204 @disable this(this); 205 } 206 207 alias Data = RefCounted!Payload; 208 Data data_; 209 210 void initialize(PangoCType *ptr, Transfer transfer) { 211 data_ = Data(ptr); 212 assert(transfer == Transfer.Full); 213 } 214 } 215 216 public alias NativePtrT = PangoCType*; 217 218 public @property inout(PangoCType) *nativePtr() inout { 219 return data_.payload_; 220 } 221 } 222 223 224 225 226 mixin template RefCountedGObj(GObjT, string prefix) 227 { 228 alias NativePtrT = GObjT*; 229 230 private GObjT* nativePtr_; 231 232 233 private void initialize(GObjT* ptr, Transfer transfer) { 234 nativePtr_ = ptr; 235 if (transfer != Transfer.Full) { 236 reference(); 237 } 238 } 239 240 pure @property inout(GObjT)* nativePtr() inout { return nativePtr_; } 241 242 243 private void reference() { 244 mixin(prefix ~ "_ref(nativePtr_);"); 245 } 246 247 private void unreference() { 248 mixin(prefix ~ "_unref(nativePtr_);"); 249 } 250 251 @disable this(); 252 253 254 this(this) { 255 reference(); 256 } 257 258 ~this() { 259 unreference(); 260 } 261 262 invariant() { 263 assert(nativePtr_ !is null); 264 } 265 } 266 267 268 269 template listGObjects(ObjT, alias ListFun) // if (__traits(hasMember, ObjT, "nativePtr_")) 270 { 271 ObjT[] listGObjects(CollecT)(CollecT* ptr, Transfer transfer) 272 { 273 ObjT.NativePtrT *arr; 274 int n; 275 ListFun(ptr, &arr, &n); 276 scope(exit) g_free(arr); 277 278 if (!n) return []; 279 280 ObjT[] res; 281 foreach(i; 0 .. n) { 282 res ~= getDObject!ObjT(arr[i], transfer); 283 } 284 285 return res; 286 } 287 } 288 289 290 template listValues(T, alias ListFun) 291 { 292 T[] listValues(CollecT)(CollecT* ptr) 293 { 294 T *arr; 295 int n; 296 ListFun(ptr, &arr, &n); 297 scope(exit) g_free(arr); 298 299 if (!n) return []; 300 301 return arr[0 .. n].dup; 302 } 303 } 304 305 306 307 GList *objsToGList(ObjT)(ObjT[] objs) if (is(ObjT == class) || isPointer!ObjT || 308 (ObjT.sizeof == gpointer.sizeof && isIntegral!ObjT)) 309 { 310 GList *res; 311 foreach (obj; objs) { 312 res = g_list_append(res, cast(gpointer)obj); 313 } 314 return res; 315 } 316 317 318 ObjT[] objsFromGList(ObjT)(GList *list) if (is(ObjT == class) || isPointer!ObjT || 319 (ObjT.sizeof == gpointer.sizeof && isIntegral!ObjT)) 320 { 321 ObjT[] res; 322 while (list !is null) { 323 res ~= cast(ObjT)list.data; 324 list = list.next; 325 } 326 return res; 327 } 328 329 GSList *objsToGSList(ObjT)(ObjT[] objs) if (is(ObjT == class) || isPointer!ObjT || 330 (ObjT.sizeof == gpointer.sizeof && isIntegral!ObjT)) 331 { 332 GSList *res; 333 foreach (obj; objs) { 334 res = g_slist_append(res, cast(gpointer)obj); 335 } 336 return res; 337 } 338 339 340 ObjT[] objsFromGSList(ObjT)(GSList *list) if (is(ObjT == class) || isPointer!ObjT || 341 (ObjT.sizeof == gpointer.sizeof && isIntegral!ObjT)) 342 { 343 ObjT[] res; 344 while (list !is null) { 345 res ~= cast(ObjT)list.data; 346 list = list.next; 347 } 348 return res; 349 } 350 351 352 353 DObjT[] dobjsFromGList(DObjT)(GList *list, Transfer transfer) if (__traits(hasMember, DObjT, "nativePtr")) 354 { 355 DObjT[] res; 356 while (list !is null) { 357 res ~= getDObject!DObjT(cast(DObjT.NativePtrT)list.data, transfer); 358 list = list.next; 359 } 360 return res; 361 } 362 363 364 365 GList *dobjsToGList(DObjT)(DObjT[] dobjs) if (__traits(hasMember, DObjT, "nativePtr")) 366 { 367 GList *res; 368 foreach (dobj; dobjs) { 369 res = g_list_append(res, cast(gpointer)dobj.nativePtr); 370 } 371 return res; 372 } 373 374 375 376 DObjT[] dobjsFromGSList(DObjT)(GSList *list, Transfer transfer) if (__traits(hasMember, DObjT, "nativePtr")) 377 { 378 DObjT[] res; 379 while (list !is null) { 380 res ~= getDObject!DObjT(cast(DObjT.NativePtrT)list.data, transfer); 381 list = list.next; 382 } 383 return res; 384 } 385 386 387 388 GList *dobjsToGSList(DObjT)(DObjT[] dobjs) if (__traits(hasMember, DObjT, "nativePtr")) 389 { 390 GSList *res; 391 foreach (dobj; dobjs) { 392 res = g_slist_append(res, cast(gpointer)dobj.nativePtr); 393 } 394 return res; 395 } 396 397 398 399 400 // FIXME: finish impl 401 //struct GListGObjProxy(ObjT) 402 //{ 403 // private alias GObjPtrT = ObjT.NativePtrT; 404 // 405 // private GList *listPtr_ =null; 406 // 407 // package this (GList *ptr) { 408 // listPtr_ = ptr; 409 // } 410 // 411 // @property inout(GList)* listPtr() inout { return listPtr_; } 412 // 413 // 414 // void free() { 415 // g_list_free(listPtr_); 416 // } 417 // 418 // void free1() { 419 // g_list_free_1(listPtr_); 420 // } 421 // 422 // //void freeFull() if (is (ObjT : D_GObject)) 423 // //{ 424 // // g_list_free_full(list, &g_object_unref); 425 // //} 426 // 427 // void freeFull(GDestroyNotify freeFunc) { 428 // g_list_free_full(list, freeFunc); 429 // } 430 // 431 // void append(ObjT obj) { 432 // listPtr_ = g_list_append(listPtr_, obj.nativePtr); 433 // } 434 // 435 // void prepend(ObjT obj) { 436 // listPtr_ = g_list_prepend(listPtr_, obj.nativePtr); 437 // } 438 // 439 // void insert(ObjT obj, int position) { 440 // listPtr_ = g_list_insert(listPtr_, obj.nativePtr, position); 441 // } 442 // 443 // 444 // //GList* g_list_insert_sorted (GList *list, 445 // // gpointer data, 446 // // GCompareFunc func); 447 // // 448 // //GList* g_list_insert_sorted_with_data (GList *list, 449 // // gpointer data, 450 // // GCompareDataFunc func, 451 // // gpointer user_data); 452 // // 453 // //GList* g_list_insert_before (GList *list, 454 // // GList *sibling, 455 // // gpointer data); 456 // // 457 // //GList* g_list_concat (GList *list1, 458 // // GList *list2); 459 // // 460 // //GList* g_list_remove (GList *list, 461 // // gconstpointer data); 462 // // 463 // //GList* g_list_remove_all (GList *list, 464 // // gconstpointer data); 465 // // 466 // //GList* g_list_remove_link (GList *list, 467 // // GList *llink); 468 // // 469 // //GList* g_list_delete_link (GList *list, 470 // // GList *link_); 471 // // 472 // //GList* g_list_reverse (GList *list); 473 // // 474 // //GList* g_list_copy (GList *list); 475 // // 476 // // 477 // //GList* g_list_copy_deep (GList *list, 478 // // GCopyFunc func, 479 // // gpointer user_data); 480 // // 481 // // 482 // //GList* g_list_nth (GList *list, 483 // // guint n); 484 // // 485 // //GList* g_list_nth_prev (GList *list, 486 // // guint n); 487 // // 488 // //GList* g_list_find (GList *list, 489 // // gconstpointer data); 490 // // 491 // //GList* g_list_find_custom (GList *list, 492 // // gconstpointer data, 493 // // GCompareFunc func); 494 // // 495 // //gint g_list_position (GList *list, 496 // // GList *llink); 497 // // 498 // //gint g_list_index (GList *list, 499 // // gconstpointer data); 500 // // 501 // //GList* g_list_last (GList *list); 502 // // 503 // //GList* g_list_first (GList *list); 504 // // 505 // //guint g_list_length (GList *list); 506 // 507 // //int opApply(int delegate(ref ObjT obj) dg) { 508 // // ApplyFuncData d; 509 // // d.dg = dg; 510 // // g_list_foreach(listPtr_, &applyFunc, &d); 511 // // return d.res; 512 // //} 513 // // 514 // //private { 515 // // struct ApplyFuncData { 516 // // int res; 517 // // int delegate(ref ObjT obj) dg; 518 // // } 519 // // extern(C) static void applyFunc(gpointer data, gointer userData) { 520 // // ApplyFuncData *d = cast(ApplyFuncData*)userData; 521 // // if (d.res) return; 522 // // 523 // // GObjPtrT gobj = cast(GObjPtrT)data; 524 // // if (!gobj) return; 525 // // GObjT obj = getDObject!GObjT(gobj); 526 // // d.res = d.dg(obj); 527 // // } 528 // //} 529 // 530 // //void g_list_foreach (GList *list, 531 // // GFunc func, 532 // // gpointer user_data); 533 // // 534 // //GList* g_list_sort (GList *list, 535 // // GCompareFunc compare_func); 536 // // 537 // //GList* g_list_sort_with_data (GList *list, 538 // // GCompareDataFunc compare_func, 539 // // gpointer user_data); 540 // // 541 // //gpointer g_list_nth_data (GList *list, 542 // // guint n); 543 // 544 //}