├── .gitignore ├── .gitmodules ├── core ├── core.c ├── core.h ├── engine.h ├── gif │ ├── gifstd.c │ └── gifstd.h └── ogl │ ├── oglstd.c │ └── oglstd.h ├── exec ├── exec.c ├── exec.h ├── icon.gif └── loc │ ├── de.lang │ ├── en.lang │ └── ru.lang ├── linux ├── DPengine.cbp ├── Makefile ├── lib.c └── run.c ├── macos ├── DPengine.cbp ├── Makefile ├── lib.c ├── rsrc │ ├── Info.plist │ └── main.icns └── run.c ├── readme.md └── win32 ├── DPengine.cbp ├── Makefile ├── lib.c ├── rsrc ├── main.ico ├── mfst.xml └── run.rc └── run.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries 2 | win32.exe 3 | linux-gtk 4 | macos.app 5 | *.dll 6 | *.so 7 | *.dylib 8 | 9 | # DP stuff 10 | Content* 11 | 12 | # Code::Blocks service files 13 | *.cscope_file_list 14 | *.layout 15 | *.depend 16 | .obj 17 | 18 | # Win32-specific 19 | Thumbs.db 20 | *.lnk 21 | *.def 22 | *.a 23 | 24 | # MacOS-specific 25 | .DS_Store 26 | __MACOSX 27 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "core/gif/load"] 2 | path = core/gif/load 3 | url = https://github.com/hidefromkgb/gif_load 4 | branch = master 5 | [submodule "core/ogl/load"] 6 | path = core/ogl/load 7 | url = https://github.com/hidefromkgb/ogl_load 8 | branch = master 9 | [submodule "macos/load"] 10 | path = macos/load 11 | url = https://github.com/hidefromkgb/mac_load 12 | branch = master 13 | [submodule "exec/zip"] 14 | path = exec/zip 15 | url = https://github.com/hidefromkgb/zip_load 16 | branch = master 17 | [submodule "exec/ctr"] 18 | path = exec/ctr 19 | url = https://github.com/hidefromkgb/std_ctrs 20 | branch = main 21 | -------------------------------------------------------------------------------- /core/core.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | 6 | #define SEM_NULL 0 7 | #define SEM_FULL ~SEM_NULL 8 | 9 | #define TXL_FAIL "[>>ERR<<]" 10 | #define TXL_DUPL "[--DUP--]" 11 | #define TXL_AEND "[==ANI==]" 12 | #define TXL_RGPU "[##GPU##]" 13 | #define TXL_RSTD "[++CPU++]" 14 | 15 | /// internal options (see ENGD::IFLG) 16 | #define IFL_HALT (1 << 0) 17 | 18 | 19 | 20 | /// AINF destination (to put one "physical" animation into multiple AINFs) 21 | typedef struct _DEST { 22 | struct _DEST *next; 23 | AINF *ainf; 24 | } DEST; 25 | 26 | /// AVL hash tree, opaque outside the module 27 | typedef struct _TREE { 28 | struct _TREE *next[3]; /// NEXT[2] is used for collisions 29 | long diff; 30 | uint64_t hash; 31 | union { 32 | struct { 33 | /// pixel-data tree element 34 | ASTD *anim; 35 | AINF ainf; 36 | long xoff, yoff, scal, tran; 37 | }; 38 | struct { 39 | /// filename tree element 40 | __typeof__(((AINF*)0)->uuid) turn; 41 | char *path; 42 | DEST *dest; 43 | struct _TREE *epix; 44 | }; 45 | }; 46 | } TREE; 47 | 48 | /// AVL hash tree iterator 49 | typedef void (*ITER)(TREE*); 50 | 51 | /// thread data, opaque outside the module 52 | typedef struct _THRD { 53 | ulong loop; 54 | SEM_TYPE uuid; 55 | ENGD *orig; 56 | void (*udis)(void*); 57 | void (*func)(struct _THRD*); 58 | union { 59 | struct { 60 | long ymin, ymax; 61 | }; 62 | struct { 63 | TREE *elem; 64 | void *data; 65 | long flgs; 66 | }; 67 | }; 68 | } THRD; 69 | 70 | /// engine data, opaque outside the module 71 | struct ENGD { 72 | uint32_t flgs, /// options 73 | dflg, /// deferred options (those which are set upon restart) 74 | iflg, /// internal runtime options (halt flag, etc.) 75 | size, /// current length of the main display list 76 | smax, /// maximum capacity of the main display list 77 | fram, /// FPS counter 78 | msec, /// frame delay 79 | ncpu, /// number of CPU cores the engine is allowed to occupy 80 | uniq; /// number of unique animations 81 | uint64_t tfrm, /// timestamp for the previous frame 82 | tfps; /// timestamp for the previous FPS count 83 | UFRM ufrm; /// callback to update the state of a frame 84 | T4IV dims; /// drawing area position and dimensions 85 | BGRA *bptr; /// drawing area bits 86 | UNIT *uarr; /// unique animations source in array form 87 | T4FV *data; /// main display list (updated every frame; FOREIGN!) 88 | THRD *thrd; /// thread data array 89 | FRBO *surf; /// supplementary renderbuffer 90 | RNDR *rndr; /// supplementary renderer (the exact type may vary) 91 | TREE *hstr, /// AVL tree for animation filename hashes & HPIX links 92 | *hpix; /// AVL tree for animation pixel data (including hashes) 93 | SEMD *isem, /// incoming semaphore 94 | *osem; /// outgoing semaphore 95 | intptr_t udat, /// user-defined data to be passed to the frame updater 96 | user[4]; /// user-defined additional values, just in case 97 | }; 98 | 99 | 100 | 101 | void cOutputFPS(ENGD *engd, char retn[]) { 102 | uint64_t time = lTimeFunc(); 103 | float corr = ((engd->tfps > 0) && (time > engd->tfps))? 104 | 1000.0 / (time - engd->tfps) : 1.0; 105 | 106 | sprintf(retn, "[%7.3f]", corr * engd->fram); 107 | engd->tfps = time; 108 | engd->fram = 0; 109 | } 110 | 111 | 112 | 113 | SEM_TYPE cFindBit(SEM_TYPE inpt) { 114 | inpt &= inpt ^ (inpt - 1); 115 | return ((inpt & 0xFFFFFFFF00000000ULL)? 32 : 0) 116 | + ((inpt & 0xFFFF0000FFFF0000ULL)? 16 : 0) 117 | + ((inpt & 0xFF00FF00FF00FF00ULL)? 8 : 0) 118 | + ((inpt & 0xF0F0F0F0F0F0F0F0ULL)? 4 : 0) 119 | + ((inpt & 0xCCCCCCCCCCCCCCCCULL)? 2 : 0) 120 | + ((inpt & 0xAAAAAAAAAAAAAAAAULL)? 1 : 0); 121 | } 122 | 123 | 124 | 125 | long DownsampleAnimStd(ASTD *anim, long *xoff, long *yoff) { 126 | long x, y, fram, dpos, retn, 127 | xmin, xmax, ymin, ymax; 128 | 129 | if (xoff) 130 | *xoff = 0; 131 | if (yoff) 132 | *yoff = 0; 133 | 134 | xmax = 0; 135 | xmin = anim->xdim; 136 | ymax = 0; 137 | ymin = anim->ydim; 138 | for (fram = 0; fram < anim->fcnt; fram++) { 139 | for (y = 0; y < anim->ydim; y++) { 140 | dpos = (fram * anim->ydim + y) * anim->xdim; 141 | retn = -1; 142 | for (x = 0; x < anim->xdim; x++) 143 | if (anim->bptr[dpos + x] != 0xFF) { 144 | xmax = (x > xmax)? x : xmax; 145 | xmin = (x < xmin)? x : xmin; 146 | retn = 0; 147 | } 148 | if (!retn) { 149 | ymax = (y > ymax)? y : ymax; 150 | ymin = (y < ymin)? y : ymin; 151 | } 152 | } 153 | if ((xmin == 0) && (ymin == 0) && 154 | (xmax == anim->xdim - 1) && (ymax == anim->ydim - 1)) { 155 | xmin = xmax + 1; 156 | break; 157 | } 158 | } 159 | if ((xmin <= xmax) && (ymin <= ymax)) { 160 | for (retn = fram = 0; fram < anim->fcnt; fram++) 161 | for (y = ymin; y <= ymax; y++) { 162 | dpos = (fram * anim->ydim + y) * anim->xdim; 163 | for (x = xmin; x <= xmax; x++) 164 | anim->bptr[retn++] = anim->bptr[dpos + x]; 165 | } 166 | anim->xdim = xmax - xmin + 1; 167 | anim->ydim = ymax - ymin + 1; 168 | anim->bptr = realloc(anim->bptr, anim->fcnt * anim->xdim * anim->ydim); 169 | if (xoff) 170 | *xoff = xmin; 171 | if (yoff) 172 | *yoff = ymin; 173 | } 174 | 175 | retn = 0; 176 | while (!(anim->xdim & 1) && !(anim->ydim & 1)) { 177 | for (fram = 0; fram < anim->fcnt; fram++) 178 | for (y = 0; y < anim->ydim; y += 2) { 179 | dpos = (fram * anim->ydim + y) * anim->xdim; 180 | for (x = 0; x < anim->xdim; x += 2) 181 | if ((anim->bptr[dpos + x] != 182 | anim->bptr[dpos + x + 1]) 183 | || (anim->bptr[dpos + x] != 184 | anim->bptr[dpos + x + anim->xdim]) 185 | || (anim->bptr[dpos + x] != 186 | anim->bptr[dpos + x + anim->xdim + 1])) 187 | goto _quit; 188 | } 189 | for (xmin = fram = 0; fram < anim->fcnt; fram++) 190 | for (y = 0; y < anim->ydim; y += 2) { 191 | dpos = (fram * anim->ydim + y) * anim->xdim; 192 | for (x = 0; x < anim->xdim; x += 2) 193 | anim->bptr[xmin++] = anim->bptr[dpos + x]; 194 | } 195 | anim->xdim >>= 1; 196 | anim->ydim >>= 1; 197 | retn++; 198 | } 199 | _quit: 200 | if (retn) 201 | anim->bptr = realloc(anim->bptr, anim->fcnt * anim->xdim * anim->ydim); 202 | 203 | return retn; 204 | } 205 | 206 | 207 | 208 | long CompareAnimStd(ASTD *a, ASTD *b, long flgs) { 209 | long x, y, fram, dpos, spos; 210 | 211 | if ((a->fcnt != b->fcnt) || (a->xdim != b->xdim) || (a->ydim != b->ydim)) 212 | return 0; 213 | 214 | for (fram = 0; fram < a->fcnt; fram++) 215 | for (y = 0; y < a->ydim; y++) { 216 | spos = dpos = (fram * a->ydim + y) * a->xdim; 217 | if (flgs & 2) 218 | spos = (fram * a->ydim - y + a->ydim - 1) * a->xdim; 219 | if (flgs & 1) { 220 | spos += a->xdim - 1; 221 | for (x = 0; x < a->xdim; x++) 222 | if (a->bpal[a->bptr[dpos + x]].bgra != 223 | b->bpal[b->bptr[spos - x]].bgra) 224 | return 0; 225 | } 226 | else { 227 | for (x = 0; x < a->xdim; x++) 228 | if (a->bpal[a->bptr[dpos + x]].bgra != 229 | b->bpal[b->bptr[spos + x]].bgra) 230 | return 0; 231 | } 232 | } 233 | 234 | return ~0; 235 | } 236 | 237 | 238 | 239 | /// String-oriented linear hash multiplier and shift, respectively 240 | enum {SLH_MULT = 0xFBC5, SLH_PLUS = 0x11}; 241 | 242 | #define HASH(hash, trgt) hash = SLH_PLUS + SLH_MULT * hash + trgt.bgra; 243 | uint64_t HashAnimStd(ASTD *anim, long *turn) { 244 | uint64_t hh00, hh01, hh10, hh11; 245 | long x, y, fram, dpos, rpos; 246 | 247 | if (!anim) 248 | return 0; 249 | 250 | hh00 = hh01 = hh10 = hh11 = 0; 251 | for (fram = 0; fram < anim->fcnt; fram++) 252 | for (y = anim->ydim >> 1; y >= 0; y--) { 253 | dpos = (fram * anim->ydim + y) * anim->xdim; 254 | rpos = (fram * anim->ydim - y + anim->ydim - 1) * anim->xdim; 255 | for (x = anim->xdim >> 1; x >= 0; x--) { 256 | HASH(hh00, anim->bpal[anim->bptr[dpos + x]]); 257 | HASH(hh01, anim->bpal[anim->bptr[dpos - x + anim->xdim - 1]]); 258 | HASH(hh10, anim->bpal[anim->bptr[rpos + x]]); 259 | HASH(hh11, anim->bpal[anim->bptr[rpos - x + anim->xdim - 1]]); 260 | } 261 | } 262 | fram = (hh00 > hh01)? 0 : 1; 263 | hh00 = (hh00 > hh01)? hh00 : hh01; 264 | dpos = (hh10 > hh11)? 2 : 3; 265 | hh10 = (hh10 > hh11)? hh10 : hh11; 266 | *turn = (hh00 > hh10)? fram : dpos; 267 | return (hh00 > hh10)? hh00 : hh10; 268 | } 269 | #undef HASH 270 | 271 | 272 | 273 | uint64_t HashLine64(char *line) { 274 | uint64_t hash = 0; 275 | 276 | while (*line) 277 | hash = SLH_PLUS + SLH_MULT * hash + *line++; 278 | return hash; 279 | } 280 | 281 | 282 | 283 | void FlushDest(TREE *elem, long fill) { 284 | DEST *item; 285 | 286 | while (elem->dest) { 287 | item = elem->dest; 288 | elem->dest = elem->dest->next; 289 | if (fill) { 290 | if (elem->epix) { 291 | *item->ainf = elem->epix->ainf; 292 | item->ainf->uuid ^= elem->turn; 293 | } 294 | else 295 | *item->ainf = (AINF){}; 296 | } 297 | free(item); 298 | } 299 | } 300 | 301 | 302 | 303 | void TreeDelPath(TREE *elem) { 304 | FlushDest(elem, 0); 305 | free(elem->path); 306 | } 307 | 308 | void TreeDelAnim(TREE *elem) { 309 | FreeAnimStd(&elem->anim); 310 | } 311 | 312 | void TreeDel(TREE **root, ITER func) { 313 | if (*root) { 314 | TreeDel(&(*root)->next[0], func); 315 | TreeDel(&(*root)->next[1], func); 316 | TreeDel(&(*root)->next[2], func); 317 | if (func) 318 | func(*root); 319 | free(*root); 320 | *root = 0; 321 | } 322 | } 323 | 324 | 325 | 326 | TREE *TreeFind(TREE *root, uint64_t hash) { 327 | while (root && (root->hash != hash)) 328 | root = root->next[(root->hash > hash)? 0 : 1]; 329 | return root; 330 | } 331 | 332 | 333 | 334 | void TreeAdd(TREE **root, TREE *elem) { 335 | TREE *btop, *iter, *temp, *prev; 336 | long diff, indx; 337 | 338 | diff = indx = 0; 339 | temp = prev = 0; 340 | if (!(btop = iter = *root)) { 341 | *root = elem; 342 | return; 343 | } 344 | while (iter) { 345 | if (elem->hash == iter->hash) { 346 | elem->next[2] = iter->next[2]; 347 | iter->next[2] = elem; 348 | return; /// collision found; add collision and exit 349 | } 350 | if (iter->diff) { 351 | btop = iter; /// ITER is unbalanced: potential turn site 352 | prev = temp; /// saving ITER`s direct parent 353 | indx = diff = 0; /// purging node path, as it begins at BTOP 354 | } 355 | temp = iter; /// max 2^32 nodes in the tree, see below 356 | diff |= (elem->hash > iter->hash)? 1 << indx : 0; 357 | iter = iter->next[(diff >> indx++) & 1]; 358 | } 359 | /// add ELEM to the tree and rebalance all nodes from BTOP to ELEM 360 | iter = btop; 361 | temp->next[(diff >> --indx) & 1] = elem; 362 | while (iter != elem) { 363 | iter->diff += ((diff & 1) << 1) - 1; 364 | iter = iter->next[diff & 1]; 365 | diff >>= 1; 366 | } 367 | if ((diff = (btop->diff < 0)? -1 : +1) << 1 == btop->diff) { 368 | /// absolute branch disbalance exceeds 1, turn needed 369 | indx = (diff + 1) >> 1; 370 | temp = btop->next[indx]; 371 | if (temp->diff == diff) { 372 | /// small turn: BTOP > TEMP ~ TEMP v BTOP 373 | btop->next[indx] = temp->next[indx ^ 1]; 374 | temp->next[indx ^ 1] = btop; 375 | temp->diff = btop->diff = 0; 376 | iter = temp; 377 | } 378 | else { 379 | /// big turn: BTOP > TEMP v ITER ~ (ITER v BTOP) > TEMP 380 | iter = temp->next[indx ^ 1]; 381 | temp->next[indx ^ 1] = iter->next[indx]; 382 | iter->next[indx] = temp; 383 | btop->next[indx] = iter->next[indx ^ 1]; 384 | iter->next[indx ^ 1] = btop; 385 | temp->diff = (iter->diff == -diff)? -iter->diff : 0; 386 | btop->diff = (iter->diff == +diff)? -iter->diff : 0; 387 | iter->diff = 0; 388 | } 389 | /// *ROOT == BTOP: replacing the tree root 390 | /// *ROOT != BTOP: replacing BTOP`s site in its parent 391 | if (*root != btop) 392 | root = &prev->next[(prev->next[0] == btop)? 0 : 1]; 393 | *root = iter; 394 | } 395 | } 396 | 397 | 398 | 399 | long TryUpdatePixTree(ENGD *engd, TREE *estr) { 400 | long turn, stat; 401 | TREE *epix; 402 | 403 | if (!estr) 404 | return 0; 405 | 406 | stat = ' '; 407 | if (!estr->epix->anim) 408 | TreeDel(&estr->epix, 0); 409 | else { 410 | /// searching for the appropriate animation 411 | epix = TreeFind(engd->hpix, estr->epix->hash); 412 | while (epix) { 413 | if (CompareAnimStd(epix->anim, estr->epix->anim, 414 | epix->ainf.uuid ^ estr->turn)) 415 | break; 416 | epix = epix->next[2]; 417 | } 418 | if (!epix) { 419 | /// not found, the animation is new 420 | TreeAdd(&engd->hpix, estr->epix); 421 | estr->epix->ainf.uuid = (++engd->uniq << 2) | estr->turn; 422 | } 423 | else { 424 | /// found, replacing 425 | TreeDel(&estr->epix, TreeDelAnim); 426 | estr->epix = epix; 427 | stat = '#'; 428 | } 429 | } 430 | if (!estr->epix) 431 | printf(TXL_FAIL" %s\n", estr->path); 432 | else { 433 | turn = estr->epix->ainf.uuid ^ estr->turn; 434 | printf("[%4ld%c%c%c] %s\n", (long)(estr->epix->ainf.uuid >> 2), 435 | (char)stat, (turn & 2)? 'D' : 'U', (turn & 1)? 'L' : 'R', 436 | estr->path); 437 | } 438 | return ~0; 439 | } 440 | 441 | 442 | 443 | void FillDest(TREE *root) { 444 | if (!root) 445 | return; 446 | 447 | FillDest(root->next[0]); 448 | FillDest(root->next[1]); 449 | FillDest(root->next[2]); 450 | FlushDest(root, ~0); 451 | } 452 | 453 | 454 | 455 | void UnitArrayFromTree(UNIT *uarr, TREE *root) { 456 | if (!root || !uarr) 457 | return; 458 | 459 | UnitArrayFromTree(uarr, root->next[0]); 460 | UnitArrayFromTree(uarr, root->next[1]); 461 | UnitArrayFromTree(uarr, root->next[2]); 462 | 463 | ASTD *anim = (ASTD*)root->anim; 464 | long iter = root->ainf.uuid >> 2; 465 | 466 | uarr[iter].anim = root->anim; 467 | uarr[iter].scal = root->scal; 468 | uarr[iter].tran = root->tran; 469 | uarr[iter].offs[0] = root->xoff; 470 | uarr[iter].offs[2] = root->yoff; 471 | uarr[iter].offs[1] = root->ainf.xdim - root->xoff 472 | - (anim->xdim << root->scal); 473 | uarr[iter].offs[3] = root->ainf.ydim - root->yoff 474 | - (anim->ydim << root->scal); 475 | } 476 | 477 | 478 | 479 | long SelectUnit(UNIT *uarr, T4FV *data, long size, long xptr, long yptr) { 480 | long iter, indx, xpos, ypos; 481 | ASTD *anim; 482 | 483 | for (iter = 0; iter < size; iter++) { 484 | indx = data[iter].w; 485 | if (!(ypos = indx >> 2)) 486 | continue; 487 | anim = uarr[ypos].anim; 488 | xpos = ( xptr - uarr[ypos].offs[(indx & 1)? 1 : 0] 489 | - (long)data[iter].x) >> uarr[ypos].scal; 490 | ypos = (-yptr - uarr[ypos].offs[(indx & 2)? 2 : 3] 491 | + (long)data[iter].y) >> uarr[ypos].scal; 492 | if ((xpos >= 0) && (xpos < anim->xdim) 493 | && (ypos >= 0) && (ypos < anim->ydim)) { 494 | if (indx & 1) 495 | xpos = anim->xdim - 1 - xpos; 496 | if (!(indx & 2)) 497 | ypos = anim->ydim - 1 - ypos; 498 | if (anim->bptr[((long)data[iter].z * anim->ydim + ypos) 499 | * anim->xdim + xpos] != 0xFF) 500 | break; 501 | } 502 | } 503 | return (iter < size)? iter : -1; 504 | } 505 | 506 | 507 | 508 | THR_FUNC cThrdFunc(void *user) { 509 | THRD *data = (THRD*)user; 510 | 511 | while (!0) { 512 | lWaitSemaphore(data->orig->isem, data->uuid); 513 | if (!data->loop) 514 | break; 515 | data->func(data); 516 | if (!lPickSemaphore(data->orig->isem, data->orig->osem, data->uuid)) 517 | return THR_FAIL; 518 | } 519 | lPickSemaphore(data->orig->isem, data->orig->osem, data->uuid); 520 | return THR_EXIT; 521 | } 522 | 523 | 524 | 525 | int pixlcmp(const void *a, const void *b) { 526 | return (((BGRA*)a)->bgra > ((BGRA*)b)->bgra)? 1 : 527 | (((BGRA*)a)->bgra == ((BGRA*)b)->bgra)? 0 : -1; 528 | } 529 | 530 | ASTD *ConvertAnim(AINF *asrc) { 531 | ASTD *retn = 0; 532 | uint64_t *temp; 533 | uint32_t iter, indx, prox, prev; 534 | 535 | if (!asrc || !asrc->xdim || !asrc->ydim 536 | || !asrc->fcnt || !asrc->time || !asrc->uuid) 537 | return 0; 538 | 539 | retn = calloc(1, sizeof(*retn)); 540 | *retn = (ASTD){calloc(asrc->fcnt, asrc->xdim * asrc->ydim), 541 | asrc->xdim, asrc->ydim, asrc->fcnt, 542 | calloc(asrc->fcnt, sizeof(uint32_t)), 543 | calloc(256, sizeof(BGRA))}; 544 | memcpy(retn->time, asrc->time, asrc->fcnt * sizeof(uint32_t)); 545 | temp = calloc(asrc->fcnt * sizeof(uint64_t), asrc->xdim * asrc->ydim); 546 | iter = asrc->fcnt * asrc->xdim * asrc->ydim; 547 | while (iter--) 548 | temp[iter] = ((uint64_t)iter << 32) | ((BGRA*)asrc->uuid)[iter].bgra; 549 | qsort(temp, asrc->fcnt * asrc->xdim * asrc->ydim, 550 | sizeof(uint64_t), pixlcmp); 551 | 552 | /// assuming that the picture contains no more than 256 colors. 553 | /// [TODO:] add dithering to fix this! 554 | 555 | retn->bpal[0xFF].bgra = 0x00000000; 556 | prev = ~(uint32_t)temp[(iter = asrc->fcnt * asrc->xdim * asrc->ydim) - 1]; 557 | prox = indx = 0; 558 | while (iter--) { 559 | if ((uint32_t)temp[iter] != prev) { 560 | if ((prev = (uint32_t)temp[iter]) & 0xFF000000) 561 | retn->bpal[prox = indx++].bgra = prev; 562 | else 563 | prox = 0xFF; 564 | } 565 | retn->bptr[temp[iter] >> 32] = prox; 566 | } 567 | free(temp); 568 | return retn; 569 | } 570 | 571 | 572 | 573 | uint32_t RecolorPalette(BGRA *bpal, char *file, long size) { 574 | #pragma pack(push, 1) 575 | struct { 576 | uint8_t srcr, srcg, srcb; 577 | uint8_t tran; 578 | uint8_t dstr, dstg, dstb; 579 | } *amap; 580 | #pragma pack(pop) 581 | 582 | char *apal; 583 | uint32_t retn = 0; 584 | 585 | if (bpal && file) 586 | for (apal = file + size - sizeof(*amap); 587 | apal >= file; apal -= sizeof(*amap)) 588 | for (amap = (__typeof__(amap))apal, size = 0; size < 256; size++) 589 | if ((bpal[size].chnl[0] == amap->srcb) 590 | && (bpal[size].chnl[1] == amap->srcg) 591 | && (bpal[size].chnl[2] == amap->srcr) 592 | && (bpal[size].chnl[3] == 0xFF)) { 593 | bpal[size].chnl[0] = ((long)amap->dstb * amap->tran) >> 8; 594 | bpal[size].chnl[1] = ((long)amap->dstg * amap->tran) >> 8; 595 | bpal[size].chnl[2] = ((long)amap->dstr * amap->tran) >> 8; 596 | bpal[size].chnl[3] = amap->tran; 597 | if (amap->tran < 0xFF) 598 | retn++; 599 | break; 600 | } 601 | 602 | return retn; 603 | } 604 | 605 | void LTHR(THRD *data) { 606 | char *name, *file; 607 | long size; 608 | ASTD *retn; 609 | TREE *elem; 610 | 611 | if (!data->data) 612 | return; 613 | 614 | retn = 0; 615 | elem = data->elem; 616 | switch (data->flgs) { 617 | case ELA_AINF: retn = ConvertAnim((AINF*)data->data); break; 618 | case ELA_LOAD: retn = MakeAnimStd(data->data, LONG_MAX); break; 619 | case ELA_DISK: file = lLoadFile((char*)data->data, &size); 620 | retn = MakeAnimStd(file, size); free(file); break; 621 | } 622 | if (retn) { 623 | elem->epix->ainf = (AINF){0, retn->xdim, retn->ydim, 624 | retn->fcnt, retn->time}; 625 | elem->epix->scal = DownsampleAnimStd(retn, &elem->epix->xoff, 626 | &elem->epix->yoff); 627 | elem->epix->hash = HashAnimStd(retn, &size); 628 | elem->turn = size & 3; 629 | 630 | if (data->flgs == ELA_DISK) { 631 | name = strdup((char*)data->data); 632 | size = strlen((char*)data->data); 633 | name[size - 3] = 'a'; 634 | name[size - 2] = 'r'; 635 | name[size - 1] = 't'; 636 | file = lLoadFile(name, &size); 637 | RecolorPalette(retn->bpal, file, size); 638 | free(file); 639 | free(name); 640 | } 641 | for (size = 0; size <= 0xFF; size++) 642 | if ((uint8_t)(retn->bpal[size].chnl[3] - 1) < (uint8_t)0xFE) { 643 | elem->epix->tran = 1; 644 | break; 645 | } 646 | } 647 | elem->epix->anim = retn; 648 | } 649 | 650 | 651 | 652 | void PTHR(THRD *data) { 653 | long iter, indx, x, y, 654 | ysrc, ydst, xmin, ymin, xmax, ymax, 655 | xoff, yoff, xinc, yinc, xpos, ypos; 656 | BGRA b_r_, _g_a, *bptr; 657 | UNIT *tail; 658 | ASTD *anim; 659 | 660 | bptr = data->orig->bptr; 661 | xoff = data->orig->dims.xdim; 662 | /// manual zeroing 663 | // memset(bptr + xoff * data->ymin, 0, 664 | // xoff * (data->ymax - data->ymin) << 2); 665 | 666 | for (iter = 0; iter < data->orig->size; iter++) { 667 | indx = data->orig->data[iter].w; 668 | tail = &data->orig->uarr[indx >> 2]; 669 | xpos = data->orig->data[iter].x + tail->offs[indx & 1]; 670 | ypos = data->orig->data[iter].y - tail->offs[3 - ((indx >> 1) & 1)]; 671 | anim = tail->anim; 672 | yoff = anim->xdim * anim->ydim * data->orig->data[iter].z; 673 | ymax = (ypos > data->ymax)? data->ymax - ypos : 0; 674 | ymin = anim->ydim << tail->scal; 675 | xinc = anim->xdim << tail->scal; 676 | if (indx & 1) { 677 | xmax = (0 < xpos )? xinc : xinc + xpos; 678 | xmin = (0 > xinc + xpos - xoff)? 0 : xinc + xpos - xoff; 679 | yinc = -1; 680 | } 681 | else { 682 | xmax = (xinc < xoff - xpos)? xinc : xoff - xpos; 683 | xmin = ( 0 > 0 - xpos)? 0 : 0 - xpos; 684 | yinc = 1; 685 | } 686 | xinc = ((yinc < 0)? xinc - 1 - xmin : xmin) + xpos; 687 | 688 | /// alpha blending here 689 | y = data->ymin - ypos; 690 | for (y = (y > -ymin)? y : -ymin; y < ymax; y++) { 691 | ydst = (y + ypos) * xoff + xinc; 692 | ysrc = (indx & 2)? -y - 1 : y + ymin; 693 | ysrc = (ysrc >> tail->scal) * anim->xdim + yoff; 694 | for (x = xmin; x < xmax; x++, ydst += yinc) 695 | if (bptr[ydst].chnl[3] != 0xFF) { 696 | b_r_ = anim->bpal[anim->bptr[ysrc + (x >> tail->scal)]]; 697 | if (bptr[ydst].chnl[3] == 0x00) 698 | bptr[ydst] = b_r_; 699 | else { 700 | _g_a.bgra = ((b_r_.bgra >> 8) & 0x00FF00FF) 701 | * (0xFF - bptr[ydst].chnl[3]); 702 | b_r_.bgra = ((b_r_.bgra ) & 0x00FF00FF) 703 | * (0xFF - bptr[ydst].chnl[3]); 704 | bptr[ydst].bgra += ((b_r_.bgra >> 8) & 0x00FF00FF) 705 | + ((_g_a.bgra ) & 0xFF00FF00); 706 | } 707 | } 708 | } 709 | } 710 | } 711 | 712 | 713 | 714 | void StopThreads(ENGD *engd) { 715 | ulong iter, loop; 716 | 717 | for (loop = iter = 0; iter < engd->ncpu; iter++) 718 | loop |= engd->thrd[iter].loop; 719 | if (loop) { 720 | lWaitSemaphore(engd->osem, SEM_FULL); 721 | for (iter = 0; iter < engd->ncpu; iter++) 722 | engd->thrd[iter].loop = 0; 723 | lPickSemaphore(engd->osem, engd->isem, SEM_FULL); 724 | lWaitSemaphore(engd->osem, SEM_FULL); 725 | } 726 | } 727 | 728 | 729 | 730 | long SwitchThreads(ENGD *engd, long draw) { 731 | long iter, temp; 732 | 733 | if (draw) { 734 | for (iter = 0; iter < engd->ncpu; iter++) 735 | if (engd->thrd[iter].loop) 736 | return -1; 737 | 738 | temp = (engd->dims.ydim / engd->ncpu) + 1; 739 | for (iter = 0; iter < engd->ncpu; iter++) { 740 | engd->thrd[iter] = (THRD){1, 1 << iter, engd, 0, PTHR, 741 | {{temp * iter, temp * (iter + 1)}}}; 742 | lMakeThread(&engd->thrd[iter]); 743 | } 744 | engd->thrd[engd->ncpu - 1].ymax = engd->dims.ydim; 745 | } 746 | else 747 | for (iter = 0; iter < engd->ncpu; iter++) { 748 | engd->thrd[iter] = (THRD){1, 1 << iter, engd, 0, LTHR}; 749 | lMakeThread(&engd->thrd[iter]); 750 | } 751 | return 0; 752 | } 753 | 754 | 755 | 756 | uint32_t cPrepareFrame(ENGD *engd, long xptr, long yptr, uint32_t attr) { 757 | uint64_t time = lTimeFunc(); 758 | long pick; 759 | 760 | if (time - engd->tfrm < engd->msec) 761 | return PFR_HALT | PFR_SKIP; 762 | engd->tfrm = time; 763 | if (~engd->flgs & COM_DRAW) 764 | return PFR_HALT; 765 | pick = SelectUnit(engd->uarr, engd->data, engd->size, xptr, yptr); 766 | engd->smax = 0; 767 | engd->size = engd->ufrm(engd, &engd->data, &engd->smax, lTimeFunc(), 768 | engd->udat, attr, xptr, yptr, pick); 769 | if (!engd->smax) { 770 | cEngineCallback(engd, ECB_QUIT, ~0); 771 | return PFR_HALT; 772 | } 773 | return ((pick >= 0) || (engd->flgs & COM_OPAQ))? PFR_PICK : 0; 774 | } 775 | 776 | 777 | 778 | void cOutputFrame(ENGD *engd, long frbo) { 779 | if (engd->flgs & COM_RGPU) { 780 | if (!MakeRendererOGL(&engd->rndr, !!frbo && !(engd->flgs & WIN_IBGR), 781 | engd->uarr, engd->uniq, engd->smax, 782 | engd->dims.xdim - engd->dims.xpos, 783 | engd->dims.ydim - engd->dims.ypos)) { 784 | cEngineCallback(engd, ECB_SFLG, engd->flgs & ~COM_RGPU); 785 | return; 786 | } 787 | if (!!frbo) { 788 | if (!engd->surf) 789 | engd->surf = MakeRBO(engd->dims.xdim - engd->dims.xpos, 790 | engd->dims.ydim - engd->dims.ypos); 791 | ReadRBO(engd->surf, engd->bptr, engd->flgs); 792 | BindRBO(engd->surf, 1); 793 | } 794 | DrawRendererOGL(engd->rndr, engd->uarr, engd->data, 795 | engd->size, engd->flgs & COM_OPAQ); 796 | if (!!frbo) 797 | BindRBO(engd->surf, 0); 798 | } 799 | else { 800 | SwitchThreads(engd, 1); 801 | lPickSemaphore(engd->osem, engd->isem, SEM_FULL); 802 | lWaitSemaphore(engd->osem, SEM_FULL); 803 | } 804 | engd->fram++; 805 | } 806 | 807 | 808 | 809 | void cDeallocFrame(ENGD *engd, long frbo) { 810 | if (~engd->flgs & COM_RGPU) 811 | StopThreads(engd); 812 | else { 813 | FreeRendererOGL(&engd->rndr); 814 | if (!!frbo) 815 | FreeRBO(&engd->surf); 816 | } 817 | } 818 | 819 | 820 | 821 | void cEngineLoadAnimAsync(ENGD *engd, AINF *ainf, uint8_t *name, 822 | void *data, uint32_t flgs, void (*udis)(void*)) { 823 | TREE *estr, *retn; 824 | DEST *dest; 825 | 826 | SEM_TYPE curr; 827 | uint64_t hash; 828 | 829 | if (!engd || !ainf || !name) 830 | return; 831 | estr = TreeFind(engd->hstr, hash = HashLine64((char*)name)); 832 | while (estr) { 833 | if (!strcmp(estr->path, (char*)name)) 834 | break; 835 | estr = estr->next[2]; 836 | } 837 | if (!estr) { 838 | estr = calloc(1, sizeof(*estr)); 839 | estr->hash = hash; 840 | estr->path = strdup((char*)name); 841 | estr->epix = calloc(1, sizeof(*estr->epix)); 842 | estr->dest = calloc(1, sizeof(*estr->dest)); 843 | estr->dest->ainf = ainf; 844 | TreeAdd(&engd->hstr, estr); 845 | curr = cFindBit(lWaitSemaphore(engd->osem, SEM_NULL)); 846 | retn = engd->thrd[curr].elem; 847 | engd->thrd[curr].elem = estr; 848 | if (engd->thrd[curr].udis) 849 | engd->thrd[curr].udis(engd->thrd[curr].data); 850 | engd->thrd[curr].udis = udis; 851 | engd->thrd[curr].data = data; 852 | engd->thrd[curr].flgs = flgs; 853 | lPickSemaphore(engd->osem, engd->isem, 1 << curr); 854 | TryUpdatePixTree(engd, retn); 855 | } 856 | else if (estr->epix) { 857 | dest = calloc(1, sizeof(*dest)); 858 | dest->next = estr->dest; 859 | estr->dest = dest; 860 | dest->ainf = ainf; 861 | printf(TXL_DUPL" %s\n", estr->path); 862 | } 863 | } 864 | 865 | 866 | 867 | void cEngineRunMainLoop(ENGD *engd, int32_t xpos, int32_t ypos, 868 | uint32_t xdim, uint32_t ydim, uint32_t flgs, 869 | uint32_t msec, intptr_t user, UFRM ufrm, UFLG uflg) { 870 | long mtmp; 871 | 872 | if (engd->uarr) { 873 | engd->dims = (T4IV){{xpos, ypos, xdim, ydim}}; 874 | engd->dflg = engd->flgs = flgs; 875 | engd->ufrm = ufrm; 876 | engd->udat = user; 877 | engd->msec = msec; 878 | engd->size = 0; 879 | 880 | mtmp = lTimeFunc() - engd->tfrm; 881 | printf(TXL_AEND" %u threads, %u objects, %ld ms: %0.3f ms/obj\n", 882 | engd->ncpu, engd->uniq, mtmp, 883 | (double)mtmp * engd->ncpu / engd->uniq); 884 | do { 885 | engd->flgs = (engd->flgs & ~COM_DDDD) | (engd->dflg & COM_DDDD); 886 | /// done setting deferred flags (e.g., ones like rendering scheme) 887 | if (uflg) 888 | engd->flgs = uflg(engd, engd->udat, engd->flgs); 889 | printf("%s\n", (engd->flgs & COM_RGPU)? TXL_RGPU : TXL_RSTD); 890 | lRunMainLoop(engd, engd->dims.xpos, engd->dims.ypos, 891 | engd->dims.xdim, engd->dims.ydim, 892 | &engd->bptr, engd->user, engd->flgs); 893 | } while (~engd->iflg & IFL_HALT); 894 | } 895 | else 896 | printf(TXL_FAIL" No animation base found! Exiting...\n"); 897 | } 898 | 899 | 900 | 901 | void cEngineCallback(ENGD *engd, uint32_t ecba, intptr_t data) { 902 | if (!engd && (ecba != ECB_INIT)) 903 | return; 904 | switch (ecba) { 905 | case ECB_INIT: 906 | engd = calloc(1, sizeof(*engd)); 907 | engd->ncpu = lCountCPUs(); 908 | engd->thrd = malloc(engd->ncpu * sizeof(*engd->thrd)); 909 | lMakeSemaphore(&engd->isem, engd->ncpu, SEM_NULL); 910 | lMakeSemaphore(&engd->osem, engd->ncpu, SEM_FULL); 911 | SwitchThreads(engd, 0); 912 | engd->tfrm = lTimeFunc(); 913 | *(intptr_t*)data = (intptr_t)engd; 914 | break; 915 | 916 | case ECB_GUSR: 917 | if (data) 918 | *(intptr_t**)data = engd->user; 919 | break; 920 | 921 | case ECB_GFLG: 922 | if (data) 923 | *(uint32_t*)data = engd->flgs; 924 | break; 925 | 926 | case ECB_SFLG: { 927 | uint32_t temp = engd->flgs; 928 | 929 | if ((data ^ temp) & COM_DDDD) { 930 | /// we have received a deferred flag, restart needed! 931 | engd->dflg = data; 932 | engd->iflg &= ~IFL_HALT; /// do not halt on restart! 933 | lRestartEngine(engd); 934 | } 935 | /// deferred flags are not to be set until restart! 936 | engd->flgs = (engd->flgs & COM_DDDD) | (data & ~COM_DDDD); 937 | lShowMainWindow(engd, engd->flgs & COM_SHOW); 938 | break; 939 | } 940 | /// [TODO:] change to PTHR() 941 | case ECB_DRAW: { 942 | AINF *ainf = (AINF*)data; 943 | BGRA *retn = (BGRA*)ainf->time, pixl; 944 | ASTD *anim = engd->uarr[ainf->uuid >> 2].anim; 945 | long x, y, xcoe, ycoe, xdim = ainf->xdim, ydim = ainf->ydim; 946 | uint8_t *bptr; 947 | 948 | if ((!anim) || (ainf->fcnt >= anim->fcnt) 949 | || (anim->xdim > xdim) || (anim->ydim > ydim)) 950 | break; 951 | xcoe = xdim / anim->xdim; 952 | ycoe = ydim / anim->ydim; 953 | xcoe = ycoe = (xcoe < ycoe)? xcoe : ycoe; 954 | retn += ((ydim - ycoe * anim->ydim) >> 1) * xdim 955 | + ((xdim - xcoe * anim->xdim) >> 1); 956 | bptr = anim->bptr + anim->xdim * anim->ydim * ainf->fcnt; 957 | for (y = anim->ydim * ycoe - 1; y >= 0; y--) 958 | for (x = anim->xdim * xcoe - 1; x >= 0; x--) { 959 | /// division, OMFG! [TODO:] get rid of this 960 | pixl = anim->bpal[bptr[anim->xdim * (y / ycoe) 961 | + (x / xcoe)]]; 962 | if (pixl.chnl[3] != 0x00) 963 | retn[xdim * y + x] = pixl; 964 | } 965 | break; 966 | } 967 | case ECB_TEST: { 968 | AINF *ainf = (AINF*)data; 969 | T4FV test = {{-(int32_t)ainf->xdim, -(int32_t)ainf->ydim, 970 | ainf->fcnt, ainf->uuid}}; 971 | 972 | ainf->fcnt = SelectUnit(engd->uarr, &test, 1, 0, 0) + 1; 973 | break; 974 | } 975 | case ECB_LOAD: 976 | StopThreads(engd); 977 | if (data) { 978 | SwitchThreads(engd, 0); 979 | engd->tfrm = lTimeFunc(); 980 | } 981 | else { 982 | data = engd->flgs; 983 | engd->flgs &= ~COM_DRAW; 984 | for (ecba = 0; ecba < engd->ncpu; ecba++) { 985 | TryUpdatePixTree(engd, engd->thrd[ecba].elem); 986 | if (engd->thrd[ecba].udis) 987 | engd->thrd[ecba].udis(engd->thrd[ecba].data); 988 | engd->thrd[ecba].udis = 0; 989 | engd->thrd[ecba].data = 0; 990 | } 991 | if (engd->hstr) { 992 | FillDest(engd->hstr); 993 | free(engd->uarr); 994 | engd->uarr = calloc(engd->uniq + 1, sizeof(*engd->uarr)); 995 | UnitArrayFromTree(engd->uarr, engd->hpix); 996 | } 997 | if (engd->uarr) 998 | engd->flgs = data; 999 | } 1000 | break; 1001 | 1002 | case ECB_QUIT: 1003 | engd->iflg |= IFL_HALT; 1004 | lRestartEngine(engd); 1005 | if (data) 1006 | break; 1007 | if (engd->uarr) { 1008 | free(engd->uarr); 1009 | engd->uarr = 0; 1010 | TreeDel(&engd->hstr, TreeDelPath); 1011 | TreeDel(&engd->hpix, TreeDelAnim); 1012 | engd->uniq = 0; 1013 | } 1014 | StopThreads(engd); 1015 | lFreeSemaphore(&engd->isem, engd->ncpu); 1016 | lFreeSemaphore(&engd->osem, engd->ncpu); 1017 | free(engd->thrd); 1018 | free(engd); 1019 | break; 1020 | } 1021 | } 1022 | -------------------------------------------------------------------------------- /core/core.h: -------------------------------------------------------------------------------- 1 | #ifndef HDR_CORE 2 | #define HDR_CORE 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | 15 | 16 | #ifdef _WIN32 17 | #include 18 | #define THR_EXIT TRUE 19 | #define THR_FAIL FALSE 20 | #define THR_FUNC DWORD APIENTRY 21 | #else 22 | #include 23 | #define THR_EXIT 0 24 | #define THR_FAIL 0 25 | #define THR_FUNC void * 26 | #endif 27 | 28 | enum {PFR_HALT = 1 << 31, PFR_SKIP = 1 << 30, PFR_PICK = 1 << 29}; 29 | 30 | 31 | 32 | /// semaphore control type 33 | typedef uint64_t SEM_TYPE; 34 | 35 | /// semaphore data, defined externally 36 | typedef struct SEMD SEMD; 37 | 38 | /// elementary animation unit 39 | typedef struct { 40 | void *anim; /// animation data (the format may vary) 41 | ulong scal; /// scaling factor in powers of 2 42 | ulong tran; /// indicator of semi-transparent pixels 43 | ulong offs[4]; /// offsets from the initial size: X_lf, X_rt, Y_up, Y_dn 44 | } UNIT; 45 | 46 | 47 | 48 | uint32_t cPrepareFrame(ENGD *engd, long xptr, long yptr, uint32_t attr); 49 | void cOutputFrame(ENGD *engd, long frbo); 50 | void cDeallocFrame(ENGD *engd, long frbo); 51 | void cOutputFPS(ENGD *engd, char retn[]); 52 | SEM_TYPE cFindBit(SEM_TYPE inpt); 53 | THR_FUNC cThrdFunc(void *user); 54 | 55 | 56 | 57 | /// external functions, have to be implemented or imported 58 | long lCountCPUs(); 59 | uint64_t lTimeFunc(); 60 | char *lLoadFile(char *name, long *size); 61 | void lMakeThread(void *thrd); 62 | void lRestartEngine(ENGD *engd); 63 | void lShowMainWindow(ENGD *engd, long show); 64 | void lRunMainLoop(ENGD *engd, long xpos, long ypos, long xdim, long ydim, 65 | BGRA **bptr, intptr_t *data, uint32_t flgs); 66 | void lFreeSemaphore(SEMD **retn, long nthr); 67 | void lMakeSemaphore(SEMD **retn, long nthr, SEM_TYPE mask); 68 | long lPickSemaphore(SEMD *drop, SEMD *pick, SEM_TYPE mask); 69 | SEM_TYPE lWaitSemaphore(SEMD *wait, SEM_TYPE mask); 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /core/engine.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define LIB_OPEN __attribute__((visibility("default"))) 5 | 6 | #ifdef _WIN32 7 | #undef LIB_OPEN 8 | #ifndef LIB_NONE 9 | #ifdef LIB_MAKE 10 | #define LIB_OPEN __attribute__((dllexport)) 11 | #else 12 | #define LIB_OPEN __attribute__((dllimport)) 13 | #endif 14 | #else 15 | #define LIB_OPEN 16 | #endif 17 | #define INCBIN(file, pvar) \ 18 | __asm__( \ 19 | ".section .data;" \ 20 | ".global _"#pvar";" \ 21 | "_"#pvar":" \ 22 | ".incbin \""file"\";" \ 23 | "_"#pvar"_end:" \ 24 | ".byte 0;" \ 25 | ".align 4;" \ 26 | ".section .text;" \ 27 | ); \ 28 | extern char pvar[] 29 | #elif __APPLE__ 30 | #define INCBIN(file, pvar) \ 31 | __asm__( \ 32 | ".section __DATA,__data\n" \ 33 | ".globl _"#pvar"\n" \ 34 | "_"#pvar":\n" \ 35 | ".incbin \""file"\"\n" \ 36 | "_"#pvar"_end:\n" \ 37 | ".byte 0\n" \ 38 | ".align 4\n" \ 39 | ".section __TEXT,__text\n" \ 40 | ); \ 41 | extern char pvar[] 42 | #else 43 | #define INCBIN(file, pvar) \ 44 | __asm__( \ 45 | ".pushsection .data;" \ 46 | ".global "#pvar";" \ 47 | #pvar":" \ 48 | ".incbin \""file"\";" \ 49 | #pvar"_end:" \ 50 | ".byte 0;" \ 51 | ".align 4;" \ 52 | ".popsection;" \ 53 | ); \ 54 | extern char pvar[] 55 | #endif 56 | 57 | 58 | 59 | enum {ECB_INIT, ECB_GUSR, ECB_GFLG, ECB_SFLG, 60 | ECB_DRAW, ECB_TEST, ECB_LOAD, ECB_QUIT}; 61 | enum {ELA_DISK, ELA_LOAD, ELA_AINF}; 62 | 63 | enum {COM_RGPU = 1 << 31, COM_DRAW = 1 << 30, 64 | COM_SHOW = 1 << 29, COM_OPAQ = 1 << 28, 65 | COM_DDDD = COM_RGPU /** deferred flags mask **/}; 66 | 67 | enum {WIN_IBGR = 1 << 0, WIN_IPBO = 1 << 1, WIN_IRGN = 1 << 2}; 68 | 69 | enum { /// UFRM flags 70 | UFR_LBTN = 1 << 0, /** left mouse button **/ 71 | UFR_MBTN = 1 << 1, /** middle mouse button **/ 72 | UFR_RBTN = 1 << 2, /** right mouse button **/ 73 | UFR_MOUS = 1 << 3, /** mouse input enabled **/ 74 | UFR_PL1W = 1 << 4, /** player #1 pressed W **/ 75 | UFR_PL1S = 1 << 5, /** player #1 pressed S **/ 76 | UFR_PL1A = 1 << 6, /** player #1 pressed A **/ 77 | UFR_PL1D = 1 << 7, /** player #1 pressed D **/ 78 | UFR_PL2W = 1 << 8, /** player #2 pressed W **/ 79 | UFR_PL2S = 1 << 9, /** player #2 pressed S **/ 80 | UFR_PL2A = 1 << 10, /** player #2 pressed A **/ 81 | UFR_PL2D = 1 << 11, /** player #2 pressed D **/ 82 | UFR_KEYB = UFR_PL1W | UFR_PL1S | UFR_PL1A | UFR_PL1D 83 | | UFR_PL2W | UFR_PL2S | UFR_PL2A | UFR_PL2D 84 | }; 85 | 86 | 87 | 88 | #pragma pack(push, 1) 89 | /// engine data 90 | typedef struct ENGD ENGD; 91 | 92 | typedef unsigned long ulong; 93 | 94 | typedef union { 95 | struct {float x, y;}; 96 | struct {float u, v;}; 97 | } T2FV; 98 | 99 | typedef union { 100 | struct {float x, y, z;}; 101 | struct {float r, g, b;}; 102 | } T3FV; 103 | 104 | typedef union { 105 | struct {float x, y, z, w;}; 106 | struct {float r, g, b, a;}; 107 | struct {float xpos, ypos, xdim, ydim;}; 108 | } T4FV; 109 | 110 | typedef union { 111 | struct {int32_t x, y;}; 112 | struct {int32_t u, v;}; 113 | } T2IV; 114 | 115 | typedef union { 116 | struct {int32_t x, y, z;}; 117 | struct {int32_t r, g, b;}; 118 | } T3IV; 119 | 120 | typedef union { 121 | struct {int32_t x, y, z, w;}; 122 | struct {int32_t r, g, b, a;}; 123 | struct {int32_t xpos, ypos, xdim, ydim;}; 124 | } T4IV; 125 | 126 | /// animation unit info 127 | typedef struct { 128 | intptr_t uuid; /// unique animation identifier (or data) 129 | uint32_t xdim, /// frame width (actual, non-modified) 130 | ydim, /// frame height (actual, non-modified) 131 | fcnt, /// number of animation`s frames 132 | *time; /// frame delays array, managed by the creator 133 | } AINF; 134 | #pragma pack(pop) 135 | 136 | 137 | 138 | /** _________________________________________________________________________ 139 | Callback function for the main program to modify the display list. Called 140 | each frame, returns new length of the updated list. 141 | _________________________________________________________________________ 142 | ENGD: handle of the engine object the call refers to 143 | DATA: callee-managed display list; shares the format with uniform 144 | (see comments in ./ogl/oglstd.c, look for "main vertex shader" tag) 145 | except that W is just the element`s UUID 146 | SIZE: total allocated length of DATA (i.e. maximum capacity) 147 | TIME: the current time value in msec 148 | USER: user-defined data (may be a pointer) 149 | ATTR: control attributes (UFR_ prefix) 150 | XPTR: cursor X coordinate, relative to the window`s upper left corner 151 | YPTR: cursor Y coordinate, relative to the window`s upper left corner 152 | ISEL: index of the element under cursor in the existing list, < 0 if none 153 | **/ 154 | typedef uint32_t (*UFRM)(ENGD *engd, T4FV **data, uint32_t *size, 155 | uint64_t time, intptr_t user, uint32_t attr, 156 | int32_t xptr, int32_t yptr, int32_t isel); 157 | 158 | typedef uint32_t (*UFLG)(ENGD *engd, intptr_t user, uint32_t flgs); 159 | 160 | 161 | 162 | /** _________________________________________________________________________ 163 | Provides the entry gate for all asynchronous engine functionality (except 164 | main loop execution and image loading itself), e.g. window manipulations. 165 | _________________________________________________________________________ 166 | ENGD: handle of the engine object the call refers to 167 | ECBA: callback action to perform: 168 | ECB_INIT: initialize the new engine; may be called with ENGD == 0 169 | DATA: pointer to the var that receives the new object 170 | ECB_GUSR: get the current user array 171 | DATA: pointer to the var that receives the array 172 | ECB_GFLG: get the current state flags 173 | DATA: pointer to the var that receives the flags 174 | ECB_SFLG: set the current state flags 175 | DATA: new flags 176 | ECB_DRAW: "immediate" CPU-to-RAM draw call, DATA -> AINF: 177 | UUID: ID of the target animation 178 | XDIM: width of the draw area 179 | YDIM: height of the draw area 180 | FCNT: frame that needs to be drawn 181 | TIME: pointer to the draw area 182 | ECB_LOAD: DATA != 0: interrupt the main loop to load more anims 183 | DATA == 0: wait for anim loading completion 184 | ECB_QUIT: DATA != 0: just stop the main loop, do not deallocate 185 | DATA == 0: terminate everything, deallocate resources 186 | DATA: accompanying data for the action; may be anything (see above) 187 | **/ 188 | LIB_OPEN void cEngineCallback(ENGD *engd, uint32_t ecba, intptr_t data); 189 | 190 | /** _________________________________________________________________________ 191 | Reads animations asynchronously. All AINF`s have to remain valid till the 192 | call to EngineCallback(ECB_LOAD), since they are only updated there. 193 | AINF::UUID will contain a positive integer on success, 0 on error. 194 | _________________________________________________________________________ 195 | ENGD: handle of the engine object the call refers to 196 | AINF: pointer to the structure to receive animation properties 197 | NAME: unique animation identifier; must be a string 198 | DATA: data structure pointer; see FLGS 199 | FLGS: data types in DATA: 200 | ELA_DISK: full path to the animation in UTF8 format 201 | ELA_LOAD: preloaded GIF file data 202 | ELA_AINF: AINF structure, with AINF::UUID as a linearized BGRA 203 | frame buffer, AINF::XDIM x (AINF::YDIM * AINF::FCNT) 204 | UDIS: data discarder function; usually just free() 205 | **/ 206 | LIB_OPEN void cEngineLoadAnimAsync(ENGD *engd, AINF *ainf, 207 | uint8_t *name, void *data, uint32_t flgs, 208 | void (*udis)(void*)); 209 | 210 | /** _________________________________________________________________________ 211 | Executes the main loop. 212 | _________________________________________________________________________ 213 | ENGD: handle of the engine object the call refers to 214 | XPOS: window position X 215 | YPOS: window position Y 216 | XDIM: window width 217 | YDIM: window height 218 | FLGS: flags 219 | COM_RGPU - GPU rendering (CPU otherwise) 220 | WIN_IBGR - [WIN32] use BGRA 221 | WIN_IPBO - [WIN32] use PBO 222 | MSEC: delay between frames in ms 223 | USER: user data pointer to be passed to the callback 224 | UFRM: (see UFRM typedef) 225 | UFLG: (see UFLG typedef) [NOT NECESSARILY NEEDED, MAY BE 0] 226 | **/ 227 | LIB_OPEN void cEngineRunMainLoop(ENGD *engd, int32_t xpos, int32_t ypos, 228 | uint32_t xdim, uint32_t ydim, 229 | uint32_t flgs, uint32_t msec, 230 | intptr_t user, UFRM ufrm, UFLG uflg); 231 | -------------------------------------------------------------------------------- /core/gif/gifstd.c: -------------------------------------------------------------------------------- 1 | #include "load/gif_load.h" 2 | #include "gifstd.h" 3 | #include 4 | 5 | 6 | 7 | static void WriteFrameStd(void *data, struct GIF_WHDR *whdr) { 8 | long x, y, yoff, iter, ifin, dsrc, ddst; 9 | ASTD *retn = (ASTD*)data; 10 | 11 | ddst = whdr->ifrm * (y = whdr->xdim * whdr->ydim) 12 | + whdr->xdim * whdr->fryo + whdr->frxo; 13 | if (!whdr->ifrm) { /** frame extraction has just begun, initializing **/ 14 | retn->bpal = realloc(retn->bpal, 256 * sizeof(*retn->bpal)); 15 | for (x = whdr->clrs - 1; x >= 0; x--) { 16 | retn->bpal[x].chnl[0] = whdr->cpal[x].B; 17 | retn->bpal[x].chnl[1] = whdr->cpal[x].G; 18 | retn->bpal[x].chnl[2] = whdr->cpal[x].R; 19 | retn->bpal[x].chnl[3] = 0xFF; 20 | } 21 | retn->bpal[0xFF].bgra = 0x00000000; 22 | retn->xdim = whdr->xdim; 23 | retn->ydim = whdr->ydim; 24 | retn->fcnt = (whdr->nfrm < 0)? -whdr->nfrm : whdr->nfrm; 25 | retn->time = realloc(retn->time, retn->fcnt * sizeof(*retn->time)); 26 | memset(retn->bptr = realloc(retn->bptr, retn->fcnt * y), 0xFF, y); 27 | } 28 | retn->time[whdr->ifrm] = 10 * ((whdr->time < 0)? 29 | -whdr->time - 1 : whdr->time); 30 | ifin = (!(iter = (whdr->intr)? 0 : 4))? 4 : 5; /** interlacing support **/ 31 | 32 | /** [TODO:] the frame is assumed to be inside global bounds, 33 | however it might exceed them in some GIFs; fix me. **/ 34 | for (dsrc = -1; iter < ifin; iter++) 35 | for (yoff = 16 >> ((iter > 1)? iter : 1), y = (8 >> iter) & 7; 36 | y < whdr->fryd; y += yoff) 37 | for (x = 0; x < whdr->frxd; x++) 38 | if (whdr->tran != (long)whdr->bptr[++dsrc]) 39 | retn->bptr[whdr->xdim * y + x + ddst] = whdr->bptr[dsrc]; 40 | if (whdr->ifrm + 1 < whdr->nfrm) { /** background for the next frame **/ 41 | dsrc = (whdr->ifrm + 1) * (y = whdr->xdim * whdr->ydim); 42 | if ((whdr->mode != GIF_BKGD) || (whdr->frxo | whdr->fryo) 43 | || (whdr->frxd != whdr->xdim) || (whdr->fryd != whdr->ydim)) 44 | memcpy(retn->bptr + dsrc, retn->bptr + dsrc 45 | - ((whdr->ifrm && (whdr->mode == GIF_PREV))? y + y : y), y); 46 | if (whdr->mode == GIF_BKGD) /** cutting a hole if need be **/ 47 | for (ddst += whdr->xdim * whdr->ydim, y = 0; y < whdr->fryd; y++) 48 | for (x = 0; x < whdr->frxd; x++) 49 | retn->bptr[whdr->xdim * y + x + ddst] = 0xFF; 50 | } 51 | } 52 | 53 | 54 | 55 | static void ReadMetadataStd(void *data, struct GIF_WHDR *whdr) { 56 | #define RMS_BGRA(i) do { uint32_t t = ((ASTD*)data)->bpal[bptr[0]].bgra; \ 57 | ((ASTD*)data)->bpal[bptr[0]].bgra = \ 58 | ((((t & 0x00FF00FF) * (1 + bptr[i])) >> 8) & 0x00FF00FF) | \ 59 | ((((t & 0xFF00FF00) >> 8) * (1 + bptr[i])) & 0xFF00FF00); \ 60 | bptr += 1 + i; } while (0) 61 | uint8_t *bptr = whdr->bptr + 8 + 3; 62 | long iter = *bptr++; 63 | 64 | if ((whdr->bptr[0] != 'O') || (whdr->bptr[4] != 'i') 65 | || (whdr->bptr[1] != 'p') || (whdr->bptr[5] != 't') 66 | || (whdr->bptr[2] != 'a') || (whdr->bptr[6] != 'y') 67 | || (whdr->bptr[3] != 'c') || (whdr->bptr[7] != ':')) 68 | return; 69 | 70 | while (!0) { 71 | for (; iter > 1; iter -= 2) 72 | RMS_BGRA(1); 73 | if (!iter && bptr[0]) { 74 | iter = *bptr++; 75 | continue; 76 | } 77 | else if (iter && bptr[1]) { 78 | iter = bptr[1] - 1; 79 | RMS_BGRA(2); 80 | continue; 81 | } 82 | break; 83 | } 84 | #undef RMS_BGRA 85 | } 86 | 87 | 88 | 89 | void FreeAnimStd(ASTD **anim) { 90 | if (anim && *anim) { 91 | free((*anim)->bptr); 92 | free((*anim)->bpal); 93 | free((*anim)->time); 94 | free(*anim); 95 | *anim = 0; 96 | } 97 | } 98 | 99 | 100 | 101 | ASTD *MakeAnimStd(char *data, long size) { 102 | ASTD *retn; 103 | 104 | if (!GIF_Load((void*)data, size, WriteFrameStd, ReadMetadataStd, 105 | (void*)(retn = calloc(1, sizeof(*retn))), 0)) 106 | FreeAnimStd(&retn); 107 | return retn; 108 | } 109 | -------------------------------------------------------------------------------- /core/gif/gifstd.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | 6 | /** main pixel format for palettes and output **/ 7 | #pragma pack(push, 1) 8 | typedef union _BGRA { 9 | uint8_t chnl[4]; 10 | uint32_t bgra; 11 | } BGRA; 12 | #pragma pack(pop) 13 | 14 | /** "standard" GIF animation type **/ 15 | typedef struct _ASTD { 16 | uint8_t *bptr; /** index data storage **/ 17 | uint32_t xdim, /** frame width **/ 18 | ydim, /** frame height **/ 19 | fcnt, /** frame count **/ 20 | *time; /** frame delays **/ 21 | BGRA *bpal; /** palette **/ 22 | } ASTD; 23 | 24 | 25 | 26 | ASTD *MakeAnimStd(char *data, long size); 27 | void FreeAnimStd(ASTD **anim); 28 | -------------------------------------------------------------------------------- /core/ogl/oglstd.c: -------------------------------------------------------------------------------- 1 | #include "load/ogl_load.h" 2 | #include "oglstd.h" 3 | 4 | struct RNDR { 5 | OGL_FVBO *surf; 6 | T4FV *temp; 7 | T4FV disz; 8 | T4FV hitd; 9 | T4FV idep; 10 | long size, mult; 11 | }; 12 | 13 | /// renderbuffer-based framebuffer object, opaque outside the module 14 | struct FRBO { 15 | GLuint fbuf, /// framebuffer 16 | rbuf[2], /// renderbuffers for pixel and depth data 17 | pbuf[2]; /// pixel-transfer buffer array 18 | GLint xdim, /// width 19 | ydim, /// height 20 | swiz; /// pixel buffer switcher 21 | }; 22 | 23 | typedef struct { 24 | GLint size, fcnt, indx; 25 | GLubyte *bptr; 26 | } TXSZ; 27 | 28 | 29 | 30 | int sizecmp(const void *a, const void *b) { 31 | return ((TXSZ*)b)->size - ((TXSZ*)a)->size; 32 | } 33 | 34 | 35 | 36 | void FreeRendererOGL(RNDR **rndr) { 37 | if (rndr && *rndr) { 38 | OGL_FreeVBO(&(*rndr)->surf); 39 | free((*rndr)->temp); 40 | free(*rndr); 41 | *rndr = 0; 42 | } 43 | } 44 | 45 | 46 | 47 | long MakeRendererOGL(RNDR **rndr, ulong rgba, UNIT *uarr, 48 | ulong uniq, ulong size, ulong xscr, ulong yscr) { 49 | /** [main vertex shader] (FB = frame bank, CA = current animation) 50 | 51 | ===== dynamic uniforms: changes every frame 52 | T4FV : x = X position of the sprite 53 | y = Y position of the sprite 54 | z = current sprite frame 55 | w = [base index (22)] [Y inv (1)] [X inv (1)] 56 | 57 | ===== vertex attributes (marked {}) and static uniforms: no or few changes 58 | T1FV {vert}: x = current quad index, 1-based; sign: X coord, +0.5: Y coord 59 | T4FV : x = frame X scale 60 | y = frame Y scale 61 | z = frame width 62 | w = frame height 63 | T4FV : x = FB index 64 | y = CA`s first frame offset in FB 65 | z = end of CA block in FB 66 | w = end of FB as if it all consisted of CA frames 67 | T4FV disz : x = 2.0 / screen width 68 | y = 2.0 / screen height 69 | z = 1.0 / depth (see pixel shader specs) 70 | w = 0.0 ##### RESERVED, DO NOT CHANGE ##### 71 | T4FV idep : x = 1.0 / and height 72 | y = 1.0 / height 73 | z = 1.0 / max texture size 74 | w = 1.0 ##### RESERVED, DO NOT CHANGE ##### 75 | 76 | ===== parameters for pixel shader: "=" means static, "~" means varying 77 | T4FV vtex : x ~ current pixel U-pos (needs to be truncated) 78 | y ~ current pixel V-pos (needs to be truncated) 79 | z = frame width in pixels 80 | w = 0.0 ##### RESERVED, DO NOT CHANGE ##### 81 | T4FV voff : x = current frame offset in FB 82 | y = FB index 83 | z = ??? ##### UNUSED ##### 84 | w = ??? ##### UNUSED ##### 85 | T4FV vdep : x = CA palette W-pos in texture, premult ##### UNUSED ##### 86 | y = CA palette W-pos in texture, premult 87 | z = CA palette U-pos in texture + 0.5 88 | w = CA palette V-pos in texture + 0.5 89 | **/ 90 | char *MainVertexShader = 91 | "attribute float vert;" 92 | 93 | "uniform sampler2D data;" 94 | "uniform sampler2D dims;" 95 | "uniform sampler2D bank;" 96 | "uniform vec4 disz;" 97 | "uniform vec4 idep;" 98 | 99 | "varying vec4 vtex;" 100 | "varying vec4 voff;" 101 | "varying vec4 vdep;" 102 | 103 | "const mat4 coef = mat4(-1.0, 1.0, 0.5, 0.0," 104 | "-2.0, 4.0, 1.0, 0.0," 105 | "-0.125, 0.25, 0.5, 0.0," 106 | " 256.0, 1.0, 0.0, 0.0);" 107 | "void main() {" 108 | "vec4 vvec = vec4(abs(vert), vert, coef[0].ww);" 109 | "vec4 vdat = texture2D(data, idep.zy" 110 | "* (floor(vvec.xx * idep.wz - idep.wz) + coef[0].zz));" 111 | "vec4 indx = vdat.wwww * coef[0].wwzw + idep.wzww * coef[1].zzxw" 112 | "* floor(vdat.wwww * coef[2].yyyy + coef[0].xxww);" 113 | "vec4 iint = floor(indx.xyzy * coef[3].yyyx);" 114 | "vec4 offs = (iint.xyww * coef[0].yyww + coef[0].zzww) * idep.zxww" 115 | "+ step(coef[2].yyyy, vvec.zzyx - floor(vvec.zzzx));" 116 | "vec4 vdim = texture2D(dims, offs.xy);" 117 | "vec4 vbnk = texture2D(bank, offs.xy);" 118 | "vtex = coef[1].yxxw * vdim.zwzw" 119 | "* ((iint.zzzz * coef[0].yxww + indx.zwww - coef[2].yzww)" 120 | "* (coef[2].zzww - offs.zwzz) - coef[2].xyzw);" 121 | "vdim = vdim.xyzw * vdim.zwww * coef[0].yyyw;" 122 | "indx = vdat.zzzz * vdim.zwww + vbnk.yxww;" 123 | "voff = (indx.x < vbnk.z)? indx.xyzw : (indx.xyzw" 124 | "+ floor((indx.xxxx - vbnk.zzzz) / vbnk.wwww)" 125 | "* (vbnk.wwww * coef[0].xwww + coef[0].wyww)" 126 | "+ (vbnk.zzzz * coef[0].xwww + coef[0].wyww));" 127 | "vdep = iint.wwxw * coef[3].wwxy + coef[0].wwzz" 128 | "+ disz.zzww * (floor(indx.wwww * idep.zzzz) + coef[0].zzzz);" 129 | "gl_Position = (offs.zwzz * vdim.xyww - coef[0].xyzw * vdat.xyyy)" 130 | "* disz.xyyy + coef[0].xyyy;" 131 | "}"; 132 | 133 | /** [main pixel shader] (FB = frame bank, CA = current animation) 134 | 135 | ===== basic concepts and theory 136 | _________ __________________ This is a frame bank array on 137 | .| . | |_#K_| | the left & a single FB on the 138 | . | . | | animation #(K+1) | right. Each FB is composed of 139 | .____|___. | | _____| animations, which in turn are 140 | _|_______ | n| |____________| | made of consecutive frames in 141 | _|_______ | |_____| | animation #(K+2) | linear form, unlike textures. 142 | | | | | . | __| Some of the animations do not 143 | | | |2| . |_______________| | fit in a single FB, occupying 144 | | |1|_|------->| animation #(K+3) | space in several FBs. In this 145 | | 0|_| | ___________| case, they are split frame by 146 | |_________| |______|///EMPTY///| frame in several parts. As an 147 | illustration, animation #K is 148 | only partially contained in the second FB: say, one or two last frames. FB 149 | dimensions are chosen to match GPU capabilities, but they cannot be larger 150 | than 4096*4096. This restriction comes from many factors like inability of 151 | GLfloat to store consecutive integers larger than 2^24, or excess unneeded 152 | space consumption when only a part of the last FB is actually used. 153 | 154 | ===== vertex attributes (marked {}) and static uniforms: no or few changes 155 | T1FV : FB array, each layer is an FB with palette indices; see above 156 | T4FV : palette texture 157 | T4FV hitd : x = 1.0 / depth 158 | y = 1.0 / height 159 | z = 1.0 / max texture size 160 | w = 1.0 ##### RESERVED, DO NOT CHANGE ##### 161 | **/ 162 | char *MainPixelShader = 163 | "uniform sampler3D atex;" 164 | "uniform sampler3D apal;" 165 | "uniform vec4 hitd;" 166 | 167 | "varying vec4 vtex;" 168 | "varying vec4 voff;" 169 | "varying vec4 vdep;" 170 | 171 | "const vec4 coef = vec4(1.0, 0.5, 255.0, 0.0);" 172 | 173 | "void main() {" 174 | "vec4 pixl = floor(vtex);" 175 | "pixl.xyz = hitd.wzw * (pixl.zzw * pixl.yyw + pixl.xxw + voff.xxy);" 176 | "pixl = texture3D(atex, hitd.zzx * (floor(pixl.xyz) + coef.yyy));" 177 | "if (pixl.x != coef.x)" 178 | "gl_FragColor = texture3D(apal, hitd.zyw *" 179 | "(pixl.xxx * coef.zww + vdep.zwy));" 180 | "else discard;" 181 | "}"; 182 | 183 | GLsizei cbnk, fill, curr, mtex, chei, dhei, phei, pdep, fcnt, fend; 184 | GLubyte *atex, *aptr; 185 | GLfloat *vert; 186 | GLuint *indx = 0; 187 | T4FV *dims, *bank; 188 | TXSZ *txsz; 189 | BGRA *apal; 190 | RNDR *retn; 191 | OGL_FTEX *test; 192 | 193 | if (!rndr || *rndr) 194 | return !!rndr; 195 | glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mtex); 196 | /// 4096 proved to be too unreliable, 4096^2 bordering 197 | /// on IEEE-754 bit capacity, so let`s use 2048 instead 198 | mtex = ((mtex < 2048)? mtex : 2048) << 1; 199 | while (mtex >>= 1) 200 | if ((test = OGL_MakeTex(mtex, mtex, 1, GL_TEXTURE_3D, 201 | GL_REPEAT, GL_NEAREST, GL_NEAREST, 202 | GL_UNSIGNED_BYTE, GL_R8, GL_RED, 0))) 203 | break; 204 | OGL_FreeTex(&test); 205 | 206 | if (!mtex) 207 | return 0; 208 | 209 | glCullFace(GL_BACK); 210 | glEnable(GL_CULL_FACE); 211 | glDepthFunc(GL_LESS); 212 | glEnable(GL_DEPTH_TEST); 213 | glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); 214 | glEnable(GL_BLEND); 215 | glViewport(0, 0, xscr, yscr); 216 | 217 | dhei = ceil((GLfloat)size / mtex) + 1; 218 | chei = ceil((GLfloat)uniq / mtex); 219 | phei = ceil((256.0 * uniq) / mtex); 220 | phei = ((pdep = ceil((GLfloat)phei / mtex)) <= 1)? phei : mtex; 221 | 222 | retn = calloc(1, sizeof(*retn)); 223 | retn->disz = (T4FV){{2.0 / xscr, 2.0 / yscr, 1.0 / pdep, 0.0}}; 224 | retn->idep = (T4FV){{1.0 / chei, 1.0 / dhei, 1.0 / mtex, 1.0}}; 225 | 226 | /// allocate vertex arrays 227 | // indx = calloc(mtex * dhei * 6, sizeof(*indx)); /// GL_QUADS if commented 228 | vert = calloc(mtex * dhei * 4, sizeof(*vert)); 229 | 230 | /// 4 * 4 = 16 MB per 2^20 sprites 231 | for (curr = mtex * dhei - 1; curr >= 0; curr--) { 232 | vert[curr * 4 + 0] = -1.5 - curr; 233 | vert[curr * 4 + 1] = -1.0 - curr; 234 | vert[curr * 4 + 2] = +1.0 + curr; 235 | vert[curr * 4 + 3] = +1.5 + curr; 236 | } 237 | if (indx) /// 6 * 4 = 24 MB per 2^20 sprites 238 | for (curr = mtex * dhei - 1; curr >= 0; curr--) { 239 | indx[curr * 6 + 0] = curr * 4 + 0; 240 | indx[curr * 6 + 1] = curr * 4 + 1; 241 | indx[curr * 6 + 2] = curr * 4 + 2; 242 | indx[curr * 6 + 3] = curr * 4 + 0; 243 | indx[curr * 6 + 4] = curr * 4 + 2; 244 | indx[curr * 6 + 5] = curr * 4 + 3; 245 | } 246 | retn->mult = (indx)? 6 : 4; 247 | fill = 6 * sizeof(*indx) * !!indx; 248 | curr = 4 * sizeof(*vert); 249 | 250 | OGL_UNIF satr[] = 251 | {{.cdat = mtex * dhei * fill, .draw = GL_STATIC_DRAW, 252 | /** No name and type for indices! **/ .pdat = indx}, 253 | {.cdat = mtex * dhei * curr, .draw = GL_STATIC_DRAW, 254 | .name = "vert", .type = OGL_UNI_T1FV, .pdat = vert}}; 255 | OGL_UNIF suni[] = 256 | {{.name = "data", .type = OGL_UNI_T1II, .pdat = (GLvoid*)0}, 257 | {.name = "dims", .type = OGL_UNI_T1II, .pdat = (GLvoid*)1}, 258 | {.name = "bank", .type = OGL_UNI_T1II, .pdat = (GLvoid*)2}, 259 | {.name = "apal", .type = OGL_UNI_T1II, .pdat = (GLvoid*)3}, 260 | {.name = "atex", .type = OGL_UNI_T1II, .pdat = (GLvoid*)4}, 261 | {.name = "disz", .type = OGL_UNI_T4FV, .pdat = &retn->disz}, 262 | {.name = "hitd", .type = OGL_UNI_T4FV, .pdat = &retn->hitd}, 263 | {.name = "idep", .type = OGL_UNI_T4FV, .pdat = &retn->idep}}; 264 | 265 | retn->surf = OGL_MakeVBO(5, (indx)? GL_TRIANGLES : GL_QUADS, 266 | sizeof(satr) / sizeof(*satr), satr, 267 | sizeof(suni) / sizeof(*suni), suni, 268 | 2, (char*[]){MainVertexShader, MainPixelShader}); 269 | free(vert); 270 | free(indx); 271 | 272 | /// allocate the sprite data texture (uninitialized) 273 | *OGL_BindTex(retn->surf, 0, OGL_TEX_NSET) = 274 | OGL_MakeTex(mtex, dhei, 0, GL_TEXTURE_2D, 275 | GL_REPEAT, GL_NEAREST, GL_NEAREST, 276 | GL_FLOAT, GL_RGBA32F, GL_RGBA, 0); 277 | 278 | txsz = calloc(uniq, sizeof(*txsz)); 279 | dims = calloc(mtex * chei, sizeof(*dims)); 280 | apal = calloc(mtex * phei * pdep, sizeof(*apal)); 281 | for (curr = 1; curr <= uniq; curr++) 282 | if (uarr[curr].anim) { 283 | ASTD *anim = uarr[curr].anim; 284 | txsz[curr - 1] = 285 | (TXSZ){anim->xdim * anim->ydim, anim->fcnt, curr, anim->bptr}; 286 | dims[curr - 1] = 287 | (T4FV){{1 << uarr[curr].scal, 1 << uarr[curr].scal, 288 | anim->xdim, anim->ydim}}; 289 | memcpy(&apal[(curr - 1) << 8], anim->bpal, 256 * sizeof(*apal)); 290 | } 291 | 292 | /// allocate the palette texture 293 | *OGL_BindTex(retn->surf, 3, OGL_TEX_NSET) = 294 | OGL_MakeTex(mtex, phei, pdep, GL_TEXTURE_3D, 295 | GL_REPEAT, GL_NEAREST, GL_NEAREST, GL_UNSIGNED_BYTE, 296 | GL_RGBA8, (rgba)? GL_RGBA : GL_BGRA, apal); 297 | free(apal); 298 | 299 | /// allocate the sprite dimension texture 300 | *OGL_BindTex(retn->surf, 1, OGL_TEX_NSET) = 301 | OGL_MakeTex(mtex, chei, 0, GL_TEXTURE_2D, 302 | GL_REPEAT, GL_NEAREST, GL_NEAREST, 303 | GL_FLOAT, GL_RGBA32F, GL_RGBA, dims); 304 | free(dims); 305 | 306 | qsort(txsz, uniq, sizeof(*txsz), sizecmp); 307 | for (cbnk = fill = curr = 0; curr < uniq; curr++) { 308 | fcnt = txsz[curr].fcnt; 309 | while (fcnt) { 310 | if (fill + txsz[curr].size > mtex * mtex) { 311 | fill = 0; 312 | cbnk++; 313 | } 314 | fend = (mtex * mtex - fill) / txsz[curr].size; 315 | fend = (fcnt < fend)? fcnt : fend; 316 | fill += txsz[curr].size * fend; 317 | fcnt -= fend; 318 | } 319 | } 320 | 321 | /// allocate the main FB array texture (uninitialized) 322 | test = *OGL_BindTex(retn->surf, 4, OGL_TEX_NSET) = 323 | OGL_MakeTex(mtex, mtex, cbnk + 1, GL_TEXTURE_3D, 324 | GL_REPEAT, GL_NEAREST, GL_NEAREST, 325 | GL_UNSIGNED_BYTE, GL_R8, GL_RED, 0); 326 | 327 | bank = calloc(mtex * chei, sizeof(*bank)); 328 | atex = aptr = calloc(fill = mtex * mtex, sizeof(*atex)); 329 | for (cbnk = fcnt = curr = 0; curr < uniq; fcnt = 0, curr++) 330 | while (txsz[curr].fcnt) { 331 | if (aptr - atex + txsz[curr].size > fill) { 332 | OGL_LoadTex(test, 0, 0, cbnk, mtex, mtex, 1, atex); 333 | aptr = atex; 334 | cbnk++; 335 | } 336 | /// final frame that may be safely allocated in the current FB 337 | fend = (atex + fill - aptr) / txsz[curr].size; 338 | fend = (txsz[curr].fcnt < fend)? txsz[curr].fcnt : fend; 339 | if (!fcnt) { 340 | /// this is the first time we allocate frames for the CA, 341 | /// so let`s add its header to the frame header bank 342 | bank[txsz[curr].indx - 1].x = cbnk; 343 | bank[txsz[curr].indx - 1].y = aptr - atex; 344 | bank[txsz[curr].indx - 1].z = 345 | bank[txsz[curr].indx - 1].y + fend * txsz[curr].size; 346 | bank[txsz[curr].indx - 1].w = fill - (fill % txsz[curr].size); 347 | fcnt = ~0; 348 | } 349 | memcpy(aptr, txsz[curr].bptr, txsz[curr].size * fend); 350 | txsz[curr].bptr += txsz[curr].size * fend; 351 | aptr += txsz[curr].size * fend; 352 | txsz[curr].fcnt -= fend; 353 | } 354 | free(txsz); 355 | if (aptr > atex) 356 | OGL_LoadTex(test, 0, 0, cbnk, mtex, mtex, 1, atex); 357 | free(atex); 358 | 359 | /// allocate the FB control texture 360 | *OGL_BindTex(retn->surf, 2, OGL_TEX_NSET) = 361 | OGL_MakeTex(mtex, chei, 0, GL_TEXTURE_2D, 362 | GL_REPEAT, GL_NEAREST, GL_NEAREST, 363 | GL_FLOAT, GL_RGBA32F, GL_RGBA, bank); 364 | free(bank); 365 | 366 | retn->hitd = (T4FV){{1.0 / (cbnk + 1), 1.0 / phei, retn->idep.z, 1.0}}; 367 | *rndr = retn; 368 | return ~0; 369 | } 370 | 371 | 372 | 373 | void DrawRendererOGL(RNDR *rndr, UNIT *uarr, T4FV *data, 374 | ulong size, ulong opaq) { 375 | long iter, indx; 376 | GLuint xdim, ydim; 377 | OGL_FTEX *surf; 378 | T4FV *pfwd, *pbwd; 379 | UNIT *retn; 380 | 381 | xdim = 0; 382 | surf = *OGL_BindTex(rndr->surf, 0, OGL_TEX_NSET); 383 | OGL_EnumTex(surf, 0, 0, 0, &xdim, 0, 0); 384 | if (!xdim || !size) 385 | return; 386 | ydim = ceil((GLfloat)size / (GLfloat)xdim); 387 | if (rndr->size != (iter = xdim * ydim)) { 388 | free(rndr->temp); 389 | rndr->size = iter; 390 | rndr->temp = malloc(rndr->size * sizeof(*rndr->temp)); 391 | } 392 | pbwd = (pfwd = rndr->temp) + size; 393 | for (iter = 0; iter < size; iter++) { 394 | retn = &uarr[(indx = (long)data[iter].w) >> 2]; 395 | /// opaque sprites go to the beginning, translucent ones to the end, 396 | /// applying offsets if a sprite is larger than its actual content 397 | /// [TODO:] somehow overcome the depth priority bug when OFFS.y > 0 398 | *((retn->tran)? --pbwd : pfwd++) = 399 | (T4FV){{data[iter].x + retn->offs[indx & 1], 400 | data[iter].y - retn->offs[3 - ((indx >> 1) & 1)], 401 | data[iter].z, data[iter].w}}; 402 | } 403 | glClearColor(0.0, 0.0, 0.0, (opaq)? 1.0 : 0.0); 404 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 405 | OGL_LoadTex(surf, 0, 0, 0, xdim, ydim, 0, rndr->temp); 406 | OGL_DrawVBO(rndr->surf, 0, size * rndr->mult); 407 | } 408 | 409 | 410 | 411 | FRBO *MakeRBO(long xdim, long ydim) { 412 | FRBO *retn = calloc(1, sizeof(*retn)); 413 | GLint data; 414 | 415 | retn->xdim = xdim; 416 | retn->ydim = ydim; 417 | retn->swiz = 0; 418 | 419 | glGenFramebuffers(1, &retn->fbuf); 420 | glBindFramebuffer(GL_FRAMEBUFFER, retn->fbuf); 421 | 422 | glGenRenderbuffers(2, retn->rbuf); 423 | glBindRenderbuffer(GL_RENDERBUFFER, retn->rbuf[0]); 424 | glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA, retn->xdim, retn->ydim); 425 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 426 | GL_RENDERBUFFER, retn->rbuf[0]); 427 | glBindRenderbuffer(GL_RENDERBUFFER, retn->rbuf[1]); 428 | glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, 429 | retn->xdim, retn->ydim); 430 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, 431 | GL_RENDERBUFFER, retn->rbuf[1]); 432 | data = retn->xdim * retn->ydim * 4; 433 | 434 | glGenBuffers(2, retn->pbuf); 435 | glBindBuffer(GL_PIXEL_PACK_BUFFER, retn->pbuf[0]); 436 | glBufferData(GL_PIXEL_PACK_BUFFER, data, 0, GL_STREAM_READ); 437 | glBindBuffer(GL_PIXEL_PACK_BUFFER, retn->pbuf[1]); 438 | glBufferData(GL_PIXEL_PACK_BUFFER, data, 0, GL_STREAM_READ); 439 | glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); 440 | glBindFramebuffer(GL_FRAMEBUFFER, 0); 441 | 442 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, retn->fbuf); 443 | glViewport(0, 0, retn->xdim, retn->ydim); 444 | glBindFramebuffer(GL_FRAMEBUFFER, 0); 445 | return retn; 446 | } 447 | 448 | 449 | 450 | void BindRBO(FRBO *robj, long bind) { 451 | GLuint buff = (bind)? robj->fbuf : 0; 452 | 453 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, buff); 454 | } 455 | 456 | 457 | 458 | void ReadRBO(FRBO *robj, void *pict, ulong flgs) { 459 | GLvoid *bptr; 460 | 461 | if (flgs & WIN_IPBO) 462 | glBindBuffer(GL_PIXEL_PACK_BUFFER, robj->pbuf[robj->swiz]); 463 | glBindFramebuffer(GL_READ_FRAMEBUFFER, robj->fbuf); 464 | glReadPixels(0, 0, robj->xdim, robj->ydim, 465 | (flgs & WIN_IBGR)? GL_BGRA : GL_RGBA, 466 | GL_UNSIGNED_BYTE, (flgs & WIN_IPBO)? 0 : pict); 467 | glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); 468 | 469 | if (flgs & WIN_IPBO) { 470 | robj->swiz ^= 1; 471 | glBindBuffer(GL_PIXEL_PACK_BUFFER, robj->pbuf[robj->swiz]); 472 | bptr = glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY); 473 | if (bptr) { 474 | memcpy(pict, bptr, robj->xdim * robj->ydim * sizeof(BGRA)); 475 | glUnmapBuffer(GL_PIXEL_PACK_BUFFER); 476 | } 477 | glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); 478 | } 479 | } 480 | 481 | 482 | 483 | void FreeRBO(FRBO **robj) { 484 | if (robj && *robj) { 485 | glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); 486 | glBindFramebuffer(GL_FRAMEBUFFER, 0); 487 | glDeleteRenderbuffers(2, (*robj)->rbuf); 488 | glDeleteFramebuffers(1, &(*robj)->fbuf); 489 | glDeleteBuffers(2, (*robj)->pbuf); 490 | free(*robj); 491 | *robj = 0; 492 | } 493 | } 494 | -------------------------------------------------------------------------------- /core/ogl/oglstd.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /// renderer data, defined externally 4 | typedef struct RNDR RNDR; 5 | 6 | /// renderbuffer-based framebuffer object, defined externally 7 | typedef struct FRBO FRBO; 8 | 9 | 10 | 11 | long MakeRendererOGL(RNDR **rndr, ulong rgba, UNIT *uarr, 12 | ulong uniq, ulong size, ulong xscr, ulong yscr); 13 | void DrawRendererOGL(RNDR *rndr, UNIT *uarr, T4FV *data, 14 | ulong size, ulong opaq); 15 | void FreeRendererOGL(RNDR **rndr); 16 | 17 | FRBO *MakeRBO(long xdim, long ydim); 18 | void BindRBO(FRBO *robj, long bind); 19 | void ReadRBO(FRBO *robj, void *pict, ulong flgs); 20 | void FreeRBO(FRBO **robj); 21 | -------------------------------------------------------------------------------- /exec/exec.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | 13 | 14 | /** default config directory **/ #define DEF_OPTS "/DesktopPonies" 15 | /** default core config **/ #define DEF_CORE "/core.conf" 16 | /** default anim directory **/ #define DEF_FLDR "Content" 17 | /** default config file **/ #define DEF_CONF "pony.ini" 18 | 19 | /// /// /// /// /// /// /// /// /// menu constants 20 | /** item is disabled **/ #define MFL_GRAY (1 << 0) 21 | /** current checkbox value **/ #define MFL_VCHK (1 << 1) 22 | /** item has a checkbox **/ #define MFL_CCHK (1 << 2) 23 | /** checkbox is a radiobutton **/ #define MFL_RCHK ((1 << 3) | MFL_CCHK) 24 | 25 | /// /// /// /// /// /// /// /// /// Flags for Controls` Types 26 | /** Container window **/ #define FCT_WNDW 0x000 27 | /** Simple label **/ #define FCT_TEXT 0x001 28 | /** Button (may be checkable) **/ #define FCT_BUTN 0x002 29 | /** Check box **/ #define FCT_CBOX 0x003 30 | /** Spin counter **/ #define FCT_SPIN 0x004 31 | /** List box (with checkboxes) **/ #define FCT_LIST 0x005 32 | /** Progress bar **/ #define FCT_PBAR 0x006 33 | /** Scroll box **/ #define FCT_SBOX 0x007 34 | /** Image box **/ #define FCT_IBOX 0x008 35 | /** [extractor] **/ #define FCT_TTTT 0x00F 36 | 37 | /// /// /// /// /// /// /// /// /// Flags for Controls` Position inheriting 38 | /** Horizontal inheriting **/ #define FCP_HORZ 0x010 39 | /** Vertical inheriting **/ #define FCP_VERT 0x020 40 | /** Both **/ #define FCP_BOTH (FCP_HORZ | FCP_VERT) 41 | 42 | /// /// /// /// /// /// /// /// /// Flags for Controls` State 43 | /** Control is enabled **/ #define FCS_ENBL 0x01 44 | /** Control is marked **/ #define FCS_MARK 0x02 45 | 46 | /// /// /// /// /// /// /// /// /// Flags for Style of W (FCT_WNDW) 47 | /** Resize/minimize/restore on **/ #define FSW_SIZE 0x040 48 | 49 | /// /// /// /// /// /// /// /// /// Flags for Style of T (FCT_TEXT) 50 | /** Sunken edge **/ #define FST_SUNK 0x040 51 | /** Center text **/ #define FST_CNTR 0x080 52 | 53 | /// /// /// /// /// /// /// /// /// Flags for Style of B (FCT_BUTN) 54 | /** Default button of a window **/ #define FSB_DFLT 0x040 55 | 56 | /// /// /// /// /// /// /// /// /// Flags for Style of X (FCT_CBOX) 57 | /** Checkbox text on the left **/ #define FSX_LEFT 0x040 58 | 59 | /// /// /// /// /// /// /// /// /// Flags for Style of N (FCT_SPIN) 60 | 61 | /// /// /// /// /// /// /// /// /// Flags for Style of L (FCT_LIST) 62 | 63 | /// /// /// /// /// /// /// /// /// Flags for Style of P (FCT_PBAR) 64 | 65 | /// /// /// /// /// /// /// /// /// Flags for Style of S (FCT_SBOX) 66 | 67 | /// /// /// /// /// /// /// /// /// controls` messages 68 | enum { 69 | /** empty message; does nothing **/ MSG__NUL = 0, 70 | /** enable or disable anything **/ MSG__ENB, 71 | /** show or hide anything **/ MSG__SHW, 72 | /** get pixel size of anything **/ MSG__GSZ, 73 | /** set position of anything **/ MSG__POS, 74 | /** set title text of anything **/ MSG__TXT, 75 | /** close the container window **/ MSG_WEND, 76 | /** resize & center wnd/sizebox **/ MSG_WSZC, 77 | /** get text dimensions **/ MSG_WTGD, 78 | /** draw text to existing AINF **/ MSG_WTDA, 79 | /** click button or checkbox **/ MSG_BCLK, 80 | /** get button/checkbox state **/ MSG_BGST, 81 | /** get spin control position **/ MSG_NGET, 82 | /** set spin control position **/ MSG_NSET, 83 | /** set spin control limits **/ MSG_NDIM, 84 | /** set progressbar upper limit **/ MSG_PLIM, 85 | /** set progressbar position **/ MSG_PPOS, 86 | /** get progressbar properties **/ MSG_PGET, 87 | /** get scrollbox total height **/ MSG_SGTH, 88 | /** set scrollbox internal dims **/ MSG_SSID, 89 | /** get scrollbox position **/ MSG_SGIP, 90 | /** add item to listbox **/ MSG_LADD, 91 | /** get listbox item state **/ MSG_LGST, 92 | /** set listbox item state **/ MSG_LSST, 93 | /** imagebox update frame **/ MSG_IFRM, 94 | }; 95 | 96 | /// preview updater function 97 | typedef void (*UPRE)(intptr_t data, uint64_t time); 98 | 99 | /// control handler function (either control-to-exec or exec-to-control) 100 | struct _CTRL; 101 | typedef intptr_t (*FCTL)(struct _CTRL *ctrl, uint32_t cmsg, intptr_t data); 102 | 103 | /// control (checkbox, listbox, counter, etc.) 104 | typedef struct _CTRL { 105 | struct /// __ 106 | _CTRL *prev; /// r \ parent 107 | intptr_t data; /// e | control-specific data 108 | int32_t uuid, /// a | unique identifier 109 | flgs; /// d | type (lowest nibble) and style flags 110 | long xpos, /// o | X position 111 | ypos, /// n | Y position 112 | xdim, /// l | width (absolute if negative) 113 | ydim; /// y | height (absolute if negative) 114 | FCTL fc2e, /// __/ control-to-exec handler (actions) 115 | fe2c; /// exec-to-control handler (properties) 116 | intptr_t priv[8]; /// implementation-defined private data 117 | } CTRL; 118 | 119 | /// menu item 120 | typedef struct _MENU { 121 | struct 122 | _MENU *chld; /// submenu (or 0 if none) 123 | char *text; /// item text 124 | intptr_t data; /// item data 125 | uint32_t flgs, /// item flags 126 | uuid; /// item ID 127 | void (*func)(struct _MENU*); /// item handler 128 | } MENU; 129 | 130 | 131 | 132 | void eProcessMenuItem(MENU *item); 133 | void eExecuteEngine(char *fcnf, char *base, ulong xico, ulong yico, 134 | long xpos, long ypos, ulong xdim, ulong ydim); 135 | 136 | 137 | 138 | /// external functions, have to be implemented or imported 139 | intptr_t rMakeParallel(UPRE func, long size); 140 | intptr_t rFindMake(char *base); 141 | intptr_t rMakeHTTPS(char *user, char *serv); 142 | intptr_t rMakeTrayIcon(MENU *mctx, char *text, 143 | uint32_t *data, long xdim, long ydim); 144 | long rLoadHTTPS(intptr_t user, char *page, char **dest, 145 | void (*load)(long, intptr_t), intptr_t lprm); 146 | long rMessage(char *text, char *head, char *byes, char *bnay); 147 | long rSaveFile(char *name, char *data, long size); 148 | long rMoveDir(char *dsrc, char *ddst); 149 | long rMakeDir(char *name, long dupl); 150 | void rInternalMainLoop(CTRL *root, uint32_t fram, UPRE upre, intptr_t data); 151 | void rMakeControl(CTRL *ctrl, long *xoff, long *yoff); 152 | void rFreeControl(CTRL *ctrl); 153 | void rFreeHTTPS(intptr_t user); 154 | void rFreeParallel(intptr_t user); 155 | void rLoadParallel(intptr_t user, intptr_t data); 156 | void rFreeTrayIcon(intptr_t icon); 157 | void rOpenContextMenu(MENU *menu); 158 | char *rConvertUTF8(char *utf8); 159 | char *rChooseDir(CTRL *root, char *base); 160 | char *rChooseFile(CTRL *root, char *fext, char *file); 161 | char *rFindFile(intptr_t data); 162 | char *rLoadFile(char *name, long *size); 163 | MENU *rOSSpecificMenu(void *engc); 164 | -------------------------------------------------------------------------------- /exec/icon.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hidefromkgb/DPengine/6ebd22021a4fc87152bf66edca8cf7ebb00ab9ba/exec/icon.gif -------------------------------------------------------------------------------- /exec/loc/de.lang: -------------------------------------------------------------------------------- 1 | Charakter entfernen 2 | Alle ähnlichen entfernen 3 | Pausiere 4 | Pausiere alle ähnlichen 5 | Kontrolliere: Spieler 1 6 | Kontrolliere: Spieler 2 7 | Optionen... 8 | [ Desktop Ponies Engine ] 9 | Betriebssystemabhängig Optionen 10 | Transparenz deaktivieren 11 | Animation wiedergeben 12 | Fenster anzeigen 13 | Beenden 14 | Grafikkarte benutzen 15 | [ leer ] 16 | [ Standard ] 17 | Konsole öffnen 18 | Fensterregionen aktivieren 19 | BGRA aktivieren 20 | Pixelpuffer aktivieren 21 | Nutzlos ohne Transparenz! 22 | Nutzlos ohne Grafikkarte! 23 | Konnte GPU nicht initialisieren! 24 | Die Animationen könnten veraltet sein. Das Internet nach Aktualisierungen durchsuchen? Warnung: Die Dauer ist äbhängig von der Verbindungsgeschwindigkeit. 25 | Verbindung zum Internet fehlgeschlagen! 26 | Verzeichnis konnte nicht erstellt werden! 27 | Aktualisieren 28 | Desktop Ponies 29 | Filter aktivieren 30 | Genaues Vergleichen 31 | [Mindestens ein:] 32 | [Alle:] 33 | Zufällige Auswahl: 34 | Gruppenauswahl: 35 | Hinzufügen 36 | Duplikate 37 | Ausgewählt: 38 | Geladen: 39 | Aktualisiert: 40 | Los! 41 | Akt. beim nächsten Start 42 | Immer im Vordergrund 43 | Effekte aktivieren 44 | Interaktionen aktivieren 45 | Sprache aktivieren 46 | Sprachfarben aktiviren 47 | Auf Mauszeiger reagieren 48 | Aktualisierungsrate 49 | % Vergrößerungsfaktor 50 | % Geschwindigkeit 51 | % Konversationschance 52 | pix. Zeiger ausweich Radius 53 | Auswählen... 54 | Aktualisieren 55 | Zurücksetzen 56 | GUI Sprache: Deutsch 57 | Animationsverzeichnis: 58 | Verschieben das Animationsverzeichnis 59 | Bestätige das Speichern des Verzeichnises an einem neuen Ort: 60 | [ACHTUNG:] Beim Abbruch wird das Quellverzeichnis gelöscht! 61 | Konnte das Verzeichnis nicht verschieben: 62 | OK 63 | Abbrechen 64 | -------------------------------------------------------------------------------- /exec/loc/en.lang: -------------------------------------------------------------------------------- 1 | Remove character 2 | Remove all similar 3 | Sleep / wake up 4 | Sleep / wake up all similar 5 | Take control: Player 1 6 | Take control: Player 2 7 | More options... 8 | [ Desktop Ponies Engine ] 9 | OS specific options 10 | Disable transparency 11 | Play animation 12 | Show window 13 | Exit 14 | Use GPU for drawing 15 | [ none ] 16 | [ default ] 17 | Show console 18 | Use regions 19 | Enable BGRA 20 | Enable pixel buffers 21 | Useless on full opacity! 22 | Useless without GPU! 23 | Cannot initialize GPU! 24 | The animation base may be out of date. Check the Internet for updates? Warning: the duration largely depends on connection speed! 25 | Internet connection failure! 26 | Failed to create directory 27 | Update 28 | Desktop Ponies 29 | Enable filters 30 | Exact matching 31 | [At least one:] 32 | [All at once:] 33 | Random selection: 34 | Group selection: 35 | Add 36 | Copies 37 | Selected: 38 | Loaded: 39 | Updated: 40 | GO! 41 | Update on next run 42 | Always on top 43 | Enable effects 44 | Enable interactions 45 | Enable speech 46 | Enable colored speech 47 | React to cursor hover 48 | runs between updates 49 | % base scaling factor 50 | % time dilation factor 51 | % random speech chance 52 | pix. cursor dodge radius 53 | Choose... 54 | Reload 55 | Reset 56 | GUI language: English 57 | Animation base directory: 58 | Moving the animation base 59 | Confirm saving the source directory in a new place: 60 | [ATTENTION:] On refusal, the source directory shall be deleted! 61 | Failed to move the source directory: 62 | OK 63 | Cancel 64 | -------------------------------------------------------------------------------- /exec/loc/ru.lang: -------------------------------------------------------------------------------- 1 | Удалить персонажа 2 | Удалить всех таких же 3 | Заснуть / проснуться 4 | Заснуть / проснуться всем таким же 5 | Отдать управление: Игрок 1 6 | Отдать управление: Игрок 2 7 | Больше настроек... 8 | [ Движок для цветных коней ] 9 | ОС-зависимые настройки 10 | Отключить прозрачность 11 | Воспроизводить анимацию 12 | Показывать окно 13 | Выход 14 | Использовать видеокарту 15 | [ пусто ] 16 | [ по умолчанию ] 17 | Отображать терминал 18 | Вкл. оконные регионы 19 | Вкл. преобразование BGRA 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 | -------------------------------------------------------------------------------- /linux/DPengine.cbp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 121 | 122 | -------------------------------------------------------------------------------- /linux/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CX = gcc 3 | 4 | 5 | OBJDIR = .obj 6 | 7 | LLIB = -lXrender -lX11 -lGL -lm -lcurl -lssl -lcrypto 8 | LINC = -I/usr/include/gtkglext-1.0 -I/usr/lib/gtkglext-1.0/include 9 | LCXFLAGS = `pkg-config gtk+-2.0 gtkglext-1.0 gtkglext-x11-1.0 --libs` -pthread -fno-stack-protector -nostartfiles -Wl,--gc-sections 10 | LCFLAGS = `pkg-config gtk+-2.0 gtkglext-1.0 gtkglext-x11-1.0 --cflags` -I../core -Wall -ffunction-sections -fdata-sections 11 | 12 | LOBJ = $(OBJDIR)/core/core.o $(OBJDIR)/core/gif/gifstd.o $(OBJDIR)/core/ogl/oglstd.o $(OBJDIR)/lib.o 13 | LOUT = ../libengine.so 14 | 15 | 16 | ELIB = -lengine -lm -lcurl -lssl -lcrypto 17 | EINC = 18 | ECXFLAGS = `pkg-config gtk+-2.0 --libs` -L.. -pthread -fno-stack-protector -nostartfiles -Wl,--gc-sections 19 | ECFLAGS = `pkg-config gtk+-2.0 --cflags` -I../core -Wall -ffunction-sections -fdata-sections 20 | 21 | EOBJ = $(OBJDIR)/exec/exec.o $(OBJDIR)/run.o 22 | EOUT = ../linux-gtk 23 | 24 | 25 | RelJoined: lrelease 26 | RelJoined: erelease 27 | RelJoined: LCXFLAGS += -s -flto -Wl,--build-id=none 28 | RelJoined: jlink 29 | RelJoined: estrip 30 | 31 | DbgJoined: ldebug 32 | DbgJoined: edebug 33 | DbgJoined: jlink 34 | 35 | RelLibOnly: LCFLAGS += -fPIC 36 | RelLibOnly: lrelease 37 | RelLibOnly: LCXFLAGS += -s -Wl,--build-id=none 38 | RelLibOnly: llink 39 | RelLibOnly: lstrip 40 | 41 | DbgLibOnly: LCFLAGS += -fPIC 42 | DbgLibOnly: ldebug 43 | DbgLibOnly: llink 44 | 45 | RelSplit: RelLibOnly 46 | RelSplit: erelease 47 | RelSplit: ECXFLAGS += -s -Wl,--build-id=none 48 | RelSplit: elink 49 | RelSplit: estrip 50 | 51 | DbgSplit: DbgLibOnly 52 | DbgSplit: edebug 53 | DbgSplit: elink 54 | 55 | 56 | cleanRelJoined: clean 57 | cleanDbgJoined: clean 58 | 59 | cleanRelSplit: clean 60 | cleanDbgSplit: clean 61 | 62 | cleanRelLibOnly: clean 63 | cleanDbgLibOnly: clean 64 | 65 | 66 | lrelease: LCFLAGS += -fexpensive-optimizations -O2 -fvisibility=hidden -fno-unwind-tables -fno-asynchronous-unwind-tables 67 | lrelease: lbuild $(LOBJ) 68 | 69 | erelease: ECFLAGS += -fexpensive-optimizations -O2 -fvisibility=hidden -fno-unwind-tables -fno-asynchronous-unwind-tables 70 | erelease: ebuild $(EOBJ) 71 | 72 | ldebug: LCFLAGS += -g 73 | ldebug: lbuild $(LOBJ) 74 | 75 | edebug: ECFLAGS += -g 76 | edebug: ebuild $(EOBJ) 77 | 78 | 79 | clean: 80 | rm -f $(LOUT) $(EOUT) 81 | rm -rf $(OBJDIR) 82 | 83 | lstrip: llink 84 | strip -R .eh_frame -R .gnu.version -R .comment $(LOUT) 85 | if type upx >/dev/null 2>&1; then upx -qq $(LOUT); fi 86 | 87 | estrip: elink 88 | strip -R .eh_frame -R .gnu.version -R .comment $(EOUT) 89 | if type upx >/dev/null 2>&1; then upx -qq $(EOUT); fi 90 | 91 | lbuild: 92 | $(OBJDIR)/core/core.o: ../core/core.c 93 | mkdir -p $(OBJDIR)/core 94 | $(CC) $(LCFLAGS) $(LINC) -c ../core/core.c -o $(OBJDIR)/core/core.o 95 | $(OBJDIR)/core/gif/gifstd.o: ../core/gif/gifstd.c 96 | mkdir -p $(OBJDIR)/core/gif 97 | $(CC) $(LCFLAGS) $(LINC) -c ../core/gif/gifstd.c -o $(OBJDIR)/core/gif/gifstd.o 98 | $(OBJDIR)/core/ogl/oglstd.o: ../core/ogl/oglstd.c 99 | mkdir -p $(OBJDIR)/core/ogl 100 | $(CC) $(LCFLAGS) $(LINC) -c ../core/ogl/oglstd.c -o $(OBJDIR)/core/ogl/oglstd.o 101 | $(OBJDIR)/lib.o: lib.c 102 | mkdir -p $(OBJDIR) 103 | $(CC) $(LCFLAGS) $(LINC) -c lib.c -o $(OBJDIR)/lib.o 104 | 105 | ebuild: 106 | $(OBJDIR)/exec/exec.o: ../exec/exec.c 107 | mkdir -p $(OBJDIR)/exec 108 | $(CC) $(ECFLAGS) $(EINC) -c ../exec/exec.c -o $(OBJDIR)/exec/exec.o 109 | $(OBJDIR)/run.o: run.c 110 | mkdir -p $(OBJDIR) 111 | $(CC) $(ECFLAGS) $(EINC) -c run.c -o $(OBJDIR)/run.o 112 | 113 | jlink: ELIB = $(LLIB) 114 | jlink: EOBJ += $(LOBJ) 115 | jlink: ECXFLAGS = $(LCXFLAGS) 116 | jlink: $(LOBJ) $(EOBJ) 117 | jlink: elink $(EOBJ) 118 | 119 | llink: LCXFLAGS += -shared 120 | llink: $(LOBJ) 121 | $(CX) $(LOBJ) $(LCXFLAGS) -o $(LOUT) $(LLIB) 122 | 123 | elink: $(EOBJ) 124 | $(CX) $(EOBJ) $(ECXFLAGS) -o $(EOUT) $(ELIB) 125 | -------------------------------------------------------------------------------- /linux/lib.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include 9 | 10 | struct SEMD { 11 | pthread_mutex_t cmtx; 12 | pthread_cond_t cvar; 13 | SEM_TYPE list, full; 14 | }; 15 | 16 | 17 | 18 | GdkGLDrawable *gtk_widget_gl_begin(GtkWidget *gwnd) { 19 | GdkGLDrawable *pGLD = gtk_widget_get_gl_drawable(gwnd); 20 | if (!gdk_gl_drawable_gl_begin(pGLD, gtk_widget_get_gl_context(gwnd))) 21 | pGLD = 0; 22 | return pGLD; 23 | } 24 | 25 | 26 | 27 | gboolean FPSFunc(gpointer user) { 28 | ENGD *engd = user; 29 | intptr_t *data; 30 | char fout[64]; 31 | 32 | cOutputFPS(engd, fout); 33 | cEngineCallback(engd, ECB_GUSR, (intptr_t)&data); 34 | gtk_window_set_title(GTK_WINDOW(data[0]), fout); 35 | printf("%s\n", fout); 36 | return TRUE; 37 | } 38 | 39 | 40 | 41 | gboolean DrawFunc(gpointer user) { 42 | ENGD *engd = user; 43 | GdkGLDrawable *pGLD = 0; 44 | GdkRectangle rect = {}; 45 | GdkModifierType gmod; 46 | gint xptr, yptr; 47 | GdkRegion *creg; 48 | GdkWindow *hwnd; 49 | intptr_t *data; 50 | uint32_t attr; 51 | 52 | cEngineCallback(engd, ECB_GUSR, (intptr_t)&data); 53 | if (!(hwnd = gtk_widget_get_window(GTK_WIDGET(data[0])))) 54 | return TRUE; 55 | 56 | gdk_window_get_pointer(hwnd, &xptr, &yptr, &gmod); 57 | attr = ((gmod & GDK_BUTTON1_MASK)? UFR_LBTN : 0) 58 | | ((gmod & GDK_BUTTON2_MASK)? UFR_MBTN : 0) 59 | | ((gmod & GDK_BUTTON3_MASK)? UFR_RBTN : 0) 60 | | ((gtk_window_is_active(GTK_WINDOW(data[0])))? UFR_MOUS : 0); 61 | attr = cPrepareFrame(engd, xptr, yptr, attr); 62 | if (attr & PFR_SKIP) 63 | usleep(1000); 64 | if (attr & PFR_HALT) 65 | return TRUE; 66 | if (attr & PFR_PICK) { 67 | rect.width = (int16_t)(data[2]); 68 | rect.height = (int16_t)(data[2] >> 16); 69 | } 70 | creg = gdk_region_rectangle(&rect); 71 | gdk_window_input_shape_combine_region(hwnd, creg, 0, 0); 72 | gdk_region_destroy(creg); 73 | 74 | cEngineCallback(engd, ECB_GFLG, (intptr_t)&attr); 75 | if (attr & COM_RGPU) 76 | pGLD = gtk_widget_gl_begin(GTK_WIDGET(data[0])); 77 | else { 78 | /// comment the lines below to enable manual zeroing 79 | //* 80 | cairo_t *temp = cairo_create((cairo_surface_t*)data[1]); 81 | cairo_set_operator(temp, CAIRO_OPERATOR_SOURCE); 82 | cairo_set_source_rgba(temp, 0, 0, 0, 0); 83 | cairo_paint(temp); 84 | cairo_destroy(temp); 85 | //*/ 86 | } 87 | cOutputFrame(engd, 0); 88 | if (attr & COM_RGPU) 89 | gdk_gl_drawable_gl_end(pGLD); 90 | 91 | gdk_window_invalidate_rect(hwnd, 0, FALSE); 92 | gdk_window_process_updates(hwnd, FALSE); 93 | 94 | return TRUE; 95 | } 96 | 97 | 98 | 99 | gboolean OnDestroy(GtkWidget *gwnd, gpointer user) { 100 | cEngineCallback((ENGD*)user, ECB_QUIT, ~0); 101 | return TRUE; 102 | } 103 | 104 | 105 | 106 | gboolean OnKeyDown(GtkWidget *gwnd, GdkEventKey *ekey, gpointer user) { 107 | if (ekey->keyval == GDK_KEY_Escape) 108 | OnDestroy(gwnd, user); 109 | return TRUE; 110 | } 111 | 112 | 113 | 114 | gboolean OnChange(GtkWidget *gwnd, GdkScreen *scrn, gpointer user) { 115 | GdkColormap *cmap; 116 | 117 | if ((cmap = gdk_screen_get_rgba_colormap(gtk_widget_get_screen(gwnd)))) 118 | gtk_widget_set_colormap(gwnd, cmap); 119 | else 120 | cEngineCallback((ENGD*)user, ECB_QUIT, 0); 121 | return TRUE; 122 | } 123 | 124 | 125 | 126 | gboolean OnRedraw(GtkWidget *gwnd, GdkEventExpose *eexp, gpointer user) { 127 | ENGD *engd = user; 128 | intptr_t *data; 129 | uint32_t flgs; 130 | 131 | cEngineCallback(engd, ECB_GFLG, (intptr_t)&flgs); 132 | if (~flgs & COM_DRAW) 133 | return TRUE; 134 | 135 | if (~flgs & COM_RGPU) { 136 | cEngineCallback(engd, ECB_GUSR, (intptr_t)&data); 137 | cairo_t *temp = gdk_cairo_create(gtk_widget_get_window(gwnd)); 138 | cairo_surface_mark_dirty((cairo_surface_t*)data[1]); 139 | cairo_set_operator(temp, CAIRO_OPERATOR_SOURCE); 140 | if (flgs & COM_OPAQ) { 141 | cairo_set_source_rgba(temp, 0, 0, 0, 1); 142 | cairo_paint(temp); 143 | cairo_set_operator(temp, CAIRO_OPERATOR_OVER); 144 | } 145 | cairo_set_source_surface(temp, (cairo_surface_t*)data[1], 0, 0); 146 | cairo_paint(temp); 147 | cairo_destroy(temp); 148 | } 149 | else { 150 | GdkGLDrawable *pGLD = gtk_widget_gl_begin(gwnd); 151 | gdk_gl_drawable_swap_buffers(pGLD); 152 | gdk_gl_drawable_gl_end(pGLD); 153 | } 154 | return TRUE; 155 | } 156 | 157 | 158 | 159 | GdkGLConfig *GetGDKGL(GtkWidget *gwnd) { 160 | GdkScreen *scrn = gtk_window_get_screen(GTK_WINDOW(gwnd)); 161 | GdkGLConfig *pGGL = 0; 162 | 163 | Display *disp = GDK_SCREEN_XDISPLAY(scrn); 164 | int iter, numc, 165 | attr[] = { 166 | GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, 167 | GLX_RENDER_TYPE, GLX_RGBA_BIT, 168 | GLX_DOUBLEBUFFER, True, 169 | GLX_RED_SIZE, 1, 170 | GLX_GREEN_SIZE, 1, 171 | GLX_BLUE_SIZE, 1, 172 | GLX_ALPHA_SIZE, 1, 173 | None 174 | }; 175 | 176 | if (XRenderQueryExtension(disp, &iter, &iter)) { 177 | GLXFBConfig *pFBC = 178 | glXChooseFBConfig(disp, GDK_SCREEN_XNUMBER(scrn), attr, &numc); 179 | if (pFBC) { 180 | for (iter = 0; !pGGL && iter < numc; iter++) { 181 | XVisualInfo *pvis = glXGetVisualFromFBConfig(disp, pFBC[iter]); 182 | if (pvis) { 183 | XRenderPictFormat *pfmt = 184 | XRenderFindVisualFormat(disp, pvis->visual); 185 | if (pfmt && pfmt->direct.alphaMask > 0) { 186 | VisualID viid = pvis->visualid; 187 | pGGL = gdk_x11_gl_config_new_from_visualid(viid); 188 | } 189 | XFree(pvis); 190 | } 191 | } 192 | XFree(pFBC); 193 | } 194 | } 195 | if (!pGGL) 196 | pGGL = gdk_gl_config_new_by_mode(GDK_GL_MODE_DEPTH | GDK_GL_MODE_DOUBLE 197 | | GDK_GL_MODE_ALPHA | GDK_GL_MODE_RGBA); 198 | return pGGL; 199 | } 200 | 201 | 202 | 203 | uint64_t lTimeFunc() { 204 | struct timespec spec = {}; 205 | 206 | clock_gettime(CLOCK_REALTIME, &spec); 207 | return spec.tv_sec * 1000 + spec.tv_nsec / 1000000; 208 | } 209 | 210 | 211 | 212 | void lRestartEngine(ENGD *engd) { 213 | if (gtk_main_level()) 214 | gtk_main_quit(); 215 | } 216 | 217 | 218 | 219 | void lShowMainWindow(ENGD *engd, long show) { 220 | intptr_t *data; 221 | 222 | cEngineCallback(engd, ECB_GUSR, (intptr_t)&data); 223 | ((show)? gtk_widget_show : gtk_widget_hide)(GTK_WIDGET(data[0])); 224 | } 225 | 226 | 227 | 228 | char *lLoadFile(char *name, long *size) { 229 | char *retn = 0; 230 | long file, flen; 231 | 232 | if ((file = open(name, O_RDONLY)) > 0) { 233 | flen = lseek(file, 0, SEEK_END); 234 | lseek(file, 0, SEEK_SET); 235 | retn = malloc(flen); 236 | if (read(file, retn, flen) == flen) { 237 | if (size) 238 | *size = flen; 239 | } 240 | else { 241 | free(retn); 242 | retn = 0; 243 | } 244 | close(file); 245 | } 246 | return retn; 247 | } 248 | 249 | 250 | 251 | long lCountCPUs() { 252 | long retn = sysconf(_SC_NPROCESSORS_ONLN); 253 | 254 | retn = (retn < sizeof(SEM_TYPE) * CHAR_BIT)? 255 | retn : sizeof(SEM_TYPE) * CHAR_BIT; 256 | return (retn > 1)? retn : 1; 257 | } 258 | 259 | 260 | 261 | void lMakeThread(void *thrd) { 262 | pthread_t pthr; 263 | 264 | pthread_create(&pthr, 0, cThrdFunc, thrd); 265 | } 266 | 267 | 268 | 269 | void lFreeSemaphore(SEMD **retn, long nthr) { 270 | if (retn && *retn) { 271 | pthread_cond_destroy(&(*retn)->cvar); 272 | pthread_mutex_destroy(&(*retn)->cmtx); 273 | free(*retn); 274 | *retn = 0; 275 | } 276 | } 277 | 278 | 279 | 280 | void lMakeSemaphore(SEMD **retn, long nthr, SEM_TYPE mask) { 281 | if (retn) { 282 | *retn = malloc(sizeof(**retn)); 283 | pthread_mutex_init(&(*retn)->cmtx, 0); 284 | pthread_cond_init(&(*retn)->cvar, 0); 285 | (*retn)->full = (1 << nthr) - 1; 286 | (*retn)->list = (*retn)->full & mask; 287 | } 288 | } 289 | 290 | 291 | 292 | long lPickSemaphore(SEMD *drop, SEMD *pick, SEM_TYPE mask) { 293 | long retn; 294 | 295 | retn = (__sync_fetch_and_and(&drop->list, ~(drop->full & mask)) & mask)? 296 | TRUE : FALSE; 297 | __sync_or_and_fetch(&pick->list, pick->full & mask); 298 | 299 | pthread_mutex_lock(&pick->cmtx); 300 | pthread_cond_broadcast(&pick->cvar); 301 | pthread_mutex_unlock(&pick->cmtx); 302 | return retn; 303 | } 304 | 305 | 306 | 307 | SEM_TYPE lWaitSemaphore(SEMD *wait, SEM_TYPE mask) { 308 | pthread_mutex_lock(&wait->cmtx); 309 | if (mask) 310 | while ((wait->list ^ wait->full) & mask) 311 | pthread_cond_wait(&wait->cvar, &wait->cmtx); 312 | else 313 | while (!wait->list) 314 | pthread_cond_wait(&wait->cvar, &wait->cmtx); 315 | mask = wait->list; 316 | pthread_mutex_unlock(&wait->cmtx); 317 | return mask; 318 | } 319 | 320 | 321 | 322 | void lRunMainLoop(ENGD *engd, long xpos, long ypos, long xdim, long ydim, 323 | BGRA **bptr, intptr_t *data, uint32_t flgs) { 324 | GdkGLDrawable *pGLD = 0; 325 | GtkWidget *gwnd; 326 | guint tmrf, tmrd; 327 | 328 | xdim -= xpos; 329 | ydim -= ypos; 330 | gtk_init(0, 0); 331 | gtk_gl_init(0, 0); 332 | data[0] = (intptr_t)(gwnd = gtk_window_new(GTK_WINDOW_TOPLEVEL)); 333 | data[2] = (int16_t)xdim | (int32_t)(ydim << 16); 334 | OnChange(gwnd, 0, (gpointer)engd); 335 | 336 | g_signal_connect(G_OBJECT(gwnd), "expose-event", 337 | G_CALLBACK(OnRedraw), engd); 338 | g_signal_connect(G_OBJECT(gwnd), "delete-event", 339 | G_CALLBACK(OnDestroy), engd); 340 | g_signal_connect(G_OBJECT(gwnd), "screen-changed", 341 | G_CALLBACK(OnChange), engd); 342 | g_signal_connect(G_OBJECT(gwnd), "key-press-event", 343 | G_CALLBACK(OnKeyDown), engd); 344 | gtk_widget_set_events(gwnd, gtk_widget_get_events(gwnd) | 345 | GDK_KEY_PRESS_MASK); 346 | if (~flgs & COM_RGPU) { 347 | data[1] = (intptr_t) 348 | cairo_image_surface_create(CAIRO_FORMAT_ARGB32, xdim, ydim); 349 | *bptr = (BGRA*)cairo_image_surface_get_data((cairo_surface_t*)data[1]); 350 | } 351 | else { 352 | gtk_widget_set_gl_capability(gwnd, GetGDKGL(gwnd), 0, 353 | TRUE, GDK_GL_RGBA_TYPE); 354 | gtk_widget_realize(gwnd); 355 | } 356 | gtk_widget_set_app_paintable(gwnd, TRUE); 357 | gtk_widget_set_size_request(gwnd, xdim, ydim); 358 | gtk_window_set_type_hint(GTK_WINDOW(gwnd), GDK_WINDOW_TYPE_HINT_TOOLBAR); 359 | gtk_window_set_position(GTK_WINDOW(gwnd), GTK_WIN_POS_CENTER); 360 | gtk_window_set_skip_taskbar_hint(GTK_WINDOW(gwnd), TRUE); 361 | gtk_window_set_skip_pager_hint(GTK_WINDOW(gwnd), TRUE); 362 | gtk_window_set_resizable(GTK_WINDOW(gwnd), FALSE); 363 | gtk_window_set_decorated(GTK_WINDOW(gwnd), FALSE); 364 | gtk_window_set_keep_above(GTK_WINDOW(gwnd), TRUE); 365 | gtk_window_stick(GTK_WINDOW(gwnd)); 366 | gtk_widget_show(gwnd); 367 | gdk_window_set_cursor(gtk_widget_get_window(gwnd), 368 | gdk_cursor_new(GDK_HAND1)); 369 | lShowMainWindow(engd, flgs & COM_SHOW); 370 | 371 | tmrf = g_timeout_add(1000, FPSFunc, engd); 372 | tmrd = g_idle_add(DrawFunc, engd); 373 | 374 | gtk_main(); 375 | 376 | if (flgs & COM_RGPU) 377 | pGLD = gtk_widget_gl_begin(gwnd); 378 | cDeallocFrame(engd, 0); 379 | if (flgs & COM_RGPU) 380 | gdk_gl_drawable_gl_end(pGLD); 381 | else 382 | cairo_surface_destroy((cairo_surface_t*)data[1]); 383 | 384 | g_source_remove(tmrd); 385 | g_source_remove(tmrf); 386 | gtk_widget_destroy(gwnd); 387 | } 388 | -------------------------------------------------------------------------------- /linux/run.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "../exec/exec.h" 10 | 11 | 12 | 13 | typedef struct { 14 | long size; 15 | UPRE func; 16 | sem_t isem; 17 | } DTHR; 18 | 19 | typedef struct { 20 | struct dirent **dirs; 21 | long iter; 22 | } FIND; 23 | 24 | pthread_mutex_t *pmtx; /// the necessary evil: global mutex table for OpenSSL 25 | 26 | 27 | 28 | void GTKMenuHandler(GtkWidget *item, gpointer user) { 29 | eProcessMenuItem((MENU*)user); 30 | } 31 | 32 | 33 | 34 | void GTKMenuDestroy(GtkWidget *item, gpointer user) { 35 | GtkWidget *chld = (user)? GTK_WIDGET(user) 36 | : gtk_menu_item_get_submenu(GTK_MENU_ITEM(item)); 37 | if (chld) { 38 | gtk_container_foreach(GTK_CONTAINER(chld), GTKMenuDestroy, 0); 39 | gtk_widget_destroy(chld); 40 | } 41 | if (!user) 42 | gtk_widget_destroy(item); 43 | } 44 | 45 | GtkWidget *Submenu(MENU *menu, ulong chld) { 46 | if (!menu) 47 | return 0; 48 | 49 | GtkWidget *item; 50 | GtkMenu *retn; 51 | ulong indx; 52 | 53 | retn = GTK_MENU(gtk_menu_new()); 54 | if (!chld) 55 | g_signal_connect(G_OBJECT(retn), "selection-done", 56 | G_CALLBACK(GTKMenuDestroy), retn); 57 | indx = 0; 58 | while (menu->text) { 59 | if (!*menu->text) 60 | item = gtk_separator_menu_item_new(); 61 | else { 62 | if (menu->flgs & MFL_CCHK) { 63 | item = gtk_check_menu_item_new_with_mnemonic(menu->text); 64 | gtk_check_menu_item_set_active 65 | (GTK_CHECK_MENU_ITEM(item), menu->flgs & MFL_VCHK); 66 | gtk_check_menu_item_set_draw_as_radio 67 | (GTK_CHECK_MENU_ITEM(item), menu->flgs & MFL_RCHK 68 | & ~MFL_CCHK); 69 | } 70 | else 71 | item = gtk_menu_item_new_with_mnemonic((char*)menu->text); 72 | gtk_widget_set_sensitive(item, !(menu->flgs & MFL_GRAY)); 73 | g_signal_connect(G_OBJECT(item), "activate", 74 | G_CALLBACK(GTKMenuHandler), menu); 75 | if (menu->chld) 76 | gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), 77 | Submenu(menu->chld, ~0)); 78 | } 79 | gtk_menu_attach(retn, item, 0, 1, indx, indx + 1); 80 | indx++; 81 | menu++; 82 | } 83 | gtk_widget_show_all(GTK_WIDGET(retn)); 84 | return GTK_WIDGET(retn); 85 | } 86 | 87 | void OpenMenu(MENU *menu, guint32 time) { 88 | GtkMenu *mwnd = GTK_MENU(Submenu(menu, 0)); 89 | 90 | if (mwnd) 91 | gtk_menu_popup(mwnd, 0, 0, 0, 0, 0, time); 92 | } 93 | 94 | void MainMenu(GtkStatusIcon *icon, guint mbtn, guint32 time, gpointer user) { 95 | OpenMenu((MENU*)user, gtk_get_current_event_time()); 96 | } 97 | 98 | void rOpenContextMenu(MENU *menu) { 99 | OpenMenu(menu, GDK_CURRENT_TIME); 100 | } 101 | 102 | 103 | 104 | inline MENU *rOSSpecificMenu(void *engc) { 105 | return 0; 106 | } 107 | 108 | 109 | 110 | char *rConvertUTF8(char *utf8) { 111 | return strdup(utf8); 112 | } 113 | 114 | 115 | 116 | long rMessage(char *text, char *head, char *byes, char *bnay) { 117 | GtkWidget *hdlg; 118 | long retn; 119 | 120 | hdlg = gtk_message_dialog_new(0, GTK_DIALOG_MODAL, GTK_MESSAGE_OTHER, 121 | GTK_BUTTONS_NONE, text); 122 | gtk_dialog_add_buttons(GTK_DIALOG(hdlg), byes, 123 | (char*)GTK_RESPONSE_OK, (char*)0); 124 | if (bnay) 125 | gtk_dialog_add_buttons(GTK_DIALOG(hdlg), bnay, 126 | (char*)GTK_RESPONSE_CANCEL, (char*)0); 127 | if (head) 128 | gtk_window_set_title(GTK_WINDOW(hdlg), head); 129 | 130 | retn = gtk_dialog_run(GTK_DIALOG(hdlg)); 131 | gtk_widget_destroy(hdlg); 132 | return !!(retn == GTK_RESPONSE_OK); 133 | } 134 | 135 | 136 | 137 | intptr_t rMakeTrayIcon(MENU *mctx, char *text, 138 | uint32_t *data, long xdim, long ydim) { 139 | GtkStatusIcon *icon = 0; 140 | GdkPixbuf *pbuf; 141 | uint32_t *temp; 142 | long iter; 143 | 144 | temp = malloc(xdim * ydim * sizeof(*temp)); 145 | for (iter = xdim * ydim - 1; iter >= 0; iter--) 146 | temp[iter] = (data[iter] & 0xFF00FF00) 147 | | ((data[iter] & 0xFF) << 16) 148 | | ((data[iter] >> 16) & 0xFF); 149 | pbuf = gdk_pixbuf_new_from_data((guchar*)temp, GDK_COLORSPACE_RGB, 150 | TRUE, CHAR_BIT, xdim, ydim, 151 | xdim * sizeof(*temp), 152 | (GdkPixbufDestroyNotify)free, temp); 153 | 154 | icon = gtk_status_icon_new_from_pixbuf(pbuf); 155 | gtk_status_icon_set_visible(icon, TRUE); 156 | gtk_status_icon_set_tooltip_text(icon, (gchar*)text); 157 | g_signal_connect(G_OBJECT(icon), "popup-menu", 158 | G_CALLBACK(MainMenu), mctx); 159 | g_object_unref(G_OBJECT(pbuf)); 160 | return (intptr_t)icon; 161 | } 162 | 163 | 164 | 165 | void rFreeTrayIcon(intptr_t icon) { 166 | g_object_unref(G_OBJECT(icon)); 167 | } 168 | 169 | 170 | 171 | intptr_t rFindMake(char *base) { 172 | FIND *find; 173 | 174 | find = calloc(1, sizeof(*find)); 175 | find->iter = scandir(base, &find->dirs, 0, alphasort); 176 | find->iter = (find->iter > 0)? find->iter : 0; 177 | return (intptr_t)find; 178 | } 179 | 180 | 181 | 182 | char *rFindFile(intptr_t data) { 183 | FIND *find = (FIND*)data; 184 | char *retn = 0; 185 | 186 | if (find->iter <= 0) 187 | return 0; 188 | while ((--find->iter + 1) && ((find->dirs[find->iter]->d_type != DT_DIR) 189 | || !strcmp(find->dirs[find->iter]->d_name, "..") 190 | || !strcmp(find->dirs[find->iter]->d_name, "."))) 191 | free(find->dirs[find->iter]); 192 | if (find->iter >= 0) { 193 | retn = strdup(find->dirs[find->iter]->d_name); 194 | free(find->dirs[find->iter]); 195 | } 196 | if (find->iter <= 0) 197 | free(find->dirs); 198 | return retn; 199 | } 200 | 201 | 202 | 203 | char *rLoadFile(char *name, long *size) { 204 | char *retn = 0; 205 | long file, flen; 206 | 207 | if ((file = open(name, O_RDONLY)) > 0) { 208 | flen = lseek(file, 0, SEEK_END); 209 | lseek(file, 0, SEEK_SET); 210 | retn = malloc(flen + 1); 211 | if (read(file, retn, flen) == flen) { 212 | retn[flen] = '\0'; 213 | if (size) 214 | *size = flen; 215 | } 216 | else { 217 | free(retn); 218 | retn = 0; 219 | } 220 | close(file); 221 | } 222 | return retn; 223 | } 224 | 225 | 226 | 227 | long rSaveFile(char *name, char *data, long size) { 228 | long file; 229 | 230 | if ((file = open(name, O_CREAT | O_WRONLY, 0644)) > 0) { 231 | size = write(file, data, size); 232 | ftruncate(file, size); 233 | close(file); 234 | return size; 235 | } 236 | return 0; 237 | } 238 | 239 | 240 | 241 | long rMakeDir(char *name, long dupl) { 242 | return !mkdir(name, 0755) || (!dupl && (errno == EEXIST)); 243 | } 244 | 245 | 246 | 247 | long rMoveDir(char *dsrc, char *ddst) { 248 | __attribute__((aligned(32))) volatile long retn, iter; 249 | char *pcmd, *ptmp, *dtmp, *dddd[] = {(ddst)? ddst : dsrc, dsrc}; 250 | 251 | ptmp = pcmd = calloc(1, 128 + 4 * (strlen(dddd[0]) + strlen(dddd[1]))); 252 | memcpy(ptmp, (ddst)? "mv -f '" : "rm -r '", retn = sizeof("mv -f '") - 1); 253 | ptmp += retn; 254 | for (iter = (ddst)? 1 : 0; iter >= 0; iter--) { 255 | while ((dtmp = strchr(dddd[iter], '\''))) { 256 | strncpy(ptmp, dddd[iter], retn = dtmp - dddd[iter]); 257 | ptmp += retn; 258 | memcpy(ptmp, "'\\''", retn = sizeof("'\\''") - 1); 259 | ptmp += retn; 260 | dddd[iter] = dtmp + 1; 261 | } 262 | strncpy(ptmp, dddd[iter], retn = strlen(dddd[iter])); 263 | ptmp += retn; 264 | dtmp = (iter)? "' '" : "'"; 265 | memcpy(ptmp, dtmp, retn = strlen(dtmp)); 266 | ptmp += retn; 267 | } 268 | retn = !system(pcmd); 269 | free(pcmd); 270 | return retn; 271 | } 272 | 273 | 274 | 275 | char *ChooseFileDir(CTRL *root, char *file, char *fext) { 276 | GtkFileFilter *fltr; 277 | GtkWidget *wdlg; 278 | char *temp; 279 | 280 | while (root->prev) 281 | root = root->prev; 282 | wdlg = gtk_file_chooser_dialog_new 283 | ("", GTK_WINDOW(root->priv[0]), 284 | (fext)? GTK_FILE_CHOOSER_ACTION_OPEN 285 | : GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, 286 | GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, 287 | GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, (char*)0); 288 | if (fext) { 289 | fltr = gtk_file_filter_new(); 290 | temp = realloc(strdup("*."), sizeof("*.") + strlen(fext)); 291 | gtk_file_filter_add_pattern(fltr, strcat(temp, fext)); 292 | gtk_file_filter_set_name(fltr, temp); 293 | gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(wdlg), fltr); 294 | free(temp); 295 | } 296 | if (file) 297 | gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(wdlg), file); 298 | if (gtk_dialog_run(GTK_DIALOG(wdlg)) != GTK_RESPONSE_ACCEPT) 299 | temp = 0; 300 | else { 301 | fext = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(wdlg)); 302 | temp = strdup(fext); 303 | g_free(fext); 304 | } 305 | gtk_widget_destroy(wdlg); 306 | return temp; 307 | } 308 | 309 | char *rChooseDir(CTRL *root, char *base) { 310 | return ChooseFileDir(root, base, 0); 311 | } 312 | 313 | char *rChooseFile(CTRL *root, char *fext, char *file) { 314 | return ChooseFileDir(root, file, fext); 315 | } 316 | 317 | 318 | 319 | void *ParallelFunc(void *data) { 320 | DTHR *dthr = (DTHR*)((intptr_t*)data)[0]; 321 | intptr_t user = ((intptr_t*)data)[1]; 322 | 323 | free(data); 324 | dthr->func(user, 0); 325 | sem_post(&dthr->isem); 326 | return 0; 327 | } 328 | 329 | 330 | 331 | intptr_t rMakeParallel(UPRE func, long size) { 332 | DTHR *dthr; 333 | 334 | size *= sysconf(_SC_NPROCESSORS_ONLN); 335 | *(dthr = calloc(1, sizeof(*dthr))) = (DTHR){(size > 1)? size : 1, func}; 336 | sem_init(&dthr->isem, 0, size); 337 | return (intptr_t)dthr; 338 | } 339 | 340 | 341 | 342 | void rLoadParallel(intptr_t user, intptr_t data) { 343 | DTHR *dthr = (DTHR*)user; 344 | pthread_t pthr; 345 | intptr_t *pass; 346 | 347 | pass = calloc(2, sizeof(intptr_t)); 348 | pass[0] = user; 349 | pass[1] = data; 350 | sem_wait(&dthr->isem); 351 | pthread_create(&pthr, 0, ParallelFunc, pass); 352 | } 353 | 354 | 355 | 356 | void rFreeParallel(intptr_t user) { 357 | DTHR *dthr = (DTHR*)user; 358 | 359 | for (; dthr->size; dthr->size--) 360 | sem_wait(&dthr->isem); 361 | sem_destroy(&dthr->isem); 362 | free(dthr); 363 | } 364 | 365 | 366 | 367 | size_t WriteHTTPS(char *cptr, size_t size, size_t memb, void *user) { 368 | intptr_t *data = (intptr_t*)user; 369 | 370 | data[1] = (intptr_t)realloc((char*)data[1], 1 + data[0] + (size *= memb)); 371 | memcpy((char*)data[1] + data[0], cptr, size); 372 | ((char*)data[1])[data[0] += size] = 0; 373 | if (data[2]) 374 | ((void (*)(long, intptr_t))data[2])(data[0], data[3]); 375 | return size; 376 | } 377 | 378 | 379 | 380 | void rFreeHTTPS(intptr_t user) { 381 | if (user) { 382 | free(((char**)user)[0]); 383 | free(((char**)user)[1]); 384 | free((char**)user); 385 | } 386 | } 387 | 388 | 389 | 390 | intptr_t rMakeHTTPS(char *user, char *serv) { 391 | char **retn; 392 | 393 | if (!user || !serv) 394 | return 0; 395 | retn = calloc(2, sizeof(*retn)); 396 | retn[0] = strdup(user); 397 | retn[1] = calloc(1, 10 + strlen(serv)); 398 | strcat(strcat(strcat(retn[1], "https://"), serv), "/"); 399 | return (intptr_t)retn; 400 | } 401 | 402 | 403 | 404 | long rLoadHTTPS(intptr_t user, char *page, char **dest, 405 | void (*load)(long, intptr_t), intptr_t lprm) { 406 | intptr_t data[4] = {0, 0, (intptr_t)load, lprm}; 407 | char *hreq, **ctxt = (char**)user; 408 | CURL *curl; 409 | 410 | if (!page || !dest) { 411 | *dest = 0; 412 | return 0; 413 | } 414 | curl = curl_easy_init(); 415 | hreq = calloc(1, 1 + strlen(ctxt[1]) + strlen(page)); 416 | curl_easy_setopt(curl, CURLOPT_URL, strcat(strcat(hreq, ctxt[1]), page)); 417 | curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteHTTPS); 418 | curl_easy_setopt(curl, CURLOPT_WRITEDATA, data); 419 | curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); 420 | curl_easy_setopt(curl, CURLOPT_USERAGENT, ctxt[0]); 421 | if (curl_easy_perform(curl)) { 422 | data[1] = (intptr_t)realloc((char*)data[1], 0); 423 | data[1] = data[0] = 0; 424 | } 425 | curl_easy_cleanup(curl); 426 | free(hreq); 427 | *dest = (char*)data[1]; 428 | return data[0]; 429 | } 430 | 431 | 432 | 433 | gboolean TmrFunc(gpointer data) { 434 | intptr_t *user = (intptr_t*)data; 435 | struct timespec spec = {}; 436 | uint64_t time; 437 | 438 | clock_gettime(CLOCK_REALTIME, &spec); 439 | time = spec.tv_sec * 1000 + spec.tv_nsec / 1000000; 440 | ((UPRE)user[0])(user[1], time); 441 | return TRUE; 442 | } 443 | 444 | 445 | 446 | void rInternalMainLoop(CTRL *root, uint32_t fram, UPRE upre, intptr_t data) { 447 | intptr_t user[2] = {(intptr_t)upre, data}; 448 | guint tmrp; 449 | 450 | tmrp = g_timeout_add(fram, TmrFunc, user); 451 | gtk_main(); 452 | g_source_remove(tmrp); 453 | } 454 | 455 | 456 | 457 | void ProcessSpin(GtkSpinButton *spin, gpointer data) { 458 | long spos = gtk_spin_button_get_value(spin); 459 | CTRL *ctrl = (CTRL*)data; 460 | 461 | spos = (spos >= ctrl->priv[1])? spos : ctrl->priv[1]; 462 | spos = (spos <= ctrl->priv[2])? spos : ctrl->priv[2]; 463 | ctrl->fc2e(ctrl, MSG_NSET, spos); 464 | } 465 | 466 | 467 | 468 | void MoveControl(CTRL *ctrl, intptr_t data) { 469 | long xpos = ((long*)data)[0], ypos = ((long*)data)[1]; 470 | CTRL *root = ctrl; 471 | 472 | while (root->prev) 473 | root = root->prev; 474 | xpos = (xpos < 0)? -xpos : xpos * (uint16_t)(root->priv[2] ); 475 | ypos = (ypos < 0)? -ypos : ypos * (uint16_t)(root->priv[2] >> 16); 476 | gtk_fixed_move(GTK_FIXED(ctrl->prev->priv[7]), 477 | GTK_WIDGET(ctrl->priv[0]), xpos, ypos); 478 | } 479 | 480 | 481 | 482 | uint32_t GetWidgetSize(intptr_t what) { 483 | GtkRequisition requ; 484 | 485 | gtk_widget_size_request(GTK_WIDGET(what), &requ); 486 | return (uint16_t)requ.width | ((uint32_t)requ.height << 16); 487 | } 488 | 489 | 490 | 491 | void LabelSize(GtkWidget *gwnd, GdkRectangle *rect, gpointer data) { 492 | CTRL *ctrl = (CTRL*)data; 493 | uint32_t size; 494 | 495 | if (((size = GetWidgetSize(ctrl->priv[7])) != ctrl->priv[2]) 496 | || ((uint16_t)(ctrl->priv[1] >> 16) != rect->height) 497 | || ((uint16_t)(ctrl->priv[1] ) != rect->width)) { 498 | ctrl->priv[2] = size; 499 | ctrl->priv[1] = (uint16_t)rect->width | ((uint32_t)rect->height << 16); 500 | gtk_layout_move(GTK_LAYOUT(ctrl->priv[6]), GTK_WIDGET(ctrl->priv[7]), 501 | ((ctrl->flgs & FST_CNTR)? 502 | (rect->width - (int16_t)(size )) / 2 : 0), 503 | (rect->height - (int16_t)(size >> 16)) / 2 - 1); 504 | } 505 | } 506 | 507 | 508 | 509 | void ButtonSize(GtkWidget *gwnd, GdkRectangle *rect, gpointer data) { 510 | PangoLayout *play = gtk_label_get_layout(GTK_LABEL(data)); 511 | PangoRectangle prec; 512 | 513 | pango_layout_set_width(play, (rect->width - 8) * PANGO_SCALE); 514 | pango_layout_set_height(play, rect->height * PANGO_SCALE); 515 | pango_layout_get_pixel_extents(play, 0, &prec); 516 | gtk_widget_set_size_request(GTK_WIDGET(data), prec.width, prec.height); 517 | gtk_widget_queue_draw(GTK_WIDGET(data)); 518 | } 519 | 520 | 521 | 522 | void ButtonSwitch(GtkToggleButton *butn, gpointer data) { 523 | CTRL *ctrl = (CTRL*)data; 524 | 525 | ctrl->fc2e(ctrl, MSG_BCLK, ((ctrl->flgs & FCT_TTTT) != FCT_BUTN)? 526 | gtk_toggle_button_get_active(butn) : 0); 527 | } 528 | 529 | 530 | 531 | void ListItemSwitch(GtkCellRendererToggle *cell, gchar *path, gpointer data) { 532 | CTRL *ctrl = (CTRL*)data; 533 | GtkTreeIter iter; 534 | gboolean bbbb; 535 | 536 | gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(ctrl->priv[2]), 537 | &iter, path); 538 | gtk_tree_model_get(GTK_TREE_MODEL(ctrl->priv[2]), &iter, 0, &bbbb, -1); 539 | ctrl->fc2e(ctrl, MSG_LSST, (strtol(path, 0, 10) << 1) | ((bbbb)? 0 : 1)); 540 | gtk_list_store_set(GTK_LIST_STORE(ctrl->priv[2]), &iter, 0, !bbbb, -1); 541 | } 542 | 543 | 544 | 545 | gboolean ListItemUpdate(GtkTreeModel *tree, GtkTreePath *path, 546 | GtkTreeIter *iter, gpointer data) { 547 | CTRL *ctrl = (CTRL*)data; 548 | char *pstr = gtk_tree_path_to_string(path); 549 | gboolean bbbb = ctrl->fc2e(ctrl, MSG_LGST, strtol(pstr, 0, 10)); 550 | 551 | gtk_list_store_set(GTK_LIST_STORE(ctrl->priv[2]), iter, 0, bbbb, -1); 552 | g_free(pstr); 553 | return FALSE; 554 | } 555 | 556 | 557 | 558 | void WndScroll(GtkAdjustment *vadj, gpointer data) { 559 | CTRL *ctrl = (CTRL*)data; 560 | 561 | ctrl->fc2e(ctrl, MSG_SGIP, ctrl->fe2c(ctrl, MSG_SGIP, 0)); 562 | } 563 | 564 | 565 | 566 | gboolean IBoxDraw(GtkWidget *gwnd, GdkEventExpose *eexp, gpointer data) { 567 | CTRL *ctrl = (CTRL*)data; 568 | cairo_surface_t *surf; 569 | cairo_t *temp; 570 | 571 | AINF anim = {(ctrl->priv[7] >> 10) & 0x3FFFFF, 572 | (int16_t)ctrl->priv[3], (int32_t)ctrl->priv[3] >> 16, 573 | ctrl->priv[7] & 0x3FF, (uint32_t*)ctrl->priv[2]}; 574 | 575 | if (anim.uuid) { 576 | temp = cairo_create(surf = (cairo_surface_t*)ctrl->priv[1]); 577 | cairo_set_operator(temp, CAIRO_OPERATOR_CLEAR); 578 | cairo_paint(temp); 579 | cairo_destroy(temp); 580 | ctrl->fc2e(ctrl, MSG_IFRM, (intptr_t)&anim); 581 | cairo_surface_mark_dirty(surf); 582 | temp = gdk_cairo_create(gtk_widget_get_window(gwnd)); 583 | cairo_set_operator(temp, CAIRO_OPERATOR_OVER); 584 | cairo_set_source_surface(temp, surf, 0, 0); 585 | cairo_paint(temp); 586 | cairo_destroy(temp); 587 | } 588 | return TRUE; 589 | } 590 | 591 | 592 | 593 | gboolean OptQuit(GtkWidget *gwnd, GdkEvent *ewcl, gpointer data) { 594 | if (((CTRL*)data)->fc2e((CTRL*)data, MSG_WEND, 0)) { 595 | gtk_main_quit(); 596 | return FALSE; 597 | } 598 | return TRUE; 599 | } 600 | 601 | 602 | 603 | gboolean OptResize(GtkWidget *gwnd, GdkEventConfigure *ecnf, gpointer data) { 604 | CTRL *ctrl = (CTRL*)data; 605 | 606 | if ((ecnf->width != (uint16_t)(ctrl->priv[1] )) 607 | || (ecnf->height != (uint16_t)(ctrl->priv[1] >> 16))) { 608 | ctrl->priv[1] = (uint16_t)ecnf->width | ((uint32_t)ecnf->height << 16); 609 | ctrl->fc2e(ctrl, MSG_WSZC, ctrl->priv[1]); 610 | } 611 | return FALSE; 612 | } 613 | 614 | 615 | 616 | /// PRIV: 617 | /// 0: GtkWidget 618 | /// 1: (wndsize.x) | (wndsize.y << 16) 619 | /// 2: (fontmul.x) | (fontmul.y << 16) 620 | /// 3: PangoFontDescription 621 | /// 4: 622 | /// 5: 623 | /// 6: 624 | /// 7: GtkFixed 625 | intptr_t FE2CW(CTRL *ctrl, uint32_t cmsg, intptr_t data) { 626 | switch (cmsg) { 627 | case MSG__SHW: 628 | ((data)? gtk_widget_show 629 | : gtk_widget_hide)(GTK_WIDGET(ctrl->priv[0])); 630 | break; 631 | 632 | case MSG__TXT: 633 | gtk_window_set_title(GTK_WINDOW(ctrl->priv[0]), (char*)data); 634 | break; 635 | 636 | case MSG_WSZC: { 637 | GtkWidget *gwnd = GTK_WIDGET(ctrl->priv[0]); 638 | long xdim, ydim; 639 | 640 | xdim = (((uint16_t)(data )) + ctrl->xdim) 641 | * (uint16_t)(ctrl->priv[2] ); 642 | ydim = (((uint16_t)(data >> 16)) + ctrl->ydim) 643 | * (uint16_t)(ctrl->priv[2] >> 16); 644 | ctrl->priv[1] = (uint16_t)xdim | ((uint32_t)ydim << 16); 645 | gtk_widget_set_size_request(gwnd, xdim, ydim); 646 | gtk_window_set_position(GTK_WINDOW(gwnd), GTK_WIN_POS_CENTER); 647 | break; 648 | } 649 | case MSG_WTDA: { 650 | AINF *anim = (AINF*)data; 651 | uint8_t cfnt[4] = {anim->fcnt >> 16, anim->fcnt >> 8, anim->fcnt}; 652 | PangoRectangle rect = 653 | {(uint8_t)(anim->xdim >> 16), (uint8_t)(anim->ydim >> 16)}; 654 | PangoFontDescription *pfnt = (PangoFontDescription*)ctrl->priv[3]; 655 | PangoLayout *play; 656 | PangoAttrList *attr; 657 | cairo_surface_t *pict, *surf; 658 | // cairo_font_options_t *opts; 659 | cairo_t *temp; 660 | 661 | rect.width = (uint16_t)anim->xdim - rect.x 662 | - (uint8_t)(anim->xdim >> 24); 663 | rect.height = (uint16_t)anim->ydim - rect.y 664 | - (uint8_t)(anim->ydim >> 24); 665 | 666 | /// [TODO:] get rid of this disgusting width hack 667 | rect.width += 6; 668 | rect.x -= 3; 669 | 670 | pict = cairo_image_surface_create_for_data 671 | ((uint8_t*)anim->uuid, CAIRO_FORMAT_ARGB32, 672 | (uint16_t)anim->xdim, (uint16_t)anim->ydim, 673 | sizeof(uint32_t) * (uint16_t)anim->xdim); 674 | surf = cairo_surface_create_for_rectangle 675 | (pict, rect.x, rect.y, rect.width, rect.height); 676 | play = pango_cairo_create_layout(temp = cairo_create(surf)); 677 | // opts = cairo_font_options_create(); 678 | // cairo_font_options_set_antialias(opts, CAIRO_ANTIALIAS_SUBPIXEL); 679 | // cairo_set_font_options(temp, opts); 680 | pango_attr_list_insert(attr = pango_attr_list_new(), 681 | pango_attr_foreground_new(cfnt[0] << 8, 682 | cfnt[1] << 8, 683 | cfnt[2] << 8)); 684 | pango_layout_set_attributes(play, attr); 685 | pango_layout_set_font_description(play, pfnt); 686 | pango_layout_set_wrap(play, PANGO_WRAP_WORD_CHAR); 687 | pango_layout_set_alignment(play, PANGO_ALIGN_CENTER); 688 | pango_layout_set_width (play, rect.width * PANGO_SCALE); 689 | pango_layout_set_height(play, rect.height * PANGO_SCALE); 690 | pango_layout_set_text(play, (char*)anim->time, -1); 691 | pango_cairo_update_layout(temp, play); 692 | pango_cairo_show_layout(temp, play); 693 | cairo_destroy(temp); 694 | g_object_unref(play); 695 | cairo_surface_destroy(surf); 696 | cairo_surface_destroy(pict); 697 | // cairo_font_options_destroy(opts); 698 | pango_attr_list_unref(attr); 699 | break; 700 | } 701 | case MSG_WTGD: { 702 | AINF *anim = (AINF*)data; 703 | int xdim = (anim->xdim > 0)? anim->xdim * PANGO_SCALE : -1, 704 | ydim = (anim->ydim > 0)? anim->ydim * PANGO_SCALE : -1; 705 | PangoFontDescription *pfnt = (PangoFontDescription*)ctrl->priv[3]; 706 | PangoLayout *play; 707 | cairo_surface_t *surf; 708 | cairo_t *temp; 709 | 710 | surf = cairo_recording_surface_create(CAIRO_CONTENT_ALPHA, 0); 711 | play = pango_cairo_create_layout(temp = cairo_create(surf)); 712 | pango_layout_set_width(play, xdim); 713 | pango_layout_set_height(play, ydim); 714 | pango_layout_set_font_description(play, pfnt); 715 | pango_layout_set_wrap(play, PANGO_WRAP_WORD_CHAR); 716 | pango_layout_set_alignment(play, PANGO_ALIGN_CENTER); 717 | pango_layout_set_text(play, (char*)anim->time, -1); 718 | pango_layout_get_pixel_size(play, &xdim, &ydim); 719 | cairo_destroy(temp); 720 | g_object_unref(play); 721 | cairo_surface_destroy(surf); 722 | return (uint16_t)xdim | ((uint32_t)ydim << 16); 723 | } 724 | } 725 | return 0; 726 | } 727 | 728 | 729 | 730 | /// PRIV: 731 | /// 0: GtkWidget 732 | /// 1: progress position 733 | /// 2: progress maximum 734 | /// 3: 735 | /// 4: 736 | /// 5: 737 | /// 6: 738 | /// 7: 739 | intptr_t FE2CP(CTRL *ctrl, uint32_t cmsg, intptr_t data) { 740 | switch (cmsg) { 741 | case MSG_PPOS: { 742 | double dcur = ctrl->priv[1] = data, 743 | dmax = (ctrl->priv[2] > 0)? ctrl->priv[2] : 1; 744 | 745 | gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ctrl->priv[0]), 746 | dcur / dmax); 747 | gtk_widget_queue_draw(GTK_WIDGET(ctrl->priv[0])); 748 | while (gtk_events_pending()) 749 | gtk_main_iteration_do(0); 750 | break; 751 | } 752 | case MSG__TXT: 753 | gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ctrl->priv[0]), 754 | (char*)data); 755 | break; 756 | 757 | case MSG_PLIM: 758 | ctrl->priv[2] = data; 759 | break; 760 | 761 | case MSG_PGET: 762 | return (data)? ctrl->priv[2] : ctrl->priv[1]; 763 | } 764 | return 0; 765 | } 766 | 767 | 768 | 769 | /// PRIV: 770 | /// 0: GtkWidget (container) 771 | /// 1: 772 | /// 2: 773 | /// 3: 774 | /// 4: 775 | /// 5: 776 | /// 6: 777 | /// 7: GtkWidget (text) 778 | intptr_t FE2CX(CTRL *ctrl, uint32_t cmsg, intptr_t data) { 779 | switch (cmsg) { 780 | case MSG__ENB: 781 | gtk_widget_set_sensitive(GTK_WIDGET(ctrl->priv[0]), !!data); 782 | break; 783 | 784 | case MSG__TXT: 785 | gtk_label_set_text(GTK_LABEL(ctrl->priv[7]), (char*)data); 786 | break; 787 | 788 | case MSG_BGST: 789 | data = ctrl->priv[0]; 790 | cmsg = (((ctrl->flgs & FCT_TTTT) == FCT_CBOX) 791 | && gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data)))? 792 | FCS_MARK : 0; 793 | cmsg |= (gtk_widget_get_sensitive(GTK_WIDGET(data)))? FCS_ENBL : 0; 794 | return cmsg; 795 | 796 | case MSG_BCLK: 797 | if ((ctrl->flgs & FCT_TTTT) != FCT_CBOX) 798 | return 0; 799 | cmsg = 800 | gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ctrl->priv[0])); 801 | gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ctrl->priv[0]), 802 | !!data); 803 | return !!cmsg; 804 | } 805 | return 0; 806 | } 807 | 808 | 809 | 810 | /// PRIV: 811 | /// 0: GtkWidget 812 | /// 1: GtkTreeView 813 | /// 2: GtkListStore 814 | /// 3: GtkTreeViewColumn 815 | /// 4: 816 | /// 5: 817 | /// 6: 818 | /// 7: 819 | intptr_t FE2CL(CTRL *ctrl, uint32_t cmsg, intptr_t data) { 820 | switch (cmsg) { 821 | case MSG__ENB: 822 | gtk_widget_set_sensitive(GTK_WIDGET(ctrl->priv[0]), !!data); 823 | break; 824 | 825 | case MSG__TXT: 826 | gtk_tree_view_column_set_title 827 | (GTK_TREE_VIEW_COLUMN(ctrl->priv[3]), (char*)data); 828 | gtk_tree_model_foreach(GTK_TREE_MODEL(ctrl->priv[2]), 829 | ListItemUpdate, ctrl); 830 | break; 831 | 832 | case MSG_LADD: { 833 | GtkTreeIter iter; 834 | 835 | gtk_list_store_append(GTK_LIST_STORE(ctrl->priv[2]), &iter); 836 | gtk_list_store_set(GTK_LIST_STORE(ctrl->priv[2]), &iter, 837 | 0, FALSE, 1, (char*)data, -1); 838 | break; 839 | } 840 | } 841 | return 0; 842 | } 843 | 844 | 845 | 846 | /// PRIV: 847 | /// 0: GtkWidget 848 | /// 1: minimum 849 | /// 2: maximum 850 | /// 3: 851 | /// 4: 852 | /// 5: 853 | /// 6: 854 | /// 7: 855 | intptr_t FE2CN(CTRL *ctrl, uint32_t cmsg, intptr_t data) { 856 | switch (cmsg) { 857 | case MSG__GSZ: 858 | return GetWidgetSize(ctrl->priv[0]); 859 | 860 | case MSG__POS: 861 | MoveControl(ctrl, data); 862 | break; 863 | 864 | case MSG__SHW: 865 | ((data)? gtk_widget_show 866 | : gtk_widget_hide)(GTK_WIDGET(ctrl->priv[0])); 867 | break; 868 | 869 | case MSG__ENB: 870 | gtk_widget_set_sensitive(GTK_WIDGET(ctrl->priv[0]), !!data); 871 | break; 872 | 873 | case MSG_NGET: 874 | return gtk_spin_button_get_value(GTK_SPIN_BUTTON(ctrl->priv[0])); 875 | 876 | case MSG_NSET: 877 | data = (data > ctrl->priv[1])? data : ctrl->priv[1]; 878 | data = (data < ctrl->priv[2])? data : ctrl->priv[2]; 879 | gtk_spin_button_set_value(GTK_SPIN_BUTTON(ctrl->priv[0]), data); 880 | break; 881 | 882 | case MSG_NDIM: 883 | ctrl->priv[1] = (int16_t)data; 884 | ctrl->priv[2] = (int16_t)(data >> 16); 885 | gtk_spin_button_set_range(GTK_SPIN_BUTTON(ctrl->priv[0]), 886 | ctrl->priv[1], ctrl->priv[2]); 887 | break; 888 | } 889 | return 0; 890 | } 891 | 892 | 893 | 894 | /// PRIV: 895 | /// 0: GtkWidget (outer container) 896 | /// 1: (inner.width) | (inner.height << 16) 897 | /// 2: (label.width) | (label.height << 16) 898 | /// 3: 899 | /// 4: 900 | /// 5: 901 | /// 6: GtkWidget (inner container) 902 | /// 7: GtkWidget (text label itself) 903 | intptr_t FE2CT(CTRL *ctrl, uint32_t cmsg, intptr_t data) { 904 | switch (cmsg) { 905 | case MSG__GSZ: 906 | return GetWidgetSize(ctrl->priv[0]); 907 | 908 | case MSG__POS: 909 | MoveControl(ctrl, data); 910 | break; 911 | 912 | case MSG__TXT: 913 | gtk_label_set_text(GTK_LABEL(ctrl->priv[7]), (char*)data); 914 | break; 915 | 916 | case MSG__ENB: 917 | gtk_widget_set_sensitive(GTK_WIDGET(ctrl->priv[0]), !!data); 918 | break; 919 | 920 | case MSG__SHW: 921 | ((data)? gtk_widget_show 922 | : gtk_widget_hide)(GTK_WIDGET(ctrl->priv[0])); 923 | break; 924 | } 925 | return 0; 926 | } 927 | 928 | 929 | 930 | /// PRIV: 931 | /// 0: GtkWidget 932 | /// 1: (wndsize.x) | (wndsize.y << 16) 933 | /// 2: (wndmove.x) | (wndmove.y << 16) 934 | /// 3: total height 935 | /// 4: 936 | /// 5: 937 | /// 6: GtkViewport 938 | /// 7: GtkFixed 939 | intptr_t FE2CS(CTRL *ctrl, uint32_t cmsg, intptr_t data) { 940 | switch (cmsg) { 941 | case MSG__GSZ: 942 | return ctrl->priv[1]; 943 | 944 | case MSG_WSZC: { 945 | GtkAllocation area; 946 | long xdim, ydim; 947 | double temp; 948 | 949 | if ((ctrl->prev->flgs & FCT_TTTT) != FCT_WNDW) 950 | return -1; 951 | 952 | if (!data) { 953 | xdim = (uint16_t)(ctrl->priv[1] ); 954 | ydim = (uint16_t)(ctrl->priv[1] >> 16); 955 | data = ctrl->priv[1]; 956 | } 957 | else { 958 | xdim = (uint16_t)(ctrl->priv[2] ); 959 | ydim = (uint16_t)(ctrl->priv[2] >> 16); 960 | xdim = (uint16_t)(data ) - xdim - ctrl->prev->xdim 961 | * (uint16_t)(ctrl->prev->priv[2] ); 962 | ydim = (uint16_t)(data >> 16) - ydim - ctrl->prev->ydim 963 | * (uint16_t)(ctrl->prev->priv[2] >> 16); 964 | data = (uint16_t)xdim | ((uint32_t)ydim << 16); 965 | } 966 | gtk_widget_set_size_request(GTK_WIDGET(ctrl->priv[0]), xdim, ydim); 967 | if (ctrl->fc2e) { 968 | gtk_widget_get_allocation 969 | (gtk_scrolled_window_get_vscrollbar 970 | (GTK_SCROLLED_WINDOW(ctrl->priv[0])), &area); 971 | temp = (ctrl->priv[3])? 972 | (double)ctrl->fe2c(ctrl, MSG_SGIP, 0) / ctrl->priv[3] : 0; 973 | ctrl->priv[3] = ctrl->fc2e(ctrl, MSG_SSID, data - area.width); 974 | gtk_widget_set_size_request(GTK_WIDGET(ctrl->priv[7]), 975 | xdim, ctrl->priv[3]); 976 | ctrl->priv[1] = data; 977 | ctrl->fc2e(ctrl, MSG_SGIP, temp *= ctrl->priv[3]); 978 | gtk_adjustment_set_value 979 | (gtk_scrolled_window_get_vadjustment 980 | (GTK_SCROLLED_WINDOW(ctrl->priv[0])), temp); 981 | } 982 | ctrl->priv[1] = data; 983 | gtk_widget_show(GTK_WIDGET(ctrl->priv[0])); 984 | break; 985 | } 986 | case MSG_SGIP: 987 | return gtk_adjustment_get_value 988 | (gtk_scrolled_window_get_vadjustment 989 | (GTK_SCROLLED_WINDOW(ctrl->priv[0]))); 990 | 991 | case MSG_SGTH: 992 | return ctrl->priv[3]; 993 | 994 | case MSG__SHW: 995 | ((data)? gtk_widget_show 996 | : gtk_widget_hide)(GTK_WIDGET(ctrl->priv[0])); 997 | break; 998 | } 999 | return 0; 1000 | } 1001 | 1002 | 1003 | 1004 | /// PRIV: 1005 | /// 0: GtkWidget 1006 | /// 1: cairo_surface_t 1007 | /// 2: data array 1008 | /// 3: (xdim) | (ydim << 16) 1009 | /// 4: 1010 | /// 5: 1011 | /// 6: 1012 | /// 7: (animation ID << 10) | (current frame) 1013 | intptr_t FE2CI(CTRL *ctrl, uint32_t cmsg, intptr_t data) { 1014 | switch (cmsg) { 1015 | case MSG__POS: 1016 | MoveControl(ctrl, data); 1017 | break; 1018 | 1019 | case MSG__SHW: 1020 | ((data)? gtk_widget_show 1021 | : gtk_widget_hide)(GTK_WIDGET(ctrl->priv[0])); 1022 | break; 1023 | 1024 | case MSG_IFRM: 1025 | ctrl->priv[7] = data; 1026 | gdk_window_invalidate_rect 1027 | (gtk_widget_get_window(GTK_WIDGET(ctrl->priv[0])), 0, FALSE); 1028 | break; 1029 | } 1030 | return 0; 1031 | } 1032 | 1033 | 1034 | 1035 | void rFreeControl(CTRL *ctrl) { 1036 | switch (ctrl->flgs & FCT_TTTT) { 1037 | case FCT_WNDW: 1038 | break; 1039 | 1040 | case FCT_TEXT: 1041 | break; 1042 | 1043 | case FCT_BUTN: 1044 | break; 1045 | 1046 | case FCT_CBOX: 1047 | break; 1048 | 1049 | case FCT_SPIN: 1050 | break; 1051 | 1052 | case FCT_LIST: 1053 | g_object_unref(G_OBJECT(ctrl->priv[2])); /// the list data 1054 | break; 1055 | 1056 | case FCT_SBOX: 1057 | break; 1058 | 1059 | case FCT_IBOX: 1060 | cairo_surface_destroy((cairo_surface_t*)ctrl->priv[1]); 1061 | break; 1062 | 1063 | case FCT_PBAR: 1064 | break; 1065 | } 1066 | /// no need to destroy widgets explicitly: 1067 | /// this all happens after gtk_main_quit() 1068 | } 1069 | 1070 | 1071 | 1072 | void rMakeControl(CTRL *ctrl, long *xoff, long *yoff) { 1073 | GtkWidget *gwnd; 1074 | CTRL *root; 1075 | 1076 | gwnd = 0; 1077 | root = ctrl->prev; 1078 | if ((ctrl->flgs & FCT_TTTT) == FCT_WNDW) { 1079 | PangoFontMetrics *fmet; 1080 | PangoContext *pctx; 1081 | long xfon, yfon; 1082 | 1083 | ctrl->fe2c = FE2CW; 1084 | gwnd = gtk_window_new(GTK_WINDOW_TOPLEVEL); 1085 | gtk_widget_set_app_paintable(gwnd, TRUE); 1086 | gtk_window_set_resizable(GTK_WINDOW(gwnd), TRUE); 1087 | gtk_window_set_decorated(GTK_WINDOW(gwnd), TRUE); 1088 | if (~ctrl->flgs & FSW_SIZE) { 1089 | gtk_window_set_skip_taskbar_hint(GTK_WINDOW(gwnd), TRUE); 1090 | gtk_window_set_resizable(GTK_WINDOW(gwnd), FALSE); 1091 | gtk_window_set_type_hint(GTK_WINDOW(gwnd), 1092 | GDK_WINDOW_TYPE_HINT_DIALOG); 1093 | } 1094 | gtk_container_set_border_width(GTK_CONTAINER(gwnd), 0); 1095 | pctx = gtk_widget_get_pango_context(gwnd); 1096 | ctrl->priv[3] = (intptr_t)gwnd->style->font_desc; 1097 | fmet = pango_context_get_metrics(pctx, gwnd->style->font_desc, 0); 1098 | xfon = pango_font_metrics_get_approximate_char_width(fmet); 1099 | yfon = pango_font_metrics_get_ascent(fmet); 1100 | ctrl->priv[2] = (uint16_t)round(1.500 * xfon / PANGO_SCALE) 1101 | | ((uint32_t)round(0.675 * yfon / PANGO_SCALE) << 16); 1102 | 1103 | g_signal_connect(G_OBJECT(gwnd), "configure-event", 1104 | G_CALLBACK(OptResize), ctrl); 1105 | g_signal_connect(G_OBJECT(gwnd), "delete-event", 1106 | G_CALLBACK(OptQuit), ctrl); 1107 | 1108 | ctrl->priv[7] = (intptr_t)gtk_fixed_new(); 1109 | gtk_container_add(GTK_CONTAINER(gwnd), GTK_WIDGET(ctrl->priv[7])); 1110 | gtk_widget_show(GTK_WIDGET(ctrl->priv[7])); 1111 | } 1112 | else if (root) { 1113 | GtkFixed *gtbl = (typeof(gtbl))ctrl->prev->priv[7]; 1114 | long xspc, yspc, xpos, ypos, xdim, ydim; 1115 | 1116 | while (root->prev) 1117 | root = root->prev; 1118 | xspc = (uint16_t)(root->priv[2]); 1119 | yspc = (uint16_t)(root->priv[2] >> 16); 1120 | xpos = ctrl->xpos + ((xoff && (ctrl->flgs & FCP_HORZ))? 1121 | *xoff : root->xpos); 1122 | ypos = ctrl->ypos + ((yoff && (ctrl->flgs & FCP_VERT))? 1123 | *yoff : root->ypos); 1124 | xdim = (ctrl->xdim < 0)? -ctrl->xdim : ctrl->xdim * xspc; 1125 | ydim = (ctrl->ydim < 0)? -ctrl->ydim : ctrl->ydim * yspc; 1126 | if (xoff) 1127 | *xoff = xpos 1128 | + ((ctrl->xdim < 0)? 1 - ctrl->xdim / xspc : ctrl->xdim); 1129 | if (yoff) 1130 | *yoff = ypos 1131 | + ((ctrl->ydim < 0)? 1 - ctrl->ydim / yspc : ctrl->ydim); 1132 | 1133 | switch (ctrl->flgs & FCT_TTTT) { 1134 | case FCT_TEXT: 1135 | gwnd = gtk_frame_new(0); 1136 | ctrl->fe2c = FE2CT; 1137 | ctrl->priv[1] = ctrl->priv[2] = 0; 1138 | ctrl->priv[7] = (intptr_t)gtk_label_new(0); 1139 | ctrl->priv[6] = (intptr_t)gtk_layout_new(0, 0); 1140 | g_signal_connect(G_OBJECT(ctrl->priv[6]), "size-allocate", 1141 | G_CALLBACK(LabelSize), ctrl); 1142 | gtk_container_add(GTK_CONTAINER(ctrl->priv[6]), 1143 | GTK_WIDGET(ctrl->priv[7])); 1144 | gtk_container_add(GTK_CONTAINER(gwnd), 1145 | GTK_WIDGET(ctrl->priv[6])); 1146 | gtk_frame_set_shadow_type(GTK_FRAME(gwnd), 1147 | (ctrl->flgs & FST_SUNK)? 1148 | GTK_SHADOW_ETCHED_IN : 1149 | GTK_SHADOW_NONE); 1150 | gtk_widget_show(GTK_WIDGET(ctrl->priv[6])); 1151 | gtk_widget_show(GTK_WIDGET(ctrl->priv[7])); 1152 | break; 1153 | 1154 | case FCT_BUTN: 1155 | gwnd = gtk_button_new(); 1156 | ctrl->fe2c = FE2CX; 1157 | ctrl->priv[7] = (intptr_t)gtk_label_new(0); 1158 | gtk_widget_show(GTK_WIDGET(ctrl->priv[7])); 1159 | gtk_label_set_line_wrap(GTK_LABEL(ctrl->priv[7]), TRUE); 1160 | gtk_label_set_justify(GTK_LABEL(ctrl->priv[7]), 1161 | GTK_JUSTIFY_CENTER); 1162 | gtk_container_add(GTK_CONTAINER(gwnd), 1163 | GTK_WIDGET(ctrl->priv[7])); 1164 | g_signal_connect(G_OBJECT(gwnd), "size-allocate", 1165 | G_CALLBACK(ButtonSize), (void*)ctrl->priv[7]); 1166 | g_signal_connect(G_OBJECT(gwnd), "clicked", 1167 | G_CALLBACK(ButtonSwitch), ctrl); 1168 | if (ctrl->flgs & FSB_DFLT) { 1169 | gtk_widget_set_can_default(gwnd, TRUE); 1170 | gtk_window_set_default(GTK_WINDOW(ctrl->prev->priv[0]), 1171 | gwnd); 1172 | } 1173 | break; 1174 | 1175 | case FCT_CBOX: { 1176 | GtkTextDirection gdir; 1177 | 1178 | gwnd = gtk_check_button_new(); 1179 | ctrl->fe2c = FE2CX; 1180 | ctrl->priv[7] = (intptr_t)gtk_label_new(0); 1181 | gtk_widget_show(GTK_WIDGET(ctrl->priv[7])); 1182 | gtk_widget_set_size_request(GTK_WIDGET(ctrl->priv[7]), 1183 | INT16_MAX, -1); 1184 | gtk_container_add(GTK_CONTAINER(gwnd), 1185 | GTK_WIDGET(ctrl->priv[7])); 1186 | gdir = gtk_widget_get_direction(gwnd); 1187 | gtk_misc_set_alignment(GTK_MISC(ctrl->priv[7]), 1188 | !!(gdir == GTK_TEXT_DIR_RTL), 0.5); 1189 | if (ctrl->flgs & FSX_LEFT) 1190 | gdir = (gdir == GTK_TEXT_DIR_RTL)? 1191 | GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL; 1192 | gtk_widget_set_direction(gwnd, gdir); 1193 | g_signal_connect(G_OBJECT(gwnd), "toggled", 1194 | G_CALLBACK(ButtonSwitch), ctrl); 1195 | break; 1196 | } 1197 | case FCT_SPIN: 1198 | ctrl->fe2c = FE2CN; 1199 | ctrl->priv[1] = 0; 1200 | ctrl->priv[2] = 1; 1201 | gwnd = gtk_spin_button_new_with_range(ctrl->priv[1], 1202 | ctrl->priv[2], 1); 1203 | g_signal_connect(G_OBJECT(gwnd), "value-changed", 1204 | G_CALLBACK(ProcessSpin), ctrl); 1205 | break; 1206 | 1207 | case FCT_LIST: { 1208 | GtkTreeView *tree = GTK_TREE_VIEW(gtk_tree_view_new()); 1209 | GtkCellRenderer *cell = gtk_cell_renderer_toggle_new(); 1210 | 1211 | ctrl->fe2c = FE2CL; 1212 | ctrl->priv[1] = (intptr_t)tree; 1213 | ctrl->priv[2] = (intptr_t)gtk_list_store_new(2, G_TYPE_BOOLEAN, 1214 | G_TYPE_STRING); 1215 | gwnd = gtk_scrolled_window_new(0, 0); 1216 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gwnd), 1217 | GTK_POLICY_NEVER, 1218 | GTK_POLICY_ALWAYS); 1219 | g_signal_connect(G_OBJECT(cell), "toggled", 1220 | G_CALLBACK(ListItemSwitch), ctrl); 1221 | gtk_tree_view_append_column 1222 | (tree, gtk_tree_view_column_new_with_attributes 1223 | ("", cell, "active", 0, NULL)); 1224 | ctrl->priv[3] = (intptr_t) 1225 | gtk_tree_view_column_new_with_attributes 1226 | ("", gtk_cell_renderer_text_new(), "text", 1, NULL); 1227 | gtk_tree_view_append_column 1228 | (tree, GTK_TREE_VIEW_COLUMN(ctrl->priv[3])); 1229 | gtk_tree_view_set_model(tree, GTK_TREE_MODEL(ctrl->priv[2])); 1230 | gtk_widget_show(GTK_WIDGET(tree)); 1231 | gtk_container_add(GTK_CONTAINER(gwnd), GTK_WIDGET(tree)); 1232 | break; 1233 | } 1234 | case FCT_SBOX: 1235 | ctrl->fe2c = FE2CS; 1236 | ctrl->priv[1] = (uint16_t)xdim | ((uint32_t)ydim << 16); 1237 | ctrl->priv[2] = (uint16_t)(xpos * xspc) 1238 | | ((uint32_t)(ypos * yspc) << 16); 1239 | gwnd = gtk_scrolled_window_new(0, 0); 1240 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gwnd), 1241 | GTK_POLICY_NEVER, 1242 | GTK_POLICY_ALWAYS); 1243 | ctrl->priv[6] = (intptr_t)gtk_viewport_new(0, 0); 1244 | ctrl->priv[7] = (intptr_t)gtk_fixed_new(); 1245 | gtk_viewport_set_shadow_type(GTK_VIEWPORT(ctrl->priv[6]), 1246 | GTK_SHADOW_NONE); 1247 | gtk_container_add(GTK_CONTAINER(ctrl->priv[6]), 1248 | GTK_WIDGET(ctrl->priv[7])); 1249 | gtk_container_add(GTK_CONTAINER(gwnd), 1250 | GTK_WIDGET(ctrl->priv[6])); 1251 | gtk_widget_show(GTK_WIDGET(ctrl->priv[6])); 1252 | gtk_widget_show(GTK_WIDGET(ctrl->priv[7])); 1253 | g_signal_connect(G_OBJECT(gtk_scrolled_window_get_vadjustment 1254 | (GTK_SCROLLED_WINDOW(gwnd))), 1255 | "value-changed", G_CALLBACK(WndScroll), ctrl); 1256 | break; 1257 | 1258 | case FCT_IBOX: 1259 | ctrl->fe2c = FE2CI; 1260 | gwnd = gtk_drawing_area_new(); 1261 | 1262 | ctrl->priv[1] = (intptr_t) 1263 | cairo_image_surface_create 1264 | (CAIRO_FORMAT_ARGB32, xdim, ydim); 1265 | ctrl->priv[2] = (intptr_t) 1266 | cairo_image_surface_get_data 1267 | ((cairo_surface_t*)ctrl->priv[1]); 1268 | ctrl->priv[3] = (uint16_t)xdim | ((uint32_t)ydim << 16); 1269 | 1270 | g_signal_connect(G_OBJECT(gwnd), "expose-event", 1271 | G_CALLBACK(IBoxDraw), ctrl); 1272 | break; 1273 | 1274 | case FCT_PBAR: 1275 | ctrl->fe2c = FE2CP; 1276 | ctrl->priv[0] = (intptr_t)(gwnd = gtk_progress_bar_new()); 1277 | break; 1278 | } 1279 | gtk_fixed_put(gtbl, gwnd, xpos * xspc, ypos * yspc); 1280 | gtk_widget_set_size_request(gwnd, xdim, ydim); 1281 | gtk_widget_show(gwnd); 1282 | } 1283 | ctrl->priv[0] = (intptr_t)gwnd; 1284 | } 1285 | 1286 | 1287 | 1288 | void lockfunc(int mode, int indx, const char *file, int line) { 1289 | if (mode & CRYPTO_LOCK) 1290 | pthread_mutex_lock(&pmtx[indx]); 1291 | else 1292 | pthread_mutex_unlock(&pmtx[indx]); 1293 | } 1294 | 1295 | 1296 | 1297 | void _start() { 1298 | /** the stack MUST be aligned to a 256-bit (32-byte) boundary: **/ 1299 | __attribute__((aligned(32))) volatile uint32_t size = 32; 1300 | char *home, *conf; 1301 | GdkScreen *gscr; 1302 | gint xdim, ydim, cmtx; 1303 | 1304 | pmtx = calloc(cmtx = CRYPTO_num_locks(), sizeof(*pmtx)); 1305 | for (xdim = 0; xdim < cmtx; xdim++) 1306 | pthread_mutex_init(&pmtx[xdim], 0); 1307 | CRYPTO_set_id_callback(pthread_self); 1308 | CRYPTO_set_locking_callback(lockfunc); 1309 | curl_global_init(CURL_GLOBAL_DEFAULT); 1310 | if (!(home = getenv("HOME"))) 1311 | home = getpwuid(getuid())->pw_dir; 1312 | conf = calloc(strlen(home) + size, sizeof(*conf)); 1313 | strcat(conf, home); 1314 | strcat(conf, "/.config"); 1315 | rMakeDir(conf, 0); 1316 | strcat(conf, DEF_OPTS); 1317 | if (!rMakeDir(conf, 0)) 1318 | printf("WARNING: cannot create '%s'!", conf); 1319 | 1320 | gtk_init(0, 0); 1321 | gscr = gdk_screen_get_default(); 1322 | gtk_icon_size_lookup(GTK_ICON_SIZE_DIALOG, &xdim, &ydim); 1323 | eExecuteEngine(conf, conf, xdim, ydim, 0, 0, 1324 | gdk_screen_get_width(gscr), gdk_screen_get_height(gscr)); 1325 | free(conf); 1326 | curl_global_cleanup(); 1327 | CRYPTO_set_locking_callback(0); 1328 | CRYPTO_set_id_callback(0); 1329 | for (xdim = 0; xdim < cmtx; xdim++) 1330 | pthread_mutex_destroy(&pmtx[xdim]); 1331 | free(pmtx); 1332 | exit(0); 1333 | } 1334 | -------------------------------------------------------------------------------- /macos/DPengine.cbp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 172 | 173 | -------------------------------------------------------------------------------- /macos/Makefile: -------------------------------------------------------------------------------- 1 | MAC_VER = $(shell sw_vers -productVersion | cut -d. -f-2 | tr -d .) 2 | MAC_OLD = $(shell [ $(MAC_VER) -le 107 ] && echo true) 3 | 4 | MAC_MIN = 10.6.0 5 | 6 | CC = clang -march=core2 -mstackrealign -fno-stack-protector -fvisibility=hidden -fconstant-cfstrings -mmacosx-version-min=$(MAC_MIN) 7 | ifeq ($(MAC_OLD), true) 8 | CC += -no-integrated-as -DMAC_OLD 9 | endif 10 | CX = $(CC) -Wl,-macosx_version_min -Wl,$(MAC_MIN) -lcurl -lcrypto -framework AppKit -headerpad_max_install_names 11 | 12 | OBJDIR = .obj 13 | EXEBASE = ../macos.app 14 | EXEPATH = $(EXEBASE)/Contents/MacOS 15 | 16 | LLIB = -framework OpenGL 17 | LINC = 18 | LCXFLAGS = 19 | LCFLAGS = -I../core -Wall -fPIC 20 | 21 | LOBJ = $(OBJDIR)/core/core.o $(OBJDIR)/core/gif/gifstd.o $(OBJDIR)/core/ogl/oglstd.o $(OBJDIR)/lib.o 22 | LOUT = ../libengine.dylib 23 | 24 | 25 | ELIB = -lengine 26 | EINC = 27 | ECXFLAGS = -L.. 28 | ECFLAGS = -I../core -Wall 29 | 30 | ERES = $(EXEPATH)/../Info.plist $(EXEPATH)/../Resources/main.icns 31 | EOBJ = $(OBJDIR)/exec/exec.o $(OBJDIR)/run.o 32 | EOUT = $(EXEPATH)/DPE 33 | 34 | 35 | RelJoined32: CC += -m32 36 | RelJoined32: lrelease 37 | RelJoined32: erelease 38 | RelJoined32: jlink 39 | RelJoined32: estrip 40 | 41 | DbgJoined32: CC += -m32 42 | DbgJoined32: ldebug 43 | DbgJoined32: edebug 44 | DbgJoined32: jlink 45 | 46 | RelLibOnly32: CC += -m32 47 | RelLibOnly32: lrelease 48 | RelLibOnly32: llink 49 | RelLibOnly32: lstrip 50 | 51 | DbgLibOnly32: CC += -m32 52 | DbgLibOnly32: ldebug 53 | DbgLibOnly32: llink 54 | 55 | RelSplit32: RelLibOnly32 56 | RelSplit32: erelease 57 | RelSplit32: elink 58 | RelSplit32: estrip 59 | 60 | DbgSplit32: DbgLibOnly32 61 | DbgSplit32: edebug 62 | DbgSplit32: elink 63 | 64 | 65 | RelJoined64: CC += -m64 66 | RelJoined64: lrelease 67 | RelJoined64: erelease 68 | RelJoined64: jlink 69 | RelJoined64: estrip 70 | 71 | DbgJoined64: CC += -m64 72 | DbgJoined64: ldebug 73 | DbgJoined64: edebug 74 | DbgJoined64: jlink 75 | 76 | RelLibOnly64: CC += -m64 77 | RelLibOnly64: lrelease 78 | RelLibOnly64: llink 79 | RelLibOnly64: lstrip 80 | 81 | DbgLibOnly64: CC += -m64 82 | DbgLibOnly64: ldebug 83 | DbgLibOnly64: llink 84 | 85 | RelSplit64: RelLibOnly64 86 | RelSplit64: erelease 87 | RelSplit64: elink 88 | RelSplit64: estrip 89 | 90 | DbgSplit64: DbgLibOnly64 91 | DbgSplit64: edebug 92 | DbgSplit64: elink 93 | 94 | 95 | cleanRelJoined32: clean 96 | cleanDbgJoined32: clean 97 | cleanRelJoined64: clean 98 | cleanDbgJoined64: clean 99 | 100 | cleanRelSplit32: clean 101 | cleanDbgSplit32: clean 102 | cleanRelSplit64: clean 103 | cleanDbgSplit64: clean 104 | 105 | cleanRelLibOnly32: clean 106 | cleanDbgLibOnly32: clean 107 | cleanRelLibOnly64: clean 108 | cleanDbgLibOnly64: clean 109 | 110 | 111 | lrelease: LCFLAGS += -O2 112 | lrelease: lbuild $(LOBJ) 113 | 114 | erelease: ECFLAGS += -O2 115 | erelease: ebuild $(EOBJ) 116 | 117 | ldebug: LCFLAGS += -g 118 | ldebug: lbuild $(LOBJ) 119 | 120 | edebug: ECFLAGS += -g 121 | edebug: ebuild $(EOBJ) 122 | 123 | 124 | clean: 125 | rm -f $(LOUT) $(EOUT) 126 | rm -rf $(OBJDIR) $(EXEBASE) 127 | 128 | lstrip: llink 129 | if type strip >/dev/null 2>&1; then strip $(LOUT); fi 130 | 131 | estrip: elink 132 | if type strip >/dev/null 2>&1; then strip $(EOUT); fi 133 | 134 | lbuild: 135 | $(OBJDIR)/core/core.o: ../core/core.c 136 | mkdir -p $(OBJDIR)/core 137 | $(CC) $(LCFLAGS) $(LINC) -c ../core/core.c -o $(OBJDIR)/core/core.o 138 | $(OBJDIR)/core/gif/gifstd.o: ../core/gif/gifstd.c 139 | mkdir -p $(OBJDIR)/core/gif 140 | $(CC) $(LCFLAGS) $(LINC) -c ../core/gif/gifstd.c -o $(OBJDIR)/core/gif/gifstd.o 141 | $(OBJDIR)/core/ogl/oglstd.o: ../core/ogl/oglstd.c 142 | mkdir -p $(OBJDIR)/core/ogl 143 | $(CC) $(LCFLAGS) $(LINC) -c ../core/ogl/oglstd.c -o $(OBJDIR)/core/ogl/oglstd.o 144 | $(OBJDIR)/lib.o: lib.c 145 | mkdir -p $(OBJDIR) 146 | $(CC) $(LCFLAGS) $(LINC) -c lib.c -o $(OBJDIR)/lib.o 147 | 148 | ebuild: 149 | $(EXEPATH)/../Resources/main.icns: rsrc/main.icns 150 | mkdir -p $(EXEPATH)/../Resources 151 | cp rsrc/main.icns $(EXEPATH)/../Resources 152 | $(EXEPATH)/../Info.plist: rsrc/Info.plist 153 | mkdir -p $(EXEPATH) 154 | cp rsrc/Info.plist $(EXEPATH)/.. 155 | $(OBJDIR)/exec/exec.o: ../exec/exec.c 156 | mkdir -p $(OBJDIR)/exec 157 | $(CC) $(ECFLAGS) $(EINC) -c ../exec/exec.c -o $(OBJDIR)/exec/exec.o 158 | $(OBJDIR)/run.o: run.c 159 | mkdir -p $(OBJDIR) 160 | $(CC) $(ECFLAGS) $(EINC) -c run.c -o $(OBJDIR)/run.o 161 | 162 | jlink: ELIB = $(LLIB) 163 | jlink: EOBJ += $(LOBJ) 164 | jlink: $(LOBJ) $(EOBJ) $(ERES) 165 | jlink: elink $(EOBJ) $(ERES) 166 | 167 | llink: LCXFLAGS += -shared 168 | llink: $(LOBJ) 169 | $(CX) $(LCXFLAGS) $(LOBJ) -o $(LOUT) $(LLIB) 170 | 171 | # the last line is a dirty hack that forces libcurl to use current_version=7.0.0 172 | # and compatibility_version=6.0.0 so that the lib was still usable on MacOS 10.6 173 | # (and in case the corresponding import was not found, nothing happens to EOUT) 174 | elink: $(EOBJ) $(ERES) 175 | $(CX) $(ECXFLAGS) $(EOBJ) -o $(EOUT) $(ELIB) 176 | strings -o -t x - $(EOUT) | grep -m1 "/usr/lib/libcurl.4.dylib" | sed "s/ .*/: 0000 0700 0000 0600/" | xxd -s -8 -r - $(EOUT) 177 | -------------------------------------------------------------------------------- /macos/lib.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include "load/mac_load.h" 5 | 6 | 7 | 8 | /// name of the instance variable to access the engine structure 9 | #define VAR_ENGD "engd" 10 | 11 | 12 | 13 | struct SEMD { 14 | pthread_mutex_t cmtx; 15 | pthread_cond_t cvar; 16 | SEM_TYPE list, full; 17 | }; 18 | 19 | typedef struct _DRAW { 20 | uint32_t attr; /// last known mouse attributes 21 | long xdim, ydim; /// drawing area dimensions 22 | NSWindow *hwnd; /// main window 23 | NSView *view; /// main view 24 | NSCursor *hand; /// main cursor 25 | CGContextRef hctx; /// output context handle 26 | } DRAW; 27 | 28 | 29 | 30 | uint64_t lTimeFunc() { 31 | struct timeval spec = {}; 32 | 33 | gettimeofday(&spec, 0); 34 | return (uint64_t)spec.tv_sec * 1000 + (uint64_t)spec.tv_usec / 1000; 35 | } 36 | 37 | 38 | 39 | void lRestartEngine(ENGD *engd) { 40 | NSApplication *thrd = sharedApplication(_(NSApplication)); 41 | NSEvent *post; 42 | 43 | /// if called from inside a timer context, bump an event to the loop 44 | /// to ensure that it did process the stop notification we just sent; 45 | /// if not in a timer context, a dummy event will be rejected anyway 46 | post = otherEventWithType_location_modifierFlags_timestamp_windowNumber_context_subtype_data1_data2_ 47 | (_(NSEvent), NSApplicationDefined, (NSPoint){}, 48 | NSApplicationDefined, 0, 0, 0, 0, 0, 0); 49 | stop_(thrd, post); 50 | postEvent_atStart_(thrd, post, true); 51 | } 52 | 53 | 54 | 55 | void lShowMainWindow(ENGD *engd, long show) { 56 | NSApplication *thrd = sharedApplication(_(NSApplication)); 57 | intptr_t *data; 58 | 59 | cEngineCallback(engd, ECB_GUSR, (intptr_t)&data); 60 | if (show) 61 | orderFront_(((DRAW*)data[0])->hwnd, thrd); 62 | else 63 | orderOut_(((DRAW*)data[0])->hwnd, thrd); 64 | } 65 | 66 | 67 | 68 | char *lLoadFile(char *name, long *size) { 69 | char *retn = 0; 70 | long file, flen; 71 | 72 | if ((file = open(name, O_RDONLY)) > 0) { 73 | flen = lseek(file, 0, SEEK_END); 74 | lseek(file, 0, SEEK_SET); 75 | retn = malloc(flen); 76 | if (read(file, retn, flen) == flen) { 77 | if (size) 78 | *size = flen; 79 | } 80 | else { 81 | free(retn); 82 | retn = 0; 83 | } 84 | close(file); 85 | } 86 | return retn; 87 | } 88 | 89 | 90 | 91 | long lCountCPUs() { 92 | long temp; 93 | 94 | temp = sysconf(_SC_NPROCESSORS_ONLN); 95 | return (temp > 0)? (temp < sizeof(SEM_TYPE) * CHAR_BIT)? 96 | temp : sizeof(SEM_TYPE) * CHAR_BIT : 1; 97 | } 98 | 99 | 100 | 101 | void lMakeThread(void *thrd) { 102 | pthread_t pthr; 103 | 104 | pthread_create(&pthr, 0, cThrdFunc, thrd); 105 | } 106 | 107 | 108 | 109 | void lFreeSemaphore(SEMD **retn, long nthr) { 110 | if (retn && *retn) { 111 | pthread_cond_destroy(&(*retn)->cvar); 112 | pthread_mutex_destroy(&(*retn)->cmtx); 113 | free(*retn); 114 | *retn = 0; 115 | } 116 | } 117 | 118 | 119 | 120 | void lMakeSemaphore(SEMD **retn, long nthr, SEM_TYPE mask) { 121 | if (retn) { 122 | *retn = malloc(sizeof(**retn)); 123 | pthread_mutex_init(&(*retn)->cmtx, 0); 124 | pthread_cond_init(&(*retn)->cvar, 0); 125 | (*retn)->full = (1 << nthr) - 1; 126 | (*retn)->list = (*retn)->full & mask; 127 | } 128 | } 129 | 130 | 131 | 132 | long lPickSemaphore(SEMD *drop, SEMD *pick, SEM_TYPE mask) { 133 | long retn; 134 | 135 | retn = (__sync_fetch_and_and(&drop->list, ~(drop->full & mask)) & mask)? 136 | TRUE : FALSE; 137 | __sync_or_and_fetch(&pick->list, pick->full & mask); 138 | 139 | pthread_mutex_lock(&pick->cmtx); 140 | pthread_cond_broadcast(&pick->cvar); 141 | pthread_mutex_unlock(&pick->cmtx); 142 | return retn; 143 | } 144 | 145 | 146 | 147 | SEM_TYPE lWaitSemaphore(SEMD *wait, SEM_TYPE mask) { 148 | pthread_mutex_lock(&wait->cmtx); 149 | if (mask) 150 | while ((wait->list ^ wait->full) & mask) 151 | pthread_cond_wait(&wait->cvar, &wait->cmtx); 152 | else 153 | while (!wait->list) 154 | pthread_cond_wait(&wait->cvar, &wait->cmtx); 155 | mask = wait->list; 156 | pthread_mutex_unlock(&wait->cmtx); 157 | return mask; 158 | } 159 | 160 | 161 | 162 | bool OnOpaq() { 163 | return false; 164 | } 165 | 166 | 167 | 168 | void OnFPS(CFRunLoopTimerRef runp, void *user) { 169 | char fout[128]; 170 | 171 | cOutputFPS((ENGD*)user, fout); 172 | printf("%s\n", fout); 173 | } 174 | 175 | 176 | 177 | void OnCalc(CFRunLoopObserverRef runp, CFRunLoopActivity acti, void *user) { 178 | NSPoint dptr; 179 | uint32_t attr; 180 | intptr_t *data; 181 | DRAW *draw; 182 | 183 | cEngineCallback((ENGD*)user, ECB_GUSR, (intptr_t)&data); 184 | draw = (DRAW*)data[0]; 185 | 186 | dptr = mouseLocation(_(NSEvent)); 187 | attr = pressedMouseButtons(_(NSEvent)); 188 | 189 | /// [TODO:] properly determine if the window is active 190 | attr = ((attr & 1)? UFR_LBTN : 0) | ((attr & 2)? UFR_RBTN : 0) 191 | | ((attr & 4)? UFR_MBTN : 0) | UFR_MOUS; 192 | attr = cPrepareFrame((ENGD*)user, dptr.x, draw->ydim - dptr.y, attr); 193 | if (attr & PFR_SKIP) 194 | usleep(1000); 195 | if (attr & PFR_HALT) 196 | return; 197 | 198 | if ((attr ^ draw->attr) & PFR_PICK) { 199 | if (attr & PFR_PICK) { 200 | setIgnoresMouseEvents_(draw->hwnd, false); 201 | push(draw->hand); 202 | } 203 | else { 204 | setIgnoresMouseEvents_(draw->hwnd, true); 205 | pop(draw->hand); 206 | } 207 | } 208 | draw->attr = attr; 209 | cEngineCallback((ENGD*)user, ECB_GFLG, (intptr_t)&attr); 210 | if (~attr & COM_RGPU) { 211 | CGContextSetBlendMode(draw->hctx, kCGBlendModeClear); 212 | CGContextFillRect(draw->hctx, 213 | (NSRect){{0, 0}, {draw->xdim, draw->ydim}}); 214 | } 215 | cOutputFrame((ENGD*)user, 0); 216 | setNeedsDisplay_(draw->view, true); 217 | } 218 | 219 | 220 | 221 | void MAC_Handler(OnDraw, NSRect rect) { 222 | CGContextRef ctxt; 223 | CGImageRef iref; 224 | 225 | uint32_t flgs; 226 | intptr_t *data; 227 | ENGD *engd; 228 | DRAW *draw; 229 | 230 | MAC_GetIvar(self, VAR_ENGD, &engd); 231 | cEngineCallback(engd, ECB_GFLG, (intptr_t)&flgs); 232 | cEngineCallback(engd, ECB_GUSR, (intptr_t)&data); 233 | draw = (DRAW*)data[0]; 234 | 235 | if (flgs & COM_RGPU) 236 | flushBuffer(openGLContext(draw->view)); 237 | else { 238 | iref = CGBitmapContextCreateImage(draw->hctx); 239 | ctxt = graphicsPort(currentContext(_(NSGraphicsContext))); 240 | CGContextSetBlendMode(ctxt, kCGBlendModeCopy); 241 | CGContextDrawImage 242 | (ctxt, (NSRect){{0, 0}, {draw->xdim, draw->ydim}}, iref); 243 | CGImageRelease(iref); 244 | } 245 | } 246 | 247 | 248 | 249 | void lRunMainLoop(ENGD *engd, long xpos, long ypos, long xdim, long ydim, 250 | BGRA **bptr, intptr_t *data, uint32_t flgs) { 251 | #define SUB_VDLG "lNSV" 252 | 253 | NSString *scib; 254 | NSApplication *thrd; 255 | NSAutoreleasePool *pool; 256 | CFRunLoopObserverRef idle; 257 | CFRunLoopTimerRef tmrf; 258 | NSRect dims; 259 | Class view; 260 | DRAW draw; 261 | 262 | /// a dirty hack to become capable of changing cursors at will 263 | CGSSetConnectionProperty 264 | (_CGSDefaultConnection(), _CGSDefaultConnection(), 265 | scib = MAC_MakeString("SetsCursorInBackground"), kCFBooleanTrue); 266 | MAC_FreeString(scib); 267 | 268 | data[0] = (intptr_t)&draw; 269 | draw.hand = pointingHandCursor(_(NSCursor)); 270 | draw.attr = 0; 271 | draw.xdim = xdim - xpos; 272 | draw.ydim = ydim - ypos; 273 | dims = (NSRect){{0, 0}, {draw.xdim, draw.ydim}}; 274 | 275 | pool = init(alloc(_(NSAutoreleasePool))); 276 | thrd = sharedApplication(_(NSApplication)); 277 | if (~flgs & COM_RGPU) { 278 | long line = draw.xdim * sizeof(**bptr); 279 | CGColorSpaceRef drgb = CGColorSpaceCreateDeviceRGB(); 280 | 281 | draw.hctx = CGBitmapContextCreate(*bptr = calloc(line, draw.ydim), 282 | draw.xdim, draw.ydim, CHAR_BIT, line, 283 | drgb, kCGImageAlphaPremultipliedFirst 284 | | kCGBitmapByteOrder32Little); 285 | CGColorSpaceRelease(drgb); 286 | 287 | view = MAC_MakeClass(SUB_VDLG, _(NSView), MAC_TempArray(VAR_ENGD), 288 | MAC_TempArray(_(drawRect_), OnDraw, 289 | _(isOpaque), OnOpaq)); 290 | draw.view = init(alloc(view)); 291 | setFrame_(draw.view, dims); 292 | } 293 | else { 294 | int attr[] = {NSOpenGLPFADoubleBuffer, NSOpenGLPFADepthSize, 32, 0}, 295 | opaq = 0; 296 | NSOpenGLPixelFormat *pfmt; 297 | NSOpenGLContext *ctxt; 298 | 299 | pfmt = initWithAttributes_(alloc(_(NSOpenGLPixelFormat)), attr); 300 | view = MAC_MakeClass(SUB_VDLG, _(NSOpenGLView), MAC_TempArray(VAR_ENGD), 301 | MAC_TempArray(_(drawRect_), OnDraw, 302 | _(isOpaque), OnOpaq)); 303 | draw.view = 304 | (NSView*)initWithFrame_pixelFormat_(alloc(view), dims, pfmt); 305 | makeCurrentContext(ctxt = openGLContext(draw.view)); 306 | setValues_forParameter_(ctxt, &opaq, NSOpenGLCPSurfaceOpacity); 307 | release(pfmt); 308 | } 309 | MAC_SetIvar(draw.view, VAR_ENGD, engd); 310 | dims.origin.x = xpos; 311 | dims.origin.y = ypos; 312 | 313 | /// [TODO:] NSNonactivatingPanelMask is flagged for deprecation in 10.12 314 | /// so a suitable alternative has to be found 315 | draw.hwnd = initWithContentRect_styleMask_backing_defer_ 316 | (alloc(_(NSPanel)), dims, NSBorderlessWindowMask 317 | | NSNonactivatingPanelMask, 318 | kCGBackingStoreBuffered, false); 319 | 320 | setContentView_(draw.hwnd, draw.view); 321 | setDelegate_(draw.hwnd, draw.view); 322 | setLevel_(draw.hwnd, NSMainMenuWindowLevel + 1); 323 | setBackgroundColor_(draw.hwnd, clearColor(_(NSColor))); 324 | setHasShadow_(draw.hwnd, false); 325 | setOpaque_(draw.hwnd, false); 326 | 327 | tmrf = MAC_MakeTimer(1000, OnFPS, engd); 328 | idle = MAC_MakeIdleFunc(OnCalc, engd); 329 | orderFront_(draw.hwnd, thrd); 330 | 331 | run(thrd); 332 | 333 | MAC_FreeIdleFunc(idle); 334 | MAC_FreeTimer(tmrf); 335 | 336 | cDeallocFrame(engd, 0); 337 | if (~flgs & COM_RGPU) { 338 | CGContextRelease(draw.hctx); 339 | free(*bptr); 340 | } 341 | if (draw.attr & PFR_PICK) 342 | pop(draw.hand); 343 | release(draw.view); 344 | release(draw.hwnd); 345 | release(pool); 346 | MAC_FreeClass(view); 347 | 348 | #undef SUB_VDLG 349 | } 350 | -------------------------------------------------------------------------------- /macos/rsrc/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | CFBundleInfoDictionaryVersion 4 | 6.0 5 | CFBundlePackageType 6 | APPL 7 | CFBundleExecutable 8 | DPE 9 | CFBundleIdentifier 10 | hidefromkgb.DesktopPoniesEngine 11 | CFBundleName 12 | Desktop Ponies Engine 13 | CFBundleIconFile 14 | main 15 | CFBundleVersion 16 | [early alpha] 17 | LSApplicationCategoryType 18 | public.app-category.entertainment 19 | LSMinimumSystemVersion 20 | 10.6.0 21 | LSUIElement 22 | 1 23 | NSRequiresAquaSystemAppearance 24 | NO 25 | NSPrincipalClass 26 | NSApplication 27 | 28 | -------------------------------------------------------------------------------- /macos/rsrc/main.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hidefromkgb/DPengine/6ebd22021a4fc87152bf66edca8cf7ebb00ab9ba/macos/rsrc/main.icns -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # DPengine 2 | 3 | An alternative experimental engine for 4 | [Desktop Ponies](https://github.com/RoosterDragon/Desktop-Ponies) 5 | written in C and GLSL and focused on performance. Don\`t trust GitHub if it 6 | shows ObjC in the list of used languages, it\`s pure C. 7 | 8 | **[WARNING:]** This repo contains submodules (core/gif/load/, core/ogl/load/, 9 | exec/ctr/, exec/zip/, macos/load/), so don\`t forget to specify `--recursive` 10 | when doing a `git clone`! As for GitHub ZIP downloader, unfortunately it still 11 | [doesn\`t support](https://stackoverflow.com/q/12936014) submodules as of now, 12 | so downloading a ZIP instead of cloning yields an incomplete copy. 13 | 14 | Win32 GUI (as seen on Windows 98 SE): 15 | 16 | 17 | 18 | Linux GUI (as seen on Arch Linux 4.6): 19 | 20 | 21 | 22 | MacOS GUI (as seen on 10.15 Catalina, Dark UI mode): 23 | 24 | 25 | 26 | To run properly, DPE requires the 27 | [DP animation base](https://github.com/RoosterDragon/Desktop-Ponies/tree/master/Content), 28 | automatically downloading it from GitHub if possible. If for any reason DPE 29 | keeps failing to download it, the said directory needs to be manually put to 30 | any location on the local hard drive, after which DPE should be configured to 31 | use it: click the 'More Options...' button on the main window and choose the 32 | 'Animation base directory' so that it points to the *parent* directory of the 33 | animation base. After that, close DPE and execute it again. Enjoy! 34 | 35 | The source code is meant to be built via either Code::Blocks IDE or makefiles 36 | included in the project. 37 | 38 | *N.B.:* to be able to build on Windows using Code::Blocks 10.05, go to 39 | Settings → Compiler and Debugger → Toolchain Executables, and change 40 | 'Make program' to 'mingw32-make.exe'. It`s how it was meant to be. 41 | More recent versions ship with correct settings. 42 | 43 | *N.B.:* to build and run DPE on Linux, install the following packages: 44 | 45 | 1. make 46 | 1. gcc 47 | 1. mesa-common-dev 48 | 1. libgtk2.0-dev 49 | 1. libgtkglext1-dev 50 | 1. libcurl4-openssl-dev 51 | 1. libssl-dev 52 | 53 | ## Releases 54 | 55 | [Binary releases](https://github.com/hidefromkgb/DPengine/releases) 56 | are available for download. Building from source is no more necessary! 57 | 58 | ## Release notes 59 | 60 | The task at hand is to implement the remaining DP features missing from DPE: 61 | 62 | 1. Interactions 63 | 1. Games 64 | 1. Profiles 65 | 1. Screensaver 66 | 1. Screen movement restriction, tab 2 67 | 1. Audio (in doubt over its necessity, but whatever) 68 | 69 | Some functionality is not present in DP but would also be nice to have: 70 | 71 | 1. Implement the "alias method" for behaviours 72 | 1. Parallelize sprite processing in frontend 73 | 1. Move rendering options from tray menu to tab 3 74 | 1. Add OS-specific options to tab 3 75 | 76 | ## License 77 | 78 | The source code and binaries are licensed under 79 | [Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0)](http://creativecommons.org/licenses/by-nc-sa/3.0/). 80 | This means you are allowed to share and alter them, provided you give credit, 81 | do not use them for commercial purposes and release them under this same 82 | license. 83 | 84 | The DP animation base is not a part of this project; 85 | [see here](https://github.com/RoosterDragon/Desktop-Ponies#license) 86 | for details on its license. 87 | -------------------------------------------------------------------------------- /win32/DPengine.cbp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 124 | 125 | -------------------------------------------------------------------------------- /win32/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc -m32 2 | CX = gcc -m32 3 | RC = windres 4 | 5 | 6 | OBJDIR = .obj 7 | 8 | RCFLAGS = -J rc -O coff 9 | CRTLIBS = -lmsvcrt -lwininet -lkernel32 -luser32 -lshell32 -lgdi32 -lole32 -lcomctl32 -lcomdlg32 10 | CRTFUNC = -Dstrdup=_strdup -D__mingw_realloc=realloc -D__mingw_free=free 11 | 12 | LLIB = $(CRTLIBS) -lopengl32 13 | LINC = 14 | LCXFLAGS = 15 | LCFLAGS = -I..\core -mno-stack-arg-probe -Wall -march=i486 -DLIB_MAKE $(CRTFUNC) 16 | 17 | LOBJ = $(OBJDIR)\core\core.o $(OBJDIR)\core\gif\gifstd.o $(OBJDIR)\core\ogl\oglstd.o $(OBJDIR)\lib.o 18 | LOUT = ..\engine.dll 19 | 20 | 21 | ELIB = $(CRTLIBS) -lengine 22 | EINC = 23 | ECXFLAGS = -L.. -Wl,--subsystem,windows -nostdlib -e_WinMain@16 24 | ECFLAGS = -I..\core -mno-stack-arg-probe -Wall $(CRTFUNC) 25 | 26 | EOBJ = $(OBJDIR)\rsrc\run.o $(OBJDIR)\exec\exec.o $(OBJDIR)\run.o 27 | EOUT = ..\win32.exe 28 | 29 | 30 | RelJoined: LCFLAGS += -DLIB_NONE 31 | RelJoined: ECFLAGS += -DLIB_NONE 32 | RelJoined: lrelease 33 | RelJoined: erelease 34 | RelJoined: ECXFLAGS += -s 35 | RelJoined: jlink 36 | 37 | DbgJoined: LCFLAGS += -DLIB_NONE -D__always_inline__= 38 | DbgJoined: ECFLAGS += -DLIB_NONE -D__always_inline__= 39 | DbgJoined: ldebug 40 | DbgJoined: edebug 41 | DbgJoined: jlink 42 | 43 | RelLibOnly: lrelease 44 | RelLibOnly: LCXFLAGS += -s 45 | RelLibOnly: llink 46 | 47 | DbgLibOnly: ldebug 48 | DbgLibOnly: llink 49 | 50 | RelSplit: RelLibOnly 51 | RelSplit: erelease 52 | RelSplit: ECXFLAGS += -s 53 | RelSplit: elink 54 | 55 | DbgSplit: DbgLibOnly 56 | DbgSplit: edebug 57 | DbgSplit: elink 58 | 59 | 60 | cleanRelJoined: clean 61 | cleanDbgJoined: clean 62 | 63 | cleanRelSplit: clean 64 | cleanDbgSplit: clean 65 | 66 | cleanRelLibOnly: clean 67 | cleanDbgLibOnly: clean 68 | 69 | 70 | lrelease: LCFLAGS += -fexpensive-optimizations -O2 71 | lrelease: lbuild $(LOBJ) 72 | 73 | erelease: ECFLAGS += -fexpensive-optimizations -O2 74 | erelease: ebuild $(EOBJ) 75 | 76 | ldebug: LCFLAGS += -g -D__always_inline__= 77 | ldebug: lbuild $(LOBJ) 78 | 79 | edebug: ECFLAGS += -g -D__always_inline__= 80 | edebug: ebuild $(EOBJ) 81 | 82 | 83 | clean: 84 | if exist $(LOUT) del /F /Q $(LOUT) 85 | if exist $(EOUT) del /F /Q $(EOUT) 86 | if exist $(OBJDIR) rmdir /S /Q $(OBJDIR) 87 | 88 | lbuild: 89 | $(OBJDIR)\core\core.o: ..\core\core.c 90 | if not exist $(OBJDIR) mkdir $(OBJDIR) 91 | if not exist $(OBJDIR)\core mkdir $(OBJDIR)\core 92 | $(CC) $(LCFLAGS) $(LINC) -c ..\core\core.c -o $(OBJDIR)\core\core.o 93 | $(OBJDIR)\core\gif\gifstd.o: ..\core\gif\gifstd.c 94 | if not exist $(OBJDIR) mkdir $(OBJDIR) 95 | if not exist $(OBJDIR)\core mkdir $(OBJDIR)\core 96 | if not exist $(OBJDIR)\core\gif mkdir $(OBJDIR)\core\gif 97 | $(CC) $(LCFLAGS) $(LINC) -c ..\core\gif\gifstd.c -o $(OBJDIR)\core\gif\gifstd.o 98 | $(OBJDIR)\core\ogl\oglstd.o: ..\core\ogl\oglstd.c 99 | if not exist $(OBJDIR) mkdir $(OBJDIR) 100 | if not exist $(OBJDIR)\core mkdir $(OBJDIR)\core 101 | if not exist $(OBJDIR)\core\ogl mkdir $(OBJDIR)\core\ogl 102 | $(CC) $(LCFLAGS) $(LINC) -c ..\core\ogl\oglstd.c -o $(OBJDIR)\core\ogl\oglstd.o 103 | $(OBJDIR)\lib.o: lib.c 104 | if not exist $(OBJDIR) mkdir $(OBJDIR) 105 | $(CC) $(LCFLAGS) $(LINC) -c lib.c -o $(OBJDIR)\lib.o 106 | 107 | ebuild: 108 | $(OBJDIR)\rsrc\run.o: rsrc\run.rc 109 | if not exist $(OBJDIR) mkdir $(OBJDIR) 110 | if not exist $(OBJDIR)\rsrc mkdir $(OBJDIR)\rsrc 111 | $(RC) $(RCFLAGS) $(EINC) -i rsrc\run.rc -o $(OBJDIR)\rsrc\run.o 112 | $(OBJDIR)\exec\exec.o: ..\exec\exec.c 113 | if not exist $(OBJDIR) mkdir $(OBJDIR) 114 | if not exist $(OBJDIR)\exec mkdir $(OBJDIR)\exec 115 | $(CC) $(ECFLAGS) $(EINC) -c ..\exec\exec.c -o $(OBJDIR)\exec\exec.o 116 | $(OBJDIR)\run.o: run.c 117 | if not exist $(OBJDIR) mkdir $(OBJDIR) 118 | $(CC) $(ECFLAGS) $(EINC) -c run.c -o $(OBJDIR)\run.o 119 | 120 | jlink: $(LOBJ) $(EOBJ) 121 | $(CX) $(ECXFLAGS) $(LOBJ) $(EOBJ) -o $(EOUT) $(LLIB) 122 | 123 | llink: LCXFLAGS += -shared 124 | llink: $(LOBJ) 125 | $(CX) $(LCXFLAGS) $(LOBJ) -o $(LOUT) $(LLIB) 126 | 127 | elink: $(EOBJ) 128 | $(CX) $(ECXFLAGS) $(EOBJ) -o $(EOUT) $(ELIB) 129 | -------------------------------------------------------------------------------- /win32/lib.c: -------------------------------------------------------------------------------- 1 | #define _WIN32_IE 0x0300 2 | #define _WIN32_WINNT 0x0501 3 | #define WINVER _WIN32_WINNT 4 | 5 | #include 6 | #include 7 | 8 | 9 | 10 | char *_ConvertUTF8(char *utf8, long oldw) { 11 | if (utf8) { 12 | long size = (strlen(utf8) + 1) * 4; 13 | LPWSTR wide = calloc(size, sizeof(*wide)); 14 | 15 | MultiByteToWideChar(CP_UTF8, 0, utf8, -1, wide, size); 16 | if (!oldw) 17 | return (char*)wide; 18 | else { 19 | utf8 = calloc((size = (wcslen(wide) + 1) * 4), sizeof(*utf8)); 20 | WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wide, -1, 21 | utf8, size - 1, "#", 0); 22 | free(wide); 23 | } 24 | } 25 | return utf8; 26 | } 27 | 28 | 29 | 30 | void lRestartEngine(ENGD *engd) { 31 | intptr_t *data; 32 | 33 | cEngineCallback(engd, ECB_GUSR, (intptr_t)&data); 34 | data[0] = 0; 35 | } 36 | 37 | 38 | 39 | void lShowMainWindow(ENGD *engd, long show) { 40 | intptr_t *data; 41 | 42 | cEngineCallback(engd, ECB_GUSR, (intptr_t)&data); 43 | ShowWindow((HWND)data[0], (show)? SW_SHOW : SW_HIDE); 44 | } 45 | 46 | 47 | 48 | char *lLoadFile(char *name, long *size) { 49 | static long oldw = 2; 50 | char *retn; 51 | DWORD flen; 52 | HANDLE file; 53 | 54 | if (oldw == 2) 55 | oldw = ((GetVersion() & 0xFF) < 5)? 1 : 0; 56 | 57 | if (oldw) { 58 | name = _ConvertUTF8(name, oldw); 59 | file = CreateFileA(name, GENERIC_READ, FILE_SHARE_READ, 0, 60 | OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); 61 | } 62 | else { 63 | retn = malloc(strlen(name) + 5); 64 | retn[0] = retn[1] = retn[3] = '\\'; 65 | retn[2] = '?'; 66 | strcpy(retn + 4, name); 67 | for (flen = 0; retn[flen]; flen++) 68 | if (retn[flen] == '/') 69 | retn[flen] = '\\'; 70 | name = _ConvertUTF8(retn, oldw); 71 | file = CreateFileW((LPWSTR)name, GENERIC_READ, FILE_SHARE_READ, 0, 72 | OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); 73 | free(retn); 74 | } 75 | free(name); 76 | retn = 0; 77 | if (file != INVALID_HANDLE_VALUE) { 78 | flen = GetFileSize(file, 0); 79 | retn = malloc(flen + 1); 80 | ReadFile(file, retn, flen, &flen, 0); 81 | CloseHandle(file); 82 | retn[flen] = '\0'; 83 | if (size) 84 | *size = flen; 85 | } 86 | return retn; 87 | } 88 | 89 | 90 | 91 | void lMakeThread(void *thrd) { 92 | DWORD retn; 93 | 94 | CreateThread(0, 0, (LPTHREAD_START_ROUTINE)cThrdFunc, thrd, 0, &retn); 95 | } 96 | 97 | 98 | 99 | long lPickSemaphore(SEMD *drop, SEMD *pick, SEM_TYPE mask) { 100 | HANDLE *dobj = (typeof(dobj))drop + 1, *pobj = (typeof(pobj))pick + 1; 101 | long iter; 102 | 103 | for (iter = 0; iter < (long)dobj[-1]; iter++) 104 | if (mask & (1 << iter)) 105 | ResetEvent(dobj[iter]); 106 | 107 | for (iter = 0; iter < (long)pobj[-1]; iter++) 108 | if (mask & (1 << iter)) 109 | SetEvent(pobj[iter]); 110 | 111 | return TRUE; 112 | } 113 | 114 | 115 | 116 | SEM_TYPE lWaitSemaphore(SEMD *wait, SEM_TYPE mask) { 117 | HANDLE objs[MAXIMUM_WAIT_OBJECTS], *iter, *list = (typeof(list))wait + 1; 118 | SEM_TYPE retn, temp; 119 | long indx; 120 | 121 | if (mask) { 122 | indx = 0; 123 | iter = objs; 124 | retn = mask &= (1 << (long)list[-1]) - 1; 125 | while (retn) { 126 | *iter++ = list[temp = cFindBit(retn)]; 127 | retn &= ~(1 << temp); 128 | indx++; 129 | } 130 | WaitForMultipleObjects(indx, objs, TRUE, INFINITE); 131 | retn = mask; 132 | } 133 | else { 134 | retn = WaitForMultipleObjects((long)list[-1], list, FALSE, INFINITE); 135 | retn = (retn < MAXIMUM_WAIT_OBJECTS)? 1 << retn : 0; 136 | } 137 | return retn; 138 | } 139 | 140 | 141 | 142 | void lFreeSemaphore(SEMD **retn, long nthr) { 143 | HANDLE **list = (typeof(list))retn; 144 | long iter; 145 | 146 | if (list) { 147 | for (iter = 1; iter <= nthr; iter++) 148 | CloseHandle((*list)[iter]); 149 | free(*list); 150 | *list = 0; 151 | } 152 | } 153 | 154 | 155 | 156 | void lMakeSemaphore(SEMD **retn, long nthr, SEM_TYPE mask) { 157 | HANDLE **list = (typeof(list))retn; 158 | long iter; 159 | 160 | if (list) { 161 | *list = malloc((nthr + 1) * sizeof(**list)); 162 | (*list)[0] = (HANDLE)nthr; 163 | for (iter = 0; iter < nthr; iter++) 164 | (*list)[iter + 1] = CreateEvent(0, TRUE, (mask >> iter) & 1, 0); 165 | } 166 | } 167 | 168 | 169 | 170 | long lCountCPUs() { 171 | SYSTEM_INFO syin = {}; 172 | 173 | GetSystemInfo(&syin); 174 | return min(MAXIMUM_WAIT_OBJECTS, max(1, syin.dwNumberOfProcessors)); 175 | } 176 | 177 | 178 | 179 | uint64_t lTimeFunc() { 180 | static int32_t lock = 0; 181 | static int32_t hbit = 0; 182 | static int32_t lbit = 0; 183 | int64_t retn; 184 | 185 | /// yes, this is a spinlock, requiring at least a 80486 186 | while (__sync_fetch_and_or(&lock, 1)); 187 | 188 | retn = lbit; 189 | lbit = GetTickCount(); 190 | if ((retn < 0) && (lbit >= 0)) 191 | hbit++; 192 | retn = ((uint64_t)lbit & 0xFFFFFFFF) | ((uint64_t)hbit << 32); 193 | 194 | /// releasing the lock 195 | lock = 0; 196 | return retn; 197 | } 198 | 199 | 200 | 201 | LRESULT APIENTRY WindowProc(HWND hWnd, UINT uMsg, WPARAM wPrm, LPARAM lPrm) { 202 | switch (uMsg) { 203 | case WM_CREATE: 204 | SetWindowLongPtr(hWnd, GWLP_USERDATA, 205 | (LONG_PTR)((CREATESTRUCT*)lPrm)->lpCreateParams); 206 | SetTimer(hWnd, 1, 1000, 0); 207 | return 0; 208 | 209 | 210 | case WM_KEYDOWN: 211 | if (wPrm == VK_ESCAPE) 212 | case WM_CLOSE: 213 | cEngineCallback((ENGD*)GetWindowLongPtr(hWnd, GWLP_USERDATA), 214 | ECB_QUIT, ~0); 215 | return 0; 216 | 217 | 218 | case WM_LBUTTONDOWN: 219 | SetCapture(hWnd); 220 | return 0; 221 | 222 | 223 | case WM_LBUTTONUP: 224 | ReleaseCapture(); 225 | return 0; 226 | 227 | 228 | case WM_TIMER: { 229 | char fout[64]; 230 | 231 | cOutputFPS((ENGD*)GetWindowLongPtr(hWnd, GWLP_USERDATA), fout); 232 | SetWindowText(hWnd, fout); 233 | printf("%s\n", fout); 234 | return 0; 235 | } 236 | 237 | case WM_DESTROY: 238 | KillTimer(hWnd, 1); 239 | PostQuitMessage(0); 240 | return 0; 241 | 242 | 243 | default: 244 | return DefWindowProc(hWnd, uMsg, wPrm, lPrm); 245 | } 246 | } 247 | 248 | 249 | 250 | BOOL APIENTRY ULWstub(HWND hwnd, HDC hdst, POINT *pdst, SIZE *size, HDC hsrc, 251 | POINT *psrc, COLORREF ckey, BGRA *bptr, DWORD flgs) { 252 | enum {MAX_RECT = 2000}; 253 | long x, y, xpos, ypos; 254 | HRGN retn, temp; 255 | struct { 256 | RGNDATAHEADER head; 257 | RECT rect[MAX_RECT]; 258 | } rgns; 259 | 260 | /// manual stack checking 261 | ((DWORD*)rgns.rect)[7 * 1024] = 0; asm volatile("" ::: "memory"); 262 | ((DWORD*)rgns.rect)[6 * 1024] = 0; asm volatile("" ::: "memory"); 263 | ((DWORD*)rgns.rect)[5 * 1024] = 0; asm volatile("" ::: "memory"); 264 | ((DWORD*)rgns.rect)[4 * 1024] = 0; asm volatile("" ::: "memory"); 265 | ((DWORD*)rgns.rect)[3 * 1024] = 0; asm volatile("" ::: "memory"); 266 | ((DWORD*)rgns.rect)[2 * 1024] = 0; asm volatile("" ::: "memory"); 267 | ((DWORD*)rgns.rect)[1 * 1024] = 0; asm volatile("" ::: "memory"); 268 | ((DWORD*)rgns.rect)[0 * 1024] = 0; asm volatile("" ::: "memory"); 269 | 270 | if (flgs != ULW_OPAQUE) { 271 | retn = CreateRectRgn(0, 0, 0, 0); 272 | rgns.head = (RGNDATAHEADER){sizeof(rgns.head), RDH_RECTANGLES, 273 | 0, 0, {0, 0, size->cx, size->cy}}; 274 | for (y = size->cy - 1; y >= 0; y--) { 275 | ypos = (psrc->y < 0)? y : size->cy - 1 - y; 276 | for (xpos = 0, x = size->cx - 1; x >= 0; x--) { 277 | if (bptr[size->cx * y + x].chnl[3] && !xpos) 278 | xpos = x + 1; 279 | else if ((!bptr[size->cx * y + x].chnl[3] || !x) && xpos) { 280 | rgns.rect[rgns.head.nCount++] = 281 | (RECT){(!x && bptr[size->cx * y].chnl[3])? 0 : x + 1, 282 | ypos, xpos, ypos + 1}; 283 | xpos = 0; 284 | } 285 | if (!(x | y) || (rgns.head.nCount >= MAX_RECT)) { 286 | rgns.head.nRgnSize = rgns.head.nCount * sizeof(RECT); 287 | temp = ExtCreateRegion(0, rgns.head.dwSize + 288 | rgns.head.nRgnSize, 289 | (RGNDATA*)&rgns); 290 | CombineRgn(temp, temp, retn, RGN_OR); 291 | DeleteObject(retn); 292 | retn = temp; 293 | rgns.head.nCount = 0; 294 | } 295 | } 296 | } 297 | SetWindowRgn(hwnd, retn, TRUE); 298 | } 299 | BitBlt(hdst, 0, 0, size->cx, size->cy, hsrc, 0, 0, SRCCOPY); 300 | return TRUE; 301 | } 302 | 303 | 304 | 305 | void lRunMainLoop(ENGD *engd, long xpos, long ypos, long xdim, long ydim, 306 | BGRA **bptr, intptr_t *data, uint32_t flgs) { 307 | enum {EXT_ATTR = WS_EX_TOPMOST | WS_EX_TOOLWINDOW | WS_EX_LAYERED}; 308 | BLENDFUNCTION bstr = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA}; 309 | WNDCLASSEX wndc = {sizeof(wndc), CS_HREDRAW | CS_VREDRAW, WindowProc, 310 | 0, 0, 0, 0, LoadCursor(0, IDC_HAND), 0, 0, " ", 0}; 311 | PIXELFORMATDESCRIPTOR ppfd = {sizeof(ppfd), 1, PFD_SUPPORT_OPENGL | 312 | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER, 313 | PFD_TYPE_RGBA, 32}; 314 | BITMAPINFO bmpi = {{sizeof(bmpi.bmiHeader), 0, 0, 315 | 1, 8 * sizeof(BGRA), BI_RGB}}; 316 | struct { /// DWM_BLURBEHIND from DWM API (Vista and later) 317 | DWORD dwFlags; 318 | BOOL fEnable; /// dwFlags | 0x01 (DWM_BB_ENABLE) 319 | HRGN hRgnBlur; /// dwFlags | 0x02 (DWM_BB_BLURREGION) 320 | BOOL fTransition; /// dwFlags | 0x04 (DWM_BB_TRANSITIONONMAXIMIZED) 321 | } blur = {0x03, TRUE, CreateRectRgn(0, 0, 1, 1), TRUE}; 322 | SIZE dims = {xdim - xpos, ydim - ypos}; 323 | POINT cpos, mpos, zpos = {}; 324 | MSG pmsg = {}; 325 | 326 | BLENDFUNCTION *bfun; 327 | BOOL APIENTRY (*ULW)(HWND, HDC, POINT*, SIZE*, HDC, POINT*, 328 | COLORREF, BLENDFUNCTION*, DWORD) = 0; 329 | BOOL APIENTRY (*SLW)(HWND, COLORREF, BYTE, DWORD) = 0; 330 | HRESULT APIENTRY (*EBW)(HWND, typeof(blur)*) = 0; 331 | HRESULT APIENTRY (*ICE)(BOOL*) = 0; 332 | HINSTANCE husr, hdwm; 333 | UINT opts, attr; 334 | HDC devc, mwdc; 335 | HBITMAP hdib; 336 | HGLRC mwrc; 337 | HWND hwnd; 338 | BOOL comp; 339 | 340 | mwrc = 0; 341 | devc = CreateCompatibleDC(0); 342 | bmpi.bmiHeader.biWidth = dims.cx; 343 | bmpi.bmiHeader.biHeight = (~flgs & COM_RGPU)? -dims.cy : dims.cy; 344 | hdib = CreateDIBSection(devc, &bmpi, DIB_RGB_COLORS, (void*)bptr, 0, 0); 345 | SelectObject(devc, hdib); 346 | 347 | if ((hdwm = LoadLibrary("dwmapi"))) 348 | EBW = (typeof(EBW))GetProcAddress(hdwm, "DwmEnableBlurBehindWindow"); 349 | 350 | bfun = &bstr; 351 | opts = ULW_ALPHA; 352 | attr = EXT_ATTR; 353 | mpos = (POINT){xpos, ypos}; 354 | 355 | husr = LoadLibrary("user32"); 356 | if ((flgs & COM_OPAQ) || (flgs & WIN_IRGN) 357 | || !(ULW = (typeof(ULW))GetProcAddress(husr, "UpdateLayeredWindow"))) { 358 | bfun = (typeof(bfun))*bptr; 359 | zpos.y = bmpi.bmiHeader.biHeight; 360 | ULW = (typeof(ULW))ULWstub; 361 | attr &= ~WS_EX_LAYERED; 362 | if (flgs & COM_OPAQ) 363 | opts = ULW_OPAQUE; 364 | } 365 | RegisterClassEx(&wndc); 366 | hwnd = CreateWindowEx(attr, wndc.lpszClassName, 0, WS_POPUP | WS_VISIBLE, 367 | 0, 0, 0, 0, 0, 0, wndc.hInstance, engd); 368 | data[0] = (intptr_t)hwnd; 369 | mwdc = GetDC(hwnd); 370 | 371 | if (EBW) { 372 | comp = FALSE; 373 | ICE = (typeof(ICE))GetProcAddress(hdwm, "DwmIsCompositionEnabled"); 374 | /// if there`s DWM, there absolutely have to be layered windows 375 | SLW = (typeof(SLW))GetProcAddress(husr, "SetLayeredWindowAttributes"); 376 | ICE(&comp); 377 | attr = GetVersion(); 378 | /// major 6 minor 1 is Win7; in newer versions ICE() is lying to us 379 | if (!comp && (MAKEWORD(HIBYTE(attr), LOBYTE(attr)) <= 0x0601)) 380 | EBW = 0; 381 | else { 382 | /// does nothing visible to the window, 383 | /// but enables input transparency! 384 | SLW(hwnd, 0x000000, 0xFF, 2 /** 2 == LWA_ALPHA **/); 385 | EBW(hwnd, &blur); 386 | } 387 | } 388 | if (flgs & COM_RGPU) { 389 | ppfd.iLayerType = PFD_MAIN_PLANE; 390 | SetPixelFormat(mwdc, ChoosePixelFormat(mwdc, &ppfd), &ppfd); 391 | wglMakeCurrent(mwdc, mwrc = wglCreateContext(mwdc)); 392 | } 393 | /// "+1" is a dirty hack to not let Windows consider us fullscreen if OGL 394 | /// is active: all sorts of weird things happen to fullscreen OGL windows 395 | /// when they are DWM + layered, at least on Intel HD 3000 + Vista / Win7 396 | SetWindowPos(hwnd, 0, mpos.x, mpos.y, dims.cx, dims.cy + ((EBW)? 1 : 0), 397 | SWP_NOZORDER | SWP_NOACTIVATE); 398 | lShowMainWindow(engd, flgs & COM_SHOW); 399 | while (data[0]) { 400 | if (PeekMessage(&pmsg, 0, 0, 0, PM_REMOVE)) { 401 | TranslateMessage(&pmsg); 402 | DispatchMessage(&pmsg); 403 | continue; 404 | } 405 | GetCursorPos(&cpos); 406 | ScreenToClient(hwnd, &cpos); 407 | attr = ((GetActiveWindow() == hwnd)? UFR_MOUS : 0) 408 | | ((GetAsyncKeyState(VK_LBUTTON))? UFR_LBTN : 0) 409 | | ((GetAsyncKeyState(VK_MBUTTON))? UFR_MBTN : 0) 410 | | ((GetAsyncKeyState(VK_RBUTTON))? UFR_RBTN : 0) 411 | | ((GetAsyncKeyState(VK_UP ))? UFR_PL1W : 0) 412 | | ((GetAsyncKeyState(VK_DOWN ))? UFR_PL1S : 0) 413 | | ((GetAsyncKeyState(VK_LEFT ))? UFR_PL1A : 0) 414 | | ((GetAsyncKeyState(VK_RIGHT ))? UFR_PL1D : 0) 415 | | ((GetAsyncKeyState('W' ))? UFR_PL2W : 0) 416 | | ((GetAsyncKeyState('S' ))? UFR_PL2S : 0) 417 | | ((GetAsyncKeyState('A' ))? UFR_PL2A : 0) 418 | | ((GetAsyncKeyState('D' ))? UFR_PL2D : 0); 419 | attr = cPrepareFrame(engd, cpos.x, cpos.y, attr); 420 | if (attr & PFR_SKIP) 421 | Sleep(1); 422 | if (!IsWindow(hwnd)) 423 | break; 424 | if (attr & PFR_HALT) 425 | continue; 426 | if (~flgs & COM_RGPU) 427 | BitBlt(devc, 0, 0, dims.cx, dims.cy, mwdc, 0, 0, BLACKNESS); 428 | cOutputFrame(engd, !EBW); 429 | if (!EBW) 430 | ULW(hwnd, mwdc, &mpos, &dims, devc, &zpos, 0, bfun, opts); 431 | else { 432 | if (flgs & COM_RGPU) 433 | SwapBuffers(mwdc); 434 | else 435 | BitBlt(mwdc, 0, 0, dims.cx, dims.cy, devc, 0, 0, SRCCOPY); 436 | SetWindowLongPtr(hwnd, GWL_EXSTYLE, (attr & PFR_PICK)? EXT_ATTR : 437 | WS_EX_TRANSPARENT | EXT_ATTR); 438 | } 439 | } 440 | cDeallocFrame(engd, !EBW); 441 | if (flgs & COM_RGPU) { 442 | wglMakeCurrent(0, 0); 443 | wglDeleteContext(mwrc); 444 | } 445 | KillTimer(hwnd, 1); 446 | ReleaseDC(hwnd, mwdc); 447 | DestroyWindow(hwnd); 448 | DeleteDC(mwdc); 449 | DeleteDC(devc); 450 | DeleteObject(hdib); 451 | DeleteObject(blur.hRgnBlur); 452 | /// mandatory: we are a library, not an application 453 | UnregisterClass(wndc.lpszClassName, wndc.hInstance); 454 | /// finally, we need to purge the message queue, as it may be reused, 455 | /// and nobody wants garbage messages for windows that are long gone 456 | while (PeekMessage(&pmsg, 0, 0, 0, PM_REMOVE)); 457 | FreeLibrary(husr); 458 | FreeLibrary(hdwm); 459 | } 460 | -------------------------------------------------------------------------------- /win32/rsrc/main.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hidefromkgb/DPengine/6ebd22021a4fc87152bf66edca8cf7ebb00ab9ba/win32/rsrc/main.ico -------------------------------------------------------------------------------- /win32/rsrc/mfst.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /win32/rsrc/run.rc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | 5 | LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL 6 | 1 ICON "main.ico" 7 | 8 | 9 | 10 | LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL 11 | 1 VERSIONINFO 12 | FILEVERSION 0,0,0,0 13 | PRODUCTVERSION 0,0,0,0 14 | FILEOS VOS__WINDOWS32 15 | FILETYPE VFT_APP 16 | FILESUBTYPE VFT2_UNKNOWN 17 | FILEFLAGSMASK 0 18 | FILEFLAGS 0 19 | { 20 | BLOCK "StringFileInfo" 21 | { 22 | BLOCK "000004B0" 23 | { 24 | VALUE "Comments", "Based off the idea of Desktop Ponies by DesktopPonyGuy and RoosterDragon" 25 | VALUE "CompanyName", "hidefromkgb" 26 | VALUE "FileDescription", "DPE launcher" 27 | VALUE "FileVersion", "" 28 | VALUE "InternalName", "DPE" 29 | VALUE "LegalCopyright", "CC BY-NC-SA 3.0" 30 | VALUE "LegalTrademarks", "" 31 | VALUE "OriginalFilename", "win32.exe" 32 | VALUE "PrivateBuild", "" 33 | VALUE "ProductName", "Desktop Ponies Engine" 34 | VALUE "ProductVersion", "[early alpha]" 35 | VALUE "SpecialBuild", "" 36 | } 37 | } 38 | BLOCK "VarFileInfo" 39 | { 40 | VALUE "Translation", 0x0000, 0x04B0 41 | } 42 | } 43 | 44 | 45 | 46 | LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL 47 | 1 RT_MANIFEST "mfst.xml" 48 | --------------------------------------------------------------------------------