Newer
Older
8001
8002
8003
8004
8005
8006
8007
8008
8009
8010
8011
8012
8013
8014
8015
8016
8017
8018
8019
8020
8021
8022
8023
8024
8025
8026
8027
8028
8029
8030
8031
8032
8033
8034
8035
8036
8037
8038
8039
8040
8041
8042
8043
8044
8045
8046
8047
8048
8049
8050
8051
8052
8053
8054
8055
8056
8057
8058
8059
8060
8061
8062
8063
8064
8065
8066
8067
8068
8069
8070
8071
8072
8073
8074
8075
8076
8077
8078
8079
8080
8081
8082
8083
8084
8085
8086
8087
8088
8089
8090
8091
8092
8093
8094
8095
8096
8097
8098
8099
8100
8101
8102
8103
8104
8105
8106
8107
8108
8109
8110
8111
8112
8113
8114
8115
8116
8117
8118
8119
8120
8121
8122
8123
8124
8125
8126
8127
8128
8129
8130
8131
8132
8133
8134
8135
8136
8137
8138
8139
8140
8141
8142
8143
8144
8145
8146
8147
8148
8149
8150
8151
8152
8153
8154
8155
8156
8157
8158
8159
8160
8161
8162
8163
8164
8165
8166
8167
8168
8169
8170
8171
8172
8173
8174
8175
8176
8177
8178
8179
8180
8181
8182
8183
8184
8185
8186
8187
8188
8189
8190
8191
8192
8193
8194
8195
8196
8197
8198
8199
8200
8201
8202
8203
8204
8205
8206
8207
8208
8209
8210
8211
8212
8213
8214
8215
8216
8217
8218
8219
8220
8221
8222
8223
8224
8225
8226
8227
8228
8229
8230
8231
8232
8233
8234
8235
8236
8237
8238
8239
8240
8241
8242
8243
8244
8245
8246
8247
8248
8249
8250
8251
8252
8253
8254
8255
8256
8257
8258
8259
8260
8261
8262
8263
8264
8265
8266
8267
8268
8269
8270
8271
8272
8273
8274
8275
8276
8277
8278
8279
8280
8281
8282
8283
8284
8285
8286
8287
8288
8289
8290
8291
8292
8293
8294
8295
8296
8297
8298
8299
8300
8301
8302
8303
8304
8305
8306
8307
8308
8309
8310
8311
8312
8313
8314
8315
8316
8317
8318
8319
8320
8321
8322
8323
8324
8325
8326
8327
8328
8329
8330
8331
8332
8333
8334
8335
8336
8337
8338
8339
8340
8341
8342
8343
8344
8345
8346
8347
8348
8349
8350
8351
8352
8353
8354
8355
8356
8357
8358
8359
8360
8361
8362
8363
8364
8365
8366
8367
8368
8369
8370
8371
8372
8373
8374
8375
8376
8377
8378
8379
8380
8381
8382
8383
8384
8385
8386
8387
8388
8389
8390
8391
8392
8393
8394
8395
8396
8397
8398
8399
8400
8401
8402
8403
8404
8405
8406
8407
8408
8409
8410
8411
8412
8413
8414
8415
8416
8417
8418
8419
8420
8421
8422
8423
8424
8425
8426
8427
8428
8429
8430
8431
8432
8433
8434
8435
8436
8437
8438
8439
8440
8441
8442
8443
8444
8445
8446
8447
8448
8449
8450
8451
8452
8453
8454
8455
8456
8457
8458
8459
8460
8461
8462
8463
8464
8465
8466
8467
8468
8469
8470
8471
8472
8473
8474
8475
8476
8477
8478
8479
8480
8481
8482
8483
8484
8485
8486
8487
8488
8489
8490
8491
8492
8493
8494
8495
8496
8497
8498
8499
8500
8501
8502
8503
8504
8505
8506
8507
8508
8509
8510
8511
8512
8513
8514
8515
8516
8517
8518
8519
8520
8521
8522
8523
8524
8525
8526
8527
8528
8529
8530
8531
8532
8533
8534
8535
8536
8537
8538
8539
8540
8541
8542
8543
8544
8545
8546
8547
8548
8549
8550
8551
8552
8553
8554
8555
8556
8557
8558
8559
8560
8561
8562
8563
8564
8565
8566
8567
8568
8569
8570
8571
8572
8573
8574
8575
8576
8577
8578
8579
8580
8581
8582
8583
8584
8585
8586
8587
8588
8589
8590
8591
8592
8593
8594
8595
8596
8597
8598
8599
8600
8601
8602
8603
8604
8605
8606
8607
8608
8609
8610
8611
8612
8613
8614
8615
8616
8617
8618
8619
8620
8621
8622
8623
8624
8625
8626
8627
8628
8629
8630
8631
8632
8633
8634
8635
8636
8637
8638
8639
8640
8641
8642
8643
8644
8645
8646
8647
8648
8649
8650
8651
8652
8653
8654
8655
8656
8657
8658
8659
8660
8661
8662
8663
8664
8665
8666
8667
8668
8669
8670
8671
8672
8673
8674
8675
8676
8677
8678
8679
8680
8681
8682
8683
8684
8685
8686
8687
8688
8689
8690
8691
8692
8693
8694
8695
8696
8697
8698
8699
8700
8701
8702
8703
8704
8705
8706
8707
8708
8709
8710
8711
8712
8713
8714
8715
8716
8717
8718
8719
8720
8721
8722
8723
8724
8725
8726
8727
8728
8729
8730
8731
8732
8733
8734
8735
8736
8737
8738
8739
8740
8741
8742
8743
8744
8745
8746
8747
8748
8749
8750
8751
8752
8753
8754
8755
8756
8757
8758
8759
8760
8761
8762
8763
8764
8765
8766
8767
8768
8769
8770
8771
8772
8773
8774
8775
8776
8777
8778
8779
8780
8781
8782
8783
8784
8785
8786
8787
8788
8789
8790
8791
8792
8793
8794
8795
8796
8797
8798
8799
8800
8801
8802
8803
8804
8805
8806
8807
8808
8809
8810
8811
8812
8813
8814
8815
8816
8817
8818
8819
8820
8821
8822
8823
8824
8825
8826
8827
8828
8829
8830
8831
8832
8833
8834
8835
8836
8837
8838
8839
8840
8841
8842
8843
8844
8845
8846
8847
8848
8849
8850
8851
8852
8853
8854
8855
8856
8857
8858
8859
8860
8861
8862
8863
8864
8865
8866
8867
8868
8869
8870
8871
8872
8873
8874
8875
8876
8877
8878
8879
8880
8881
8882
8883
8884
8885
8886
8887
8888
8889
8890
8891
8892
8893
8894
8895
8896
8897
8898
8899
8900
8901
8902
8903
8904
8905
8906
8907
8908
8909
8910
8911
8912
8913
8914
8915
8916
8917
8918
8919
8920
8921
8922
8923
8924
8925
8926
8927
8928
8929
8930
8931
8932
8933
8934
8935
8936
8937
8938
8939
8940
8941
8942
8943
8944
8945
8946
8947
8948
8949
8950
8951
8952
8953
8954
8955
8956
8957
8958
8959
8960
8961
8962
8963
8964
8965
8966
8967
8968
8969
8970
8971
8972
8973
8974
8975
8976
8977
8978
8979
8980
8981
8982
8983
8984
8985
8986
8987
8988
8989
8990
8991
8992
8993
8994
8995
8996
8997
8998
8999
9000
const T *ptr = ptrs;
poffx = offx;
for (unsigned int x = 0; x<wd; ++x) { *(ptrd++) = *ptr; ptr+=*(poffx++); }
++y;
unsigned int dy = *(poffy++);
for ( ; !dy && y<hd; std::memcpy(ptrd,ptrd - wd,sizeof(t)*wd), ++y, ptrd+=wd, dy = *(poffy++)) {}
ptrs+=dy;
}
delete[] offx; delete[] offy;
}
//! Set normalization type.
/**
\param normalization New normalization mode.
**/
CImgDisplay& set_normalization(const unsigned int normalization) {
_normalization = normalization;
_min = _max = 0;
return *this;
}
#if cimg_display==0
//! Set title of the associated window.
/**
\param format C-string containing the format of the title, as with <tt>std::printf()</tt>.
\warning As the first argument is a format string, it is highly recommended to write
\code
disp.set_title("%s",window_title);
\endcode
instead of
\code
disp.set_title(window_title);
\endcode
if \c window_title can be arbitrary, to prevent nasty memory access.
**/
CImgDisplay& set_title(const char *const format, ...) {
return assign(0,0,format);
}
#endif
//! Enable or disable fullscreen mode.
/**
\param is_fullscreen Tells is the fullscreen mode must be activated or not.
\param force_redraw Tells if the previous window content must be displayed as well.
\note
- When the fullscreen mode is enabled, the associated window fills the entire screen but the size of the
current display is not modified.
- The screen resolution may be switched to fit the associated window size and ensure it appears the largest
as possible.
For X-Window (X11) users, the configuration flag \c cimg_use_xrandr has to be set to allow the screen
resolution change (requires the X11 extensions to be enabled).
**/
CImgDisplay& set_fullscreen(const bool is_fullscreen, const bool force_redraw=true) {
if (is_empty() || _is_fullscreen==is_fullscreen) return *this;
return toggle_fullscreen(force_redraw);
}
#if cimg_display==0
//! Toggle fullscreen mode.
/**
\param force_redraw Tells if the previous window content must be displayed as well.
\note Enable fullscreen mode if it was not enabled, and disable it otherwise.
**/
CImgDisplay& toggle_fullscreen(const bool force_redraw=true) {
return assign(_width,_height,0,3,force_redraw);
}
//! Show mouse pointer.
/**
\note Depending on the window manager behavior, this method may not succeed
(no exceptions are thrown nevertheless).
**/
CImgDisplay& show_mouse() {
return assign();
}
//! Hide mouse pointer.
/**
\note Depending on the window manager behavior, this method may not succeed
(no exceptions are thrown nevertheless).
**/
CImgDisplay& hide_mouse() {
return assign();
}
//! Move mouse pointer to a specified location.
/**
\note Depending on the window manager behavior, this method may not succeed
(no exceptions are thrown nevertheless).
**/
CImgDisplay& set_mouse(const int pos_x, const int pos_y) {
return assign(pos_x,pos_y);
}
#endif
//! Simulate a mouse button release event.
/**
\note All mouse buttons are considered released at the same time.
**/
CImgDisplay& set_button() {
_button = 0;
_is_event = true;
#if cimg_display==1
pthread_cond_broadcast(&cimg::X11_attr().wait_event);
#elif cimg_display==2
SetEvent(cimg::Win32_attr().wait_event);
#endif
return *this;
}
//! Simulate a mouse button press or release event.
/**
\param button Buttons event code, where each button is associated to a single bit.
\param is_pressed Tells if the mouse button is considered as pressed or released.
**/
CImgDisplay& set_button(const unsigned int button, const bool is_pressed=true) {
const unsigned int buttoncode = button==1U?1U:button==2U?2U:button==3U?4U:0U;
if (is_pressed) _button |= buttoncode; else _button &= ~buttoncode;
_is_event = buttoncode?true:false;
if (buttoncode) {
#if cimg_display==1
pthread_cond_broadcast(&cimg::X11_attr().wait_event);
#elif cimg_display==2
SetEvent(cimg::Win32_attr().wait_event);
#endif
}
return *this;
}
//! Flush all mouse wheel events.
/**
\note Make wheel() to return \c 0, if called afterwards.
**/
CImgDisplay& set_wheel() {
_wheel = 0;
_is_event = true;
#if cimg_display==1
pthread_cond_broadcast(&cimg::X11_attr().wait_event);
#elif cimg_display==2
SetEvent(cimg::Win32_attr().wait_event);
#endif
return *this;
}
//! Simulate a wheel event.
/**
\param amplitude Amplitude of the wheel scrolling to simulate.
\note Make wheel() to return \c amplitude, if called afterwards.
**/
CImgDisplay& set_wheel(const int amplitude) {
_wheel+=amplitude;
_is_event = amplitude?true:false;
if (amplitude) {
#if cimg_display==1
pthread_cond_broadcast(&cimg::X11_attr().wait_event);
#elif cimg_display==2
SetEvent(cimg::Win32_attr().wait_event);
#endif
}
return *this;
}
//! Flush all key events.
/**
\note Make key() to return \c 0, if called afterwards.
**/
CImgDisplay& set_key() {
std::memset((void*)_keys,0,128*sizeof(unsigned int));
std::memset((void*)_released_keys,0,128*sizeof(unsigned int));
_is_keyESC = _is_keyF1 = _is_keyF2 = _is_keyF3 = _is_keyF4 = _is_keyF5 = _is_keyF6 = _is_keyF7 = _is_keyF8 =
_is_keyF9 = _is_keyF10 = _is_keyF11 = _is_keyF12 = _is_keyPAUSE = _is_key1 = _is_key2 = _is_key3 = _is_key4 =
_is_key5 = _is_key6 = _is_key7 = _is_key8 = _is_key9 = _is_key0 = _is_keyBACKSPACE = _is_keyINSERT =
_is_keyHOME = _is_keyPAGEUP = _is_keyTAB = _is_keyQ = _is_keyW = _is_keyE = _is_keyR = _is_keyT = _is_keyY =
_is_keyU = _is_keyI = _is_keyO = _is_keyP = _is_keyDELETE = _is_keyEND = _is_keyPAGEDOWN = _is_keyCAPSLOCK =
_is_keyA = _is_keyS = _is_keyD = _is_keyF = _is_keyG = _is_keyH = _is_keyJ = _is_keyK = _is_keyL =
_is_keyENTER = _is_keySHIFTLEFT = _is_keyZ = _is_keyX = _is_keyC = _is_keyV = _is_keyB = _is_keyN =
_is_keyM = _is_keySHIFTRIGHT = _is_keyARROWUP = _is_keyCTRLLEFT = _is_keyAPPLEFT = _is_keyALT = _is_keySPACE =
_is_keyALTGR = _is_keyAPPRIGHT = _is_keyMENU = _is_keyCTRLRIGHT = _is_keyARROWLEFT = _is_keyARROWDOWN =
_is_keyARROWRIGHT = _is_keyPAD0 = _is_keyPAD1 = _is_keyPAD2 = _is_keyPAD3 = _is_keyPAD4 = _is_keyPAD5 =
_is_keyPAD6 = _is_keyPAD7 = _is_keyPAD8 = _is_keyPAD9 = _is_keyPADADD = _is_keyPADSUB = _is_keyPADMUL =
_is_keyPADDIV = false;
_is_event = true;
#if cimg_display==1
pthread_cond_broadcast(&cimg::X11_attr().wait_event);
#elif cimg_display==2
SetEvent(cimg::Win32_attr().wait_event);
#endif
return *this;
}
//! Simulate a keyboard press/release event.
/**
\param keycode Keycode of the associated key.
\param is_pressed Tells if the key is considered as pressed or released.
\note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
your code stay portable (see cimg::keyESC).
**/
CImgDisplay& set_key(const unsigned int keycode, const bool is_pressed=true) {
#define _cimg_set_key(k) if (keycode==cimg::key##k) _is_key##k = is_pressed;
_cimg_set_key(ESC); _cimg_set_key(F1); _cimg_set_key(F2); _cimg_set_key(F3);
_cimg_set_key(F4); _cimg_set_key(F5); _cimg_set_key(F6); _cimg_set_key(F7);
_cimg_set_key(F8); _cimg_set_key(F9); _cimg_set_key(F10); _cimg_set_key(F11);
_cimg_set_key(F12); _cimg_set_key(PAUSE); _cimg_set_key(1); _cimg_set_key(2);
_cimg_set_key(3); _cimg_set_key(4); _cimg_set_key(5); _cimg_set_key(6);
_cimg_set_key(7); _cimg_set_key(8); _cimg_set_key(9); _cimg_set_key(0);
_cimg_set_key(BACKSPACE); _cimg_set_key(INSERT); _cimg_set_key(HOME);
_cimg_set_key(PAGEUP); _cimg_set_key(TAB); _cimg_set_key(Q); _cimg_set_key(W);
_cimg_set_key(E); _cimg_set_key(R); _cimg_set_key(T); _cimg_set_key(Y);
_cimg_set_key(U); _cimg_set_key(I); _cimg_set_key(O); _cimg_set_key(P);
_cimg_set_key(DELETE); _cimg_set_key(END); _cimg_set_key(PAGEDOWN);
_cimg_set_key(CAPSLOCK); _cimg_set_key(A); _cimg_set_key(S); _cimg_set_key(D);
_cimg_set_key(F); _cimg_set_key(G); _cimg_set_key(H); _cimg_set_key(J);
_cimg_set_key(K); _cimg_set_key(L); _cimg_set_key(ENTER);
_cimg_set_key(SHIFTLEFT); _cimg_set_key(Z); _cimg_set_key(X); _cimg_set_key(C);
_cimg_set_key(V); _cimg_set_key(B); _cimg_set_key(N); _cimg_set_key(M);
_cimg_set_key(SHIFTRIGHT); _cimg_set_key(ARROWUP); _cimg_set_key(CTRLLEFT);
_cimg_set_key(APPLEFT); _cimg_set_key(ALT); _cimg_set_key(SPACE); _cimg_set_key(ALTGR);
_cimg_set_key(APPRIGHT); _cimg_set_key(MENU); _cimg_set_key(CTRLRIGHT);
_cimg_set_key(ARROWLEFT); _cimg_set_key(ARROWDOWN); _cimg_set_key(ARROWRIGHT);
_cimg_set_key(PAD0); _cimg_set_key(PAD1); _cimg_set_key(PAD2);
_cimg_set_key(PAD3); _cimg_set_key(PAD4); _cimg_set_key(PAD5);
_cimg_set_key(PAD6); _cimg_set_key(PAD7); _cimg_set_key(PAD8);
_cimg_set_key(PAD9); _cimg_set_key(PADADD); _cimg_set_key(PADSUB);
_cimg_set_key(PADMUL); _cimg_set_key(PADDIV);
if (is_pressed) {
if (*_keys)
std::memmove((void*)(_keys + 1),(void*)_keys,127*sizeof(unsigned int));
*_keys = keycode;
if (*_released_keys) {
std::memmove((void*)(_released_keys + 1),(void*)_released_keys,127*sizeof(unsigned int));
*_released_keys = 0;
}
} else {
if (*_keys) {
std::memmove((void*)(_keys + 1),(void*)_keys,127*sizeof(unsigned int));
*_keys = 0;
}
if (*_released_keys)
std::memmove((void*)(_released_keys + 1),(void*)_released_keys,127*sizeof(unsigned int));
*_released_keys = keycode;
}
_is_event = keycode?true:false;
if (keycode) {
#if cimg_display==1
pthread_cond_broadcast(&cimg::X11_attr().wait_event);
#elif cimg_display==2
SetEvent(cimg::Win32_attr().wait_event);
#endif
}
return *this;
}
//! Flush all display events.
/**
\note Remove all passed events from the current display.
**/
CImgDisplay& flush() {
set_key().set_button().set_wheel();
_is_resized = _is_moved = _is_event = false;
_fps_timer = _fps_frames = _timer = 0;
_fps_fps = 0;
return *this;
}
//! Wait for any user event occuring on the current display.
CImgDisplay& wait() {
wait(*this);
return *this;
}
//! Wait for a given number of milliseconds since the last call to wait().
/**
\param milliseconds Number of milliseconds to wait for.
\note Similar to cimg::wait().
**/
CImgDisplay& wait(const unsigned int milliseconds) {
cimg::_wait(milliseconds,_timer);
return *this;
}
//! Wait for any event occuring on the display \c disp1.
static void wait(CImgDisplay& disp1) {
disp1._is_event = false;
while (!disp1._is_closed && !disp1._is_event) wait_all();
}
//! Wait for any event occuring either on the display \c disp1 or \c disp2.
static void wait(CImgDisplay& disp1, CImgDisplay& disp2) {
disp1._is_event = disp2._is_event = false;
while ((!disp1._is_closed || !disp2._is_closed) &&
!disp1._is_event && !disp2._is_event) wait_all();
}
//! Wait for any event occuring either on the display \c disp1, \c disp2 or \c disp3.
static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3) {
disp1._is_event = disp2._is_event = disp3._is_event = false;
while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed) &&
!disp1._is_event && !disp2._is_event && !disp3._is_event) wait_all();
}
//! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3 or \c disp4.
static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4) {
disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = false;
while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed) &&
!disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event) wait_all();
}
//! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4 or \c disp5.
static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4,
CImgDisplay& disp5) {
disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = false;
while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed) &&
!disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event)
wait_all();
}
//! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp6.
static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
CImgDisplay& disp6) {
disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
disp6._is_event = false;
while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
!disp6._is_closed) &&
!disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
!disp6._is_event) wait_all();
}
//! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp7.
static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
CImgDisplay& disp6, CImgDisplay& disp7) {
disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
disp6._is_event = disp7._is_event = false;
while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
!disp6._is_closed || !disp7._is_closed) &&
!disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
!disp6._is_event && !disp7._is_event) wait_all();
}
//! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp8.
static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8) {
disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
disp6._is_event = disp7._is_event = disp8._is_event = false;
while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
!disp6._is_closed || !disp7._is_closed || !disp8._is_closed) &&
!disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
!disp6._is_event && !disp7._is_event && !disp8._is_event) wait_all();
}
//! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp9.
static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8, CImgDisplay& disp9) {
disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
disp6._is_event = disp7._is_event = disp8._is_event = disp9._is_event = false;
while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
!disp6._is_closed || !disp7._is_closed || !disp8._is_closed || !disp9._is_closed) &&
!disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
!disp6._is_event && !disp7._is_event && !disp8._is_event && !disp9._is_event) wait_all();
}
//! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp10.
static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8, CImgDisplay& disp9,
CImgDisplay& disp10) {
disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
disp6._is_event = disp7._is_event = disp8._is_event = disp9._is_event = disp10._is_event = false;
while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
!disp6._is_closed || !disp7._is_closed || !disp8._is_closed || !disp9._is_closed || !disp10._is_closed) &&
!disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
!disp6._is_event && !disp7._is_event && !disp8._is_event && !disp9._is_event && !disp10._is_event)
wait_all();
}
#if cimg_display==0
//! Wait for any window event occuring in any opened CImgDisplay.
static void wait_all() {
return _no_display_exception();
}
//! Render image into internal display buffer.
/**
\param img Input image data to render.
\note
- Convert image data representation into the internal display buffer (architecture-dependent structure).
- The content of the associated window is not modified, until paint() is called.
- Should not be used for common CImgDisplay uses, since display() is more useful.
**/
template<typename T>
CImgDisplay& render(const CImg<T>& img) {
return assign(img);
}
//! Paint internal display buffer on associated window.
/**
\note
- Update the content of the associated window with the internal display buffer, e.g. after a render() call.
- Should not be used for common CImgDisplay uses, since display() is more useful.
**/
CImgDisplay& paint() {
return assign();
}
//! Take a snapshot of the current screen content.
/**
\param x0 X-coordinate of the upper left corner.
\param y0 Y-coordinate of the upper left corner.
\param x1 X-coordinate of the lower right corner.
\param y1 Y-coordinate of the lower right corner.
\param[out] img Output screenshot. Can be empty on input
**/
template<typename T>
static void screenshot(const int x0, const int y0, const int x1, const int y1, CImg<T>& img) {
cimg::unused(x0,y0,x1,y1,&img);
_no_display_exception();
}
//! Take a snapshot of the associated window content.
/**
\param[out] img Output snapshot. Can be empty on input.
**/
template<typename T>
const CImgDisplay& snapshot(CImg<T>& img) const {
cimg::unused(img);
_no_display_exception();
return *this;
}
#endif
// X11-based implementation
//--------------------------
#if cimg_display==1
Atom _wm_window_atom, _wm_protocol_atom;
Window _window, _background_window;
Colormap _colormap;
XImage *_image;
void *_data;
#ifdef cimg_use_xshm
XShmSegmentInfo *_shminfo;
#endif
static int screen_width() {
Display *const dpy = cimg::X11_attr().display;
int res = 0;
if (!dpy) {
Display *const _dpy = XOpenDisplay(0);
if (!_dpy)
throw CImgDisplayException("CImgDisplay::screen_width(): Failed to open X11 display.");
res = DisplayWidth(_dpy,DefaultScreen(_dpy));
XCloseDisplay(_dpy);
} else {
#ifdef cimg_use_xrandr
if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution)
res = cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].width;
else res = DisplayWidth(dpy,DefaultScreen(dpy));
#else
res = DisplayWidth(dpy,DefaultScreen(dpy));
#endif
}
return res;
}
static int screen_height() {
Display *const dpy = cimg::X11_attr().display;
int res = 0;
if (!dpy) {
Display *const _dpy = XOpenDisplay(0);
if (!_dpy)
throw CImgDisplayException("CImgDisplay::screen_height(): Failed to open X11 display.");
res = DisplayHeight(_dpy,DefaultScreen(_dpy));
XCloseDisplay(_dpy);
} else {
#ifdef cimg_use_xrandr
if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution)
res = cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].height;
else res = DisplayHeight(dpy,DefaultScreen(dpy));
#else
res = DisplayHeight(dpy,DefaultScreen(dpy));
#endif
}
return res;
}
static void wait_all() {
if (!cimg::X11_attr().display) return;
pthread_mutex_lock(&cimg::X11_attr().wait_event_mutex);
pthread_cond_wait(&cimg::X11_attr().wait_event,&cimg::X11_attr().wait_event_mutex);
pthread_mutex_unlock(&cimg::X11_attr().wait_event_mutex);
}
void _handle_events(const XEvent *const pevent) {
Display *const dpy = cimg::X11_attr().display;
XEvent event = *pevent;
switch (event.type) {
case ClientMessage : {
if ((int)event.xclient.message_type==(int)_wm_protocol_atom &&
(int)event.xclient.data.l[0]==(int)_wm_window_atom) {
XUnmapWindow(cimg::X11_attr().display,_window);
_is_closed = _is_event = true;
pthread_cond_broadcast(&cimg::X11_attr().wait_event);
}
} break;
case ConfigureNotify : {
while (XCheckWindowEvent(dpy,_window,StructureNotifyMask,&event)) {}
const unsigned int nw = event.xconfigure.width, nh = event.xconfigure.height;
const int nx = event.xconfigure.x, ny = event.xconfigure.y;
if (nw && nh && (nw!=_window_width || nh!=_window_height)) {
_window_width = nw; _window_height = nh; _mouse_x = _mouse_y = -1;
XResizeWindow(dpy,_window,_window_width,_window_height);
_is_resized = _is_event = true;
pthread_cond_broadcast(&cimg::X11_attr().wait_event);
}
if (nx!=_window_x || ny!=_window_y) {
_window_x = nx; _window_y = ny; _is_moved = _is_event = true;
pthread_cond_broadcast(&cimg::X11_attr().wait_event);
}
} break;
case Expose : {
while (XCheckWindowEvent(dpy,_window,ExposureMask,&event)) {}
_paint(false);
if (_is_fullscreen) {
XWindowAttributes attr;
XGetWindowAttributes(dpy,_window,&attr);
while (attr.map_state!=IsViewable) XSync(dpy,0);
XSetInputFocus(dpy,_window,RevertToParent,CurrentTime);
}
} break;
case ButtonPress : {
do {
_mouse_x = event.xmotion.x; _mouse_y = event.xmotion.y;
if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
switch (event.xbutton.button) {
case 1 : set_button(1); break;
case 3 : set_button(2); break;
case 2 : set_button(3); break;
}
} while (XCheckWindowEvent(dpy,_window,ButtonPressMask,&event));
} break;
case ButtonRelease : {
do {
_mouse_x = event.xmotion.x; _mouse_y = event.xmotion.y;
if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
switch (event.xbutton.button) {
case 1 : set_button(1,false); break;
case 3 : set_button(2,false); break;
case 2 : set_button(3,false); break;
case 4 : set_wheel(1); break;
case 5 : set_wheel(-1); break;
}
} while (XCheckWindowEvent(dpy,_window,ButtonReleaseMask,&event));
} break;
case KeyPress : {
char tmp = 0; KeySym ksym;
XLookupString(&event.xkey,&tmp,1,&ksym,0);
set_key((unsigned int)ksym,true);
} break;
case KeyRelease : {
char keys_return[32]; // Check that the key has been physically unpressed.
XQueryKeymap(dpy,keys_return);
const unsigned int kc = event.xkey.keycode, kc1 = kc/8, kc2 = kc%8;
const bool is_key_pressed = kc1>=32?false:(keys_return[kc1]>>kc2)&1;
if (!is_key_pressed) {
char tmp = 0; KeySym ksym;
XLookupString(&event.xkey,&tmp,1,&ksym,0);
set_key((unsigned int)ksym,false);
}
} break;
case EnterNotify: {
while (XCheckWindowEvent(dpy,_window,EnterWindowMask,&event)) {}
_mouse_x = event.xmotion.x;
_mouse_y = event.xmotion.y;
if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
} break;
case LeaveNotify : {
while (XCheckWindowEvent(dpy,_window,LeaveWindowMask,&event)) {}
_mouse_x = _mouse_y = -1; _is_event = true;
pthread_cond_broadcast(&cimg::X11_attr().wait_event);
} break;
case MotionNotify : {
while (XCheckWindowEvent(dpy,_window,PointerMotionMask,&event)) {}
_mouse_x = event.xmotion.x;
_mouse_y = event.xmotion.y;
if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
_is_event = true;
pthread_cond_broadcast(&cimg::X11_attr().wait_event);
} break;
}
}
static void* _events_thread(void *arg) { // Thread to manage events for all opened display windows.
Display *const dpy = cimg::X11_attr().display;
XEvent event;
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,0);
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,0);
if (!arg) for ( ; ; ) {
cimg_lock_display();
bool event_flag = XCheckTypedEvent(dpy,ClientMessage,&event);
if (!event_flag) event_flag = XCheckMaskEvent(dpy,
ExposureMask | StructureNotifyMask | ButtonPressMask |
KeyPressMask | PointerMotionMask | EnterWindowMask |
LeaveWindowMask | ButtonReleaseMask | KeyReleaseMask,&event);
if (event_flag)
for (unsigned int i = 0; i<cimg::X11_attr().nb_wins; ++i)
if (!cimg::X11_attr().wins[i]->_is_closed && event.xany.window==cimg::X11_attr().wins[i]->_window)
cimg::X11_attr().wins[i]->_handle_events(&event);
cimg_unlock_display();
pthread_testcancel();
cimg::sleep(8);
}
return 0;
}
void _set_colormap(Colormap& _colormap, const unsigned int dim) {
XColor *const colormap = new XColor[256];
switch (dim) {
case 1 : { // colormap for greyscale images
for (unsigned int index = 0; index<256; ++index) {
colormap[index].pixel = index;
colormap[index].red = colormap[index].green = colormap[index].blue = (unsigned short)(index<<8);
colormap[index].flags = DoRed | DoGreen | DoBlue;
}
} break;
case 2 : { // colormap for RG images
for (unsigned int index = 0, r = 8; r<256; r+=16)
for (unsigned int g = 8; g<256; g+=16) {
colormap[index].pixel = index;
colormap[index].red = colormap[index].blue = (unsigned short)(r<<8);
colormap[index].green = (unsigned short)(g<<8);
colormap[index++].flags = DoRed | DoGreen | DoBlue;
}
} break;
default : { // colormap for RGB images
for (unsigned int index = 0, r = 16; r<256; r+=32)
for (unsigned int g = 16; g<256; g+=32)
for (unsigned int b = 32; b<256; b+=64) {
colormap[index].pixel = index;
colormap[index].red = (unsigned short)(r<<8);
colormap[index].green = (unsigned short)(g<<8);
colormap[index].blue = (unsigned short)(b<<8);
colormap[index++].flags = DoRed | DoGreen | DoBlue;
}
}
}
XStoreColors(cimg::X11_attr().display,_colormap,colormap,256);
delete[] colormap;
}
void _map_window() {
Display *const dpy = cimg::X11_attr().display;
bool is_exposed = false, is_mapped = false;
XWindowAttributes attr;
XEvent event;
XMapRaised(dpy,_window);
do { // Wait for the window to be mapped.
XWindowEvent(dpy,_window,StructureNotifyMask | ExposureMask,&event);
switch (event.type) {
case MapNotify : is_mapped = true; break;
case Expose : is_exposed = true; break;
}
} while (!is_exposed || !is_mapped);
do { // Wait for the window to be visible.
XGetWindowAttributes(dpy,_window,&attr);
if (attr.map_state!=IsViewable) { XSync(dpy,0); cimg::sleep(10); }
} while (attr.map_state!=IsViewable);
_window_x = attr.x;
_window_y = attr.y;
}
void _paint(const bool wait_expose=true) {
if (_is_closed || !_image) return;
Display *const dpy = cimg::X11_attr().display;
if (wait_expose) { // Send an expose event sticked to display window to force repaint.
XEvent event;
event.xexpose.type = Expose;
event.xexpose.serial = 0;
event.xexpose.send_event = 1;
event.xexpose.display = dpy;
event.xexpose.window = _window;
event.xexpose.x = 0;
event.xexpose.y = 0;
event.xexpose.width = width();
event.xexpose.height = height();
event.xexpose.count = 0;
XSendEvent(dpy,_window,0,0,&event);
} else { // Repaint directly (may be called from the expose event).
GC gc = DefaultGC(dpy,DefaultScreen(dpy));
#ifdef cimg_use_xshm
if (_shminfo) XShmPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height,1);
else XPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height);
#else
XPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height);
#endif
}
}
template<typename T>
void _resize(T pixel_type, const unsigned int ndimx, const unsigned int ndimy, const bool force_redraw) {
Display *const dpy = cimg::X11_attr().display;
cimg::unused(pixel_type);
#ifdef cimg_use_xshm
if (_shminfo) {
XShmSegmentInfo *const nshminfo = new XShmSegmentInfo;
XImage *const nimage = XShmCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),
cimg::X11_attr().nb_bits,ZPixmap,0,nshminfo,ndimx,ndimy);
if (!nimage) { delete nshminfo; return; }
else {
nshminfo->shmid = shmget(IPC_PRIVATE,ndimx*ndimy*sizeof(T),IPC_CREAT | 0777);
if (nshminfo->shmid==-1) { XDestroyImage(nimage); delete nshminfo; return; }
else {
nshminfo->shmaddr = nimage->data = (char*)shmat(nshminfo->shmid,0,0);
if (nshminfo->shmaddr==(char*)-1) {
shmctl(nshminfo->shmid,IPC_RMID,0); XDestroyImage(nimage); delete nshminfo; return;
} else {
nshminfo->readOnly = 0;
cimg::X11_attr().is_shm_enabled = true;
XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm);
XShmAttach(dpy,nshminfo);
XFlush(dpy);
XSetErrorHandler(oldXErrorHandler);
if (!cimg::X11_attr().is_shm_enabled) {
shmdt(nshminfo->shmaddr);
shmctl(nshminfo->shmid,IPC_RMID,0);
XDestroyImage(nimage);
delete nshminfo;
return;
} else {
T *const ndata = (T*)nimage->data;
if (force_redraw) _render_resize((T*)_data,_width,_height,ndata,ndimx,ndimy);
else std::memset(ndata,0,sizeof(T)*ndimx*ndimy);
XShmDetach(dpy,_shminfo);
XDestroyImage(_image);
shmdt(_shminfo->shmaddr);
shmctl(_shminfo->shmid,IPC_RMID,0);
delete _shminfo;
_shminfo = nshminfo;
_image = nimage;
_data = (void*)ndata;
}
}
}
}
} else
#endif
{
T *ndata = (T*)std::malloc(ndimx*ndimy*sizeof(T));
if (force_redraw) _render_resize((T*)_data,_width,_height,ndata,ndimx,ndimy);
else std::memset(ndata,0,sizeof(T)*ndimx*ndimy);
_data = (void*)ndata;
XDestroyImage(_image);
_image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),
cimg::X11_attr().nb_bits,ZPixmap,0,(char*)_data,ndimx,ndimy,8,0);
}
}
void _init_fullscreen() {
if (!_is_fullscreen || _is_closed) return;
Display *const dpy = cimg::X11_attr().display;
_background_window = 0;
#ifdef cimg_use_xrandr
int foo;
if (XRRQueryExtension(dpy,&foo,&foo)) {
XRRRotations(dpy,DefaultScreen(dpy),&cimg::X11_attr().curr_rotation);
if (!cimg::X11_attr().resolutions) {
cimg::X11_attr().resolutions = XRRSizes(dpy,DefaultScreen(dpy),&foo);
cimg::X11_attr().nb_resolutions = (unsigned int)foo;
}
if (cimg::X11_attr().resolutions) {
cimg::X11_attr().curr_resolution = 0;
for (unsigned int i = 0; i<cimg::X11_attr().nb_resolutions; ++i) {
const unsigned int
nw = (unsigned int)(cimg::X11_attr().resolutions[i].width),
nh = (unsigned int)(cimg::X11_attr().resolutions[i].height);
if (nw>=_width && nh>=_height &&
nw<=(unsigned int)(cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].width) &&
nh<=(unsigned int)(cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].height))
cimg::X11_attr().curr_resolution = i;
}
if (cimg::X11_attr().curr_resolution>0) {
XRRScreenConfiguration *config = XRRGetScreenInfo(dpy,DefaultRootWindow(dpy));
XRRSetScreenConfig(dpy,config,DefaultRootWindow(dpy),
cimg::X11_attr().curr_resolution,cimg::X11_attr().curr_rotation,CurrentTime);
XRRFreeScreenConfigInfo(config);
XSync(dpy,0);
}
}
}
if (!cimg::X11_attr().resolutions)
cimg::warn(_cimgdisplay_instance
"init_fullscreen(): Xrandr extension not supported by the X server.",
cimgdisplay_instance);
#endif
const unsigned int sx = screen_width(), sy = screen_height();
if (sx==_width && sy==_height) return;
XSetWindowAttributes winattr;
winattr.override_redirect = 1;
_background_window = XCreateWindow(dpy,DefaultRootWindow(dpy),0,0,sx,sy,0,0,
InputOutput,CopyFromParent,CWOverrideRedirect,&winattr);
const cimg_ulong buf_size = (cimg_ulong)sx*sy*(cimg::X11_attr().nb_bits==8?1:
(cimg::X11_attr().nb_bits==16?2:4));
void *background_data = std::malloc(buf_size);
std::memset(background_data,0,buf_size);
XImage *background_image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits,
ZPixmap,0,(char*)background_data,sx,sy,8,0);
XEvent event;
XSelectInput(dpy,_background_window,StructureNotifyMask);
XMapRaised(dpy,_background_window);
do XWindowEvent(dpy,_background_window,StructureNotifyMask,&event);
while (event.type!=MapNotify);
GC gc = DefaultGC(dpy,DefaultScreen(dpy));
#ifdef cimg_use_xshm
if (_shminfo) XShmPutImage(dpy,_background_window,gc,background_image,0,0,0,0,sx,sy,0);
else XPutImage(dpy,_background_window,gc,background_image,0,0,0,0,sx,sy);
#else
XPutImage(dpy,_background_window,gc,background_image,0,0,0,0,sx,sy);
#endif
XWindowAttributes attr;
XGetWindowAttributes(dpy,_background_window,&attr);
while (attr.map_state!=IsViewable) XSync(dpy,0);
XDestroyImage(background_image);
}
void _desinit_fullscreen() {
if (!_is_fullscreen) return;
Display *const dpy = cimg::X11_attr().display;
XUngrabKeyboard(dpy,CurrentTime);
#ifdef cimg_use_xrandr
if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution) {
XRRScreenConfiguration *config = XRRGetScreenInfo(dpy,DefaultRootWindow(dpy));
XRRSetScreenConfig(dpy,config,DefaultRootWindow(dpy),0,cimg::X11_attr().curr_rotation,CurrentTime);
XRRFreeScreenConfigInfo(config);
XSync(dpy,0);
cimg::X11_attr().curr_resolution = 0;
}
#endif
if (_background_window) XDestroyWindow(dpy,_background_window);
_background_window = 0;
_is_fullscreen = false;
}
static int _assign_xshm(Display *dpy, XErrorEvent *error) {
cimg::unused(dpy,error);
cimg::X11_attr().is_shm_enabled = false;
return 0;
}
void _assign(const unsigned int dimw, const unsigned int dimh, const char *const ptitle=0,
const unsigned int normalization_type=3,
const bool fullscreen_flag=false, const bool closed_flag=false) {
cimg::mutex(14);
// Allocate space for window title
const char *const nptitle = ptitle?ptitle:"";
const unsigned int s = (unsigned int)std::strlen(nptitle) + 1;
char *const tmp_title = s?new char[s]:0;
if (s) std::memcpy(tmp_title,nptitle,s*sizeof(char));
// Destroy previous display window if existing
if (!is_empty()) assign();
// Open X11 display and retrieve graphical properties.
Display* &dpy = cimg::X11_attr().display;
if (!dpy) {
dpy = XOpenDisplay(0);
if (!dpy)
throw CImgDisplayException(_cimgdisplay_instance
"assign(): Failed to open X11 display.",
cimgdisplay_instance);
cimg::X11_attr().nb_bits = DefaultDepth(dpy,DefaultScreen(dpy));
if (cimg::X11_attr().nb_bits!=8 && cimg::X11_attr().nb_bits!=16 &&
cimg::X11_attr().nb_bits!=24 && cimg::X11_attr().nb_bits!=32)
throw CImgDisplayException(_cimgdisplay_instance
"assign(): Invalid %u bits screen mode detected "
"(only 8, 16, 24 and 32 bits modes are managed).",
cimgdisplay_instance,
cimg::X11_attr().nb_bits);
XVisualInfo vtemplate;
vtemplate.visualid = XVisualIDFromVisual(DefaultVisual(dpy,DefaultScreen(dpy)));
int nb_visuals;
XVisualInfo *vinfo = XGetVisualInfo(dpy,VisualIDMask,&vtemplate,&nb_visuals);
if (vinfo && vinfo->red_mask<vinfo->blue_mask) cimg::X11_attr().is_blue_first = true;
cimg::X11_attr().byte_order = ImageByteOrder(dpy);
XFree(vinfo);
cimg_lock_display();
cimg::X11_attr().events_thread = new pthread_t;
pthread_create(cimg::X11_attr().events_thread,0,_events_thread,0);
} else cimg_lock_display();
// Set display variables.
_width = std::min(dimw,(unsigned int)screen_width());
_height = std::min(dimh,(unsigned int)screen_height());
_normalization = normalization_type<4?normalization_type:3;
_is_fullscreen = fullscreen_flag;
_window_x = _window_y = 0;
_is_closed = closed_flag;
_title = tmp_title;
flush();
// Create X11 window (and LUT, if 8bits display)
if (_is_fullscreen) {
if (!_is_closed) _init_fullscreen();
const unsigned int sx = screen_width(), sy = screen_height();
XSetWindowAttributes winattr;
winattr.override_redirect = 1;
_window = XCreateWindow(dpy,DefaultRootWindow(dpy),(sx - _width)/2,(sy - _height)/2,_width,_height,0,0,
InputOutput,CopyFromParent,CWOverrideRedirect,&winattr);
} else
_window = XCreateSimpleWindow(dpy,DefaultRootWindow(dpy),0,0,_width,_height,0,0L,0L);
XSelectInput(dpy,_window,
ExposureMask | StructureNotifyMask | ButtonPressMask | KeyPressMask | PointerMotionMask |
EnterWindowMask | LeaveWindowMask | ButtonReleaseMask | KeyReleaseMask);
XStoreName(dpy,_window,_title?_title:" ");
if (cimg::X11_attr().nb_bits==8) {
_colormap = XCreateColormap(dpy,_window,DefaultVisual(dpy,DefaultScreen(dpy)),AllocAll);
_set_colormap(_colormap,3);
XSetWindowColormap(dpy,_window,_colormap);
}
static const char *const _window_class = cimg_appname;
XClassHint *const window_class = XAllocClassHint();
window_class->res_name = (char*)_window_class;
window_class->res_class = (char*)_window_class;
XSetClassHint(dpy,_window,window_class);
XFree(window_class);
_window_width = _width;
_window_height = _height;
// Create XImage
#ifdef cimg_use_xshm
_shminfo = 0;
if (XShmQueryExtension(dpy)) {
_shminfo = new XShmSegmentInfo;
_image = XShmCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits,
ZPixmap,0,_shminfo,_width,_height);
if (!_image) { delete _shminfo; _shminfo = 0; }
else {
_shminfo->shmid = shmget(IPC_PRIVATE,_image->bytes_per_line*_image->height,IPC_CREAT|0777);
if (_shminfo->shmid==-1) { XDestroyImage(_image); delete _shminfo; _shminfo = 0; }
else {
_shminfo->shmaddr = _image->data = (char*)(_data = shmat(_shminfo->shmid,0,0));
if (_shminfo->shmaddr==(char*)-1) {
shmctl(_shminfo->shmid,IPC_RMID,0); XDestroyImage(_image); delete _shminfo; _shminfo = 0;
} else {
_shminfo->readOnly = 0;
cimg::X11_attr().is_shm_enabled = true;
XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm);
XShmAttach(dpy,_shminfo);
XSync(dpy,0);
XSetErrorHandler(oldXErrorHandler);
if (!cimg::X11_attr().is_shm_enabled) {
shmdt(_shminfo->shmaddr); shmctl(_shminfo->shmid,IPC_RMID,0); XDestroyImage(_image);
delete _shminfo; _shminfo = 0;
}
}
}
}
}
if (!_shminfo)
#endif
{
const cimg_ulong buf_size = (cimg_ulong)_width*_height*(cimg::X11_attr().nb_bits==8?1:
(cimg::X11_attr().nb_bits==16?2:4));
_data = std::malloc(buf_size);
_image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits,
ZPixmap,0,(char*)_data,_width,_height,8,0);
}
_wm_window_atom = XInternAtom(dpy,"WM_DELETE_WINDOW",0);
_wm_protocol_atom = XInternAtom(dpy,"WM_PROTOCOLS",0);
XSetWMProtocols(dpy,_window,&_wm_window_atom,1);
if (_is_fullscreen) XGrabKeyboard(dpy,_window,1,GrabModeAsync,GrabModeAsync,CurrentTime);
cimg::X11_attr().wins[cimg::X11_attr().nb_wins++]=this;
if (!_is_closed) _map_window(); else { _window_x = _window_y = cimg::type<int>::min(); }
cimg_unlock_display();
cimg::mutex(14,0);
}
CImgDisplay& assign() {
if (is_empty()) return flush();
Display *const dpy = cimg::X11_attr().display;
cimg_lock_display();
// Remove display window from event thread list.
unsigned int i;
for (i = 0; i<cimg::X11_attr().nb_wins && cimg::X11_attr().wins[i]!=this; ++i) {}
for ( ; i<cimg::X11_attr().nb_wins - 1; ++i) cimg::X11_attr().wins[i] = cimg::X11_attr().wins[i + 1];