summaryrefslogtreecommitdiffstats
path: root/src/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/bearer/android/jar/bundledjar.pro3
-rw-r--r--src/plugins/bearer/android/jar/distributedjar.pro2
-rw-r--r--src/plugins/bearer/android/jar/jar.pri11
-rw-r--r--src/plugins/bearer/android/jar/jar.pro15
-rw-r--r--src/plugins/bearer/android/src/main.cpp2
-rw-r--r--src/plugins/bearer/android/src/qandroidbearerengine.h28
-rw-r--r--src/plugins/bearer/connman/qconnmanservice_linux.cpp2
-rw-r--r--src/plugins/bearer/generic/main.cpp2
-rw-r--r--src/plugins/bearer/generic/qgenericengine.cpp8
-rw-r--r--src/plugins/bearer/generic/qgenericengine.h18
-rw-r--r--src/plugins/bearer/linux_common/qofonoservice_linux.cpp2
-rw-r--r--src/plugins/bearer/platformdefs_win.h1
-rw-r--r--src/plugins/bearer/qnetworksession_impl.h44
-rw-r--r--src/plugins/generic/evdevkeyboard/main.cpp2
-rw-r--r--src/plugins/generic/evdevmouse/main.cpp2
-rw-r--r--src/plugins/generic/evdevtablet/main.cpp2
-rw-r--r--src/plugins/generic/evdevtouch/main.cpp2
-rw-r--r--src/plugins/generic/generic.pro4
-rw-r--r--src/plugins/generic/tuiotouch/qoscbundle.cpp6
-rw-r--r--src/plugins/generic/tuiotouch/qoscmessage.cpp2
-rw-r--r--src/plugins/generic/tuiotouch/qtuiohandler.cpp41
-rw-r--r--src/plugins/imageformats/gif/main.h4
-rw-r--r--src/plugins/imageformats/gif/qgifhandler.cpp4
-rw-r--r--src/plugins/imageformats/gif/qgifhandler_p.h22
-rw-r--r--src/plugins/imageformats/ico/ico.pro1
-rw-r--r--src/plugins/imageformats/ico/main.h4
-rw-r--r--src/plugins/imageformats/ico/qicohandler.cpp128
-rw-r--r--src/plugins/imageformats/ico/qicohandler.h18
-rw-r--r--src/plugins/imageformats/jpeg/main.h4
-rw-r--r--src/plugins/imageformats/jpeg/qjpeghandler.cpp3
-rw-r--r--src/plugins/imageformats/jpeg/qjpeghandler_p.h14
-rw-r--r--src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp32
-rw-r--r--src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.h10
-rw-r--r--src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontextmain.cpp2
-rw-r--r--src/plugins/platforminputcontexts/ibus/main.cpp2
-rw-r--r--src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.cpp71
-rw-r--r--src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.h21
-rw-r--r--src/plugins/platforminputcontexts/ibus/qibusproxy.cpp45
-rw-r--r--src/plugins/platforminputcontexts/ibus/qibusproxy.h50
-rw-r--r--src/plugins/platforms/android/android.pro9
-rw-r--r--src/plugins/platforms/android/androidjniinput.cpp4
-rw-r--r--src/plugins/platforms/android/androidjnimain.cpp120
-rw-r--r--src/plugins/platforms/android/qandroidplatformdialoghelpers.cpp6
-rw-r--r--src/plugins/platforms/android/qandroidplatformintegration.cpp37
-rw-r--r--src/plugins/platforms/android/qandroidplatformintegration.h7
-rw-r--r--src/plugins/platforms/android/qandroidplatformmenu.cpp11
-rw-r--r--src/plugins/platforms/android/qandroidplatformmenu.h3
-rw-r--r--src/plugins/platforms/android/qandroidplatformmenuitem.cpp11
-rw-r--r--src/plugins/platforms/android/qandroidplatformmenuitem.h3
-rw-r--r--src/plugins/platforms/android/qandroidplatformscreen.cpp27
-rw-r--r--src/plugins/platforms/android/qandroidplatformscreen.h4
-rw-r--r--src/plugins/platforms/android/qandroidplatformvulkaninstance.cpp66
-rw-r--r--src/plugins/platforms/android/qandroidplatformvulkaninstance.h63
-rw-r--r--src/plugins/platforms/android/qandroidplatformvulkanwindow.cpp210
-rw-r--r--src/plugins/platforms/android/qandroidplatformvulkanwindow.h91
-rw-r--r--src/plugins/platforms/android/qandroidplatformwindow.cpp5
-rw-r--r--src/plugins/platforms/android/qandroidplatformwindow.h4
-rw-r--r--src/plugins/platforms/bsdfb/qbsdfbscreen.cpp3
-rw-r--r--src/plugins/platforms/cocoa/cocoa.pro10
-rw-r--r--src/plugins/platforms/cocoa/qcocoaaccessibility.h8
-rw-r--r--src/plugins/platforms/cocoa/qcocoaaccessibility.mm4
-rw-r--r--src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm20
-rw-r--r--src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm43
-rw-r--r--src/plugins/platforms/cocoa/qcocoabackingstore.h8
-rw-r--r--src/plugins/platforms/cocoa/qcocoabackingstore.mm227
-rw-r--r--src/plugins/platforms/cocoa/qcocoaclipboard.h8
-rw-r--r--src/plugins/platforms/cocoa/qcocoacolordialoghelper.h10
-rw-r--r--src/plugins/platforms/cocoa/qcocoacursor.h6
-rw-r--r--src/plugins/platforms/cocoa/qcocoacursor.mm5
-rw-r--r--src/plugins/platforms/cocoa/qcocoadrag.h6
-rw-r--r--src/plugins/platforms/cocoa/qcocoadrag.mm4
-rw-r--r--src/plugins/platforms/cocoa/qcocoaeventdispatcher.h2
-rw-r--r--src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm18
-rw-r--r--src/plugins/platforms/cocoa/qcocoafiledialoghelper.h22
-rw-r--r--src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm26
-rw-r--r--src/plugins/platforms/cocoa/qcocoafontdialoghelper.h10
-rw-r--r--src/plugins/platforms/cocoa/qcocoaglcontext.h15
-rw-r--r--src/plugins/platforms/cocoa/qcocoaglcontext.mm114
-rw-r--r--src/plugins/platforms/cocoa/qcocoahelpers.h153
-rw-r--r--src/plugins/platforms/cocoa/qcocoahelpers.mm73
-rw-r--r--src/plugins/platforms/cocoa/qcocoainputcontext.h6
-rw-r--r--src/plugins/platforms/cocoa/qcocoainputcontext.mm2
-rw-r--r--src/plugins/platforms/cocoa/qcocoaintegration.h99
-rw-r--r--src/plugins/platforms/cocoa/qcocoaintegration.mm274
-rw-r--r--src/plugins/platforms/cocoa/qcocoakeymapper.h2
-rw-r--r--src/plugins/platforms/cocoa/qcocoakeymapper.mm19
-rw-r--r--src/plugins/platforms/cocoa/qcocoamenu.h45
-rw-r--r--src/plugins/platforms/cocoa/qcocoamenu.mm224
-rw-r--r--src/plugins/platforms/cocoa/qcocoamenubar.h11
-rw-r--r--src/plugins/platforms/cocoa/qcocoamenubar.mm7
-rw-r--r--src/plugins/platforms/cocoa/qcocoamenuitem.h32
-rw-r--r--src/plugins/platforms/cocoa/qcocoamenuitem.mm5
-rw-r--r--src/plugins/platforms/cocoa/qcocoamenuloader.mm8
-rw-r--r--src/plugins/platforms/cocoa/qcocoamimetypes.mm92
-rw-r--r--src/plugins/platforms/cocoa/qcocoanativeinterface.h8
-rw-r--r--src/plugins/platforms/cocoa/qcocoanativeinterface.mm4
-rw-r--r--src/plugins/platforms/cocoa/qcocoansmenu.h86
-rw-r--r--src/plugins/platforms/cocoa/qcocoansmenu.mm296
-rw-r--r--src/plugins/platforms/cocoa/qcocoaprintdevice.h34
-rw-r--r--src/plugins/platforms/cocoa/qcocoaprintdevice.mm11
-rw-r--r--src/plugins/platforms/cocoa/qcocoaprintersupport.h10
-rw-r--r--src/plugins/platforms/cocoa/qcocoascreen.h108
-rw-r--r--src/plugins/platforms/cocoa/qcocoascreen.mm309
-rw-r--r--src/plugins/platforms/cocoa/qcocoaservices.h4
-rw-r--r--src/plugins/platforms/cocoa/qcocoasystemsettings.mm103
-rw-r--r--src/plugins/platforms/cocoa/qcocoasystemtrayicon.h18
-rw-r--r--src/plugins/platforms/cocoa/qcocoatheme.h24
-rw-r--r--src/plugins/platforms/cocoa/qcocoatheme.mm13
-rw-r--r--src/plugins/platforms/cocoa/qcocoawindow.h205
-rw-r--r--src/plugins/platforms/cocoa/qcocoawindow.mm1699
-rw-r--r--src/plugins/platforms/cocoa/qmultitouch_mac.mm22
-rw-r--r--src/plugins/platforms/cocoa/qmultitouch_mac_p.h2
-rw-r--r--src/plugins/platforms/cocoa/qnsview.h18
-rw-r--r--src/plugins/platforms/cocoa/qnsview.mm427
-rw-r--r--src/plugins/platforms/cocoa/qnswindow.h81
-rw-r--r--src/plugins/platforms/cocoa/qnswindow.mm331
-rw-r--r--src/plugins/platforms/cocoa/qnswindowdelegate.mm28
-rw-r--r--src/plugins/platforms/cocoa/qpaintengine_mac.mm98
-rw-r--r--src/plugins/platforms/cocoa/qpaintengine_mac_p.h6
-rw-r--r--src/plugins/platforms/cocoa/qprintengine_mac.mm2
-rw-r--r--src/plugins/platforms/cocoa/qprintengine_mac_p.h4
-rw-r--r--src/plugins/platforms/direct2d/direct2d.pro3
-rw-r--r--src/plugins/platforms/direct2d/qwindowsdirect2dbackingstore.h10
-rw-r--r--src/plugins/platforms/direct2d/qwindowsdirect2ddevicecontext.cpp2
-rw-r--r--src/plugins/platforms/direct2d/qwindowsdirect2dintegration.cpp144
-rw-r--r--src/plugins/platforms/direct2d/qwindowsdirect2dintegration.h10
-rw-r--r--src/plugins/platforms/direct2d/qwindowsdirect2dnativeinterface.h2
-rw-r--r--src/plugins/platforms/direct2d/qwindowsdirect2dpaintdevice.h6
-rw-r--r--src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp2
-rw-r--r--src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.h50
-rw-r--r--src/plugins/platforms/direct2d/qwindowsdirect2dplatformpixmap.h20
-rw-r--r--src/plugins/platforms/direct2d/qwindowsdirect2dwindow.cpp6
-rw-r--r--src/plugins/platforms/direct2d/qwindowsdirect2dwindow.h2
-rw-r--r--src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp23
-rw-r--r--src/plugins/platforms/eglfs/api/qeglfsdeviceintegration_p.h4
-rw-r--r--src/plugins/platforms/eglfs/api/qeglfsintegration.cpp8
-rw-r--r--src/plugins/platforms/eglfs/api/qeglfswindow.cpp17
-rw-r--r--src/plugins/platforms/eglfs/api/qeglfswindow_p.h2
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/deviceintegration.pro1
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/qeglfsemulatorintegration.h2
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/qeglfsemulatorscreen.cpp5
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/qeglfsemulatorscreen.h1
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/eglfs_kms.pro8
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmcursor.cpp8
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.cpp50
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.h12
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmintegration.cpp54
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmintegration.h5
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp240
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.h34
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmwindow.cpp80
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmwindow.h (renamed from src/plugins/platforms/windows/accessible/comutils.h)35
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/eglfs_kms_egldevice.pro2
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevice.cpp6
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldeviceintegration.cpp10
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevicescreen.cpp10
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/eglfs_kms_support.pro5
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmshelpers.h60
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsintegration.cpp22
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsintegration.h2
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.cpp88
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.h19
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/eglfs_kms_vsp2.json3
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/eglfs_kms_vsp2.pro31
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2device.cpp136
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2device.h78
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2integration.cpp250
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2integration.h81
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2main.cpp56
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2screen.cpp365
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2screen.h116
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qlinuxmediadevice.cpp627
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qlinuxmediadevice.h116
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qvsp2blendingdevice.cpp333
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qvsp2blendingdevice.h95
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11integration.cpp71
-rw-r--r--src/plugins/platforms/haiku/main.cpp2
-rw-r--r--src/plugins/platforms/haiku/main.h2
-rw-r--r--src/plugins/platforms/haiku/qhaikuapplication.h4
-rw-r--r--src/plugins/platforms/haiku/qhaikubuffer.cpp6
-rw-r--r--src/plugins/platforms/haiku/qhaikuclipboard.cpp10
-rw-r--r--src/plugins/platforms/haiku/qhaikuclipboard.h10
-rw-r--r--src/plugins/platforms/haiku/qhaikucursor.h2
-rw-r--r--src/plugins/platforms/haiku/qhaikuintegration.cpp6
-rw-r--r--src/plugins/platforms/haiku/qhaikuintegration.h14
-rw-r--r--src/plugins/platforms/haiku/qhaikurasterbackingstore.cpp6
-rw-r--r--src/plugins/platforms/haiku/qhaikurasterbackingstore.h6
-rw-r--r--src/plugins/platforms/haiku/qhaikurasterwindow.cpp4
-rw-r--r--src/plugins/platforms/haiku/qhaikurasterwindow.h16
-rw-r--r--src/plugins/platforms/haiku/qhaikuscreen.cpp8
-rw-r--r--src/plugins/platforms/haiku/qhaikuscreen.h10
-rw-r--r--src/plugins/platforms/haiku/qhaikuservices.h6
-rw-r--r--src/plugins/platforms/haiku/qhaikuwindow.cpp31
-rw-r--r--src/plugins/platforms/haiku/qhaikuwindow.h38
-rw-r--r--src/plugins/platforms/integrity/main.cpp2
-rw-r--r--src/plugins/platforms/integrity/qintegrityfbintegration.h18
-rw-r--r--src/plugins/platforms/integrity/qintegrityfbscreen.cpp13
-rw-r--r--src/plugins/platforms/integrity/qintegrityfbscreen.h4
-rw-r--r--src/plugins/platforms/integrity/qintegrityhidmanager.cpp8
-rw-r--r--src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.mm8
-rw-r--r--src/plugins/platforms/ios/qiosbackingstore.h5
-rw-r--r--src/plugins/platforms/ios/qiosbackingstore.mm15
-rw-r--r--src/plugins/platforms/ios/qiosclipboard.h8
-rw-r--r--src/plugins/platforms/ios/qiosclipboard.mm6
-rw-r--r--src/plugins/platforms/ios/qioscontext.h16
-rw-r--r--src/plugins/platforms/ios/qioseventdispatcher.h4
-rw-r--r--src/plugins/platforms/ios/qioseventdispatcher.mm41
-rw-r--r--src/plugins/platforms/ios/qiosfiledialog.h22
-rw-r--r--src/plugins/platforms/ios/qiosfiledialog.mm4
-rw-r--r--src/plugins/platforms/ios/qiosinputcontext.h22
-rw-r--r--src/plugins/platforms/ios/qiosintegration.h41
-rw-r--r--src/plugins/platforms/ios/qiosintegration.mm24
-rw-r--r--src/plugins/platforms/ios/qiosmenu.h60
-rw-r--r--src/plugins/platforms/ios/qiosmenu.mm22
-rw-r--r--src/plugins/platforms/ios/qiosmessagedialog.h6
-rw-r--r--src/plugins/platforms/ios/qiosmessagedialog.mm8
-rw-r--r--src/plugins/platforms/ios/qiosoptionalplugininterface.h2
-rw-r--r--src/plugins/platforms/ios/qiosscreen.h20
-rw-r--r--src/plugins/platforms/ios/qiosscreen.mm32
-rw-r--r--src/plugins/platforms/ios/qiosservices.mm6
-rw-r--r--src/plugins/platforms/ios/qiostextinputoverlay.mm27
-rw-r--r--src/plugins/platforms/ios/qiostextresponder.mm9
-rw-r--r--src/plugins/platforms/ios/qiostheme.h14
-rw-r--r--src/plugins/platforms/ios/qiosviewcontroller.mm7
-rw-r--r--src/plugins/platforms/ios/qioswindow.h34
-rw-r--r--src/plugins/platforms/ios/qioswindow.mm24
-rw-r--r--src/plugins/platforms/ios/quiview.h1
-rw-r--r--src/plugins/platforms/ios/quiview.mm133
-rw-r--r--src/plugins/platforms/ios/quiview_accessibility.mm2
-rw-r--r--src/plugins/platforms/linuxfb/main.cpp2
-rw-r--r--src/plugins/platforms/linuxfb/qlinuxfbdrmscreen.cpp76
-rw-r--r--src/plugins/platforms/linuxfb/qlinuxfbintegration.h18
-rw-r--r--src/plugins/platforms/minimal/main.cpp2
-rw-r--r--src/plugins/platforms/minimal/minimal.pro2
-rw-r--r--src/plugins/platforms/minimal/qminimalbackingstore.h6
-rw-r--r--src/plugins/platforms/minimal/qminimalintegration.cpp34
-rw-r--r--src/plugins/platforms/minimal/qminimalintegration.h19
-rw-r--r--src/plugins/platforms/minimalegl/main.cpp2
-rw-r--r--src/plugins/platforms/minimalegl/qminimaleglbackingstore.h10
-rw-r--r--src/plugins/platforms/minimalegl/qminimaleglintegration.cpp4
-rw-r--r--src/plugins/platforms/minimalegl/qminimaleglintegration.h14
-rw-r--r--src/plugins/platforms/minimalegl/qminimaleglscreen.cpp2
-rw-r--r--src/plugins/platforms/minimalegl/qminimaleglscreen.h6
-rw-r--r--src/plugins/platforms/minimalegl/qminimaleglwindow.h4
-rw-r--r--src/plugins/platforms/mirclient/qmirclientinput.cpp30
-rw-r--r--src/plugins/platforms/mirclient/qmirclientwindow.cpp12
-rw-r--r--src/plugins/platforms/mirclient/qmirclientwindow.h2
-rw-r--r--src/plugins/platforms/offscreen/main.cpp2
-rw-r--r--src/plugins/platforms/offscreen/qoffscreencommon.cpp6
-rw-r--r--src/plugins/platforms/offscreen/qoffscreencommon.h21
-rw-r--r--src/plugins/platforms/offscreen/qoffscreenintegration.cpp44
-rw-r--r--src/plugins/platforms/offscreen/qoffscreenintegration.h21
-rw-r--r--src/plugins/platforms/offscreen/qoffscreenintegration_x11.h18
-rw-r--r--src/plugins/platforms/offscreen/qoffscreenwindow.cpp23
-rw-r--r--src/plugins/platforms/offscreen/qoffscreenwindow.h12
-rw-r--r--src/plugins/platforms/qnx/qqnxrasterbackingstore.cpp5
-rw-r--r--src/plugins/platforms/qnx/qqnxrasterwindow.cpp7
-rw-r--r--src/plugins/platforms/qnx/qqnxwindow.cpp46
-rw-r--r--src/plugins/platforms/qnx/qqnxwindow.h6
-rw-r--r--src/plugins/platforms/vnc/qvnc.cpp9
-rw-r--r--src/plugins/platforms/vnc/qvncintegration.cpp3
-rw-r--r--src/plugins/platforms/windows/accessible/accessible.pri18
-rw-r--r--src/plugins/platforms/windows/accessible/comutils.cpp280
-rw-r--r--src/plugins/platforms/windows/accessible/iaccessible2.cpp1744
-rw-r--r--src/plugins/platforms/windows/accessible/iaccessible2.h351
-rw-r--r--src/plugins/platforms/windows/accessible/qwindowsaccessibility.cpp253
-rw-r--r--src/plugins/platforms/windows/accessible/qwindowsmsaaaccessible.cpp1225
-rw-r--r--src/plugins/platforms/windows/accessible/qwindowsmsaaaccessible.h175
-rw-r--r--src/plugins/platforms/windows/qtwindowsglobal.h8
-rw-r--r--src/plugins/platforms/windows/qwin10helpers.cpp3
-rw-r--r--src/plugins/platforms/windows/qwindowsbackingstore.cpp2
-rw-r--r--src/plugins/platforms/windows/qwindowscombase.h112
-rw-r--r--src/plugins/platforms/windows/qwindowscontext.cpp135
-rw-r--r--src/plugins/platforms/windows/qwindowscontext.h22
-rw-r--r--src/plugins/platforms/windows/qwindowscursor.cpp36
-rw-r--r--src/plugins/platforms/windows/qwindowscursor.h13
-rw-r--r--src/plugins/platforms/windows/qwindowsdialoghelpers.cpp69
-rw-r--r--src/plugins/platforms/windows/qwindowsdrag.cpp87
-rw-r--r--src/plugins/platforms/windows/qwindowsdrag.h11
-rw-r--r--src/plugins/platforms/windows/qwindowsdropdataobject.cpp98
-rw-r--r--src/plugins/platforms/windows/qwindowsdropdataobject.h63
-rw-r--r--src/plugins/platforms/windows/qwindowseglcontext.h2
-rw-r--r--src/plugins/platforms/windows/qwindowsglcontext.cpp76
-rw-r--r--src/plugins/platforms/windows/qwindowsglcontext.h7
-rw-r--r--src/plugins/platforms/windows/qwindowsinputcontext.cpp22
-rw-r--r--src/plugins/platforms/windows/qwindowsinputcontext.h4
-rw-r--r--src/plugins/platforms/windows/qwindowsintegration.cpp44
-rw-r--r--src/plugins/platforms/windows/qwindowsintegration.h8
-rw-r--r--src/plugins/platforms/windows/qwindowskeymapper.cpp6
-rw-r--r--src/plugins/platforms/windows/qwindowsmenu.cpp969
-rw-r--r--src/plugins/platforms/windows/qwindowsmenu.h243
-rw-r--r--src/plugins/platforms/windows/qwindowsmime.cpp64
-rw-r--r--src/plugins/platforms/windows/qwindowsmousehandler.cpp43
-rw-r--r--src/plugins/platforms/windows/qwindowsnativeinterface.cpp16
-rw-r--r--src/plugins/platforms/windows/qwindowsole.cpp62
-rw-r--r--src/plugins/platforms/windows/qwindowsole.h17
-rw-r--r--src/plugins/platforms/windows/qwindowsscreen.cpp2
-rw-r--r--src/plugins/platforms/windows/qwindowssystemtrayicon.cpp443
-rw-r--r--src/plugins/platforms/windows/qwindowssystemtrayicon.h103
-rw-r--r--src/plugins/platforms/windows/qwindowstabletsupport.cpp14
-rw-r--r--src/plugins/platforms/windows/qwindowstheme.cpp74
-rw-r--r--src/plugins/platforms/windows/qwindowstheme.h10
-rw-r--r--src/plugins/platforms/windows/qwindowsvulkaninstance.cpp136
-rw-r--r--src/plugins/platforms/windows/qwindowsvulkaninstance.h76
-rw-r--r--src/plugins/platforms/windows/qwindowswindow.cpp359
-rw-r--r--src/plugins/platforms/windows/qwindowswindow.h39
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.cpp140
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.h (renamed from src/plugins/platforms/windows/accessible/qwindowsaccessibility.h)28
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiabaseprovider.cpp81
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiabaseprovider.h78
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiagriditemprovider.cpp176
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiagriditemprovider.h71
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiagridprovider.cpp136
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiagridprovider.h69
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiainvokeprovider.cpp84
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiainvokeprovider.h67
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp638
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.h105
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiaprovidercache.cpp106
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiaprovidercache.h77
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiarangevalueprovider.cpp190
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiarangevalueprovider.h73
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionitemprovider.cpp201
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionitemprovider.h71
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionprovider.cpp147
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionprovider.h69
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiatableitemprovider.cpp129
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiatableitemprovider.h68
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiatableprovider.cpp154
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiatableprovider.h69
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiatextprovider.cpp261
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiatextprovider.h80
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiatextrangeprovider.cpp554
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiatextrangeprovider.h88
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiatoggleprovider.cpp105
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiatoggleprovider.h68
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp221
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiautils.h89
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiavalueprovider.cpp132
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiavalueprovider.h70
-rw-r--r--src/plugins/platforms/windows/uiautomation/uiautomation.pri43
-rw-r--r--src/plugins/platforms/windows/windows.pri19
-rw-r--r--src/plugins/platforms/windows/windows.pro1
-rw-r--r--src/plugins/platforms/winrt/qwinrtclipboard.cpp2
-rw-r--r--src/plugins/platforms/winrt/qwinrtcursor.cpp2
-rw-r--r--src/plugins/platforms/winrt/qwinrtdrag.cpp8
-rw-r--r--src/plugins/platforms/winrt/qwinrtdrag.h1
-rw-r--r--src/plugins/platforms/winrt/qwinrtfiledialoghelper.cpp2
-rw-r--r--src/plugins/platforms/winrt/qwinrtfileengine.cpp5
-rw-r--r--src/plugins/platforms/winrt/qwinrtscreen.cpp53
-rw-r--r--src/plugins/platforms/winrt/qwinrtwindow.cpp21
-rw-r--r--src/plugins/platforms/winrt/qwinrtwindow.h6
-rw-r--r--src/plugins/platforms/xcb/gl_integrations/qxcbglintegration.h2
-rw-r--r--src/plugins/platforms/xcb/gl_integrations/qxcbnativeinterfacehandler.cpp12
-rw-r--r--src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglcontext.h25
-rw-r--r--src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglintegration.cpp3
-rw-r--r--src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglnativeinterfacehandler.cpp10
-rw-r--r--src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglwindow.cpp2
-rw-r--r--src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp34
-rw-r--r--src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.h1
-rw-r--r--src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxintegration.cpp15
-rw-r--r--src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxnativeinterfacehandler.cpp2
-rw-r--r--src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxwindow.cpp22
-rw-r--r--src/plugins/platforms/xcb/nativepainting/nativepainting.pri22
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qbackingstore_x11.cpp209
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qbackingstore_x11_p.h76
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qcolormap_x11.cpp650
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qcolormap_x11_p.h81
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qpaintengine_x11.cpp2843
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qpaintengine_x11_p.h124
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qpixmap_x11.cpp2114
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qpixmap_x11_p.h166
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qt_x11_p.h199
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qtessellator.cpp1500
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qtessellator_p.h89
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qxcbnativepainting.cpp321
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qxcbnativepainting.h102
-rw-r--r--src/plugins/platforms/xcb/qxcbbackingstore.cpp285
-rw-r--r--src/plugins/platforms/xcb/qxcbbackingstore.h2
-rw-r--r--src/plugins/platforms/xcb/qxcbclipboard.cpp91
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.cpp687
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.h199
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection_xi2.cpp1134
-rw-r--r--src/plugins/platforms/xcb/qxcbcursor.cpp84
-rw-r--r--src/plugins/platforms/xcb/qxcbdrag.cpp137
-rw-r--r--src/plugins/platforms/xcb/qxcbdrag.h3
-rw-r--r--src/plugins/platforms/xcb/qxcbimage.cpp204
-rw-r--r--src/plugins/platforms/xcb/qxcbimage.h4
-rw-r--r--src/plugins/platforms/xcb/qxcbintegration.cpp75
-rw-r--r--src/plugins/platforms/xcb/qxcbintegration.h7
-rw-r--r--src/plugins/platforms/xcb/qxcbkeyboard.cpp635
-rw-r--r--src/plugins/platforms/xcb/qxcbkeyboard.h8
-rw-r--r--src/plugins/platforms/xcb/qxcbmime.cpp11
-rw-r--r--src/plugins/platforms/xcb/qxcbnativeinterface.cpp191
-rw-r--r--src/plugins/platforms/xcb/qxcbnativeinterface.h17
-rw-r--r--src/plugins/platforms/xcb/qxcbscreen.cpp610
-rw-r--r--src/plugins/platforms/xcb/qxcbscreen.h73
-rw-r--r--src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp38
-rw-r--r--src/plugins/platforms/xcb/qxcbvulkaninstance.cpp156
-rw-r--r--src/plugins/platforms/xcb/qxcbvulkaninstance.h79
-rw-r--r--src/plugins/platforms/xcb/qxcbvulkanwindow.cpp88
-rw-r--r--src/plugins/platforms/xcb/qxcbvulkanwindow.h65
-rw-r--r--src/plugins/platforms/xcb/qxcbwindow.cpp887
-rw-r--r--src/plugins/platforms/xcb/qxcbwindow.h30
-rw-r--r--src/plugins/platforms/xcb/qxcbwmsupport.cpp21
-rw-r--r--src/plugins/platforms/xcb/qxcbxsettings.cpp43
-rw-r--r--src/plugins/platforms/xcb/xcb-plugin.pro2
-rw-r--r--src/plugins/platforms/xcb/xcb_qpa_lib.pro16
-rw-r--r--src/plugins/platformthemes/flatpak/flatpak.json3
-rw-r--r--src/plugins/platformthemes/flatpak/flatpak.pro17
-rw-r--r--src/plugins/platformthemes/flatpak/main.cpp65
-rw-r--r--src/plugins/platformthemes/flatpak/qflatpakfiledialog.cpp352
-rw-r--r--src/plugins/platformthemes/flatpak/qflatpakfiledialog_p.h106
-rw-r--r--src/plugins/platformthemes/flatpak/qflatpaktheme.cpp254
-rw-r--r--src/plugins/platformthemes/flatpak/qflatpaktheme.h90
-rw-r--r--src/plugins/platformthemes/gtk3/main.cpp2
-rw-r--r--src/plugins/platformthemes/gtk3/qgtk3dialoghelpers.h42
-rw-r--r--src/plugins/platformthemes/gtk3/qgtk3menu.cpp22
-rw-r--r--src/plugins/platformthemes/gtk3/qgtk3menu.h8
-rw-r--r--src/plugins/platformthemes/gtk3/qgtk3theme.h12
-rw-r--r--src/plugins/platformthemes/platformthemes.pro2
-rw-r--r--src/plugins/plugins.pro1
-rw-r--r--src/plugins/printsupport/cups/main.cpp2
-rw-r--r--src/plugins/printsupport/cups/qcupsprintengine.cpp44
-rw-r--r--src/plugins/printsupport/cups/qcupsprintengine_p.h9
-rw-r--r--src/plugins/printsupport/cups/qcupsprintersupport_p.h10
-rw-r--r--src/plugins/printsupport/cups/qppdprintdevice.cpp46
-rw-r--r--src/plugins/printsupport/cups/qppdprintdevice.h38
-rw-r--r--src/plugins/printsupport/windows/qwindowsprintdevice.cpp4
-rw-r--r--src/plugins/printsupport/windows/qwindowsprintdevice.h30
-rw-r--r--src/plugins/printsupport/windows/qwindowsprintersupport.h10
-rw-r--r--src/plugins/sqldrivers/db2/qsql_db2.cpp103
-rw-r--r--src/plugins/sqldrivers/db2/qsql_db2_p.h26
-rw-r--r--src/plugins/sqldrivers/ibase/qsql_ibase.cpp28
-rw-r--r--src/plugins/sqldrivers/ibase/qsql_ibase_p.h32
-rw-r--r--src/plugins/sqldrivers/mysql/main.cpp2
-rw-r--r--src/plugins/sqldrivers/mysql/qsql_mysql.cpp39
-rw-r--r--src/plugins/sqldrivers/mysql/qsql_mysql_p.h28
-rw-r--r--src/plugins/sqldrivers/oci/qsql_oci.cpp232
-rw-r--r--src/plugins/sqldrivers/oci/qsql_oci_p.h24
-rw-r--r--src/plugins/sqldrivers/odbc/qsql_odbc.cpp49
-rw-r--r--src/plugins/sqldrivers/odbc/qsql_odbc_p.h28
-rw-r--r--src/plugins/sqldrivers/psql/main.cpp2
-rw-r--r--src/plugins/sqldrivers/psql/qsql_psql.cpp584
-rw-r--r--src/plugins/sqldrivers/psql/qsql_psql_p.h34
-rw-r--r--src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp148
-rw-r--r--src/plugins/sqldrivers/sqlite/qsql_sqlite_p.h30
-rw-r--r--src/plugins/sqldrivers/sqlite/smain.cpp2
-rw-r--r--src/plugins/sqldrivers/sqlite2/qsql_sqlite2.cpp21
-rw-r--r--src/plugins/sqldrivers/sqlite2/qsql_sqlite2_p.h24
-rw-r--r--src/plugins/sqldrivers/tds/qsql_tds.cpp19
-rw-r--r--src/plugins/sqldrivers/tds/qsql_tds_p.h26
-rw-r--r--src/plugins/styles/android/android.pro16
-rw-r--r--src/plugins/styles/android/androidstyle.json3
-rw-r--r--src/plugins/styles/android/main.cpp64
-rw-r--r--src/plugins/styles/android/qandroidstyle.cpp1816
-rw-r--r--src/plugins/styles/android/qandroidstyle_p.h391
-rw-r--r--src/plugins/styles/mac/mac.pro19
-rw-r--r--src/plugins/styles/mac/macstyle.json3
-rw-r--r--src/plugins/styles/mac/main.mm65
-rw-r--r--src/plugins/styles/mac/qmacstyle.qdoc207
-rw-r--r--src/plugins/styles/mac/qmacstyle_mac.mm6705
-rw-r--r--src/plugins/styles/mac/qmacstyle_mac_p.h124
-rw-r--r--src/plugins/styles/mac/qmacstyle_mac_p_p.h306
-rw-r--r--src/plugins/styles/styles.pro8
-rw-r--r--src/plugins/styles/windowsvista/main.cpp64
-rw-r--r--src/plugins/styles/windowsvista/qwindowsvistastyle.cpp2502
-rw-r--r--src/plugins/styles/windowsvista/qwindowsvistastyle_p.h106
-rw-r--r--src/plugins/styles/windowsvista/qwindowsvistastyle_p_p.h200
-rw-r--r--src/plugins/styles/windowsvista/qwindowsxpstyle.cpp4240
-rw-r--r--src/plugins/styles/windowsvista/qwindowsxpstyle_p.h104
-rw-r--r--src/plugins/styles/windowsvista/qwindowsxpstyle_p_p.h346
-rw-r--r--src/plugins/styles/windowsvista/windowsvista.pro22
-rw-r--r--src/plugins/styles/windowsvista/windowsvistastyle.json3
474 files changed, 46632 insertions, 11872 deletions
diff --git a/src/plugins/bearer/android/jar/bundledjar.pro b/src/plugins/bearer/android/jar/bundledjar.pro
deleted file mode 100644
index 6037f813f2..0000000000
--- a/src/plugins/bearer/android/jar/bundledjar.pro
+++ /dev/null
@@ -1,3 +0,0 @@
-TARGET = QtAndroidBearer-bundled
-CONFIG += bundled_jar_file
-include(jar.pri)
diff --git a/src/plugins/bearer/android/jar/distributedjar.pro b/src/plugins/bearer/android/jar/distributedjar.pro
deleted file mode 100644
index c769a113d5..0000000000
--- a/src/plugins/bearer/android/jar/distributedjar.pro
+++ /dev/null
@@ -1,2 +0,0 @@
-TARGET = QtAndroidBearer
-include(jar.pri)
diff --git a/src/plugins/bearer/android/jar/jar.pri b/src/plugins/bearer/android/jar/jar.pri
deleted file mode 100644
index e43dbf0711..0000000000
--- a/src/plugins/bearer/android/jar/jar.pri
+++ /dev/null
@@ -1,11 +0,0 @@
-load(qt_build_paths)
-CONFIG += java
-DESTDIR = $$MODULE_BASE_OUTDIR/jar
-
-JAVACLASSPATH += $$PWD/src
-
-JAVASOURCES += $$PWD/src/org/qtproject/qt5/android/bearer/QtNetworkReceiver.java
-
-# install
-target.path = $$[QT_INSTALL_PREFIX]/jar
-INSTALLS += target
diff --git a/src/plugins/bearer/android/jar/jar.pro b/src/plugins/bearer/android/jar/jar.pro
index 923e757d9b..f988019dac 100644
--- a/src/plugins/bearer/android/jar/jar.pro
+++ b/src/plugins/bearer/android/jar/jar.pro
@@ -1,2 +1,13 @@
-TEMPLATE=subdirs
-SUBDIRS += distributedjar.pro bundledjar.pro
+TARGET = QtAndroidBearer
+
+load(qt_build_paths)
+CONFIG += java
+DESTDIR = $$MODULE_BASE_OUTDIR/jar
+
+JAVACLASSPATH += $$PWD/src
+
+JAVASOURCES += $$PWD/src/org/qtproject/qt5/android/bearer/QtNetworkReceiver.java
+
+# install
+target.path = $$[QT_INSTALL_PREFIX]/jar
+INSTALLS += target
diff --git a/src/plugins/bearer/android/src/main.cpp b/src/plugins/bearer/android/src/main.cpp
index 3892766b89..f3a5074e5d 100644
--- a/src/plugins/bearer/android/src/main.cpp
+++ b/src/plugins/bearer/android/src/main.cpp
@@ -50,7 +50,7 @@ class QAndroidBearerEnginePlugin : public QBearerEnginePlugin
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QBearerEngineFactoryInterface" FILE "android.json")
public:
- QBearerEngine *create(const QString &key) const Q_DECL_OVERRIDE
+ QBearerEngine *create(const QString &key) const override
{
return (key == QLatin1String("android")) ? new QAndroidBearerEngine() : 0;
}
diff --git a/src/plugins/bearer/android/src/qandroidbearerengine.h b/src/plugins/bearer/android/src/qandroidbearerengine.h
index f404ca65eb..837b02232d 100644
--- a/src/plugins/bearer/android/src/qandroidbearerengine.h
+++ b/src/plugins/bearer/android/src/qandroidbearerengine.h
@@ -60,20 +60,20 @@ class QAndroidBearerEngine : public QBearerEngineImpl
public:
explicit QAndroidBearerEngine(QObject *parent = 0);
- ~QAndroidBearerEngine() Q_DECL_OVERRIDE;
-
- QString getInterfaceFromId(const QString &id) Q_DECL_OVERRIDE;
- bool hasIdentifier(const QString &id) Q_DECL_OVERRIDE;
- void connectToId(const QString &id) Q_DECL_OVERRIDE;
- void disconnectFromId(const QString &id) Q_DECL_OVERRIDE;
- QNetworkSession::State sessionStateForId(const QString &id) Q_DECL_OVERRIDE;
- QNetworkConfigurationManager::Capabilities capabilities() const Q_DECL_OVERRIDE;
- QNetworkSessionPrivate *createSessionBackend() Q_DECL_OVERRIDE;
- QNetworkConfigurationPrivatePointer defaultConfiguration() Q_DECL_OVERRIDE;
- bool requiresPolling() const Q_DECL_OVERRIDE;
- quint64 bytesWritten(const QString &id) Q_DECL_OVERRIDE;
- quint64 bytesReceived(const QString &id) Q_DECL_OVERRIDE;
- quint64 startTime(const QString &id) Q_DECL_OVERRIDE;
+ ~QAndroidBearerEngine() override;
+
+ QString getInterfaceFromId(const QString &id) override;
+ bool hasIdentifier(const QString &id) override;
+ void connectToId(const QString &id) override;
+ void disconnectFromId(const QString &id) override;
+ QNetworkSession::State sessionStateForId(const QString &id) override;
+ QNetworkConfigurationManager::Capabilities capabilities() const override;
+ QNetworkSessionPrivate *createSessionBackend() override;
+ QNetworkConfigurationPrivatePointer defaultConfiguration() override;
+ bool requiresPolling() const override;
+ quint64 bytesWritten(const QString &id) override;
+ quint64 bytesReceived(const QString &id) override;
+ quint64 startTime(const QString &id) override;
Q_INVOKABLE void initialize();
Q_INVOKABLE void requestUpdate();
diff --git a/src/plugins/bearer/connman/qconnmanservice_linux.cpp b/src/plugins/bearer/connman/qconnmanservice_linux.cpp
index 55eec57270..7c9db4640b 100644
--- a/src/plugins/bearer/connman/qconnmanservice_linux.cpp
+++ b/src/plugins/bearer/connman/qconnmanservice_linux.cpp
@@ -506,7 +506,7 @@ void QConnmanTechnologyInterface::scan()
void QConnmanTechnologyInterface::scanReply(QDBusPendingCallWatcher *call)
{
- QDBusPendingReply<QVariantMap> props_reply = *call;
+ QDBusPendingReply<> props_reply = *call;
if (props_reply.isError()) {
qDebug() << props_reply.error().message();
}
diff --git a/src/plugins/bearer/generic/main.cpp b/src/plugins/bearer/generic/main.cpp
index 80e7b7d9aa..82a9ce97f4 100644
--- a/src/plugins/bearer/generic/main.cpp
+++ b/src/plugins/bearer/generic/main.cpp
@@ -54,7 +54,7 @@ public:
QGenericEnginePlugin();
~QGenericEnginePlugin();
- QBearerEngine *create(const QString &key) const Q_DECL_OVERRIDE;
+ QBearerEngine *create(const QString &key) const override;
};
QGenericEnginePlugin::QGenericEnginePlugin()
diff --git a/src/plugins/bearer/generic/qgenericengine.cpp b/src/plugins/bearer/generic/qgenericengine.cpp
index cd3b202001..7472758418 100644
--- a/src/plugins/bearer/generic/qgenericengine.cpp
+++ b/src/plugins/bearer/generic/qgenericengine.cpp
@@ -55,10 +55,10 @@
#if defined(Q_OS_WIN32)
// PMIB_TCPTABLE2 is only available since Vista
-#if _WIN32_WINNT < 0x0600
+#if _WIN32_WINNT < 0x0601
# undef _WIN32_WINNT
-# define _WIN32_WINNT 0x0600
-#endif // _WIN32_WINNT < 0x0600
+# define _WIN32_WINNT 0x0601
+#endif // _WIN32_WINNT < 0x0601
#include "../platformdefs_win.h"
#include <iphlpapi.h>
#endif
@@ -132,7 +132,7 @@ static QNetworkConfiguration::BearerType qGetInterfaceType(const QString &interf
int sock = socket(AF_INET, SOCK_DGRAM, 0);
ifreq request;
- strncpy(request.ifr_name, interface.toLocal8Bit().data(), sizeof(request.ifr_name));
+ strncpy(request.ifr_name, interface.toLocal8Bit().data(), sizeof(request.ifr_name) - 1);
request.ifr_name[sizeof(request.ifr_name) - 1] = '\0';
int result = ioctl(sock, SIOCGIFHWADDR, &request);
close(sock);
diff --git a/src/plugins/bearer/generic/qgenericengine.h b/src/plugins/bearer/generic/qgenericengine.h
index e680eb9cc0..08960d66f6 100644
--- a/src/plugins/bearer/generic/qgenericengine.h
+++ b/src/plugins/bearer/generic/qgenericengine.h
@@ -58,24 +58,24 @@ public:
QGenericEngine(QObject *parent = 0);
~QGenericEngine();
- QString getInterfaceFromId(const QString &id) Q_DECL_OVERRIDE;
- bool hasIdentifier(const QString &id) Q_DECL_OVERRIDE;
+ QString getInterfaceFromId(const QString &id) override;
+ bool hasIdentifier(const QString &id) override;
- void connectToId(const QString &id) Q_DECL_OVERRIDE;
- void disconnectFromId(const QString &id) Q_DECL_OVERRIDE;
+ void connectToId(const QString &id) override;
+ void disconnectFromId(const QString &id) override;
Q_INVOKABLE void initialize();
Q_INVOKABLE void requestUpdate();
- QNetworkSession::State sessionStateForId(const QString &id) Q_DECL_OVERRIDE;
+ QNetworkSession::State sessionStateForId(const QString &id) override;
- QNetworkConfigurationManager::Capabilities capabilities() const Q_DECL_OVERRIDE;
+ QNetworkConfigurationManager::Capabilities capabilities() const override;
- QNetworkSessionPrivate *createSessionBackend() Q_DECL_OVERRIDE;
+ QNetworkSessionPrivate *createSessionBackend() override;
- QNetworkConfigurationPrivatePointer defaultConfiguration() Q_DECL_OVERRIDE;
+ QNetworkConfigurationPrivatePointer defaultConfiguration() override;
- bool requiresPolling() const Q_DECL_OVERRIDE;
+ bool requiresPolling() const override;
private Q_SLOTS:
void doRequestUpdate();
diff --git a/src/plugins/bearer/linux_common/qofonoservice_linux.cpp b/src/plugins/bearer/linux_common/qofonoservice_linux.cpp
index adf7feef2e..897ee953c0 100644
--- a/src/plugins/bearer/linux_common/qofonoservice_linux.cpp
+++ b/src/plugins/bearer/linux_common/qofonoservice_linux.cpp
@@ -118,7 +118,7 @@ QString QOfonoManagerInterface::currentModem()
for (const QString &modem : modems) {
QOfonoModemInterface device(modem);
if (device.isPowered() && device.isOnline()
- && device.interfaces().contains(QStringLiteral("org.ofono.NetworkRegistration")))
+ && device.interfaces().contains(QLatin1String("org.ofono.NetworkRegistration")))
return modem;
}
return QString();
diff --git a/src/plugins/bearer/platformdefs_win.h b/src/plugins/bearer/platformdefs_win.h
index 5a8487d868..0477dc45c3 100644
--- a/src/plugins/bearer/platformdefs_win.h
+++ b/src/plugins/bearer/platformdefs_win.h
@@ -45,6 +45,7 @@
#include <winsock2.h>
#include <mswsock.h>
#undef interface
+#include <wincrypt.h>
#include <winioctl.h>
QT_BEGIN_NAMESPACE
diff --git a/src/plugins/bearer/qnetworksession_impl.h b/src/plugins/bearer/qnetworksession_impl.h
index e9c0e383fc..d9aa6ca8fb 100644
--- a/src/plugins/bearer/qnetworksession_impl.h
+++ b/src/plugins/bearer/qnetworksession_impl.h
@@ -75,31 +75,31 @@ public:
//that the state is immediately updated (w/o actually opening
//a session). Also this function should take care of
//notification hooks to discover future state changes.
- void syncStateWithInterface() Q_DECL_OVERRIDE;
+ void syncStateWithInterface() override;
#ifndef QT_NO_NETWORKINTERFACE
- QNetworkInterface currentInterface() const Q_DECL_OVERRIDE;
+ QNetworkInterface currentInterface() const override;
#endif
- QVariant sessionProperty(const QString& key) const Q_DECL_OVERRIDE;
- void setSessionProperty(const QString& key, const QVariant& value) Q_DECL_OVERRIDE;
-
- void open() Q_DECL_OVERRIDE;
- void close() Q_DECL_OVERRIDE;
- void stop() Q_DECL_OVERRIDE;
- void migrate() Q_DECL_OVERRIDE;
- void accept() Q_DECL_OVERRIDE;
- void ignore() Q_DECL_OVERRIDE;
- void reject() Q_DECL_OVERRIDE;
-
- QString errorString() const Q_DECL_OVERRIDE; //must return translated string
- QNetworkSession::SessionError error() const Q_DECL_OVERRIDE;
-
- quint64 bytesWritten() const Q_DECL_OVERRIDE;
- quint64 bytesReceived() const Q_DECL_OVERRIDE;
- quint64 activeTime() const Q_DECL_OVERRIDE;
-
- QNetworkSession::UsagePolicies usagePolicies() const Q_DECL_OVERRIDE;
- void setUsagePolicies(QNetworkSession::UsagePolicies) Q_DECL_OVERRIDE;
+ QVariant sessionProperty(const QString& key) const override;
+ void setSessionProperty(const QString& key, const QVariant& value) override;
+
+ void open() override;
+ void close() override;
+ void stop() override;
+ void migrate() override;
+ void accept() override;
+ void ignore() override;
+ void reject() override;
+
+ QString errorString() const override; //must return translated string
+ QNetworkSession::SessionError error() const override;
+
+ quint64 bytesWritten() const override;
+ quint64 bytesReceived() const override;
+ quint64 activeTime() const override;
+
+ QNetworkSession::UsagePolicies usagePolicies() const override;
+ void setUsagePolicies(QNetworkSession::UsagePolicies) override;
private Q_SLOTS:
void networkConfigurationsChanged();
diff --git a/src/plugins/generic/evdevkeyboard/main.cpp b/src/plugins/generic/evdevkeyboard/main.cpp
index ef08cd56ab..5c170e1a06 100644
--- a/src/plugins/generic/evdevkeyboard/main.cpp
+++ b/src/plugins/generic/evdevkeyboard/main.cpp
@@ -50,7 +50,7 @@ class QEvdevKeyboardPlugin : public QGenericPlugin
public:
QEvdevKeyboardPlugin();
- QObject* create(const QString &key, const QString &specification) Q_DECL_OVERRIDE;
+ QObject* create(const QString &key, const QString &specification) override;
};
QEvdevKeyboardPlugin::QEvdevKeyboardPlugin()
diff --git a/src/plugins/generic/evdevmouse/main.cpp b/src/plugins/generic/evdevmouse/main.cpp
index 86ec7cb575..1b8517f4c4 100644
--- a/src/plugins/generic/evdevmouse/main.cpp
+++ b/src/plugins/generic/evdevmouse/main.cpp
@@ -50,7 +50,7 @@ class QEvdevMousePlugin : public QGenericPlugin
public:
QEvdevMousePlugin();
- QObject* create(const QString &key, const QString &specification) Q_DECL_OVERRIDE;
+ QObject* create(const QString &key, const QString &specification) override;
};
QEvdevMousePlugin::QEvdevMousePlugin()
diff --git a/src/plugins/generic/evdevtablet/main.cpp b/src/plugins/generic/evdevtablet/main.cpp
index 0646d71146..f43a32910d 100644
--- a/src/plugins/generic/evdevtablet/main.cpp
+++ b/src/plugins/generic/evdevtablet/main.cpp
@@ -50,7 +50,7 @@ class QEvdevTabletPlugin : public QGenericPlugin
public:
QEvdevTabletPlugin();
- QObject* create(const QString &key, const QString &specification) Q_DECL_OVERRIDE;
+ QObject* create(const QString &key, const QString &specification) override;
};
QEvdevTabletPlugin::QEvdevTabletPlugin()
diff --git a/src/plugins/generic/evdevtouch/main.cpp b/src/plugins/generic/evdevtouch/main.cpp
index aa60614936..f1a0ad53ab 100644
--- a/src/plugins/generic/evdevtouch/main.cpp
+++ b/src/plugins/generic/evdevtouch/main.cpp
@@ -50,7 +50,7 @@ class QEvdevTouchScreenPlugin : public QGenericPlugin
public:
QEvdevTouchScreenPlugin();
- QObject* create(const QString &key, const QString &specification) Q_DECL_OVERRIDE;
+ QObject* create(const QString &key, const QString &specification) override;
};
QEvdevTouchScreenPlugin::QEvdevTouchScreenPlugin()
diff --git a/src/plugins/generic/generic.pro b/src/plugins/generic/generic.pro
index 70e433f89d..980aa1a506 100644
--- a/src/plugins/generic/generic.pro
+++ b/src/plugins/generic/generic.pro
@@ -1,5 +1,5 @@
TEMPLATE = subdirs
-QT_FOR_CONFIG += gui-private network-private
+QT_FOR_CONFIG += gui-private
qtConfig(evdev) {
SUBDIRS += evdevmouse evdevtouch evdevkeyboard
@@ -11,7 +11,7 @@ qtConfig(tslib) {
SUBDIRS += tslib
}
-qtHaveModule(network):qtConfig(udpsocket) {
+qtConfig(tuiotouch) {
SUBDIRS += tuiotouch
}
diff --git a/src/plugins/generic/tuiotouch/qoscbundle.cpp b/src/plugins/generic/tuiotouch/qoscbundle.cpp
index b84ae39aca..e9166922a1 100644
--- a/src/plugins/generic/tuiotouch/qoscbundle.cpp
+++ b/src/plugins/generic/tuiotouch/qoscbundle.cpp
@@ -125,7 +125,7 @@ QOscBundle::QOscBundle(const QByteArray &data)
if (size == 0) {
// empty bundle; these are valid, but should they be allowed? the
// spec is unclear on this...
- qWarning("Empty bundle?");
+ qCWarning(lcTuioBundle, "Empty bundle?");
m_isValid = true;
m_immediate = isImmediate;
m_timeEpoch = oscTimeEpoch;
@@ -155,7 +155,7 @@ QOscBundle::QOscBundle(const QByteArray &data)
m_timePico = oscTimePico;
m_messages.append(subMessage);
} else {
- qWarning("Invalid sub-message");
+ qCWarning(lcTuioBundle, "Invalid sub-message");
return;
}
} else if (subdata.startsWith(bundleIdentifier)) {
@@ -169,7 +169,7 @@ QOscBundle::QOscBundle(const QByteArray &data)
m_bundles.append(subBundle);
}
} else {
- qWarning("Malformed sub-data!");
+ qCWarning(lcTuioBundle, "Malformed sub-data!");
return;
}
}
diff --git a/src/plugins/generic/tuiotouch/qoscmessage.cpp b/src/plugins/generic/tuiotouch/qoscmessage.cpp
index b2004903bd..3c30caa923 100644
--- a/src/plugins/generic/tuiotouch/qoscmessage.cpp
+++ b/src/plugins/generic/tuiotouch/qoscmessage.cpp
@@ -113,7 +113,7 @@ QOscMessage::QOscMessage(const QByteArray &data)
parsedBytes += sizeof(quint32);
arguments.append(value.f);
} else {
- qWarning() << "Reading argument of unknown type " << typeTag;
+ qCWarning(lcTuioMessage) << "Reading argument of unknown type " << typeTag;
return;
}
}
diff --git a/src/plugins/generic/tuiotouch/qtuiohandler.cpp b/src/plugins/generic/tuiotouch/qtuiohandler.cpp
index eb646644ec..bb18ba5085 100644
--- a/src/plugins/generic/tuiotouch/qtuiohandler.cpp
+++ b/src/plugins/generic/tuiotouch/qtuiohandler.cpp
@@ -57,6 +57,7 @@
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(lcTuioHandler, "qt.qpa.tuio.handler")
Q_LOGGING_CATEGORY(lcTuioSource, "qt.qpa.tuio.source")
Q_LOGGING_CATEGORY(lcTuioSet, "qt.qpa.tuio.set")
@@ -82,7 +83,7 @@ QTuioHandler::QTuioHandler(const QString &specification)
} else if (args.at(i).startsWith("tcp=")) {
QString portString = args.at(i).section('=', 1, 1);
portNumber = portString.toInt();
- qWarning() << "TCP is not yet supported. Falling back to UDP on " << portNumber;
+ qCWarning(lcTuioHandler) << "TCP is not yet supported. Falling back to UDP on " << portNumber;
} else if (args.at(i) == "invertx") {
invertx = true;
} else if (args.at(i) == "inverty") {
@@ -119,7 +120,7 @@ QTuioHandler::QTuioHandler(const QString &specification)
QWindowSystemInterface::registerTouchDevice(m_device);
if (!m_socket.bind(QHostAddress::Any, portNumber)) {
- qWarning() << "Failed to bind TUIO socket: " << m_socket.errorString();
+ qCWarning(lcTuioHandler) << "Failed to bind TUIO socket: " << m_socket.errorString();
return;
}
@@ -172,7 +173,7 @@ void QTuioHandler::processPackets()
if (message.addressPattern() == "/tuio/2Dcur") {
QList<QVariant> arguments = message.arguments();
if (arguments.count() == 0) {
- qWarning("Ignoring TUIO message with no arguments");
+ qCWarning(lcTuioHandler, "Ignoring TUIO message with no arguments");
continue;
}
@@ -186,13 +187,13 @@ void QTuioHandler::processPackets()
} else if (messageType == "fseq") {
process2DCurFseq(message);
} else {
- qWarning() << "Ignoring unknown TUIO message type: " << messageType;
+ qCWarning(lcTuioHandler) << "Ignoring unknown TUIO message type: " << messageType;
continue;
}
} else if (message.addressPattern() == "/tuio/2Dobj") {
QList<QVariant> arguments = message.arguments();
if (arguments.count() == 0) {
- qWarning("Ignoring TUIO message with no arguments");
+ qCWarning(lcTuioHandler, "Ignoring TUIO message with no arguments");
continue;
}
@@ -206,11 +207,11 @@ void QTuioHandler::processPackets()
} else if (messageType == "fseq") {
process2DObjFseq(message);
} else {
- qWarning() << "Ignoring unknown TUIO message type: " << messageType;
+ qCWarning(lcTuioHandler) << "Ignoring unknown TUIO message type: " << messageType;
continue;
}
} else {
- qWarning() << "Ignoring unknown address pattern " << message.addressPattern();
+ qCWarning(lcTuioHandler) << "Ignoring unknown address pattern " << message.addressPattern();
continue;
}
}
@@ -221,12 +222,12 @@ void QTuioHandler::process2DCurSource(const QOscMessage &message)
{
QList<QVariant> arguments = message.arguments();
if (arguments.count() != 2) {
- qWarning() << "Ignoring malformed TUIO source message: " << arguments.count();
+ qCWarning(lcTuioSource) << "Ignoring malformed TUIO source message: " << arguments.count();
return;
}
if (QMetaType::Type(arguments.at(1).type()) != QMetaType::QByteArray) {
- qWarning("Ignoring malformed TUIO source message (bad argument type)");
+ qCWarning(lcTuioSource, "Ignoring malformed TUIO source message (bad argument type)");
return;
}
@@ -248,7 +249,7 @@ void QTuioHandler::process2DCurAlive(const QOscMessage &message)
for (int i = 1; i < arguments.count(); ++i) {
if (QMetaType::Type(arguments.at(i).type()) != QMetaType::Int) {
- qWarning() << "Ignoring malformed TUIO alive message (bad argument on position" << i << arguments << ')';
+ qCWarning(lcTuioHandler) << "Ignoring malformed TUIO alive message (bad argument on position" << i << arguments << ')';
return;
}
@@ -288,7 +289,7 @@ void QTuioHandler::process2DCurSet(const QOscMessage &message)
{
QList<QVariant> arguments = message.arguments();
if (arguments.count() < 7) {
- qWarning() << "Ignoring malformed TUIO set message with too few arguments: " << arguments.count();
+ qCWarning(lcTuioSet) << "Ignoring malformed TUIO set message with too few arguments: " << arguments.count();
return;
}
@@ -299,7 +300,7 @@ void QTuioHandler::process2DCurSet(const QOscMessage &message)
QMetaType::Type(arguments.at(5).type()) != QMetaType::Float ||
QMetaType::Type(arguments.at(6).type()) != QMetaType::Float
) {
- qWarning() << "Ignoring malformed TUIO set message with bad types: " << arguments;
+ qCWarning(lcTuioSet) << "Ignoring malformed TUIO set message with bad types: " << arguments;
return;
}
@@ -312,7 +313,7 @@ void QTuioHandler::process2DCurSet(const QOscMessage &message)
QMap<int, QTuioCursor>::Iterator it = m_activeCursors.find(cursorId);
if (it == m_activeCursors.end()) {
- qWarning() << "Ignoring malformed TUIO set for nonexistent cursor " << cursorId;
+ qCWarning(lcTuioSet) << "Ignoring malformed TUIO set for nonexistent cursor " << cursorId;
return;
}
@@ -386,12 +387,12 @@ void QTuioHandler::process2DObjSource(const QOscMessage &message)
{
QList<QVariant> arguments = message.arguments();
if (arguments.count() != 2) {
- qWarning() << "Ignoring malformed TUIO source message: " << arguments.count();
+ qCWarning(lcTuioSource, ) << "Ignoring malformed TUIO source message: " << arguments.count();
return;
}
if (QMetaType::Type(arguments.at(1).type()) != QMetaType::QByteArray) {
- qWarning("Ignoring malformed TUIO source message (bad argument type)");
+ qCWarning(lcTuioSource, "Ignoring malformed TUIO source message (bad argument type)");
return;
}
@@ -413,7 +414,7 @@ void QTuioHandler::process2DObjAlive(const QOscMessage &message)
for (int i = 1; i < arguments.count(); ++i) {
if (QMetaType::Type(arguments.at(i).type()) != QMetaType::Int) {
- qWarning() << "Ignoring malformed TUIO alive message (bad argument on position" << i << arguments << ')';
+ qCWarning(lcTuioHandler) << "Ignoring malformed TUIO alive message (bad argument on position" << i << arguments << ')';
return;
}
@@ -453,7 +454,7 @@ void QTuioHandler::process2DObjSet(const QOscMessage &message)
{
QList<QVariant> arguments = message.arguments();
if (arguments.count() < 7) {
- qWarning() << "Ignoring malformed TUIO set message with too few arguments: " << arguments.count();
+ qCWarning(lcTuioSet) << "Ignoring malformed TUIO set message with too few arguments: " << arguments.count();
return;
}
@@ -467,7 +468,7 @@ void QTuioHandler::process2DObjSet(const QOscMessage &message)
QMetaType::Type(arguments.at(8).type()) != QMetaType::Float ||
QMetaType::Type(arguments.at(9).type()) != QMetaType::Float ||
QMetaType::Type(arguments.at(10).type()) != QMetaType::Float) {
- qWarning() << "Ignoring malformed TUIO set message with bad types: " << arguments;
+ qCWarning(lcTuioSet) << "Ignoring malformed TUIO set message with bad types: " << arguments;
return;
}
@@ -484,7 +485,7 @@ void QTuioHandler::process2DObjSet(const QOscMessage &message)
QMap<int, QTuioToken>::Iterator it = m_activeTokens.find(id);
if (it == m_activeTokens.end()) {
- qWarning() << "Ignoring malformed TUIO set for nonexistent token " << classId;
+ qCWarning(lcTuioSet) << "Ignoring malformed TUIO set for nonexistent token " << classId;
return;
}
@@ -522,7 +523,7 @@ QWindowSystemInterface::TouchPoint QTuioHandler::tokenToTouchPoint(const QTuioTo
QPointF delta = relPos - relPos.toPoint();
tp.area.moveCenter(win->mapToGlobal(relPos.toPoint()) + delta);
tp.velocity = QVector2D(win->size().width() * tc.vx(), win->size().height() * tc.vy());
- tp.rotation = tc.angle() * 180.0 / M_PI; // convert radians to degrees
+ tp.rotation = qRadiansToDegrees(tc.angle());
return tp;
}
diff --git a/src/plugins/imageformats/gif/main.h b/src/plugins/imageformats/gif/main.h
index 4287a8a39c..84913a31d7 100644
--- a/src/plugins/imageformats/gif/main.h
+++ b/src/plugins/imageformats/gif/main.h
@@ -55,8 +55,8 @@ public:
QGifPlugin();
~QGifPlugin();
- Capabilities capabilities(QIODevice *device, const QByteArray &format) const Q_DECL_OVERRIDE;
- QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const Q_DECL_OVERRIDE;
+ Capabilities capabilities(QIODevice *device, const QByteArray &format) const override;
+ QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const override;
};
QT_END_NAMESPACE
diff --git a/src/plugins/imageformats/gif/qgifhandler.cpp b/src/plugins/imageformats/gif/qgifhandler.cpp
index bae74bf9a1..e0f7f44701 100644
--- a/src/plugins/imageformats/gif/qgifhandler.cpp
+++ b/src/plugins/imageformats/gif/qgifhandler.cpp
@@ -354,7 +354,7 @@ int QGIFFormat::decode(QImage *image, const uchar *buffer, int length,
(*image) = QImage(swidth, sheight, format);
bpl = image->bytesPerLine();
bits = image->bits();
- memset(bits, 0, image->byteCount());
+ memset(bits, 0, image->sizeInBytes());
}
// Check if the previous attempt to create the image failed. If it
@@ -415,7 +415,7 @@ int QGIFFormat::decode(QImage *image, const uchar *buffer, int length,
backingstore = QImage(qMax(backingstore.width(), w),
qMax(backingstore.height(), h),
QImage::Format_RGB32);
- memset(backingstore.bits(), 0, backingstore.byteCount());
+ memset(backingstore.bits(), 0, backingstore.sizeInBytes());
}
const int dest_bpl = backingstore.bytesPerLine();
unsigned char *dest_data = backingstore.bits();
diff --git a/src/plugins/imageformats/gif/qgifhandler_p.h b/src/plugins/imageformats/gif/qgifhandler_p.h
index bc3debe83c..b004ee610d 100644
--- a/src/plugins/imageformats/gif/qgifhandler_p.h
+++ b/src/plugins/imageformats/gif/qgifhandler_p.h
@@ -69,22 +69,22 @@ public:
QGifHandler();
~QGifHandler();
- bool canRead() const Q_DECL_OVERRIDE;
- bool read(QImage *image) Q_DECL_OVERRIDE;
- bool write(const QImage &image) Q_DECL_OVERRIDE;
+ bool canRead() const override;
+ bool read(QImage *image) override;
+ bool write(const QImage &image) override;
- QByteArray name() const Q_DECL_OVERRIDE;
+ QByteArray name() const override;
static bool canRead(QIODevice *device);
- QVariant option(ImageOption option) const Q_DECL_OVERRIDE;
- void setOption(ImageOption option, const QVariant &value) Q_DECL_OVERRIDE;
- bool supportsOption(ImageOption option) const Q_DECL_OVERRIDE;
+ QVariant option(ImageOption option) const override;
+ void setOption(ImageOption option, const QVariant &value) override;
+ bool supportsOption(ImageOption option) const override;
- int imageCount() const Q_DECL_OVERRIDE;
- int loopCount() const Q_DECL_OVERRIDE;
- int nextImageDelay() const Q_DECL_OVERRIDE;
- int currentImageNumber() const Q_DECL_OVERRIDE;
+ int imageCount() const override;
+ int loopCount() const override;
+ int nextImageDelay() const override;
+ int currentImageNumber() const override;
private:
bool imageIsComing() const;
diff --git a/src/plugins/imageformats/ico/ico.pro b/src/plugins/imageformats/ico/ico.pro
index 7ca1f18cb1..c8bb37eff2 100644
--- a/src/plugins/imageformats/ico/ico.pro
+++ b/src/plugins/imageformats/ico/ico.pro
@@ -3,6 +3,7 @@ TARGET = qico
HEADERS += main.h qicohandler.h
SOURCES += main.cpp qicohandler.cpp
OTHER_FILES += ico.json
+QT += core-private
PLUGIN_TYPE = imageformats
PLUGIN_CLASS_NAME = QICOPlugin
diff --git a/src/plugins/imageformats/ico/main.h b/src/plugins/imageformats/ico/main.h
index 8e54d7c643..b5875183c1 100644
--- a/src/plugins/imageformats/ico/main.h
+++ b/src/plugins/imageformats/ico/main.h
@@ -52,8 +52,8 @@ class QICOPlugin : public QImageIOPlugin
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QImageIOHandlerFactoryInterface" FILE "ico.json")
public:
- Capabilities capabilities(QIODevice *device, const QByteArray &format) const Q_DECL_OVERRIDE;
- QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const Q_DECL_OVERRIDE;
+ Capabilities capabilities(QIODevice *device, const QByteArray &format) const override;
+ QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const override;
};
QT_END_NAMESPACE
diff --git a/src/plugins/imageformats/ico/qicohandler.cpp b/src/plugins/imageformats/ico/qicohandler.cpp
index 17d1aeeb5d..e61173db30 100644
--- a/src/plugins/imageformats/ico/qicohandler.cpp
+++ b/src/plugins/imageformats/ico/qicohandler.cpp
@@ -48,6 +48,7 @@
#include "qicohandler.h"
#include <QtCore/qendian.h>
+#include <private/qendian_p.h>
#include <QtGui/QImage>
#include <QtCore/QFile>
#include <QtCore/QBuffer>
@@ -63,34 +64,34 @@ typedef struct
quint8 bHeight; // Height of the image (actual height, not times 2)
quint8 bColorCount; // Number of colors in image (0 if >=8bpp) [ not ture ]
quint8 bReserved; // Reserved
- quint16 wPlanes; // Color Planes
- quint16 wBitCount; // Bits per pixel
- quint32 dwBytesInRes; // how many bytes in this resource?
- quint32 dwImageOffset; // where in the file is this image
+ quint16_le wPlanes; // Color Planes
+ quint16_le wBitCount; // Bits per pixel
+ quint32_le dwBytesInRes; // how many bytes in this resource?
+ quint32_le dwImageOffset; // where in the file is this image
} ICONDIRENTRY, *LPICONDIRENTRY;
#define ICONDIRENTRY_SIZE 16
typedef struct
{
- quint16 idReserved; // Reserved
- quint16 idType; // resource type (1 for icons, 2 for cursors)
- quint16 idCount; // how many images?
+ quint16_le idReserved; // Reserved
+ quint16_le idType; // resource type (1 for icons, 2 for cursors)
+ quint16_le idCount; // how many images?
ICONDIRENTRY idEntries[1]; // the entries for each image
} ICONDIR, *LPICONDIR;
#define ICONDIR_SIZE 6 // Exclude the idEntries field
typedef struct { // BMP information header
- quint32 biSize; // size of this struct
- quint32 biWidth; // pixmap width
- quint32 biHeight; // pixmap height (specifies the combined height of the XOR and AND masks)
- quint16 biPlanes; // should be 1
- quint16 biBitCount; // number of bits per pixel
- quint32 biCompression; // compression method
- quint32 biSizeImage; // size of image
- quint32 biXPelsPerMeter; // horizontal resolution
- quint32 biYPelsPerMeter; // vertical resolution
- quint32 biClrUsed; // number of colors used
- quint32 biClrImportant; // number of important colors
+ quint32_le biSize; // size of this struct
+ quint32_le biWidth; // pixmap width
+ quint32_le biHeight; // pixmap height (specifies the combined height of the XOR and AND masks)
+ quint16_le biPlanes; // should be 1
+ quint16_le biBitCount; // number of bits per pixel
+ quint32_le biCompression; // compression method
+ quint32_le biSizeImage; // size of image
+ quint32_le biXPelsPerMeter; // horizontal resolution
+ quint32_le biYPelsPerMeter; // vertical resolution
+ quint32_le biClrUsed; // number of colors used
+ quint32_le biClrImportant; // number of important colors
} BMP_INFOHDR ,*LPBMP_INFOHDR;
#define BMP_INFOHDR_SIZE 40
@@ -140,108 +141,43 @@ private:
// Data readers and writers that takes care of alignment and endian stuff.
static bool readIconDirEntry(QIODevice *iodev, ICONDIRENTRY *iconDirEntry)
{
- if (iodev) {
- uchar tmp[ICONDIRENTRY_SIZE];
- if (iodev->read((char*)tmp, ICONDIRENTRY_SIZE) == ICONDIRENTRY_SIZE) {
- iconDirEntry->bWidth = tmp[0];
- iconDirEntry->bHeight = tmp[1];
- iconDirEntry->bColorCount = tmp[2];
- iconDirEntry->bReserved = tmp[3];
-
- iconDirEntry->wPlanes = qFromLittleEndian<quint16>(&tmp[4]);
- iconDirEntry->wBitCount = qFromLittleEndian<quint16>(&tmp[6]);
- iconDirEntry->dwBytesInRes = qFromLittleEndian<quint32>(&tmp[8]);
- iconDirEntry->dwImageOffset = qFromLittleEndian<quint32>(&tmp[12]);
- return true;
- }
- }
+ if (iodev)
+ return (iodev->read((char*)iconDirEntry, ICONDIRENTRY_SIZE) == ICONDIRENTRY_SIZE);
return false;
}
static bool writeIconDirEntry(QIODevice *iodev, const ICONDIRENTRY &iconEntry)
{
- if (iodev) {
- uchar tmp[ICONDIRENTRY_SIZE];
- tmp[0] = iconEntry.bWidth;
- tmp[1] = iconEntry.bHeight;
- tmp[2] = iconEntry.bColorCount;
- tmp[3] = iconEntry.bReserved;
- qToLittleEndian<quint16>(iconEntry.wPlanes, &tmp[4]);
- qToLittleEndian<quint16>(iconEntry.wBitCount, &tmp[6]);
- qToLittleEndian<quint32>(iconEntry.dwBytesInRes, &tmp[8]);
- qToLittleEndian<quint32>(iconEntry.dwImageOffset, &tmp[12]);
- return iodev->write((char*)tmp, ICONDIRENTRY_SIZE) == ICONDIRENTRY_SIZE;
- }
-
+ if (iodev)
+ return iodev->write((char*)&iconEntry, ICONDIRENTRY_SIZE) == ICONDIRENTRY_SIZE;
return false;
}
static bool readIconDir(QIODevice *iodev, ICONDIR *iconDir)
{
- if (iodev) {
- uchar tmp[ICONDIR_SIZE];
- if (iodev->read((char*)tmp, ICONDIR_SIZE) == ICONDIR_SIZE) {
- iconDir->idReserved = qFromLittleEndian<quint16>(&tmp[0]);
- iconDir->idType = qFromLittleEndian<quint16>(&tmp[2]);
- iconDir->idCount = qFromLittleEndian<quint16>(&tmp[4]);
- return true;
- }
- }
+ if (iodev)
+ return (iodev->read((char*)iconDir, ICONDIR_SIZE) == ICONDIR_SIZE);
return false;
}
static bool writeIconDir(QIODevice *iodev, const ICONDIR &iconDir)
{
- if (iodev) {
- uchar tmp[6];
- qToLittleEndian(iconDir.idReserved, tmp);
- qToLittleEndian(iconDir.idType, &tmp[2]);
- qToLittleEndian(iconDir.idCount, &tmp[4]);
- return iodev->write((char*)tmp, 6) == 6;
- }
+ if (iodev)
+ return iodev->write((char*)&iconDir, 6) == 6;
return false;
}
static bool readBMPInfoHeader(QIODevice *iodev, BMP_INFOHDR *pHeader)
{
- if (iodev) {
- uchar header[BMP_INFOHDR_SIZE];
- if (iodev->read((char*)header, BMP_INFOHDR_SIZE) == BMP_INFOHDR_SIZE) {
- pHeader->biSize = qFromLittleEndian<quint32>(&header[0]);
- pHeader->biWidth = qFromLittleEndian<quint32>(&header[4]);
- pHeader->biHeight = qFromLittleEndian<quint32>(&header[8]);
- pHeader->biPlanes = qFromLittleEndian<quint16>(&header[12]);
- pHeader->biBitCount = qFromLittleEndian<quint16>(&header[14]);
- pHeader->biCompression = qFromLittleEndian<quint32>(&header[16]);
- pHeader->biSizeImage = qFromLittleEndian<quint32>(&header[20]);
- pHeader->biXPelsPerMeter = qFromLittleEndian<quint32>(&header[24]);
- pHeader->biYPelsPerMeter = qFromLittleEndian<quint32>(&header[28]);
- pHeader->biClrUsed = qFromLittleEndian<quint32>(&header[32]);
- pHeader->biClrImportant = qFromLittleEndian<quint32>(&header[36]);
- return true;
- }
- }
+ if (iodev)
+ return (iodev->read((char*)pHeader, BMP_INFOHDR_SIZE) == BMP_INFOHDR_SIZE);
return false;
}
static bool writeBMPInfoHeader(QIODevice *iodev, const BMP_INFOHDR &header)
{
- if (iodev) {
- uchar tmp[BMP_INFOHDR_SIZE];
- qToLittleEndian<quint32>(header.biSize, &tmp[0]);
- qToLittleEndian<quint32>(header.biWidth, &tmp[4]);
- qToLittleEndian<quint32>(header.biHeight, &tmp[8]);
- qToLittleEndian<quint16>(header.biPlanes, &tmp[12]);
- qToLittleEndian<quint16>(header.biBitCount, &tmp[14]);
- qToLittleEndian<quint32>(header.biCompression, &tmp[16]);
- qToLittleEndian<quint32>(header.biSizeImage, &tmp[20]);
- qToLittleEndian<quint32>(header.biXPelsPerMeter, &tmp[24]);
- qToLittleEndian<quint32>(header.biYPelsPerMeter, &tmp[28]);
- qToLittleEndian<quint32>(header.biClrUsed, &tmp[32]);
- qToLittleEndian<quint32>(header.biClrImportant, &tmp[36]);
-
- return iodev->write((char*)tmp, BMP_INFOHDR_SIZE) == BMP_INFOHDR_SIZE;
- }
+ if (iodev)
+ return iodev->write((char*)&header, BMP_INFOHDR_SIZE) == BMP_INFOHDR_SIZE;
return false;
}
@@ -561,7 +497,7 @@ QImage ICOReader::iconAt(int index)
if (icoAttrib.depth == 32) // there's no colormap
icoAttrib.ncolors = 0;
else // # colors used
- icoAttrib.ncolors = header.biClrUsed ? header.biClrUsed : 1 << icoAttrib.nbits;
+ icoAttrib.ncolors = header.biClrUsed ? uint(header.biClrUsed) : 1 << icoAttrib.nbits;
if (icoAttrib.ncolors > 256) //color table can't be more than 256
return img;
icoAttrib.w = iconEntry.bWidth;
diff --git a/src/plugins/imageformats/ico/qicohandler.h b/src/plugins/imageformats/ico/qicohandler.h
index eea894a950..435f036113 100644
--- a/src/plugins/imageformats/ico/qicohandler.h
+++ b/src/plugins/imageformats/ico/qicohandler.h
@@ -50,20 +50,20 @@ public:
QtIcoHandler(QIODevice *device);
virtual ~QtIcoHandler();
- bool canRead() const Q_DECL_OVERRIDE;
- bool read(QImage *image) Q_DECL_OVERRIDE;
- bool write(const QImage &image) Q_DECL_OVERRIDE;
+ bool canRead() const override;
+ bool read(QImage *image) override;
+ bool write(const QImage &image) override;
- QByteArray name() const Q_DECL_OVERRIDE;
+ QByteArray name() const override;
- int imageCount() const Q_DECL_OVERRIDE;
- bool jumpToImage(int imageNumber) Q_DECL_OVERRIDE;
- bool jumpToNextImage() Q_DECL_OVERRIDE;
+ int imageCount() const override;
+ bool jumpToImage(int imageNumber) override;
+ bool jumpToNextImage() override;
static bool canRead(QIODevice *device);
- bool supportsOption(ImageOption option) const Q_DECL_OVERRIDE;
- QVariant option(ImageOption option) const Q_DECL_OVERRIDE;
+ bool supportsOption(ImageOption option) const override;
+ QVariant option(ImageOption option) const override;
private:
int m_currentIconIndex;
diff --git a/src/plugins/imageformats/jpeg/main.h b/src/plugins/imageformats/jpeg/main.h
index 10619757c4..1845c8c124 100644
--- a/src/plugins/imageformats/jpeg/main.h
+++ b/src/plugins/imageformats/jpeg/main.h
@@ -51,8 +51,8 @@ class QJpegPlugin : public QImageIOPlugin
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QImageIOHandlerFactoryInterface" FILE "jpeg.json")
public:
- Capabilities capabilities(QIODevice *device, const QByteArray &format) const Q_DECL_OVERRIDE;
- QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const Q_DECL_OVERRIDE;
+ Capabilities capabilities(QIODevice *device, const QByteArray &format) const override;
+ QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const override;
};
QT_END_NAMESPACE
diff --git a/src/plugins/imageformats/jpeg/qjpeghandler.cpp b/src/plugins/imageformats/jpeg/qjpeghandler.cpp
index 68619928d3..1cdc28c077 100644
--- a/src/plugins/imageformats/jpeg/qjpeghandler.cpp
+++ b/src/plugins/imageformats/jpeg/qjpeghandler.cpp
@@ -65,9 +65,6 @@ extern "C" {
#endif
#define XMD_H // shut JPEGlib up
-#if defined(Q_OS_UNIXWARE)
-# define HAVE_BOOLEAN // libjpeg under Unixware seems to need this
-#endif
#include <jpeglib.h>
#ifdef const
# undef const // remove crazy C hackery in jconfig.h
diff --git a/src/plugins/imageformats/jpeg/qjpeghandler_p.h b/src/plugins/imageformats/jpeg/qjpeghandler_p.h
index 534ce12115..d832bf82f3 100644
--- a/src/plugins/imageformats/jpeg/qjpeghandler_p.h
+++ b/src/plugins/imageformats/jpeg/qjpeghandler_p.h
@@ -64,17 +64,17 @@ public:
QJpegHandler();
~QJpegHandler();
- bool canRead() const Q_DECL_OVERRIDE;
- bool read(QImage *image) Q_DECL_OVERRIDE;
- bool write(const QImage &image) Q_DECL_OVERRIDE;
+ bool canRead() const override;
+ bool read(QImage *image) override;
+ bool write(const QImage &image) override;
- QByteArray name() const Q_DECL_OVERRIDE;
+ QByteArray name() const override;
static bool canRead(QIODevice *device);
- QVariant option(ImageOption option) const Q_DECL_OVERRIDE;
- void setOption(ImageOption option, const QVariant &value) Q_DECL_OVERRIDE;
- bool supportsOption(ImageOption option) const Q_DECL_OVERRIDE;
+ QVariant option(ImageOption option) const override;
+ void setOption(ImageOption option, const QVariant &value) override;
+ bool supportsOption(ImageOption option) const override;
private:
QJpegHandlerPrivate *d;
diff --git a/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp b/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp
index 48693ccdb0..81a730232c 100644
--- a/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp
+++ b/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp
@@ -82,7 +82,37 @@ static const int composingKeys[] = {
Qt::Key_Dead_Semivoiced_Sound,
Qt::Key_Dead_Belowdot,
Qt::Key_Dead_Hook,
- Qt::Key_Dead_Horn
+ Qt::Key_Dead_Horn,
+ Qt::Key_Dead_Stroke,
+ Qt::Key_Dead_Abovecomma,
+ Qt::Key_Dead_Abovereversedcomma,
+ Qt::Key_Dead_Doublegrave,
+ Qt::Key_Dead_Belowring,
+ Qt::Key_Dead_Belowmacron,
+ Qt::Key_Dead_Belowcircumflex,
+ Qt::Key_Dead_Belowtilde,
+ Qt::Key_Dead_Belowbreve,
+ Qt::Key_Dead_Belowdiaeresis,
+ Qt::Key_Dead_Invertedbreve,
+ Qt::Key_Dead_Belowcomma,
+ Qt::Key_Dead_Currency,
+ Qt::Key_Dead_a,
+ Qt::Key_Dead_A,
+ Qt::Key_Dead_e,
+ Qt::Key_Dead_E,
+ Qt::Key_Dead_i,
+ Qt::Key_Dead_I,
+ Qt::Key_Dead_o,
+ Qt::Key_Dead_O,
+ Qt::Key_Dead_u,
+ Qt::Key_Dead_U,
+ Qt::Key_Dead_Small_Schwa,
+ Qt::Key_Dead_Capital_Schwa,
+ Qt::Key_Dead_Greek,
+ Qt::Key_Dead_Lowline,
+ Qt::Key_Dead_Aboveverticalline,
+ Qt::Key_Dead_Belowverticalline,
+ Qt::Key_Dead_Longsolidusoverlay
};
QComposeInputContext::QComposeInputContext()
diff --git a/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.h b/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.h
index f82280903d..4830959665 100644
--- a/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.h
+++ b/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.h
@@ -58,11 +58,11 @@ public:
QComposeInputContext();
~QComposeInputContext();
- bool isValid() const Q_DECL_OVERRIDE;
- void setFocusObject(QObject *object) Q_DECL_OVERRIDE;
- void reset() Q_DECL_OVERRIDE;
- void update(Qt::InputMethodQueries) Q_DECL_OVERRIDE;
- bool filterEvent(const QEvent *event) Q_DECL_OVERRIDE;
+ bool isValid() const override;
+ void setFocusObject(QObject *object) override;
+ void reset() override;
+ void update(Qt::InputMethodQueries) override;
+ bool filterEvent(const QEvent *event) override;
protected:
void clearComposeBuffer();
diff --git a/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontextmain.cpp b/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontextmain.cpp
index 97a7242ca6..6b33df65b9 100644
--- a/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontextmain.cpp
+++ b/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontextmain.cpp
@@ -51,7 +51,7 @@ class QComposePlatformInputContextPlugin : public QPlatformInputContextPlugin
Q_PLUGIN_METADATA(IID QPlatformInputContextFactoryInterface_iid FILE "compose.json")
public:
- QComposeInputContext *create(const QString &, const QStringList &) Q_DECL_OVERRIDE;
+ QComposeInputContext *create(const QString &, const QStringList &) override;
};
QComposeInputContext *QComposePlatformInputContextPlugin::create(const QString &system, const QStringList &paramList)
diff --git a/src/plugins/platforminputcontexts/ibus/main.cpp b/src/plugins/platforminputcontexts/ibus/main.cpp
index 2846f52c8c..0a7da3b14c 100644
--- a/src/plugins/platforminputcontexts/ibus/main.cpp
+++ b/src/plugins/platforminputcontexts/ibus/main.cpp
@@ -51,7 +51,7 @@ class QIbusPlatformInputContextPlugin : public QPlatformInputContextPlugin
Q_PLUGIN_METADATA(IID QPlatformInputContextFactoryInterface_iid FILE "ibus.json")
public:
- QIBusPlatformInputContext *create(const QString&, const QStringList&) Q_DECL_OVERRIDE;
+ QIBusPlatformInputContext *create(const QString&, const QStringList&) override;
};
QIBusPlatformInputContext *QIbusPlatformInputContextPlugin::create(const QString& system, const QStringList& paramList)
diff --git a/src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.cpp b/src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.cpp
index 9675d828e7..76f42d764d 100644
--- a/src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.cpp
+++ b/src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.cpp
@@ -60,6 +60,10 @@
#ifndef IBUS_RELEASE_MASK
#define IBUS_RELEASE_MASK (1 << 30)
+#define IBUS_SHIFT_MASK (1 << 0)
+#define IBUS_CONTROL_MASK (1 << 2)
+#define IBUS_MOD1_MASK (1 << 3)
+#define IBUS_META_MASK (1 << 28)
#endif
QT_BEGIN_NAMESPACE
@@ -90,6 +94,7 @@ public:
bool valid;
bool busConnected;
QString predit;
+ QList<QInputMethodEvent::Attribute> attributes;
bool needsSurroundingText;
QLocale locale;
};
@@ -165,6 +170,7 @@ void QIBusPlatformInputContext::reset()
d->context->Reset();
d->predit = QString();
+ d->attributes.clear();
}
void QIBusPlatformInputContext::commit()
@@ -177,6 +183,7 @@ void QIBusPlatformInputContext::commit()
QObject *input = qApp->focusObject();
if (!input) {
d->predit = QString();
+ d->attributes.clear();
return;
}
@@ -188,6 +195,7 @@ void QIBusPlatformInputContext::commit()
d->context->Reset();
d->predit = QString();
+ d->attributes.clear();
}
@@ -274,6 +282,7 @@ void QIBusPlatformInputContext::commitText(const QDBusVariant &text)
QCoreApplication::sendEvent(input, &event);
d->predit = QString();
+ d->attributes.clear();
}
void QIBusPlatformInputContext::updatePreeditText(const QDBusVariant &text, uint cursorPos, bool visible)
@@ -292,16 +301,48 @@ void QIBusPlatformInputContext::updatePreeditText(const QDBusVariant &text, uint
if (debug)
qDebug() << "preedit text:" << t.text;
- QList<QInputMethodEvent::Attribute> attributes = t.attributes.imAttributes();
+ d->attributes = t.attributes.imAttributes();
if (!t.text.isEmpty())
- attributes += QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, cursorPos, visible ? 1 : 0);
+ d->attributes += QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, cursorPos, visible ? 1 : 0, QVariant());
- QInputMethodEvent event(t.text, attributes);
+ QInputMethodEvent event(t.text, d->attributes);
QCoreApplication::sendEvent(input, &event);
d->predit = t.text;
}
+void QIBusPlatformInputContext::forwardKeyEvent(uint keyval, uint keycode, uint state)
+{
+ if (!qApp)
+ return;
+
+ QObject *input = qApp->focusObject();
+ if (!input)
+ return;
+
+ if (debug)
+ qDebug() << "forwardKeyEvent" << keyval << keycode << state;
+
+ QEvent::Type type = QEvent::KeyPress;
+ if (state & IBUS_RELEASE_MASK)
+ type = QEvent::KeyRelease;
+
+ state &= ~IBUS_RELEASE_MASK;
+
+ Qt::KeyboardModifiers modifiers = Qt::NoModifier;
+ if (state & IBUS_SHIFT_MASK)
+ modifiers |= Qt::ShiftModifier;
+ if (state & IBUS_CONTROL_MASK)
+ modifiers |= Qt::ControlModifier;
+ if (state & IBUS_MOD1_MASK)
+ modifiers |= Qt::AltModifier;
+ if (state & IBUS_META_MASK)
+ modifiers |= Qt::MetaModifier;
+
+ QKeyEvent event(type, keyval, modifiers, QString(keyval));
+ QCoreApplication::sendEvent(input, &event);
+}
+
void QIBusPlatformInputContext::surroundingTextRequired()
{
if (debug)
@@ -324,6 +365,27 @@ void QIBusPlatformInputContext::deleteSurroundingText(int offset, uint n_chars)
QCoreApplication::sendEvent(input, &event);
}
+void QIBusPlatformInputContext::hidePreeditText()
+{
+ QObject *input = QGuiApplication::focusObject();
+ if (!input)
+ return;
+
+ QList<QInputMethodEvent::Attribute> attributes;
+ QInputMethodEvent event(QString(), attributes);
+ QCoreApplication::sendEvent(input, &event);
+}
+
+void QIBusPlatformInputContext::showPreeditText()
+{
+ QObject *input = QGuiApplication::focusObject();
+ if (!input)
+ return;
+
+ QInputMethodEvent event(d->predit, d->attributes);
+ QCoreApplication::sendEvent(input, &event);
+}
+
bool QIBusPlatformInputContext::filterEvent(const QEvent *event)
{
if (!d->busConnected)
@@ -496,8 +558,11 @@ void QIBusPlatformInputContext::connectToContextSignals()
if (d->context) {
connect(d->context, SIGNAL(CommitText(QDBusVariant)), SLOT(commitText(QDBusVariant)));
connect(d->context, SIGNAL(UpdatePreeditText(QDBusVariant,uint,bool)), this, SLOT(updatePreeditText(QDBusVariant,uint,bool)));
+ connect(d->context, SIGNAL(ForwardKeyEvent(uint, uint, uint)), this, SLOT(forwardKeyEvent(uint, uint, uint)));
connect(d->context, SIGNAL(DeleteSurroundingText(int,uint)), this, SLOT(deleteSurroundingText(int,uint)));
connect(d->context, SIGNAL(RequireSurroundingText()), this, SLOT(surroundingTextRequired()));
+ connect(d->context, SIGNAL(HidePreeditText()), this, SLOT(hidePreeditText()));
+ connect(d->context, SIGNAL(ShowPreeditText()), this, SLOT(showPreeditText()));
}
}
diff --git a/src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.h b/src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.h
index 7baa9ad1da..9b92b2e1f9 100644
--- a/src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.h
+++ b/src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.h
@@ -86,23 +86,26 @@ public:
QIBusPlatformInputContext();
~QIBusPlatformInputContext();
- bool isValid() const Q_DECL_OVERRIDE;
- void setFocusObject(QObject *object) Q_DECL_OVERRIDE;
+ bool isValid() const override;
+ void setFocusObject(QObject *object) override;
- void invokeAction(QInputMethod::Action a, int x) Q_DECL_OVERRIDE;
- void reset() Q_DECL_OVERRIDE;
- void commit() Q_DECL_OVERRIDE;
- void update(Qt::InputMethodQueries) Q_DECL_OVERRIDE;
- bool filterEvent(const QEvent *event) Q_DECL_OVERRIDE;
- QLocale locale() const Q_DECL_OVERRIDE;
- bool hasCapability(Capability capability) const Q_DECL_OVERRIDE;
+ void invokeAction(QInputMethod::Action a, int x) override;
+ void reset() override;
+ void commit() override;
+ void update(Qt::InputMethodQueries) override;
+ bool filterEvent(const QEvent *event) override;
+ QLocale locale() const override;
+ bool hasCapability(Capability capability) const override;
public Q_SLOTS:
void commitText(const QDBusVariant &text);
void updatePreeditText(const QDBusVariant &text, uint cursor_pos, bool visible);
+ void forwardKeyEvent(uint keyval, uint keycode, uint state);
void cursorRectChanged();
void deleteSurroundingText(int offset, uint n_chars);
void surroundingTextRequired();
+ void hidePreeditText();
+ void showPreeditText();
void filterEventFinished(QDBusPendingCallWatcher *call);
void socketChanged(const QString &str);
void connectToBus();
diff --git a/src/plugins/platforminputcontexts/ibus/qibusproxy.cpp b/src/plugins/platforminputcontexts/ibus/qibusproxy.cpp
index 9efa6f7eb0..156e9b7c90 100644
--- a/src/plugins/platforminputcontexts/ibus/qibusproxy.cpp
+++ b/src/plugins/platforminputcontexts/ibus/qibusproxy.cpp
@@ -33,10 +33,53 @@ QIBusProxy::~QIBusProxy()
{
}
+#ifdef QIBUS_GET_ADDRESS
+QString QIBusProxy::getAddress()
+{
+ QDBusReply<QDBusVariant> reply = Address();
+ QVariant variant = reply.value().variant();
+ if (!variant.isValid())
+ return QString();
+ return variant.toString();
+}
+#endif
+
+#ifdef QIBUS_GET_ENGINES
+QList<QIBusEngineDesc> QIBusProxy::getEngines()
+{
+ QList<QIBusEngineDesc> engines;
+ QDBusReply<QDBusVariant> reply = Engines();
+ QVariant variant = reply.value().variant();
+ if (!variant.isValid())
+ return engines;
+ const QDBusArgument argument = variant.value<QDBusArgument>();
+ qCDebug(qtQpaInputMethodsSerialize) << "QIBusProxy::getEngines()" << argument.currentSignature();
+
+ int i = 1;
+ argument.beginMap();
+ while (!argument.atEnd()) {
+ QDBusVariant value;
+ argument >> value;
+ if (!value.variant().isValid()) {
+ qWarning() << "Warning in QIBusProxy::getEngines():" << QString::asprintf("%dth variant is wrong", i);
+ break;
+ }
+ const QDBusArgument desc_arg = value.variant().value<QDBusArgument>();
+
+ QIBusEngineDesc desc;
+ desc_arg >> desc;
+ engines.append(desc);
+ ++i;
+ }
+ argument.endMap();
+ return engines;
+}
+#endif
+
QIBusEngineDesc QIBusProxy::getGlobalEngine()
{
QIBusEngineDesc desc;
- QDBusReply<QDBusVariant> reply = GetGlobalEngine();
+ QDBusReply<QDBusVariant> reply = GlobalEngine();
QVariant variant = reply.value().variant();
if (!variant.isValid())
return desc;
diff --git a/src/plugins/platforminputcontexts/ibus/qibusproxy.h b/src/plugins/platforminputcontexts/ibus/qibusproxy.h
index bbaebe1b96..839e972c34 100644
--- a/src/plugins/platforminputcontexts/ibus/qibusproxy.h
+++ b/src/plugins/platforminputcontexts/ibus/qibusproxy.h
@@ -54,24 +54,6 @@ public Q_SLOTS: // METHODS
return asyncCallWithArgumentList(QLatin1String("Exit"), argumentList);
}
- inline QDBusPendingReply<QString> GetAddress()
- {
- QList<QVariant> argumentList;
- return asyncCallWithArgumentList(QLatin1String("GetAddress"), argumentList);
- }
-
- inline QDBusPendingReply<QVariantList> ListActiveEngines()
- {
- QList<QVariant> argumentList;
- return asyncCallWithArgumentList(QLatin1String("ListActiveEngines"), argumentList);
- }
-
- inline QDBusPendingReply<QVariantList> ListEngines()
- {
- QList<QVariant> argumentList;
- return asyncCallWithArgumentList(QLatin1String("ListEngines"), argumentList);
- }
-
inline QDBusPendingReply<QDBusVariant> Ping(const QDBusVariant &data)
{
QList<QVariant> argumentList;
@@ -86,19 +68,45 @@ public Q_SLOTS: // METHODS
return asyncCallWithArgumentList(QLatin1String("RegisterComponent"), argumentList);
}
- inline QDBusPendingReply<QDBusVariant> GetGlobalEngine()
+// Property
+ inline QDBusPendingCall GetProperty(const QString method)
{
if (!this->isValid() || this->service().isEmpty() || this->path().isEmpty())
- return QDBusMessage::createError(this->lastError());
+ return QDBusPendingCall::fromError(this->lastError());
QDBusMessage msg = QDBusMessage::createMethodCall(this->service(),
this->path(),
dbusInterfaceProperties(),
QStringLiteral("Get"));
- msg << this->interface() << QStringLiteral("GlobalEngine");
+ msg << this->interface() << method;
return this->connection().asyncCall(msg, this->timeout());
}
+#ifdef QIBUS_GET_ADDRESS
+ inline QDBusPendingCall Address()
+ {
+ return GetProperty(QStringLiteral("Address"));
+ }
+#endif
+
+#ifdef QIBUS_GET_ENGINES
+ inline QDBusPendingCall Engines()
+ {
+ return GetProperty(QStringLiteral("Engines"));
+ }
+#endif
+
+ inline QDBusPendingCall GlobalEngine()
+ {
+ return GetProperty(QStringLiteral("GlobalEngine"));
+ }
+
+#ifdef QIBUS_GET_ADDRESS
+ QString getAddress();
+#endif
+#ifdef QIBUS_GET_ENGINES
+ QList<QIBusEngineDesc> getEngines();
+#endif
QIBusEngineDesc getGlobalEngine();
private:
diff --git a/src/plugins/platforms/android/android.pro b/src/plugins/platforms/android/android.pro
index 03592bfa7d..73db9e93a3 100644
--- a/src/plugins/platforms/android/android.pro
+++ b/src/plugins/platforms/android/android.pro
@@ -11,6 +11,8 @@ QT += \
eventdispatcher_support-private accessibility_support-private \
fontdatabase_support-private egl_support-private
+qtConfig(vulkan): QT += vulkan_support-private
+
OTHER_FILES += $$PWD/android.json
INCLUDEPATH += \
@@ -78,6 +80,13 @@ HEADERS += $$PWD/qandroidplatformintegration.h \
qtConfig(android-style-assets): SOURCES += $$PWD/extract.cpp
else: SOURCES += $$PWD/extract-dummy.cpp
+qtConfig(vulkan) {
+ SOURCES += $$PWD/qandroidplatformvulkaninstance.cpp \
+ $$PWD/qandroidplatformvulkanwindow.cpp
+ HEADERS += $$PWD/qandroidplatformvulkaninstance.h \
+ $$PWD/qandroidplatformvulkanwindow.h
+}
+
PLUGIN_TYPE = platforms
load(qt_plugin)
diff --git a/src/plugins/platforms/android/androidjniinput.cpp b/src/plugins/platforms/android/androidjniinput.cpp
index dc55ccd615..dabab553c2 100644
--- a/src/plugins/platforms/android/androidjniinput.cpp
+++ b/src/plugins/platforms/android/androidjniinput.cpp
@@ -50,7 +50,7 @@
#include <QGuiApplication>
#include <QDebug>
-#include <math.h>
+#include <QtMath>
QT_BEGIN_NAMESPACE
@@ -252,7 +252,7 @@ namespace QtAndroidInput
QWindowSystemInterface::TouchPoint touchPoint;
touchPoint.id = id;
touchPoint.pressure = pressure;
- touchPoint.rotation = rotation * 180 / M_PI;
+ touchPoint.rotation = qRadiansToDegrees(rotation);
touchPoint.normalPosition = QPointF(double(x / dw), double(y / dh));
touchPoint.state = state;
touchPoint.area = QRectF(x - double(minor),
diff --git a/src/plugins/platforms/android/androidjnimain.cpp b/src/plugins/platforms/android/androidjnimain.cpp
index 594fcbadad..13d41bea99 100644
--- a/src/plugins/platforms/android/androidjnimain.cpp
+++ b/src/plugins/platforms/android/androidjnimain.cpp
@@ -59,6 +59,7 @@
#include "qandroideventdispatcher.h"
#include <android/api-level.h>
+#include <QtCore/qthread.h>
#include <QtCore/private/qjnihelpers_p.h>
#include <QtCore/private/qjni_p.h>
#include <QtGui/private/qguiapplication_p.h>
@@ -99,7 +100,6 @@ extern "C" typedef int (*Main)(int, char **); //use the standard main method to
static Main m_main = nullptr;
static void *m_mainLibraryHnd = nullptr;
static QList<QByteArray> m_applicationParams;
-pthread_t m_qtAppThread = 0;
static sem_t m_exitSemaphore, m_terminateSemaphore;
QHash<int, AndroidSurfaceClient *> m_surfaces;
@@ -441,57 +441,10 @@ namespace QtAndroid
} // namespace QtAndroid
-static jboolean startQtAndroidPlugin(JNIEnv* /*env*/, jobject /*object*//*, jobject applicationAssetManager*/)
+static jboolean startQtAndroidPlugin(JNIEnv *env, jobject /*object*/, jstring paramsString, jstring environmentString)
{
m_androidPlatformIntegration = nullptr;
m_androidAssetsFileEngineHandler = new AndroidAssetsFileEngineHandler();
- return true;
-}
-
-static void *startMainMethod(void */*data*/)
-{
- {
- JNIEnv* env = nullptr;
- JavaVMAttachArgs args;
- args.version = JNI_VERSION_1_6;
- args.name = "QtMainThread";
- args.group = NULL;
- JavaVM *vm = QtAndroidPrivate::javaVM();
- if (vm != 0)
- vm->AttachCurrentThread(&env, &args);
- }
-
- QVarLengthArray<const char *> params(m_applicationParams.size());
- for (int i = 0; i < m_applicationParams.size(); i++)
- params[i] = static_cast<const char *>(m_applicationParams[i].constData());
-
- int ret = m_main(m_applicationParams.length(), const_cast<char **>(params.data()));
-
- if (m_mainLibraryHnd) {
- int res = dlclose(m_mainLibraryHnd);
- if (res < 0)
- qWarning() << "dlclose failed:" << dlerror();
- }
-
- if (m_applicationClass)
- QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass, "quitApp", "()V");
-
- // All attached threads should be detached before returning from this function.
- JavaVM *vm = QtAndroidPrivate::javaVM();
- if (vm != 0)
- vm->DetachCurrentThread();
-
- sem_post(&m_terminateSemaphore);
- sem_wait(&m_exitSemaphore);
- sem_destroy(&m_exitSemaphore);
-
- // We must call exit() to ensure that all global objects will be destructed
- exit(ret);
- return 0;
-}
-
-static jboolean startQtApplication(JNIEnv *env, jobject /*object*/, jstring paramsString, jstring environmentString)
-{
m_mainLibraryHnd = nullptr;
{ // Set env. vars
const char *nativeString = env->GetStringUTFChars(environmentString, 0);
@@ -540,7 +493,54 @@ static jboolean startQtApplication(JNIEnv *env, jobject /*object*/, jstring para
if (sem_init(&m_terminateSemaphore, 0, 0) == -1)
return false;
- return pthread_create(&m_qtAppThread, nullptr, startMainMethod, nullptr) == 0;
+ return true;
+}
+
+static void waitForServiceSetup(JNIEnv *env, jclass /*clazz*/)
+{
+ Q_UNUSED(env);
+ // The service must wait until the QCoreApplication starts otherwise onBind will be
+ // called too early
+ if (m_serviceObject)
+ QtAndroidPrivate::waitForServiceSetup();
+}
+
+static jboolean startQtApplication(JNIEnv */*env*/, jclass /*clazz*/)
+{
+ {
+ JNIEnv* env = nullptr;
+ JavaVMAttachArgs args;
+ args.version = JNI_VERSION_1_6;
+ args.name = "QtMainThread";
+ args.group = NULL;
+ JavaVM *vm = QtAndroidPrivate::javaVM();
+ if (vm != 0)
+ vm->AttachCurrentThread(&env, &args);
+ }
+
+ QVarLengthArray<const char *> params(m_applicationParams.size());
+ for (int i = 0; i < m_applicationParams.size(); i++)
+ params[i] = static_cast<const char *>(m_applicationParams[i].constData());
+
+ int ret = m_main(m_applicationParams.length(), const_cast<char **>(params.data()));
+
+ if (m_mainLibraryHnd) {
+ int res = dlclose(m_mainLibraryHnd);
+ if (res < 0)
+ qWarning() << "dlclose failed:" << dlerror();
+ }
+
+ if (m_applicationClass) {
+ qWarning("exit app 0");
+ QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass, "quitApp", "()V");
+ }
+
+ sem_post(&m_terminateSemaphore);
+ sem_wait(&m_exitSemaphore);
+ sem_destroy(&m_exitSemaphore);
+
+ // We must call exit() to ensure that all global objects will be destructed
+ exit(ret);
}
static void quitQtCoreApplication(JNIEnv *env, jclass /*clazz*/)
@@ -586,7 +586,6 @@ static void terminateQt(JNIEnv *env, jclass /*clazz*/)
if (!QAndroidEventDispatcherStopper::instance()->stopped()) {
sem_post(&m_exitSemaphore);
- pthread_join(m_qtAppThread, nullptr);
}
}
@@ -745,19 +744,26 @@ static void onNewIntent(JNIEnv *env, jclass /*cls*/, jobject data)
QtAndroidPrivate::handleNewIntent(env, data);
}
+static jobject onBind(JNIEnv */*env*/, jclass /*cls*/, jobject intent)
+{
+ return QtAndroidPrivate::callOnBindListener(intent);
+}
+
static JNINativeMethod methods[] = {
- {"startQtAndroidPlugin", "()Z", (void *)startQtAndroidPlugin},
- {"startQtApplication", "(Ljava/lang/String;Ljava/lang/String;)V", (void *)startQtApplication},
+ {"startQtAndroidPlugin", "(Ljava/lang/String;Ljava/lang/String;)Z", (void *)startQtAndroidPlugin},
+ {"startQtApplication", "()V", (void *)startQtApplication},
{"quitQtAndroidPlugin", "()V", (void *)quitQtAndroidPlugin},
{"quitQtCoreApplication", "()V", (void *)quitQtCoreApplication},
{"terminateQt", "()V", (void *)terminateQt},
+ {"waitForServiceSetup", "()V", (void *)waitForServiceSetup},
{"setDisplayMetrics", "(IIIIDDDD)V", (void *)setDisplayMetrics},
{"setSurface", "(ILjava/lang/Object;II)V", (void *)setSurface},
{"updateWindow", "()V", (void *)updateWindow},
{"updateApplicationState", "(I)V", (void *)updateApplicationState},
{"handleOrientationChanged", "(II)V", (void *)handleOrientationChanged},
{"onActivityResult", "(IILandroid/content/Intent;)V", (void *)onActivityResult},
- {"onNewIntent", "(Landroid/content/Intent;)V", (void *)onNewIntent}
+ {"onNewIntent", "(Landroid/content/Intent;)V", (void *)onNewIntent},
+ {"onBind", "(Landroid/content/Intent;)Landroid/os/IBinder;", (void *)onBind}
};
#define FIND_AND_CHECK_CLASS(CLASS_NAME) \
@@ -871,7 +877,6 @@ Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void */*reserved*/)
void *venv;
} UnionJNIEnvToVoid;
- __android_log_print(ANDROID_LOG_INFO, "Qt", "qt start");
UnionJNIEnvToVoid uenv;
uenv.venv = nullptr;
m_javaVM = nullptr;
@@ -893,5 +898,10 @@ Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void */*reserved*/)
QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(false);
m_javaVM = vm;
+ // attach qt main thread data to this thread
+ QObject threadSetter;
+ if (threadSetter.thread())
+ threadSetter.thread()->setObjectName("QtMainLoopThread");
+ __android_log_print(ANDROID_LOG_INFO, "Qt", "qt started");
return JNI_VERSION_1_4;
}
diff --git a/src/plugins/platforms/android/qandroidplatformdialoghelpers.cpp b/src/plugins/platforms/android/qandroidplatformdialoghelpers.cpp
index 08498d0582..ced35c4cfa 100644
--- a/src/plugins/platforms/android/qandroidplatformdialoghelpers.cpp
+++ b/src/plugins/platforms/android/qandroidplatformdialoghelpers.cpp
@@ -103,11 +103,7 @@ bool QAndroidPlatformMessageDialogHelper::show(Qt::WindowFlags windowFlags
if (!str.isEmpty())
m_javaMessageDialog.callMethod<void>("setDetailedText", "(Ljava/lang/String;)V", QJNIObjectPrivate::fromString(str).object());
- // http://developer.android.com/design/building-blocks/dialogs.html
- // dismissive action on the left, affirmative on the right
- // There don't seem to be more fine-grained rules, but the OS X layout
- // at least conforms to this one rule and makes the rest deterministic.
- const int * currentLayout = buttonLayout(Qt::Horizontal, MacLayout);
+ const int * currentLayout = buttonLayout(Qt::Horizontal, AndroidLayout);
while (*currentLayout != QPlatformDialogHelper::EOL) {
int role = (*currentLayout & ~QPlatformDialogHelper::Reverse);
addButtons(opt, static_cast<ButtonRole>(role));
diff --git a/src/plugins/platforms/android/qandroidplatformintegration.cpp b/src/plugins/platforms/android/qandroidplatformintegration.cpp
index 7185b573cd..763b294660 100644
--- a/src/plugins/platforms/android/qandroidplatformintegration.cpp
+++ b/src/plugins/platforms/android/qandroidplatformintegration.cpp
@@ -69,6 +69,11 @@
#include <QtPlatformHeaders/QEGLNativeContext>
+#if QT_CONFIG(vulkan)
+#include "qandroidplatformvulkanwindow.h"
+#include "qandroidplatformvulkaninstance.h"
+#endif
+
QT_BEGIN_NAMESPACE
int QAndroidPlatformIntegration::m_defaultGeometryWidth = 320;
@@ -119,6 +124,23 @@ void *QAndroidPlatformNativeInterface::nativeResourceForIntegration(const QByteA
return 0;
}
+void *QAndroidPlatformNativeInterface::nativeResourceForWindow(const QByteArray &resource, QWindow *window)
+{
+#if QT_CONFIG(vulkan)
+ if (resource == "vkSurface") {
+ if (window->surfaceType() == QSurface::VulkanSurface) {
+ QAndroidPlatformVulkanWindow *w = static_cast<QAndroidPlatformVulkanWindow *>(window->handle());
+ // return a pointer to the VkSurfaceKHR, not the value
+ return w ? w->vkSurface() : nullptr;
+ }
+ }
+#else
+ Q_UNUSED(resource);
+ Q_UNUSED(window);
+#endif
+ return nullptr;
+}
+
void QAndroidPlatformNativeInterface::customEvent(QEvent *event)
{
if (event->type() != QEvent::User)
@@ -243,6 +265,7 @@ bool QAndroidPlatformIntegration::hasCapability(Capability cap) const
case ForeignWindows: return QtAndroid::activity();
case ThreadedOpenGL: return !needsBasicRenderloopWorkaround() && QtAndroid::activity();
case RasterGLSurface: return QtAndroid::activity();
+ case TopStackedNativeChildWindows: return false;
default:
return QPlatformIntegration::hasCapability(cap);
}
@@ -295,6 +318,11 @@ QPlatformWindow *QAndroidPlatformIntegration::createPlatformWindow(QWindow *wind
if (!QtAndroid::activity())
return nullptr;
+#if QT_CONFIG(vulkan)
+ if (window->surfaceType() == QSurface::VulkanSurface)
+ return new QAndroidPlatformVulkanWindow(window);
+#endif
+
return new QAndroidPlatformOpenGLWindow(window, m_eglDisplay);
}
@@ -443,4 +471,13 @@ void QAndroidPlatformIntegration::setScreenSize(int width, int height)
QMetaObject::invokeMethod(m_primaryScreen, "setSize", Qt::AutoConnection, Q_ARG(QSize, QSize(width, height)));
}
+#if QT_CONFIG(vulkan)
+
+QPlatformVulkanInstance *QAndroidPlatformIntegration::createPlatformVulkanInstance(QVulkanInstance *instance) const
+{
+ return new QAndroidPlatformVulkanInstance(instance);
+}
+
+#endif // QT_CONFIG(vulkan)
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/android/qandroidplatformintegration.h b/src/plugins/platforms/android/qandroidplatformintegration.h
index 337f4a9279..c795c499bc 100644
--- a/src/plugins/platforms/android/qandroidplatformintegration.h
+++ b/src/plugins/platforms/android/qandroidplatformintegration.h
@@ -40,6 +40,8 @@
#ifndef QANDROIDPLATFORMINTERATION_H
#define QANDROIDPLATFORMINTERATION_H
+#include <QtGui/qtguiglobal.h>
+
#include <qpa/qplatformintegration.h>
#include <qpa/qplatformmenu.h>
#include <qpa/qplatformnativeinterface.h>
@@ -64,6 +66,7 @@ class QAndroidPlatformNativeInterface: public QPlatformNativeInterface
{
public:
void *nativeResourceForIntegration(const QByteArray &resource) override;
+ void *nativeResourceForWindow(const QByteArray &resource, QWindow *window) override;
std::shared_ptr<AndroidStyle> m_androidStyle;
protected:
@@ -128,6 +131,10 @@ public:
void flushPendingUpdates();
+#if QT_CONFIG(vulkan)
+ QPlatformVulkanInstance *createPlatformVulkanInstance(QVulkanInstance *instance) const override;
+#endif
+
private:
EGLDisplay m_eglDisplay;
QTouchDevice *m_touchDevice;
diff --git a/src/plugins/platforms/android/qandroidplatformmenu.cpp b/src/plugins/platforms/android/qandroidplatformmenu.cpp
index 06b297a1ad..d9cecebf2c 100644
--- a/src/plugins/platforms/android/qandroidplatformmenu.cpp
+++ b/src/plugins/platforms/android/qandroidplatformmenu.cpp
@@ -46,7 +46,6 @@ QT_BEGIN_NAMESPACE
QAndroidPlatformMenu::QAndroidPlatformMenu()
{
- m_tag = reinterpret_cast<quintptr>(this); // QMenu will overwrite this later, but we need a unique ID for QtQuick
m_enabled = true;
m_isVisible = true;
}
@@ -92,16 +91,6 @@ void QAndroidPlatformMenu::syncSeparatorsCollapsible(bool enable)
Q_UNUSED(enable)
}
-void QAndroidPlatformMenu::setTag(quintptr tag)
-{
- m_tag = tag;
-}
-
-quintptr QAndroidPlatformMenu::tag() const
-{
- return m_tag;
-}
-
void QAndroidPlatformMenu::setText(const QString &text)
{
m_text = text;
diff --git a/src/plugins/platforms/android/qandroidplatformmenu.h b/src/plugins/platforms/android/qandroidplatformmenu.h
index 00968672c5..47e650f2d7 100644
--- a/src/plugins/platforms/android/qandroidplatformmenu.h
+++ b/src/plugins/platforms/android/qandroidplatformmenu.h
@@ -61,8 +61,6 @@ public:
void syncMenuItem(QPlatformMenuItem *menuItem) override;
void syncSeparatorsCollapsible(bool enable) override;
- void setTag(quintptr tag) override;
- quintptr tag() const override;
void setText(const QString &text) override;
QString text() const;
void setIcon(const QIcon &icon) override;
@@ -81,7 +79,6 @@ public:
private:
PlatformMenuItemsType m_menuItems;
- quintptr m_tag;
QString m_text;
QIcon m_icon;
bool m_enabled;
diff --git a/src/plugins/platforms/android/qandroidplatformmenuitem.cpp b/src/plugins/platforms/android/qandroidplatformmenuitem.cpp
index 0591522e55..e24c5f974e 100644
--- a/src/plugins/platforms/android/qandroidplatformmenuitem.cpp
+++ b/src/plugins/platforms/android/qandroidplatformmenuitem.cpp
@@ -44,7 +44,6 @@ QT_BEGIN_NAMESPACE
QAndroidPlatformMenuItem::QAndroidPlatformMenuItem()
{
- m_tag = reinterpret_cast<quintptr>(this); // QMenu will overwrite this later, but we need a unique ID for QtQuick
m_menu = 0;
m_isVisible = true;
m_isSeparator = false;
@@ -54,16 +53,6 @@ QAndroidPlatformMenuItem::QAndroidPlatformMenuItem()
m_isEnabled = true;
}
-void QAndroidPlatformMenuItem::setTag(quintptr tag)
-{
- m_tag = tag;
-}
-
-quintptr QAndroidPlatformMenuItem::tag() const
-{
- return m_tag;
-}
-
void QAndroidPlatformMenuItem::setText(const QString &text)
{
m_text = text;
diff --git a/src/plugins/platforms/android/qandroidplatformmenuitem.h b/src/plugins/platforms/android/qandroidplatformmenuitem.h
index be5240cfa6..b8782f995d 100644
--- a/src/plugins/platforms/android/qandroidplatformmenuitem.h
+++ b/src/plugins/platforms/android/qandroidplatformmenuitem.h
@@ -49,8 +49,6 @@ class QAndroidPlatformMenuItem: public QPlatformMenuItem
{
public:
QAndroidPlatformMenuItem();
- void setTag(quintptr tag) override;
- quintptr tag() const override;
void setText(const QString &text) override;
QString text() const;
@@ -86,7 +84,6 @@ public:
void setIconSize(int size) override;
private:
- quintptr m_tag;
QString m_text;
QIcon m_icon;
QAndroidPlatformMenu *m_menu;
diff --git a/src/plugins/platforms/android/qandroidplatformscreen.cpp b/src/plugins/platforms/android/qandroidplatformscreen.cpp
index 3b59b293a5..7dc8bb8080 100644
--- a/src/plugins/platforms/android/qandroidplatformscreen.cpp
+++ b/src/plugins/platforms/android/qandroidplatformscreen.cpp
@@ -59,6 +59,8 @@
#include <QtGui/QWindow>
#include <QtGui/private/qwindow_p.h>
+#include <vector>
+
QT_BEGIN_NAMESPACE
#ifdef QANDROIDPLATFORMSCREEN_DEBUG
@@ -85,7 +87,8 @@ private:
# define PROFILE_SCOPE
#endif
-QAndroidPlatformScreen::QAndroidPlatformScreen():QObject(),QPlatformScreen()
+QAndroidPlatformScreen::QAndroidPlatformScreen()
+ : QObject(), QPlatformScreen()
{
m_availableGeometry = QRect(0, 0, QAndroidPlatformIntegration::m_defaultGeometryWidth, QAndroidPlatformIntegration::m_defaultGeometryHeight);
m_size = QSize(QAndroidPlatformIntegration::m_defaultScreenWidth, QAndroidPlatformIntegration::m_defaultScreenHeight);
@@ -100,9 +103,6 @@ QAndroidPlatformScreen::QAndroidPlatformScreen():QObject(),QPlatformScreen()
}
m_physicalSize.setHeight(QAndroidPlatformIntegration::m_defaultPhysicalSizeHeight);
m_physicalSize.setWidth(QAndroidPlatformIntegration::m_defaultPhysicalSizeWidth);
- m_redrawTimer.setSingleShot(true);
- m_redrawTimer.setInterval(0);
- connect(&m_redrawTimer, SIGNAL(timeout()), this, SLOT(doRedraw()));
connect(qGuiApp, &QGuiApplication::applicationStateChanged, this, &QAndroidPlatformScreen::applicationStateChanged);
}
@@ -136,6 +136,16 @@ QWindow *QAndroidPlatformScreen::topLevelAt(const QPoint &p) const
return 0;
}
+bool QAndroidPlatformScreen::event(QEvent *event)
+{
+ if (event->type() == QEvent::UpdateRequest) {
+ doRedraw();
+ m_updatePending = false;
+ return true;
+ }
+ return QObject::event(event);
+}
+
void QAndroidPlatformScreen::addWindow(QAndroidPlatformWindow *window)
{
if (window->parent() && window->isRaster())
@@ -209,8 +219,10 @@ void QAndroidPlatformScreen::lower(QAndroidPlatformWindow *window)
void QAndroidPlatformScreen::scheduleUpdate()
{
- if (!m_redrawTimer.isActive())
- m_redrawTimer.start();
+ if (!m_updatePending) {
+ m_updatePending = true;
+ QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest));
+ }
}
void QAndroidPlatformScreen::setDirty(const QRect &rect)
@@ -366,8 +378,7 @@ void QAndroidPlatformScreen::doRedraw()
|| !window->isRaster())
continue;
- const QVector<QRect> visibleRects = visibleRegion.rects();
- for (const QRect &rect : visibleRects) {
+ for (const QRect &rect : std::vector<QRect>(visibleRegion.begin(), visibleRegion.end())) {
QRect targetRect = window->geometry();
targetRect &= rect;
diff --git a/src/plugins/platforms/android/qandroidplatformscreen.h b/src/plugins/platforms/android/qandroidplatformscreen.h
index 923c9e8832..f15aeae3fd 100644
--- a/src/plugins/platforms/android/qandroidplatformscreen.h
+++ b/src/plugins/platforms/android/qandroidplatformscreen.h
@@ -89,10 +89,12 @@ public slots:
void setSize(const QSize &size);
protected:
+ bool event(QEvent *event) override;
+
typedef QList<QAndroidPlatformWindow *> WindowStackType;
WindowStackType m_windowStack;
QRect m_dirtyRect;
- QTimer m_redrawTimer;
+ bool m_updatePending = false;
QRect m_availableGeometry;
int m_depth;
diff --git a/src/plugins/platforms/android/qandroidplatformvulkaninstance.cpp b/src/plugins/platforms/android/qandroidplatformvulkaninstance.cpp
new file mode 100644
index 0000000000..a411d0f007
--- /dev/null
+++ b/src/plugins/platforms/android/qandroidplatformvulkaninstance.cpp
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qandroidplatformvulkaninstance.h"
+
+QT_BEGIN_NAMESPACE
+
+QAndroidPlatformVulkanInstance::QAndroidPlatformVulkanInstance(QVulkanInstance *instance)
+ : m_instance(instance)
+{
+ m_lib.setFileName(QStringLiteral("vulkan"));
+
+ if (!m_lib.load()) {
+ qWarning("Failed to load %s", qPrintable(m_lib.fileName()));
+ return;
+ }
+
+ init(&m_lib);
+}
+
+void QAndroidPlatformVulkanInstance::createOrAdoptInstance()
+{
+ initInstance(m_instance, QByteArrayList() << QByteArrayLiteral("VK_KHR_android_surface"));
+}
+
+QAndroidPlatformVulkanInstance::~QAndroidPlatformVulkanInstance()
+{
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/android/qandroidplatformvulkaninstance.h b/src/plugins/platforms/android/qandroidplatformvulkaninstance.h
new file mode 100644
index 0000000000..67edcceed9
--- /dev/null
+++ b/src/plugins/platforms/android/qandroidplatformvulkaninstance.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QANDROIDPLATFORMVULKANINSTANCE_H
+#define QANDROIDPLATFORMVULKANINSTANCE_H
+
+#include <QtVulkanSupport/private/qbasicvulkanplatforminstance_p.h>
+#include <QLibrary>
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidPlatformVulkanInstance : public QBasicPlatformVulkanInstance
+{
+public:
+ QAndroidPlatformVulkanInstance(QVulkanInstance *instance);
+ ~QAndroidPlatformVulkanInstance();
+
+ void createOrAdoptInstance() override;
+
+private:
+ QVulkanInstance *m_instance;
+ QLibrary m_lib;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDPLATFORMVULKANINSTANCE_H
diff --git a/src/plugins/platforms/android/qandroidplatformvulkanwindow.cpp b/src/plugins/platforms/android/qandroidplatformvulkanwindow.cpp
new file mode 100644
index 0000000000..cc41a871f3
--- /dev/null
+++ b/src/plugins/platforms/android/qandroidplatformvulkanwindow.cpp
@@ -0,0 +1,210 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qandroidplatformvulkanwindow.h"
+#include "qandroidplatformscreen.h"
+#include "androidjnimain.h"
+#include "qandroideventdispatcher.h"
+#include "androiddeadlockprotector.h"
+
+#include <QSurfaceFormat>
+#include <qpa/qwindowsysteminterface.h>
+#include <qpa/qplatformscreen.h>
+
+#include <android/native_window.h>
+#include <android/native_window_jni.h>
+
+QT_BEGIN_NAMESPACE
+
+QAndroidPlatformVulkanWindow::QAndroidPlatformVulkanWindow(QWindow *window)
+ : QAndroidPlatformWindow(window),
+ m_nativeSurfaceId(-1),
+ m_nativeWindow(nullptr),
+ m_vkSurface(0),
+ m_createVkSurface(nullptr),
+ m_destroyVkSurface(nullptr)
+{
+}
+
+QAndroidPlatformVulkanWindow::~QAndroidPlatformVulkanWindow()
+{
+ m_surfaceWaitCondition.wakeOne();
+ lockSurface();
+ if (m_nativeSurfaceId != -1)
+ QtAndroid::destroySurface(m_nativeSurfaceId);
+ clearSurface();
+ unlockSurface();
+}
+
+void QAndroidPlatformVulkanWindow::setGeometry(const QRect &rect)
+{
+ if (rect == geometry())
+ return;
+
+ m_oldGeometry = geometry();
+
+ QAndroidPlatformWindow::setGeometry(rect);
+ if (m_nativeSurfaceId != -1)
+ QtAndroid::setSurfaceGeometry(m_nativeSurfaceId, rect);
+
+ QRect availableGeometry = screen()->availableGeometry();
+ if (m_oldGeometry.width() == 0
+ && m_oldGeometry.height() == 0
+ && rect.width() > 0
+ && rect.height() > 0
+ && availableGeometry.width() > 0
+ && availableGeometry.height() > 0) {
+ QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), rect.size()));
+ }
+
+ if (rect.topLeft() != m_oldGeometry.topLeft())
+ repaint(QRegion(rect));
+}
+
+void QAndroidPlatformVulkanWindow::applicationStateChanged(Qt::ApplicationState state)
+{
+ QAndroidPlatformWindow::applicationStateChanged(state);
+ if (state <= Qt::ApplicationHidden) {
+ lockSurface();
+ if (m_nativeSurfaceId != -1) {
+ QtAndroid::destroySurface(m_nativeSurfaceId);
+ m_nativeSurfaceId = -1;
+ }
+ clearSurface();
+ unlockSurface();
+ }
+}
+
+QSurfaceFormat QAndroidPlatformVulkanWindow::format() const
+{
+ return window()->requestedFormat();
+}
+
+void QAndroidPlatformVulkanWindow::clearSurface()
+{
+ if (m_vkSurface && m_destroyVkSurface) {
+ m_destroyVkSurface(window()->vulkanInstance()->vkInstance(), m_vkSurface, nullptr);
+ m_vkSurface = 0;
+ }
+
+ if (m_nativeWindow) {
+ ANativeWindow_release(m_nativeWindow);
+ m_nativeWindow = nullptr;
+ }
+}
+
+void QAndroidPlatformVulkanWindow::sendExpose()
+{
+ QRect availableGeometry = screen()->availableGeometry();
+ if (geometry().width() > 0 && geometry().height() > 0 && availableGeometry.width() > 0 && availableGeometry.height() > 0)
+ QWindowSystemInterface::handleExposeEvent(window(), QRegion(QRect(QPoint(), geometry().size())));
+}
+
+void QAndroidPlatformVulkanWindow::surfaceChanged(JNIEnv *jniEnv, jobject surface, int w, int h)
+{
+ Q_UNUSED(jniEnv);
+ Q_UNUSED(w);
+ Q_UNUSED(h);
+
+ lockSurface();
+ m_androidSurfaceObject = surface;
+ if (surface)
+ m_surfaceWaitCondition.wakeOne();
+ unlockSurface();
+
+ if (surface)
+ sendExpose();
+}
+
+VkSurfaceKHR *QAndroidPlatformVulkanWindow::vkSurface()
+{
+ if (QAndroidEventDispatcherStopper::stopped())
+ return &m_vkSurface;
+
+ bool needsExpose = false;
+ if (!m_vkSurface) {
+ clearSurface();
+
+ QMutexLocker lock(&m_surfaceMutex);
+ if (m_nativeSurfaceId == -1) {
+ AndroidDeadlockProtector protector;
+ if (!protector.acquire())
+ return &m_vkSurface;
+ const bool windowStaysOnTop = bool(window()->flags() & Qt::WindowStaysOnTopHint);
+ m_nativeSurfaceId = QtAndroid::createSurface(this, geometry(), windowStaysOnTop, 32);
+ m_surfaceWaitCondition.wait(&m_surfaceMutex);
+ }
+
+ if (m_nativeSurfaceId == -1 || !m_androidSurfaceObject.isValid())
+ return &m_vkSurface;
+
+ QJNIEnvironmentPrivate env;
+ m_nativeWindow = ANativeWindow_fromSurface(env, m_androidSurfaceObject.object());
+
+ VkAndroidSurfaceCreateInfoKHR surfaceInfo;
+ memset(&surfaceInfo, 0, sizeof(surfaceInfo));
+ surfaceInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
+ surfaceInfo.window = m_nativeWindow;
+ QVulkanInstance *inst = window()->vulkanInstance();
+ if (!inst) {
+ qWarning("Attempted to create Vulkan surface without an instance; was QWindow::setVulkanInstance() called?");
+ return &m_vkSurface;
+ }
+ if (!m_createVkSurface) {
+ m_createVkSurface = reinterpret_cast<PFN_vkCreateAndroidSurfaceKHR>(
+ inst->getInstanceProcAddr("vkCreateAndroidSurfaceKHR"));
+ }
+ if (!m_destroyVkSurface) {
+ m_destroyVkSurface = reinterpret_cast<PFN_vkDestroySurfaceKHR>(
+ inst->getInstanceProcAddr("vkDestroySurfaceKHR"));
+ }
+ VkResult err = m_createVkSurface(inst->vkInstance(), &surfaceInfo, nullptr, &m_vkSurface);
+ if (err != VK_SUCCESS)
+ qWarning("Failed to create Android VkSurface: %d", err);
+
+ needsExpose = true;
+ }
+
+ if (needsExpose)
+ sendExpose();
+
+ return &m_vkSurface;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/android/qandroidplatformvulkanwindow.h b/src/plugins/platforms/android/qandroidplatformvulkanwindow.h
new file mode 100644
index 0000000000..19eca98720
--- /dev/null
+++ b/src/plugins/platforms/android/qandroidplatformvulkanwindow.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QANDROIDPLATFORMVULKANWINDOW_H
+#define QANDROIDPLATFORMVULKANWINDOW_H
+
+#if defined(VULKAN_H_) && !defined(VK_USE_PLATFORM_ANDROID_KHR)
+#error "vulkan.h included without Android WSI"
+#endif
+
+#define VK_USE_PLATFORM_ANDROID_KHR
+
+#include <QWaitCondition>
+#include <QtCore/private/qjni_p.h>
+
+#include "androidsurfaceclient.h"
+#include "qandroidplatformwindow.h"
+
+#include "qandroidplatformvulkaninstance.h"
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidPlatformVulkanWindow : public QAndroidPlatformWindow, public AndroidSurfaceClient
+{
+public:
+ explicit QAndroidPlatformVulkanWindow(QWindow *window);
+ ~QAndroidPlatformVulkanWindow();
+
+ void setGeometry(const QRect &rect) override;
+ QSurfaceFormat format() const override;
+ void applicationStateChanged(Qt::ApplicationState) override;
+
+ VkSurfaceKHR *vkSurface();
+
+protected:
+ void surfaceChanged(JNIEnv *jniEnv, jobject surface, int w, int h) override;
+
+private:
+ void sendExpose();
+ void clearSurface();
+
+ int m_nativeSurfaceId;
+ ANativeWindow *m_nativeWindow;
+ QJNIObjectPrivate m_androidSurfaceObject;
+ QWaitCondition m_surfaceWaitCondition;
+ QSurfaceFormat m_format;
+ QRect m_oldGeometry;
+ VkSurfaceKHR m_vkSurface;
+ PFN_vkCreateAndroidSurfaceKHR m_createVkSurface;
+ PFN_vkDestroySurfaceKHR m_destroyVkSurface;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDPLATFORMVULKANWINDOW_H
diff --git a/src/plugins/platforms/android/qandroidplatformwindow.cpp b/src/plugins/platforms/android/qandroidplatformwindow.cpp
index 424cfefc6c..c095f51fa3 100644
--- a/src/plugins/platforms/android/qandroidplatformwindow.cpp
+++ b/src/plugins/platforms/android/qandroidplatformwindow.cpp
@@ -56,7 +56,7 @@ QAndroidPlatformWindow::QAndroidPlatformWindow(QWindow *window)
m_windowState = Qt::WindowNoState;
static QAtomicInt winIdGenerator(1);
m_windowId = winIdGenerator.fetchAndAddRelaxed(1);
- setWindowState(window->windowState());
+ setWindowState(window->windowStates());
}
void QAndroidPlatformWindow::lower()
@@ -73,7 +73,6 @@ void QAndroidPlatformWindow::raise()
void QAndroidPlatformWindow::setGeometry(const QRect &rect)
{
QWindowSystemInterface::handleGeometryChange(window(), rect);
- QPlatformWindow::setGeometry(rect);
}
void QAndroidPlatformWindow::setVisible(bool visible)
@@ -98,7 +97,7 @@ void QAndroidPlatformWindow::setVisible(bool visible)
QPlatformWindow::setVisible(visible);
}
-void QAndroidPlatformWindow::setWindowState(Qt::WindowState state)
+void QAndroidPlatformWindow::setWindowState(Qt::WindowStates state)
{
if (m_windowState == state)
return;
diff --git a/src/plugins/platforms/android/qandroidplatformwindow.h b/src/plugins/platforms/android/qandroidplatformwindow.h
index 91cb1e76e6..f2e51bd3df 100644
--- a/src/plugins/platforms/android/qandroidplatformwindow.h
+++ b/src/plugins/platforms/android/qandroidplatformwindow.h
@@ -59,7 +59,7 @@ public:
void setVisible(bool visible) override;
- void setWindowState(Qt::WindowState state) override;
+ void setWindowState(Qt::WindowStates state) override;
void setWindowFlags(Qt::WindowFlags flags) override;
Qt::WindowFlags windowFlags() const;
void setParent(const QPlatformWindow *window) override;
@@ -91,7 +91,7 @@ protected:
protected:
Qt::WindowFlags m_windowFlags;
- Qt::WindowState m_windowState;
+ Qt::WindowStates m_windowState;
WId m_windowId;
diff --git a/src/plugins/platforms/bsdfb/qbsdfbscreen.cpp b/src/plugins/platforms/bsdfb/qbsdfbscreen.cpp
index 067a26a7b1..d752b539a0 100644
--- a/src/plugins/platforms/bsdfb/qbsdfbscreen.cpp
+++ b/src/plugins/platforms/bsdfb/qbsdfbscreen.cpp
@@ -247,8 +247,7 @@ QRegion QBsdFbScreen::doRedraw()
if (!m_blitter)
m_blitter.reset(new QPainter(&m_onscreenImage));
- const auto rects = touched.rects();
- for (const QRect &rect : rects)
+ for (const QRect &rect : touched)
m_blitter->drawImage(rect, mScreenImage, rect);
return touched;
}
diff --git a/src/plugins/platforms/cocoa/cocoa.pro b/src/plugins/platforms/cocoa/cocoa.pro
index 5a00e1b7ec..2694cb3607 100644
--- a/src/plugins/platforms/cocoa/cocoa.pro
+++ b/src/plugins/platforms/cocoa/cocoa.pro
@@ -2,16 +2,19 @@ TARGET = qcocoa
SOURCES += main.mm \
qcocoaintegration.mm \
+ qcocoascreen.mm \
qcocoatheme.mm \
qcocoabackingstore.mm \
qcocoawindow.mm \
qnsview.mm \
+ qnswindow.mm \
qnsviewaccessibility.mm \
qnswindowdelegate.mm \
qcocoanativeinterface.mm \
qcocoaeventdispatcher.mm \
qcocoaapplicationdelegate.mm \
qcocoaapplication.mm \
+ qcocoansmenu.mm \
qcocoamenu.mm \
qcocoamenuitem.mm \
qcocoamenubar.mm \
@@ -34,15 +37,18 @@ SOURCES += main.mm \
messages.cpp
HEADERS += qcocoaintegration.h \
+ qcocoascreen.h \
qcocoatheme.h \
qcocoabackingstore.h \
qcocoawindow.h \
qnsview.h \
+ qnswindow.h \
qnswindowdelegate.h \
qcocoanativeinterface.h \
qcocoaeventdispatcher.h \
qcocoaapplicationdelegate.h \
qcocoaapplication.h \
+ qcocoansmenu.h \
qcocoamenu.h \
qcocoamenuitem.h \
qcocoamenubar.h \
@@ -72,12 +78,12 @@ qtConfig(opengl.*) {
RESOURCES += qcocoaresources.qrc
-LIBS += -framework AppKit -framework Carbon -framework IOKit -lcups
+LIBS += -framework AppKit -framework Carbon -framework IOKit -framework QuartzCore -lcups
QT += \
core-private gui-private \
accessibility_support-private clipboard_support-private theme_support-private \
- fontdatabase_support-private graphics_support-private cgl_support-private
+ fontdatabase_support-private graphics_support-private
CONFIG += no_app_extension_api_only
diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibility.h b/src/plugins/platforms/cocoa/qcocoaaccessibility.h
index e7957276c2..e456364555 100644
--- a/src/plugins/platforms/cocoa/qcocoaaccessibility.h
+++ b/src/plugins/platforms/cocoa/qcocoaaccessibility.h
@@ -53,10 +53,10 @@ class QCocoaAccessibility : public QPlatformAccessibility
public:
QCocoaAccessibility();
~QCocoaAccessibility();
- void notifyAccessibilityUpdate(QAccessibleEvent *event) Q_DECL_OVERRIDE;
- void setRootObject(QObject *o) Q_DECL_OVERRIDE;
- void initialize() Q_DECL_OVERRIDE;
- void cleanup() Q_DECL_OVERRIDE;
+ void notifyAccessibilityUpdate(QAccessibleEvent *event) override;
+ void setRootObject(QObject *o) override;
+ void initialize() override;
+ void cleanup() override;
};
namespace QCocoaAccessible {
diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibility.mm b/src/plugins/platforms/cocoa/qcocoaaccessibility.mm
index b15c486e9d..8b6dcb35a6 100644
--- a/src/plugins/platforms/cocoa/qcocoaaccessibility.mm
+++ b/src/plugins/platforms/cocoa/qcocoaaccessibility.mm
@@ -41,8 +41,6 @@
#include <QtGui/qaccessible.h>
#include <private/qcore_mac_p.h>
-#include <Carbon/Carbon.h>
-
QT_BEGIN_NAMESPACE
#ifndef QT_NO_ACCESSIBILITY
@@ -369,7 +367,7 @@ id getValueAttribute(QAccessibleInterface *interface)
QString text;
if (interface->state().passwordEdit) {
// return round password replacement chars
- text = QString(end, QChar(kBulletUnicode));
+ text = QString(end, QChar(0x2022));
} else {
// VoiceOver will read out the entire text string at once when returning
// text as a value. For large text edits the size of the returned string
diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm
index 7e6bae127c..df2336d08b 100644
--- a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm
+++ b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm
@@ -40,6 +40,7 @@
#include "qcocoaaccessibility.h"
#include "qcocoahelpers.h"
#include "qcocoawindow.h"
+#include "qcocoascreen.h"
#include "private/qaccessiblecache_p.h"
#include <QtAccessibilitySupport/private/qaccessiblebridgeutils_p.h>
#include <QtGui/qaccessible.h>
@@ -298,9 +299,9 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of
// We're in the same top level element as our parent.
return [[self parentElement] accessibilityAttributeValue:NSAccessibilityTopLevelUIElementAttribute];
} else if ([attribute isEqualToString:NSAccessibilityPositionAttribute]) {
- QPoint qtPosition = iface->rect().topLeft();
- QSize qtSize = iface->rect().size();
- return [NSValue valueWithPoint: NSMakePoint(qtPosition.x(), qt_mac_flipYCoordinate(qtPosition.y() + qtSize.height()))];
+ // The position in points of the element's lower-left corner in screen-relative coordinates
+ QPointF qtPosition = QRectF(iface->rect()).bottomLeft();
+ return [NSValue valueWithPoint:QCocoaScreen::mapToNative(qtPosition)];
} else if ([attribute isEqualToString:NSAccessibilitySizeAttribute]) {
QSize qtSize = iface->rect().size();
return [NSValue valueWithSize: NSMakeSize(qtSize.width(), qtSize.height())];
@@ -430,7 +431,7 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of
if ([attribute isEqualToString: NSAccessibilityBoundsForRangeParameterizedAttribute]) {
NSRange range = [parameter rangeValue];
QRect firstRect = iface->textInterface()->characterRect(range.location);
- QRect rect;
+ QRectF rect;
if (range.length > 0) {
NSUInteger position = range.location + range.length - 1;
if (position > range.location && iface->textInterface()->text(position, position + 1) == QStringLiteral("\n"))
@@ -441,15 +442,14 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of
rect = firstRect;
rect.setWidth(1);
}
- return [NSValue valueWithRect: NSMakeRect((CGFloat) rect.x(),(CGFloat) qt_mac_flipYCoordinate(rect.y() + rect.height()), rect.width(), rect.height())];
+ return [NSValue valueWithRect:QCocoaScreen::mapToNative(rect)];
}
if ([attribute isEqualToString: NSAccessibilityAttributedStringForRangeParameterizedAttribute]) {
NSRange range = [parameter rangeValue];
QString text = iface->textInterface()->text(range.location, range.location + range.length);
return [[NSAttributedString alloc] initWithString:text.toNSString()];
} else if ([attribute isEqualToString: NSAccessibilityRangeForPositionParameterizedAttribute]) {
- NSPoint nsPoint = [parameter pointValue];
- QPoint point(static_cast<int>(nsPoint.x), static_cast<int>(qt_mac_flipYCoordinate(nsPoint.y)));
+ QPoint point = QCocoaScreen::mapFromNative([parameter pointValue]).toPoint();
int offset = iface->textInterface()->offsetAtPoint(point);
return [NSValue valueWithRange:NSMakeRange(static_cast<NSUInteger>(offset), 1)];
} else if ([attribute isEqualToString: NSAccessibilityStyleRangeForIndexParameterizedAttribute]) {
@@ -566,8 +566,8 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of
return NSAccessibilityUnignoredAncestor(self);
}
- int y = qt_mac_flipYCoordinate(point.y);
- QAccessibleInterface *childInterface = iface->childAt(point.x, y);
+ QPointF screenPoint = QCocoaScreen::mapFromNative(point);
+ QAccessibleInterface *childInterface = iface->childAt(screenPoint.x(), screenPoint.y());
// No child found, meaning we hit this element.
if (!childInterface || !childInterface->isValid())
return NSAccessibilityUnignoredAncestor(self);
@@ -575,7 +575,7 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of
// find the deepest child at the point
QAccessibleInterface *childOfChildInterface = 0;
do {
- childOfChildInterface = childInterface->childAt(point.x, y);
+ childOfChildInterface = childInterface->childAt(screenPoint.x(), screenPoint.y());
if (childOfChildInterface && childOfChildInterface->isValid())
childInterface = childOfChildInterface;
} while (childOfChildInterface && childOfChildInterface->isValid());
diff --git a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
index 03148c769b..a94e0dc517 100644
--- a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
+++ b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
@@ -86,16 +86,21 @@
QT_USE_NAMESPACE
-QT_BEGIN_NAMESPACE
-static QCocoaApplicationDelegate *sharedCocoaApplicationDelegate = nil;
+@implementation QCocoaApplicationDelegate
-static void cleanupCocoaApplicationDelegate()
++ (instancetype)sharedDelegate
{
- [sharedCocoaApplicationDelegate release];
+ static QCocoaApplicationDelegate *shared = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ shared = [[self alloc] init];
+ atexit_b(^{
+ [shared release];
+ shared = nil;
+ });
+ });
+ return shared;
}
-QT_END_NAMESPACE
-
-@implementation QCocoaApplicationDelegate
- (id)init
{
@@ -120,7 +125,6 @@ QT_END_NAMESPACE
- (void)dealloc
{
- sharedCocoaApplicationDelegate = nil;
[dockMenu release];
if (reflectionDelegate) {
[[NSApplication sharedApplication] setDelegate:reflectionDelegate];
@@ -131,27 +135,6 @@ QT_END_NAMESPACE
[super dealloc];
}
-+ (id)allocWithZone:(NSZone *)zone
-{
- @synchronized(self) {
- if (sharedCocoaApplicationDelegate == nil) {
- sharedCocoaApplicationDelegate = [super allocWithZone:zone];
- qAddPostRoutine(cleanupCocoaApplicationDelegate);
- return sharedCocoaApplicationDelegate;
- }
- }
- return nil;
-}
-
-+ (QCocoaApplicationDelegate *)sharedDelegate
-{
- @synchronized(self) {
- if (sharedCocoaApplicationDelegate == nil)
- [[self alloc] init];
- }
- return [[sharedCocoaApplicationDelegate retain] autorelease];
-}
-
- (void)setDockMenu:(NSMenu*)newMenu
{
[newMenu retain];
@@ -284,7 +267,7 @@ QT_END_NAMESPACE
inLaunch = false;
if (qEnvironmentVariableIsEmpty("QT_MAC_DISABLE_FOREGROUND_APPLICATION_TRANSFORM")) {
- if (QSysInfo::macVersion() >= QSysInfo::MV_10_12) {
+ if (__builtin_available(macOS 10.12, *)) {
// Move the application window to front to avoid launching behind the terminal.
// Ignoring other apps is necessary (we must ignore the terminal), but makes
// Qt apps play slightly less nice with other apps when lanching from Finder
diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.h b/src/plugins/platforms/cocoa/qcocoabackingstore.h
index 5ed455fd71..b4cd506513 100644
--- a/src/plugins/platforms/cocoa/qcocoabackingstore.h
+++ b/src/plugins/platforms/cocoa/qcocoabackingstore.h
@@ -42,6 +42,8 @@
#include <QtGraphicsSupport/private/qrasterbackingstore_p.h>
+#include <private/qcore_mac_p.h>
+
QT_BEGIN_NAMESPACE
class QCocoaBackingStore : public QRasterBackingStore
@@ -50,10 +52,12 @@ public:
QCocoaBackingStore(QWindow *window);
~QCocoaBackingStore();
- void flush(QWindow *, const QRegion &, const QPoint &) Q_DECL_OVERRIDE;
+ void flush(QWindow *, const QRegion &, const QPoint &) override;
private:
- QImage::Format format() const Q_DECL_OVERRIDE;
+ bool windowHasUnifiedToolbar() const;
+ QImage::Format format() const override;
+ void redrawRoundedBottomCorners(CGRect) const;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.mm b/src/plugins/platforms/cocoa/qcocoabackingstore.mm
index 1d7ad772dc..57a03905ab 100644
--- a/src/plugins/platforms/cocoa/qcocoabackingstore.mm
+++ b/src/plugins/platforms/cocoa/qcocoabackingstore.mm
@@ -44,6 +44,8 @@
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(lcCocoaBackingStore, "qt.qpa.cocoa.backingstore");
+
QCocoaBackingStore::QCocoaBackingStore(QWindow *window)
: QRasterBackingStore(window)
{
@@ -51,26 +53,239 @@ QCocoaBackingStore::QCocoaBackingStore(QWindow *window)
QCocoaBackingStore::~QCocoaBackingStore()
{
- if (QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow *>(window()->handle()))
- [qnsview_cast(cocoaWindow->view()) clearBackingStore:this];
+}
+
+bool QCocoaBackingStore::windowHasUnifiedToolbar() const
+{
+ Q_ASSERT(window()->handle());
+ return static_cast<QCocoaWindow *>(window()->handle())->m_drawContentBorderGradient;
}
QImage::Format QCocoaBackingStore::format() const
{
- QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow *>(window()->handle());
- if (cocoaWindow && cocoaWindow->m_drawContentBorderGradient)
+ if (windowHasUnifiedToolbar())
return QImage::Format_ARGB32_Premultiplied;
return QRasterBackingStore::format();
}
+#if !QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_12)
+static const NSCompositingOperation NSCompositingOperationCopy = NSCompositeCopy;
+static const NSCompositingOperation NSCompositingOperationSourceOver = NSCompositeSourceOver;
+#endif
+
+/*!
+ Flushes the given \a region from the specified \a window onto the
+ screen.
+
+ The \a window is the top level window represented by this backingstore,
+ or a non-transient child of that window.
+
+ If the \a window is a child window, the \a region will be in child window
+ coordinates, and the \a offset will be the child window's offset in relation
+ to the backingstore's top level window.
+*/
void QCocoaBackingStore::flush(QWindow *window, const QRegion &region, const QPoint &offset)
{
if (m_image.isNull())
return;
- if (QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow *>(window->handle()))
- [qnsview_cast(cocoaWindow->view()) flushBackingStore:this region:region offset:offset];
+ // Use local pool so that any stale image references are cleaned up after flushing
+ QMacAutoReleasePool pool;
+
+ const QWindow *topLevelWindow = this->window();
+
+ Q_ASSERT(topLevelWindow->handle() && window->handle());
+ Q_ASSERT(!topLevelWindow->handle()->isForeignWindow() && !window->handle()->isForeignWindow());
+
+ QNSView *topLevelView = qnsview_cast(static_cast<QCocoaWindow *>(topLevelWindow->handle())->view());
+ QNSView *view = qnsview_cast(static_cast<QCocoaWindow *>(window->handle())->view());
+
+ if (lcCocoaBackingStore().isDebugEnabled()) {
+ QString targetViewDescription;
+ if (view != topLevelView) {
+ QDebug targetDebug(&targetViewDescription);
+ targetDebug << "onto" << topLevelView << "at" << offset;
+ }
+ qCDebug(lcCocoaBackingStore) << "Flushing" << region << "of" << view << qPrintable(targetViewDescription);
+ }
+
+ // Prevent potentially costly color conversion by assigning the display color space
+ // to the backingstore image. This does not copy the underlying image data.
+ CGColorSpaceRef displayColorSpace = view.window.screen.colorSpace.CGColorSpace;
+ QCFType<CGImageRef> cgImage = CGImageCreateCopyWithColorSpace(
+ QCFType<CGImageRef>(m_image.toCGImage()), displayColorSpace);
+
+ if (view.layer) {
+ // In layer-backed mode, locking focus on a view does not give the right
+ // view transformation, and doesn't give us a graphics context to render
+ // via when drawing outside of the display cycle. Instead we tell AppKit
+ // that we want to update the layer's content, via [NSView wantsUpdateLayer],
+ // which result in AppKit not creating a backingstore for each layer, and
+ // we then directly set the layer's backingstore (content) to our backingstore,
+ // masked to the part of the subview that is relevant.
+ // FIXME: Figure out if there's a way to do partial updates
+ view.layer.contents = (__bridge id)static_cast<CGImageRef>(cgImage);
+ if (view != topLevelView) {
+ view.layer.contentsRect = CGRectApplyAffineTransform(
+ [view convertRect:view.bounds toView:topLevelView],
+ // The contentsRect is in unit coordinate system
+ CGAffineTransformMakeScale(1.0 / m_image.width(), 1.0 / m_image.height()));
+ }
+ return;
+ }
+
+ // Normally a NSView is drawn via drawRect, as part of the display cycle in the
+ // main runloop, via setNeedsDisplay and friends. AppKit will lock focus on each
+ // individual view, starting with the top level and then traversing any subviews,
+ // calling drawRect for each of them. This pull model results in expose events
+ // sent to Qt, which result in drawing to the backingstore and flushing it.
+ // Qt may also decide to paint and flush the backingstore via e.g. timers,
+ // or other events such as mouse events, in which case we're in a push model.
+ // If there is no focused view, it means we're in the latter case, and need
+ // to manually flush the NSWindow after drawing to its graphic context.
+ const bool drawingOutsideOfDisplayCycle = ![NSView focusView];
+
+ // We also need to ensure the flushed view has focus, so that the graphics
+ // context is set up correctly (coordinate system, clipping, etc). Outside
+ // of the normal display cycle there is no focused view, as explained above,
+ // so we have to handle it manually. There's also a corner case inside the
+ // normal display cycle due to way QWidgetBackingStore composits native child
+ // widgets, where we'll get a flush of a native child during the drawRect of
+ // its parent/ancestor, and the parent/ancestor being the one locked by AppKit.
+ // In this case we also need to lock and unlock focus manually.
+ const bool shouldHandleViewLockManually = [NSView focusView] != view;
+ if (shouldHandleViewLockManually && ![view lockFocusIfCanDraw]) {
+ qWarning() << "failed to lock focus of" << view;
+ return;
+ }
+
+ const qreal devicePixelRatio = m_image.devicePixelRatio();
+
+ // If the flushed window is a content view, and not in unified toolbar mode,
+ // we can get away with copying the backingstore instead of blending.
+ const NSCompositingOperation compositingOperation = static_cast<QCocoaWindow *>(
+ window->handle())->isContentView() && !windowHasUnifiedToolbar() ?
+ NSCompositingOperationCopy : NSCompositingOperationSourceOver;
+
+#ifdef QT_DEBUG
+ static bool debugBackingStoreFlush = [[NSUserDefaults standardUserDefaults]
+ boolForKey:@"QtCocoaDebugBackingStoreFlush"];
+#endif
+
+ // -------------------------------------------------------------------------
+
+ // The current contexts is typically a NSWindowGraphicsContext, but can be
+ // NSBitmapGraphicsContext e.g. when debugging the view hierarchy in Xcode.
+ // If we need to distinguish things here in the future, we can use e.g.
+ // [NSGraphicsContext drawingToScreen], or the attributes of the context.
+ NSGraphicsContext *graphicsContext = [NSGraphicsContext currentContext];
+ Q_ASSERT_X(graphicsContext, "QCocoaBackingStore",
+ "Focusing the view should give us a current graphics context");
+
+ // Create temporary image to use for blitting, without copying image data
+ NSImage *backingStoreImage = [[[NSImage alloc] initWithCGImage:cgImage size:NSZeroSize] autorelease];
+
+ QRegion clippedRegion = region;
+ for (QWindow *w = window; w; w = w->parent()) {
+ if (!w->mask().isEmpty()) {
+ clippedRegion &= w == window ? w->mask()
+ : w->mask().translated(window->mapFromGlobal(w->mapToGlobal(QPoint(0, 0))));
+ }
+ }
+
+ for (const QRect &viewLocalRect : clippedRegion) {
+ QPoint backingStoreOffset = viewLocalRect.topLeft() + offset;
+ QRect backingStoreRect(backingStoreOffset * devicePixelRatio, viewLocalRect.size() * devicePixelRatio);
+ if (graphicsContext.flipped) // Flip backingStoreRect to match graphics context
+ backingStoreRect.moveTop(m_image.height() - (backingStoreRect.y() + backingStoreRect.height()));
+
+ CGRect viewRect = viewLocalRect.toCGRect();
+
+ if (windowHasUnifiedToolbar())
+ NSDrawWindowBackground(viewRect);
+
+ [backingStoreImage drawInRect:viewRect fromRect:backingStoreRect.toCGRect()
+ operation:compositingOperation fraction:1.0 respectFlipped:YES hints:nil];
+
+#ifdef QT_DEBUG
+ if (Q_UNLIKELY(debugBackingStoreFlush)) {
+ [[NSColor colorWithCalibratedRed:drand48() green:drand48() blue:drand48() alpha:0.3] set];
+ [NSBezierPath fillRect:viewRect];
+
+ if (drawingOutsideOfDisplayCycle) {
+ [[[NSColor magentaColor] colorWithAlphaComponent:0.5] set];
+ [NSBezierPath strokeLineFromPoint:viewLocalRect.topLeft().toCGPoint()
+ toPoint:viewLocalRect.bottomRight().toCGPoint()];
+ }
+ }
+#endif
+ }
+
+ QCocoaWindow *topLevelCocoaWindow = static_cast<QCocoaWindow *>(topLevelWindow->handle());
+ if (Q_UNLIKELY(topLevelCocoaWindow->m_needsInvalidateShadow)) {
+ [topLevelView.window invalidateShadow];
+ topLevelCocoaWindow->m_needsInvalidateShadow = false;
+ }
+
+ // -------------------------------------------------------------------------
+
+ if (shouldHandleViewLockManually)
+ [view unlockFocus];
+
+ if (drawingOutsideOfDisplayCycle) {
+ redrawRoundedBottomCorners([view convertRect:region.boundingRect().toCGRect() toView:nil]);
+ [view.window flushWindow];
+ }
+}
+
+/*
+ When drawing outside of the display cycle, which Qt Widget does a lot,
+ we end up drawing over the NSThemeFrame, losing the rounded corners of
+ windows in the process.
+
+ To work around this, until we've enabled updates via setNeedsDisplay and/or
+ enabled layer-backed views, we ask the NSWindow to redraw the bottom corners
+ if they intersect with the flushed region.
+
+ This is the same logic used internally by e.g [NSView displayIfNeeded],
+ [NSRulerView _scrollToMatchContentView], and [NSClipView _immediateScrollToPoint:],
+ as well as the workaround used by WebKit to fix a similar bug:
+
+ https://trac.webkit.org/changeset/85376/webkit
+*/
+void QCocoaBackingStore::redrawRoundedBottomCorners(CGRect windowRect) const
+{
+#if !defined(QT_APPLE_NO_PRIVATE_APIS)
+ Q_ASSERT(this->window()->handle());
+ NSWindow *window = static_cast<QCocoaWindow *>(this->window()->handle())->nativeWindow();
+
+ static SEL intersectBottomCornersWithRect = NSSelectorFromString(
+ [NSString stringWithFormat:@"_%s%s:", "intersectBottomCorners", "WithRect"]);
+ if (NSMethodSignature *signature = [window methodSignatureForSelector:intersectBottomCornersWithRect]) {
+ NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
+ invocation.target = window;
+ invocation.selector = intersectBottomCornersWithRect;
+ [invocation setArgument:&windowRect atIndex:2];
+ [invocation invoke];
+
+ NSRect cornerOverlap = NSZeroRect;
+ [invocation getReturnValue:&cornerOverlap];
+ if (!NSIsEmptyRect(cornerOverlap)) {
+ static SEL maskRoundedBottomCorners = NSSelectorFromString(
+ [NSString stringWithFormat:@"_%s%s:", "maskRounded", "BottomCorners"]);
+ if ((signature = [window methodSignatureForSelector:maskRoundedBottomCorners])) {
+ invocation = [NSInvocation invocationWithMethodSignature:signature];
+ invocation.target = window;
+ invocation.selector = maskRoundedBottomCorners;
+ [invocation setArgument:&cornerOverlap atIndex:2];
+ [invocation invoke];
+ }
+ }
+ }
+#else
+ Q_UNUSED(windowRect);
+#endif
}
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/cocoa/qcocoaclipboard.h b/src/plugins/platforms/cocoa/qcocoaclipboard.h
index cab2cfaa91..a78d8f4187 100644
--- a/src/plugins/platforms/cocoa/qcocoaclipboard.h
+++ b/src/plugins/platforms/cocoa/qcocoaclipboard.h
@@ -56,10 +56,10 @@ class QCocoaClipboard : public QObject, public QPlatformClipboard
public:
QCocoaClipboard();
- QMimeData *mimeData(QClipboard::Mode mode = QClipboard::Clipboard) Q_DECL_OVERRIDE;
- void setMimeData(QMimeData *data, QClipboard::Mode mode = QClipboard::Clipboard) Q_DECL_OVERRIDE;
- bool supportsMode(QClipboard::Mode mode) const Q_DECL_OVERRIDE;
- bool ownsMode(QClipboard::Mode mode) const Q_DECL_OVERRIDE;
+ QMimeData *mimeData(QClipboard::Mode mode = QClipboard::Clipboard) override;
+ void setMimeData(QMimeData *data, QClipboard::Mode mode = QClipboard::Clipboard) override;
+ bool supportsMode(QClipboard::Mode mode) const override;
+ bool ownsMode(QClipboard::Mode mode) const override;
private Q_SLOTS:
void handleApplicationStateChanged(Qt::ApplicationState state);
diff --git a/src/plugins/platforms/cocoa/qcocoacolordialoghelper.h b/src/plugins/platforms/cocoa/qcocoacolordialoghelper.h
index 133efd6db8..01ac7e181d 100644
--- a/src/plugins/platforms/cocoa/qcocoacolordialoghelper.h
+++ b/src/plugins/platforms/cocoa/qcocoacolordialoghelper.h
@@ -55,12 +55,12 @@ public:
QCocoaColorDialogHelper();
~QCocoaColorDialogHelper();
- void exec() Q_DECL_OVERRIDE;
- bool show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent) Q_DECL_OVERRIDE;
- void hide() Q_DECL_OVERRIDE;
+ void exec() override;
+ bool show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent) override;
+ void hide() override;
- void setCurrentColor(const QColor&) Q_DECL_OVERRIDE;
- QColor currentColor() const Q_DECL_OVERRIDE;
+ void setCurrentColor(const QColor&) override;
+ QColor currentColor() const override;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/cocoa/qcocoacursor.h b/src/plugins/platforms/cocoa/qcocoacursor.h
index d1586660a4..58b9ef2151 100644
--- a/src/plugins/platforms/cocoa/qcocoacursor.h
+++ b/src/plugins/platforms/cocoa/qcocoacursor.h
@@ -53,9 +53,9 @@ public:
QCocoaCursor();
~QCocoaCursor();
- void changeCursor(QCursor *cursor, QWindow *window) Q_DECL_OVERRIDE;
- QPoint pos() const Q_DECL_OVERRIDE;
- void setPos(const QPoint &position) Q_DECL_OVERRIDE;
+ void changeCursor(QCursor *cursor, QWindow *window) override;
+ QPoint pos() const override;
+ void setPos(const QPoint &position) override;
private:
QHash<Qt::CursorShape, NSCursor *> m_cursors;
NSCursor *convertCursor(QCursor *cursor);
diff --git a/src/plugins/platforms/cocoa/qcocoacursor.mm b/src/plugins/platforms/cocoa/qcocoacursor.mm
index 99a136d384..c021128e4c 100644
--- a/src/plugins/platforms/cocoa/qcocoacursor.mm
+++ b/src/plugins/platforms/cocoa/qcocoacursor.mm
@@ -39,6 +39,7 @@
#include "qcocoacursor.h"
#include "qcocoawindow.h"
+#include "qcocoascreen.h"
#include "qcocoahelpers.h"
#include <QtGui/private/qcoregraphics_p.h>
@@ -70,7 +71,7 @@ void QCocoaCursor::changeCursor(QCursor *cursor, QWindow *window)
QPoint QCocoaCursor::pos() const
{
- return qt_mac_flipPoint([NSEvent mouseLocation]).toPoint();
+ return QCocoaScreen::mapFromNative([NSEvent mouseLocation]).toPoint();
}
void QCocoaCursor::setPos(const QPoint &position)
@@ -86,7 +87,7 @@ void QCocoaCursor::setPos(const QPoint &position)
NSCursor *QCocoaCursor::convertCursor(QCursor *cursor)
{
- if (cursor == Q_NULLPTR)
+ if (cursor == nullptr)
return 0;
const Qt::CursorShape newShape = cursor->shape();
diff --git a/src/plugins/platforms/cocoa/qcocoadrag.h b/src/plugins/platforms/cocoa/qcocoadrag.h
index 9ebb090989..dc0cc17dfb 100644
--- a/src/plugins/platforms/cocoa/qcocoadrag.h
+++ b/src/plugins/platforms/cocoa/qcocoadrag.h
@@ -55,11 +55,11 @@ public:
QCocoaDrag();
~QCocoaDrag();
- QMimeData *platformDropData() Q_DECL_OVERRIDE;
- Qt::DropAction drag(QDrag *m_drag) Q_DECL_OVERRIDE;
+ QMimeData *dragMimeData();
+ Qt::DropAction drag(QDrag *m_drag) override;
Qt::DropAction defaultAction(Qt::DropActions possibleActions,
- Qt::KeyboardModifiers modifiers) const Q_DECL_OVERRIDE;
+ Qt::KeyboardModifiers modifiers) const override;
/**
* to meet NSView dragImage:at guarantees, we need to record the original
diff --git a/src/plugins/platforms/cocoa/qcocoadrag.mm b/src/plugins/platforms/cocoa/qcocoadrag.mm
index c71e80d191..e1f36b47a1 100644
--- a/src/plugins/platforms/cocoa/qcocoadrag.mm
+++ b/src/plugins/platforms/cocoa/qcocoadrag.mm
@@ -68,7 +68,7 @@ void QCocoaDrag::setLastMouseEvent(NSEvent *event, NSView *view)
m_lastView = view;
}
-QMimeData *QCocoaDrag::platformDropData()
+QMimeData *QCocoaDrag::dragMimeData()
{
if (m_drag)
return m_drag->mimeData();
@@ -188,7 +188,7 @@ QPixmap QCocoaDrag::dragPixmap(QDrag *drag, QPoint &hotSpot) const
if (s.length() > dragImageMaxChars)
s = s.left(dragImageMaxChars -3) + QChar(0x2026);
if (!s.isEmpty()) {
- const int width = fm.width(s);
+ const int width = fm.horizontalAdvance(s);
const int height = fm.height();
if (width > 0 && height > 0) {
qreal dpr = 1.0;
diff --git a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h
index 70887c41c9..2ffc1395ba 100644
--- a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h
+++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h
@@ -177,8 +177,6 @@ public:
void maybeCancelWaitForMoreEvents();
void ensureNSAppInitialized();
- void removeQueuedUserInputEvents(int nsWinNumber);
-
QCFSocketNotifier cfSocketNotifier;
QList<void *> queuedUserInputEvents; // NSEvent *
CFRunLoopSourceRef postedEventsSource;
diff --git a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm
index b22f1b1f54..bf9ba4eccf 100644
--- a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm
+++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm
@@ -86,7 +86,6 @@
#include <qdebug.h>
#include <AppKit/AppKit.h>
-#include <Carbon/Carbon.h>
QT_BEGIN_NAMESPACE
@@ -285,7 +284,7 @@ bool QCocoaEventDispatcher::hasPendingEvents()
{
extern uint qGlobalPostedEventsCount();
extern bool qt_is_gui_used; //qapplication.cpp
- return qGlobalPostedEventsCount() || (qt_is_gui_used && GetNumEventsInQueue(GetMainEventQueue()));
+ return qGlobalPostedEventsCount() || (qt_is_gui_used && !CFRunLoopIsWaiting(CFRunLoopGetMain()));
}
static bool IsMouseOrKeyEvent( NSEvent* event )
@@ -900,21 +899,6 @@ void QCocoaEventDispatcherPrivate::processPostedEvents()
}
}
-void QCocoaEventDispatcherPrivate::removeQueuedUserInputEvents(int nsWinNumber)
-{
- if (nsWinNumber) {
- int eventIndex = queuedUserInputEvents.size();
-
- while (--eventIndex >= 0) {
- NSEvent * nsevent = static_cast<NSEvent *>(queuedUserInputEvents.at(eventIndex));
- if ([nsevent windowNumber] == nsWinNumber) {
- queuedUserInputEvents.removeAt(eventIndex);
- [nsevent release];
- }
- }
- }
-}
-
void QCocoaEventDispatcherPrivate::firstLoopEntry(CFRunLoopObserverRef ref,
CFRunLoopActivity activity,
void *info)
diff --git a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.h b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.h
index f5ba1dc22e..2ddda14289 100644
--- a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.h
+++ b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.h
@@ -59,19 +59,19 @@ public:
QCocoaFileDialogHelper();
virtual ~QCocoaFileDialogHelper();
- void exec() Q_DECL_OVERRIDE;
+ void exec() override;
- bool defaultNameFilterDisables() const Q_DECL_OVERRIDE;
+ bool defaultNameFilterDisables() const override;
- bool show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent) Q_DECL_OVERRIDE;
- void hide() Q_DECL_OVERRIDE;
- void setDirectory(const QUrl &directory) Q_DECL_OVERRIDE;
- QUrl directory() const Q_DECL_OVERRIDE;
- void selectFile(const QUrl &filename) Q_DECL_OVERRIDE;
- QList<QUrl> selectedFiles() const Q_DECL_OVERRIDE;
- void setFilter() Q_DECL_OVERRIDE;
- void selectNameFilter(const QString &filter) Q_DECL_OVERRIDE;
- QString selectedNameFilter() const Q_DECL_OVERRIDE;
+ bool show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent) override;
+ void hide() override;
+ void setDirectory(const QUrl &directory) override;
+ QUrl directory() const override;
+ void selectFile(const QUrl &filename) override;
+ QList<QUrl> selectedFiles() const override;
+ void setFilter() override;
+ void selectNameFilter(const QString &filter) override;
+ QString selectedNameFilter() const override;
public:
bool showCocoaFilePanel(Qt::WindowModality windowModality, QWindow *parent);
diff --git a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm
index c1c7903766..94f2125bad 100644
--- a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm
+++ b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm
@@ -63,7 +63,8 @@
#include <qsysinfo.h>
#include <qoperatingsystemversion.h>
#include <qglobal.h>
-#include <QDir>
+#include <qdir.h>
+#include <qregularexpression.h>
#include <qpa/qplatformnativeinterface.h>
@@ -161,11 +162,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSOpenSavePanelDelegate);
// resetting our mCurrentDir, set the delegate
// here to make sure it gets the correct value.
[mSavePanel setDelegate:self];
-
-#if QT_OSX_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_11)
- if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::OSXElCapitan)
- mOpenPanel.accessoryViewDisclosed = YES;
-#endif
+ mOpenPanel.accessoryViewDisclosed = YES;
if (mOptions->isLabelExplicitlySet(QFileDialogOptions::Accept))
[mSavePanel setPrompt:[self strip:options->labelText(QFileDialogOptions::Accept)]];
@@ -197,7 +194,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSOpenSavePanelDelegate);
static QString strippedText(QString s)
{
- s.remove( QString::fromLatin1("...") );
+ s.remove(QLatin1String("..."));
return QPlatformTheme::removeMnemonics(s).trimmed();
}
@@ -231,7 +228,7 @@ static QString strippedText(QString s)
[mOpenPanel beginWithCompletionHandler:^(NSInteger result){
mReturnCode = result;
if (mHelper)
- mHelper->QNSOpenSavePanelDelegate_panelClosed(result == NSOKButton);
+ mHelper->QNSOpenSavePanelDelegate_panelClosed(result == NSModalResponseOK);
}];
}
}
@@ -260,12 +257,12 @@ static QString strippedText(QString s)
QCocoaMenuBar::resetKnownMenuItemsToQt();
QAbstractEventDispatcher::instance()->interrupt();
- return (mReturnCode == NSOKButton);
+ return (mReturnCode == NSModalResponseOK);
}
- (QPlatformDialogHelper::DialogCode)dialogResultCode
{
- return (mReturnCode == NSOKButton) ? QPlatformDialogHelper::Accepted : QPlatformDialogHelper::Rejected;
+ return (mReturnCode == NSModalResponseOK) ? QPlatformDialogHelper::Accepted : QPlatformDialogHelper::Rejected;
}
- (void)showWindowModalSheet:(QWindow *)parent
@@ -286,7 +283,7 @@ static QString strippedText(QString s)
[mSavePanel beginSheetModalForWindow:nsparent completionHandler:^(NSInteger result){
mReturnCode = result;
if (mHelper)
- mHelper->QNSOpenSavePanelDelegate_panelClosed(result == NSOKButton);
+ mHelper->QNSOpenSavePanelDelegate_panelClosed(result == NSModalResponseOK);
}];
}
@@ -513,9 +510,10 @@ static QString strippedText(QString s)
- (QString)removeExtensions:(const QString &)filter
{
- QRegExp regExp(QString::fromLatin1(QPlatformFileDialogHelper::filterRegExp));
- if (regExp.indexIn(filter) != -1)
- return regExp.cap(1).trimmed();
+ QRegularExpression regExp(QString::fromLatin1(QPlatformFileDialogHelper::filterRegExp));
+ QRegularExpressionMatch match = regExp.match(filter);
+ if (match.hasMatch())
+ return match.captured(1).trimmed();
return filter;
}
diff --git a/src/plugins/platforms/cocoa/qcocoafontdialoghelper.h b/src/plugins/platforms/cocoa/qcocoafontdialoghelper.h
index c3fad7cfd6..14f12f2d76 100644
--- a/src/plugins/platforms/cocoa/qcocoafontdialoghelper.h
+++ b/src/plugins/platforms/cocoa/qcocoafontdialoghelper.h
@@ -54,13 +54,13 @@ public:
QCocoaFontDialogHelper();
~QCocoaFontDialogHelper();
- void exec() Q_DECL_OVERRIDE;
+ void exec() override;
- bool show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent) Q_DECL_OVERRIDE;
- void hide() Q_DECL_OVERRIDE;
+ bool show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent) override;
+ void hide() override;
- void setCurrentFont(const QFont &) Q_DECL_OVERRIDE;
- QFont currentFont() const Q_DECL_OVERRIDE;
+ void setCurrentFont(const QFont &) override;
+ QFont currentFont() const override;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/cocoa/qcocoaglcontext.h b/src/plugins/platforms/cocoa/qcocoaglcontext.h
index 1a66cec0e3..0530aa8201 100644
--- a/src/plugins/platforms/cocoa/qcocoaglcontext.h
+++ b/src/plugins/platforms/cocoa/qcocoaglcontext.h
@@ -55,22 +55,22 @@ public:
QCocoaGLContext(const QSurfaceFormat &format, QPlatformOpenGLContext *share, const QVariant &nativeHandle);
~QCocoaGLContext();
- QSurfaceFormat format() const Q_DECL_OVERRIDE;
+ QSurfaceFormat format() const override;
- void swapBuffers(QPlatformSurface *surface) Q_DECL_OVERRIDE;
+ void swapBuffers(QPlatformSurface *surface) override;
- bool makeCurrent(QPlatformSurface *surface) Q_DECL_OVERRIDE;
- void doneCurrent() Q_DECL_OVERRIDE;
+ bool makeCurrent(QPlatformSurface *surface) override;
+ void doneCurrent() override;
- QFunctionPointer getProcAddress(const char *procName) Q_DECL_OVERRIDE;
+ QFunctionPointer getProcAddress(const char *procName) override;
void update();
static NSOpenGLPixelFormat *createNSOpenGLPixelFormat(const QSurfaceFormat &format);
NSOpenGLContext *nsOpenGLContext() const;
- bool isSharing() const Q_DECL_OVERRIDE;
- bool isValid() const Q_DECL_OVERRIDE;
+ bool isSharing() const override;
+ bool isValid() const override;
void windowWasHidden();
@@ -84,6 +84,7 @@ private:
NSOpenGLContext *m_shareContext;
QSurfaceFormat m_format;
QPointer<QWindow> m_currentWindow;
+ bool m_didCheckForSoftwareContext;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/cocoa/qcocoaglcontext.mm b/src/plugins/platforms/cocoa/qcocoaglcontext.mm
index 9e688f4d1b..b656025ec7 100644
--- a/src/plugins/platforms/cocoa/qcocoaglcontext.mm
+++ b/src/plugins/platforms/cocoa/qcocoaglcontext.mm
@@ -42,7 +42,6 @@
#include "qcocoahelpers.h"
#include <qdebug.h>
#include <QtCore/private/qcore_mac_p.h>
-#include <QtCglSupport/private/cglconvenience_p.h>
#include <QtPlatformHeaders/qcocoanativecontext.h>
#include <dlfcn.h>
@@ -122,7 +121,8 @@ QCocoaGLContext::QCocoaGLContext(const QSurfaceFormat &format, QPlatformOpenGLCo
const QVariant &nativeHandle)
: m_context(nil),
m_shareContext(nil),
- m_format(format)
+ m_format(format),
+ m_didCheckForSoftwareContext(false)
{
if (!nativeHandle.isNull()) {
if (!nativeHandle.canConvert<QCocoaNativeContext>()) {
@@ -153,9 +153,32 @@ QCocoaGLContext::QCocoaGLContext(const QSurfaceFormat &format, QPlatformOpenGLCo
QMacAutoReleasePool pool; // For the SG Canvas render thread
- // create native context for the requested pixel format and share
- NSOpenGLPixelFormat *pixelFormat = static_cast <NSOpenGLPixelFormat *>(qcgl_createNSOpenGLPixelFormat(m_format));
m_shareContext = share ? static_cast<QCocoaGLContext *>(share)->nsOpenGLContext() : nil;
+
+ if (m_shareContext) {
+ // Allow sharing between 3.2 Core and 4.1 Core profile versions in
+ // cases where NSOpenGLContext creates a 4.1 context where a 3.2
+ // context was requested. Due to the semantics of QSurfaceFormat
+ // this 4.1 version can find its way onto the format for the new
+ // context, even though it was at no point requested by the user.
+ GLint shareContextRequestedProfile;
+ [m_shareContext.pixelFormat getValues:&shareContextRequestedProfile
+ forAttribute:NSOpenGLPFAOpenGLProfile forVirtualScreen:0];
+ auto shareContextActualProfile = share->format().version();
+
+ if (shareContextRequestedProfile == NSOpenGLProfileVersion3_2Core &&
+ shareContextActualProfile >= qMakePair(4, 1)) {
+
+ // There is a mismatch, downgrade requested format to make the
+ // NSOpenGLPFAOpenGLProfile attributes match. (NSOpenGLContext will
+ // fail to create a new context if there is a mismatch).
+ if (m_format.version() >= qMakePair(4, 1))
+ m_format.setVersion(3, 2);
+ }
+ }
+
+ // create native context for the requested pixel format and share
+ NSOpenGLPixelFormat *pixelFormat = createNSOpenGLPixelFormat(m_format);
m_context = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:m_shareContext];
// retry without sharing on context creation failure.
@@ -202,7 +225,6 @@ QVariant QCocoaGLContext::nativeHandle() const
return QVariant::fromValue<QCocoaNativeContext>(QCocoaNativeContext(m_context));
}
-// Match up with createNSOpenGLPixelFormat!
QSurfaceFormat QCocoaGLContext::format() const
{
return m_format;
@@ -220,6 +242,9 @@ void QCocoaGLContext::windowWasHidden()
void QCocoaGLContext::swapBuffers(QPlatformSurface *surface)
{
+ if (surface->surface()->surfaceClass() == QSurface::Offscreen)
+ return; // Nothing to do
+
QWindow *window = static_cast<QCocoaWindow *>(surface)->window();
setActiveWindow(window);
@@ -231,11 +256,29 @@ bool QCocoaGLContext::makeCurrent(QPlatformSurface *surface)
Q_ASSERT(surface->surface()->supportsOpenGL());
QMacAutoReleasePool pool;
+ [m_context makeCurrentContext];
+
+ if (surface->surface()->surfaceClass() == QSurface::Offscreen)
+ return true;
QWindow *window = static_cast<QCocoaWindow *>(surface)->window();
setActiveWindow(window);
- [m_context makeCurrentContext];
+ // Disable high-resolution surfaces when using the software renderer, which has the
+ // problem that the system silently falls back to a to using a low-resolution buffer
+ // when a high-resolution buffer is requested. This is not detectable using the NSWindow
+ // convertSizeToBacking and backingScaleFactor APIs. A typical result of this is that Qt
+ // will display a quarter of the window content when running in a virtual machine.
+ if (!m_didCheckForSoftwareContext) {
+ m_didCheckForSoftwareContext = true;
+
+ const GLubyte* renderer = glGetString(GL_RENDERER);
+ if (qstrcmp((const char *)renderer, "Apple Software Renderer") == 0) {
+ NSView *view = static_cast<QCocoaWindow *>(surface)->m_view;
+ [view setWantsBestResolutionOpenGLSurface:NO];
+ }
+ }
+
update();
return true;
}
@@ -362,7 +405,64 @@ void QCocoaGLContext::update()
NSOpenGLPixelFormat *QCocoaGLContext::createNSOpenGLPixelFormat(const QSurfaceFormat &format)
{
- return static_cast<NSOpenGLPixelFormat *>(qcgl_createNSOpenGLPixelFormat(format));
+ QVector<NSOpenGLPixelFormatAttribute> attrs;
+
+ if (format.swapBehavior() == QSurfaceFormat::DoubleBuffer
+ || format.swapBehavior() == QSurfaceFormat::DefaultSwapBehavior)
+ attrs.append(NSOpenGLPFADoubleBuffer);
+ else if (format.swapBehavior() == QSurfaceFormat::TripleBuffer)
+ attrs.append(NSOpenGLPFATripleBuffer);
+
+
+ // Select OpenGL profile
+ attrs << NSOpenGLPFAOpenGLProfile;
+ if (format.profile() == QSurfaceFormat::CoreProfile) {
+ if (format.version() >= qMakePair(4, 1))
+ attrs << NSOpenGLProfileVersion4_1Core;
+ else if (format.version() >= qMakePair(3, 2))
+ attrs << NSOpenGLProfileVersion3_2Core;
+ else
+ attrs << NSOpenGLProfileVersionLegacy;
+ } else {
+ attrs << NSOpenGLProfileVersionLegacy;
+ }
+
+ if (format.depthBufferSize() > 0)
+ attrs << NSOpenGLPFADepthSize << format.depthBufferSize();
+ if (format.stencilBufferSize() > 0)
+ attrs << NSOpenGLPFAStencilSize << format.stencilBufferSize();
+ if (format.alphaBufferSize() > 0)
+ attrs << NSOpenGLPFAAlphaSize << format.alphaBufferSize();
+ if ((format.redBufferSize() > 0) &&
+ (format.greenBufferSize() > 0) &&
+ (format.blueBufferSize() > 0)) {
+ const int colorSize = format.redBufferSize() +
+ format.greenBufferSize() +
+ format.blueBufferSize();
+ attrs << NSOpenGLPFAColorSize << colorSize << NSOpenGLPFAMinimumPolicy;
+ }
+
+ if (format.samples() > 0) {
+ attrs << NSOpenGLPFAMultisample
+ << NSOpenGLPFASampleBuffers << (NSOpenGLPixelFormatAttribute) 1
+ << NSOpenGLPFASamples << (NSOpenGLPixelFormatAttribute) format.samples();
+ }
+
+ if (format.stereo())
+ attrs << NSOpenGLPFAStereo;
+
+ attrs << NSOpenGLPFAAllowOfflineRenderers;
+
+ QByteArray useLayer = qgetenv("QT_MAC_WANTS_LAYER");
+ if (!useLayer.isEmpty() && useLayer.toInt() > 0) {
+ // Disable the software rendering fallback. This makes compositing
+ // OpenGL and raster NSViews using Core Animation layers possible.
+ attrs << NSOpenGLPFANoRecovery;
+ }
+
+ attrs << 0;
+
+ return [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs.constData()];
}
NSOpenGLContext *QCocoaGLContext::nsOpenGLContext() const
diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.h b/src/plugins/platforms/cocoa/qcocoahelpers.h
index 0cf9cc0aff..84632c1487 100644
--- a/src/plugins/platforms/cocoa/qcocoahelpers.h
+++ b/src/plugins/platforms/cocoa/qcocoahelpers.h
@@ -55,11 +55,16 @@
#include <QtGui/qpalette.h>
#include <QtGui/qscreen.h>
+#include <objc/runtime.h>
+#include <objc/message.h>
+
Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(QNSView));
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcQpaCocoaWindow)
+Q_DECLARE_LOGGING_CATEGORY(lcQpaCocoaDrawing)
+Q_DECLARE_LOGGING_CATEGORY(lcQpaCocoaMouse)
class QPixmap;
class QString;
@@ -82,13 +87,8 @@ QT_MANGLE_NAMESPACE(QNSView) *qnsview_cast(NSView *view);
void qt_mac_transformProccessToForegroundApplication();
QString qt_mac_applicationName();
-int qt_mac_flipYCoordinate(int y);
-qreal qt_mac_flipYCoordinate(qreal y);
-QPointF qt_mac_flipPoint(const NSPoint &p);
-NSPoint qt_mac_flipPoint(const QPoint &p);
-NSPoint qt_mac_flipPoint(const QPointF &p);
-
-NSRect qt_mac_flipRect(const QRect &rect);
+QPointF qt_mac_flip(const QPointF &pos, const QRectF &reference);
+QRectF qt_mac_flip(const QRectF &rect, const QRectF &reference);
Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum);
@@ -191,5 +191,144 @@ QT_END_NAMESPACE
QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSPanelContentsWrapper);
+// -------------------------------------------------------------------------
+
+// QAppleRefCounted expects the retain function to return the object
+io_object_t q_IOObjectRetain(io_object_t obj);
+// QAppleRefCounted expects the release function to return void
+void q_IOObjectRelease(io_object_t obj);
+
+template <typename T>
+class QIOType : public QAppleRefCounted<T, io_object_t, q_IOObjectRetain, q_IOObjectRelease>
+{
+ using QAppleRefCounted<T, io_object_t, q_IOObjectRetain, q_IOObjectRelease>::QAppleRefCounted;
+};
+
+// -------------------------------------------------------------------------
+
+// Depending on the ABI of the platform, we may need to use objc_msgSendSuper_stret:
+// - http://www.sealiesoftware.com/blog/archive/2008/10/30/objc_explain_objc_msgSend_stret.html
+// - https://lists.apple.com/archives/cocoa-dev/2008/Feb/msg02338.html
+template <typename T>
+struct objc_msgsend_requires_stret
+{ static const bool value =
+#if defined(Q_PROCESSOR_X86)
+ // Any return value larger than two registers on i386/x86_64
+ sizeof(T) > sizeof(void*) * 2;
+#elif defined(Q_PROCESSOR_ARM_32)
+ // Any return value larger than a single register on arm
+ sizeof(T) > sizeof(void*);
+#elif defined(Q_PROCESSOR_ARM_64)
+ // Stret not used on arm64
+ false;
+#endif
+};
+
+template <>
+struct objc_msgsend_requires_stret<void>
+{ static const bool value = false; };
+
+template <typename ReturnType, typename... Args>
+ReturnType qt_msgSendSuper(id receiver, SEL selector, Args... args)
+{
+ static_assert(!objc_msgsend_requires_stret<ReturnType>::value,
+ "The given return type requires stret on this platform");
+
+ typedef ReturnType (*SuperFn)(objc_super *, SEL, Args...);
+ SuperFn superFn = reinterpret_cast<SuperFn>(objc_msgSendSuper);
+ objc_super sup = { receiver, [receiver superclass] };
+ return superFn(&sup, selector, args...);
+}
+
+template <typename ReturnType, typename... Args>
+ReturnType qt_msgSendSuper_stret(id receiver, SEL selector, Args... args)
+{
+ static_assert(objc_msgsend_requires_stret<ReturnType>::value,
+ "The given return type does not use stret on this platform");
+
+ typedef void (*SuperStretFn)(ReturnType *, objc_super *, SEL, Args...);
+ SuperStretFn superStretFn = reinterpret_cast<SuperStretFn>(objc_msgSendSuper_stret);
+
+ objc_super sup = { receiver, [receiver superclass] };
+ ReturnType ret;
+ superStretFn(&ret, &sup, selector, args...);
+ return ret;
+}
+
+template<typename... Args>
+class QSendSuperHelper {
+public:
+ QSendSuperHelper(id receiver, SEL sel, Args... args)
+ : m_receiver(receiver), m_selector(sel), m_args(std::make_tuple(args...)), m_sent(false)
+ {
+ }
+
+ ~QSendSuperHelper()
+ {
+ if (!m_sent)
+ msgSendSuper<void>(m_args);
+ }
+
+ template <typename ReturnType>
+ operator ReturnType()
+ {
+#if defined(QT_DEBUG)
+ Method method = class_getInstanceMethod(object_getClass(m_receiver), m_selector);
+ char returnTypeEncoding[256];
+ method_getReturnType(method, returnTypeEncoding, sizeof(returnTypeEncoding));
+ NSUInteger alignedReturnTypeSize = 0;
+ NSGetSizeAndAlignment(returnTypeEncoding, nullptr, &alignedReturnTypeSize);
+ Q_ASSERT(alignedReturnTypeSize == sizeof(ReturnType));
+#endif
+ m_sent = true;
+ return msgSendSuper<ReturnType>(m_args);
+ }
+
+private:
+ template <std::size_t... Ts>
+ struct index {};
+
+ template <std::size_t N, std::size_t... Ts>
+ struct gen_seq : gen_seq<N - 1, N - 1, Ts...> {};
+
+ template <std::size_t... Ts>
+ struct gen_seq<0, Ts...> : index<Ts...> {};
+
+ template <typename ReturnType, bool V>
+ using if_requires_stret = typename std::enable_if<objc_msgsend_requires_stret<ReturnType>::value == V, ReturnType>::type;
+
+ template <typename ReturnType, std::size_t... Is>
+ if_requires_stret<ReturnType, false> msgSendSuper(std::tuple<Args...>& args, index<Is...>)
+ {
+ return qt_msgSendSuper<ReturnType>(m_receiver, m_selector, std::get<Is>(args)...);
+ }
+
+ template <typename ReturnType, std::size_t... Is>
+ if_requires_stret<ReturnType, true> msgSendSuper(std::tuple<Args...>& args, index<Is...>)
+ {
+ return qt_msgSendSuper_stret<ReturnType>(m_receiver, m_selector, std::get<Is>(args)...);
+ }
+
+ template <typename ReturnType>
+ ReturnType msgSendSuper(std::tuple<Args...>& args)
+ {
+ return msgSendSuper<ReturnType>(args, gen_seq<sizeof...(Args)>{});
+ }
+
+ id m_receiver;
+ SEL m_selector;
+ std::tuple<Args...> m_args;
+ bool m_sent;
+};
+
+template<typename... Args>
+QSendSuperHelper<Args...> qt_objcDynamicSuperHelper(id receiver, SEL selector, Args... args)
+{
+ return QSendSuperHelper<Args...>(receiver, selector, args...);
+}
+
+// Same as calling super, but the super_class field resolved at runtime instead of compile time
+#define qt_objcDynamicSuper(...) qt_objcDynamicSuperHelper(self, _cmd, ##__VA_ARGS__)
+
#endif //QCOCOAHELPERS_H
diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.mm b/src/plugins/platforms/cocoa/qcocoahelpers.mm
index c3efe158c7..b729c7f4c0 100644
--- a/src/plugins/platforms/cocoa/qcocoahelpers.mm
+++ b/src/plugins/platforms/cocoa/qcocoahelpers.mm
@@ -55,11 +55,11 @@
#include <algorithm>
-#include <Carbon/Carbon.h>
-
QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcQpaCocoaWindow, "qt.qpa.cocoa.window");
+Q_LOGGING_CATEGORY(lcQpaCocoaDrawing, "qt.qpa.cocoa.drawing");
+Q_LOGGING_CATEGORY(lcQpaCocoaMouse, "qt.qpa.cocoa.mouse");
//
// Conversion Functions
@@ -234,50 +234,37 @@ QString qt_mac_applicationName()
return appName;
}
-int qt_mac_primaryScreenHeight()
-{
- QMacAutoReleasePool pool;
- NSArray *screens = [NSScreen screens];
- if ([screens count] > 0) {
- // The first screen in the screens array is documented to
- // have the (0,0) origin and is designated the primary screen.
- NSRect screenFrame = [[screens objectAtIndex: 0] frame];
- return screenFrame.size.height;
- }
- return 0;
-}
+// -------------------------------------------------------------------------
-int qt_mac_flipYCoordinate(int y)
-{
- return qt_mac_primaryScreenHeight() - y;
-}
+/*!
+ \fn QPointF qt_mac_flip(const QPointF &pos, const QRectF &reference)
+ \fn QRectF qt_mac_flip(const QRectF &rect, const QRectF &reference)
-qreal qt_mac_flipYCoordinate(qreal y)
-{
- return qt_mac_primaryScreenHeight() - y;
-}
+ Flips the Y coordinate of the point/rect between quadrant I and IV.
-QPointF qt_mac_flipPoint(const NSPoint &p)
-{
- return QPointF(p.x, qt_mac_flipYCoordinate(p.y));
-}
+ The native coordinate system on macOS uses quadrant I, with origin
+ in bottom left, and Qt uses quadrant IV, with origin in top left.
-NSPoint qt_mac_flipPoint(const QPoint &p)
-{
- return NSMakePoint(p.x(), qt_mac_flipYCoordinate(p.y()));
-}
+ By flipping the Y coordinate, we can map the point/rect between
+ the two coordinate systems.
-NSPoint qt_mac_flipPoint(const QPointF &p)
+ The flip is always in relation to a reference rectangle, e.g.
+ the frame of the parent view, or the screen geometry. In the
+ latter case the specialized QCocoaScreen::mapFrom/To functions
+ should be used instead.
+*/
+QPointF qt_mac_flip(const QPointF &pos, const QRectF &reference)
{
- return NSMakePoint(p.x(), qt_mac_flipYCoordinate(p.y()));
+ return QPointF(pos.x(), reference.height() - pos.y());
}
-NSRect qt_mac_flipRect(const QRect &rect)
+QRectF qt_mac_flip(const QRectF &rect, const QRectF &reference)
{
- int flippedY = qt_mac_flipYCoordinate(rect.y() + rect.height());
- return NSMakeRect(rect.x(), flippedY, rect.width(), rect.height());
+ return QRectF(qt_mac_flip(rect.bottomLeft(), reference), rect.size());
}
+// -------------------------------------------------------------------------
+
Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum)
{
if (buttonNum >= 0 && buttonNum <= 31)
@@ -407,6 +394,22 @@ QT_END_NAMESPACE
self.panelContents.needsDisplay = YES;
self.needsDisplay = YES;
+ [super layout];
+}
+
+// -------------------------------------------------------------------------
+
+io_object_t q_IOObjectRetain(io_object_t obj)
+{
+ kern_return_t ret = IOObjectRetain(obj);
+ Q_ASSERT(!ret);
+ return obj;
+}
+
+void q_IOObjectRelease(io_object_t obj)
+{
+ kern_return_t ret = IOObjectRelease(obj);
+ Q_ASSERT(!ret);
}
@end
diff --git a/src/plugins/platforms/cocoa/qcocoainputcontext.h b/src/plugins/platforms/cocoa/qcocoainputcontext.h
index a248ca0d8d..7190ba0de8 100644
--- a/src/plugins/platforms/cocoa/qcocoainputcontext.h
+++ b/src/plugins/platforms/cocoa/qcocoainputcontext.h
@@ -53,11 +53,11 @@ public:
explicit QCocoaInputContext();
~QCocoaInputContext();
- bool isValid() const Q_DECL_OVERRIDE { return true; }
+ bool isValid() const override { return true; }
- void reset() Q_DECL_OVERRIDE;
+ void reset() override;
- QLocale locale() const Q_DECL_OVERRIDE { return m_locale; }
+ QLocale locale() const override { return m_locale; }
void updateLocale();
private Q_SLOTS:
diff --git a/src/plugins/platforms/cocoa/qcocoainputcontext.mm b/src/plugins/platforms/cocoa/qcocoainputcontext.mm
index 9221099a57..d0baea5b36 100644
--- a/src/plugins/platforms/cocoa/qcocoainputcontext.mm
+++ b/src/plugins/platforms/cocoa/qcocoainputcontext.mm
@@ -129,6 +129,8 @@ void QCocoaInputContext::focusObjectChanged(QObject *focusObject)
return;
QCocoaWindow *window = static_cast<QCocoaWindow *>(mWindow->handle());
+ if (!window)
+ return;
QNSView *view = qnsview_cast(window->view());
if (!view)
return;
diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.h b/src/plugins/platforms/cocoa/qcocoaintegration.h
index ecdd20c4dc..301771fd53 100644
--- a/src/plugins/platforms/cocoa/qcocoaintegration.h
+++ b/src/plugins/platforms/cocoa/qcocoaintegration.h
@@ -58,60 +58,11 @@
QT_BEGIN_NAMESPACE
-class QCocoaScreen : public QPlatformScreen
-{
-public:
- QCocoaScreen(int screenIndex);
- ~QCocoaScreen();
-
- // ----------------------------------------------------
- // Virtual methods overridden from QPlatformScreen
- QPixmap grabWindow(WId window, int x, int y, int width, int height) const Q_DECL_OVERRIDE;
- QRect geometry() const Q_DECL_OVERRIDE { return m_geometry; }
- QRect availableGeometry() const Q_DECL_OVERRIDE { return m_availableGeometry; }
- int depth() const Q_DECL_OVERRIDE { return m_depth; }
- QImage::Format format() const Q_DECL_OVERRIDE { return m_format; }
- qreal devicePixelRatio() const Q_DECL_OVERRIDE;
- QSizeF physicalSize() const Q_DECL_OVERRIDE { return m_physicalSize; }
- QDpi logicalDpi() const Q_DECL_OVERRIDE { return m_logicalDpi; }
- qreal refreshRate() const Q_DECL_OVERRIDE { return m_refreshRate; }
- QString name() const Q_DECL_OVERRIDE { return m_name; }
- QPlatformCursor *cursor() const Q_DECL_OVERRIDE { return m_cursor; }
- QWindow *topLevelAt(const QPoint &point) const Q_DECL_OVERRIDE;
- QList<QPlatformScreen *> virtualSiblings() const Q_DECL_OVERRIDE { return m_siblings; }
- QPlatformScreen::SubpixelAntialiasingType subpixelAntialiasingTypeHint() const Q_DECL_OVERRIDE;
-
- // ----------------------------------------------------
- // Additional methods
- void setVirtualSiblings(const QList<QPlatformScreen *> &siblings) { m_siblings = siblings; }
- NSScreen *nativeScreen() const;
- void updateGeometry();
-
- QPointF mapToNative(const QPointF &pos) const { return flipCoordinate(pos); }
- QRectF mapToNative(const QRectF &rect) const { return flipCoordinate(rect); }
- QPointF mapFromNative(const QPointF &pos) const { return flipCoordinate(pos); }
- QRectF mapFromNative(const QRectF &rect) const { return flipCoordinate(rect); }
-
-private:
- QPointF flipCoordinate(const QPointF &pos) const;
- QRectF flipCoordinate(const QRectF &rect) const;
+class QCocoaScreen;
-public:
- int m_screenIndex;
- QRect m_geometry;
- QRect m_availableGeometry;
- QDpi m_logicalDpi;
- qreal m_refreshRate;
- int m_depth;
- QString m_name;
- QImage::Format m_format;
- QSizeF m_physicalSize;
- QCocoaCursor *m_cursor;
- QList<QPlatformScreen *> m_siblings;
-};
-
-class QCocoaIntegration : public QPlatformIntegration
+class QCocoaIntegration : public QObject, public QPlatformIntegration
{
+ Q_OBJECT
public:
enum Option {
UseFreeTypeFontEngine = 0x1
@@ -124,34 +75,35 @@ public:
static QCocoaIntegration *instance();
Options options() const;
- bool hasCapability(QPlatformIntegration::Capability cap) const Q_DECL_OVERRIDE;
- QPlatformWindow *createPlatformWindow(QWindow *window) const Q_DECL_OVERRIDE;
- QPlatformWindow *createForeignWindow(QWindow *window, WId nativeHandle) const Q_DECL_OVERRIDE;
+ bool hasCapability(QPlatformIntegration::Capability cap) const override;
+ QPlatformWindow *createPlatformWindow(QWindow *window) const override;
+ QPlatformWindow *createForeignWindow(QWindow *window, WId nativeHandle) const override;
+ QPlatformOffscreenSurface *createPlatformOffscreenSurface(QOffscreenSurface *surface) const override;
#ifndef QT_NO_OPENGL
- QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const Q_DECL_OVERRIDE;
+ QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const override;
#endif
- QPlatformBackingStore *createPlatformBackingStore(QWindow *widget) const Q_DECL_OVERRIDE;
+ QPlatformBackingStore *createPlatformBackingStore(QWindow *widget) const override;
- QAbstractEventDispatcher *createEventDispatcher() const Q_DECL_OVERRIDE;
+ QAbstractEventDispatcher *createEventDispatcher() const override;
- QCoreTextFontDatabase *fontDatabase() const Q_DECL_OVERRIDE;
- QCocoaNativeInterface *nativeInterface() const Q_DECL_OVERRIDE;
- QPlatformInputContext *inputContext() const Q_DECL_OVERRIDE;
+ QCoreTextFontDatabase *fontDatabase() const override;
+ QCocoaNativeInterface *nativeInterface() const override;
+ QPlatformInputContext *inputContext() const override;
#ifndef QT_NO_ACCESSIBILITY
- QCocoaAccessibility *accessibility() const Q_DECL_OVERRIDE;
+ QCocoaAccessibility *accessibility() const override;
#endif
#ifndef QT_NO_CLIPBOARD
- QCocoaClipboard *clipboard() const Q_DECL_OVERRIDE;
+ QCocoaClipboard *clipboard() const override;
#endif
- QCocoaDrag *drag() const Q_DECL_OVERRIDE;
+ QCocoaDrag *drag() const override;
- QStringList themeNames() const Q_DECL_OVERRIDE;
- QPlatformTheme *createPlatformTheme(const QString &name) const Q_DECL_OVERRIDE;
- QCocoaServices *services() const Q_DECL_OVERRIDE;
- QVariant styleHint(StyleHint hint) const Q_DECL_OVERRIDE;
+ QStringList themeNames() const override;
+ QPlatformTheme *createPlatformTheme(const QString &name) const override;
+ QCocoaServices *services() const override;
+ QVariant styleHint(StyleHint hint) const override;
- Qt::KeyboardModifiers queryKeyboardModifiers() const Q_DECL_OVERRIDE;
- QList<int> possibleKeys(const QKeyEvent *event) const Q_DECL_OVERRIDE;
+ Qt::KeyboardModifiers queryKeyboardModifiers() const override;
+ QList<int> possibleKeys(const QKeyEvent *event) const override;
void updateScreens();
QCocoaScreen *screenForNSScreen(NSScreen *nsScreen);
@@ -165,9 +117,12 @@ public:
QCocoaWindow *activePopupWindow() const;
QList<QCocoaWindow *> *popupWindowStack();
- void setApplicationIcon(const QIcon &icon) const Q_DECL_OVERRIDE;
+ void setApplicationIcon(const QIcon &icon) const override;
+
+ void beep() const override;
- void beep() const Q_DECL_OVERRIDE;
+private Q_SLOTS:
+ void focusWindowChanged(QWindow *);
private:
static QCocoaIntegration *mInstance;
diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm
index bac49cfad9..55b3805df3 100644
--- a/src/plugins/platforms/cocoa/qcocoaintegration.mm
+++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm
@@ -51,10 +51,12 @@
#include "qcocoainputcontext.h"
#include "qcocoamimetypes.h"
#include "qcocoaaccessibility.h"
+#include "qcocoascreen.h"
#include <qpa/qplatforminputcontextfactory_p.h>
#include <qpa/qplatformaccessibility.h>
#include <qpa/qplatforminputcontextfactory_p.h>
+#include <qpa/qplatformoffscreensurface.h>
#include <QtCore/qcoreapplication.h>
#include <QtGui/private/qcoregraphics_p.h>
@@ -78,221 +80,6 @@ QT_BEGIN_NAMESPACE
class QCoreTextFontEngine;
class QFontEngineFT;
-QCocoaScreen::QCocoaScreen(int screenIndex)
- : QPlatformScreen(), m_screenIndex(screenIndex), m_refreshRate(60.0)
-{
- updateGeometry();
- m_cursor = new QCocoaCursor;
-}
-
-QCocoaScreen::~QCocoaScreen()
-{
- delete m_cursor;
-}
-
-NSScreen *QCocoaScreen::nativeScreen() const
-{
- NSArray *screens = [NSScreen screens];
-
- // Stale reference, screen configuration has changed
- if (m_screenIndex < 0 || (NSUInteger)m_screenIndex >= [screens count])
- return nil;
-
- return [screens objectAtIndex:m_screenIndex];
-}
-
-/*!
- Flips the Y coordinate of the point between quadrant I and IV.
-
- The native coordinate system on macOS uses quadrant I, with origin
- in bottom left, and Qt uses quadrant IV, with origin in top left.
-
- By flippig the Y coordinate, we can map the position between the
- two coordinate systems.
-*/
-QPointF QCocoaScreen::flipCoordinate(const QPointF &pos) const
-{
- return QPointF(pos.x(), m_geometry.height() - pos.y());
-}
-
-/*!
- Flips the Y coordinate of the rectangle between quadrant I and IV.
-
- The native coordinate system on macOS uses quadrant I, with origin
- in bottom left, and Qt uses quadrant IV, with origin in top left.
-
- By flippig the Y coordinate, we can map the rectangle between the
- two coordinate systems.
-*/
-QRectF QCocoaScreen::flipCoordinate(const QRectF &rect) const
-{
- return QRectF(flipCoordinate(rect.topLeft() + QPoint(0, rect.height())), rect.size());
-}
-
-void QCocoaScreen::updateGeometry()
-{
- NSScreen *nsScreen = nativeScreen();
- if (!nsScreen)
- return;
-
- // At this point the geometry is in native coordinates, but the size
- // is correct, which we take advantage of next when we map the native
- // coordinates to the Qt coordinate system.
- m_geometry = QRectF::fromCGRect(NSRectToCGRect(nsScreen.frame)).toRect();
- m_availableGeometry = QRectF::fromCGRect(NSRectToCGRect(nsScreen.visibleFrame)).toRect();
-
- // The reference screen for the geometry is always the primary screen, but since
- // we may be in the process of creating and registering the primary screen, we
- // must special-case that and assign it direcly.
- QCocoaScreen *primaryScreen = (nsScreen == [[NSScreen screens] firstObject]) ?
- this : static_cast<QCocoaScreen*>(QGuiApplication::primaryScreen()->handle());
-
- m_geometry = primaryScreen->mapFromNative(m_geometry).toRect();
- m_availableGeometry = primaryScreen->mapFromNative(m_availableGeometry).toRect();
-
- m_format = QImage::Format_RGB32;
- m_depth = NSBitsPerPixelFromDepth([nsScreen depth]);
-
- NSDictionary *devDesc = [nsScreen deviceDescription];
- CGDirectDisplayID dpy = [[devDesc objectForKey:@"NSScreenNumber"] unsignedIntValue];
- CGSize size = CGDisplayScreenSize(dpy);
- m_physicalSize = QSizeF(size.width, size.height);
- m_logicalDpi.first = 72;
- m_logicalDpi.second = 72;
- CGDisplayModeRef displayMode = CGDisplayCopyDisplayMode(dpy);
- float refresh = CGDisplayModeGetRefreshRate(displayMode);
- CGDisplayModeRelease(displayMode);
- if (refresh > 0)
- m_refreshRate = refresh;
-
- // Get m_name (brand/model of the monitor)
- NSDictionary *deviceInfo = (NSDictionary *)IODisplayCreateInfoDictionary(CGDisplayIOServicePort(dpy), kIODisplayOnlyPreferredName);
- NSDictionary *localizedNames = [deviceInfo objectForKey:[NSString stringWithUTF8String:kDisplayProductName]];
- if ([localizedNames count] > 0)
- m_name = QString::fromUtf8([[localizedNames objectForKey:[[localizedNames allKeys] objectAtIndex:0]] UTF8String]);
- [deviceInfo release];
-
- QWindowSystemInterface::handleScreenGeometryChange(screen(), geometry(), availableGeometry());
- QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(screen(), m_logicalDpi.first, m_logicalDpi.second);
- QWindowSystemInterface::handleScreenRefreshRateChange(screen(), m_refreshRate);
-}
-
-qreal QCocoaScreen::devicePixelRatio() const
-{
- QMacAutoReleasePool pool;
- NSScreen *nsScreen = nativeScreen();
- return qreal(nsScreen ? [nsScreen backingScaleFactor] : 1.0);
-}
-
-QPlatformScreen::SubpixelAntialiasingType QCocoaScreen::subpixelAntialiasingTypeHint() const
-{
- QPlatformScreen::SubpixelAntialiasingType type = QPlatformScreen::subpixelAntialiasingTypeHint();
- if (type == QPlatformScreen::Subpixel_None) {
- // Every OSX machine has RGB pixels unless a peculiar or rotated non-Apple screen is attached
- type = QPlatformScreen::Subpixel_RGB;
- }
- return type;
-}
-
-QWindow *QCocoaScreen::topLevelAt(const QPoint &point) const
-{
- NSPoint screenPoint = qt_mac_flipPoint(point);
-
- // Search (hit test) for the top-level window. [NSWidow windowNumberAtPoint:
- // belowWindowWithWindowNumber] may return windows that are not interesting
- // to Qt. The search iterates until a suitable window or no window is found.
- NSInteger topWindowNumber = 0;
- QWindow *window = 0;
- do {
- // Get the top-most window, below any previously rejected window.
- topWindowNumber = [NSWindow windowNumberAtPoint:screenPoint
- belowWindowWithWindowNumber:topWindowNumber];
-
- // Continue the search if the window does not belong to this process.
- NSWindow *nsWindow = [NSApp windowWithWindowNumber:topWindowNumber];
- if (nsWindow == 0)
- continue;
-
- // Continue the search if the window does not belong to Qt.
- if (![nsWindow conformsToProtocol:@protocol(QNSWindowProtocol)])
- continue;
-
- id<QNSWindowProtocol> proto = static_cast<id<QNSWindowProtocol> >(nsWindow);
- QCocoaWindow *cocoaWindow = proto.helper.platformWindow;
- if (!cocoaWindow)
- continue;
- window = cocoaWindow->window();
-
- // Continue the search if the window is not a top-level window.
- if (!window->isTopLevel())
- continue;
-
- // Stop searching. The current window is the correct window.
- break;
- } while (topWindowNumber > 0);
-
- return window;
-}
-
-QPixmap QCocoaScreen::grabWindow(WId window, int x, int y, int width, int height) const
-{
- // TODO window should be handled
- Q_UNUSED(window)
-
- const int maxDisplays = 128; // 128 displays should be enough for everyone.
- CGDirectDisplayID displays[maxDisplays];
- CGDisplayCount displayCount;
- CGRect cgRect;
-
- if (width < 0 || height < 0) {
- // get all displays
- cgRect = CGRectInfinite;
- } else {
- cgRect = CGRectMake(x, y, width, height);
- }
- const CGDisplayErr err = CGGetDisplaysWithRect(cgRect, maxDisplays, displays, &displayCount);
-
- if (err && displayCount == 0)
- return QPixmap();
-
- // calculate pixmap size
- QSize windowSize(width, height);
- if (width < 0 || height < 0) {
- QRect windowRect;
- for (uint i = 0; i < displayCount; ++i) {
- const CGRect cgRect = CGDisplayBounds(displays[i]);
- QRect qRect(cgRect.origin.x, cgRect.origin.y, cgRect.size.width, cgRect.size.height);
- windowRect = windowRect.united(qRect);
- }
- if (width < 0)
- windowSize.setWidth(windowRect.width());
- if (height < 0)
- windowSize.setHeight(windowRect.height());
- }
-
- QPixmap windowPixmap(windowSize * devicePixelRatio());
- windowPixmap.fill(Qt::transparent);
-
- for (uint i = 0; i < displayCount; ++i) {
- const CGRect bounds = CGDisplayBounds(displays[i]);
- int w = (width < 0 ? bounds.size.width : width) * devicePixelRatio();
- int h = (height < 0 ? bounds.size.height : height) * devicePixelRatio();
- QRect displayRect = QRect(x, y, w, h);
- displayRect = displayRect.translated(qRound(-bounds.origin.x), qRound(-bounds.origin.y));
- QCFType<CGImageRef> image = CGDisplayCreateImageForRect(displays[i],
- CGRectMake(displayRect.x(), displayRect.y(), displayRect.width(), displayRect.height()));
- QPixmap pix(w, h);
- pix.fill(Qt::transparent);
- CGRect rect = CGRectMake(0, 0, w, h);
- QMacCGContext ctx(&pix);
- qt_mac_drawCGImage(ctx, &rect, image);
-
- QPainter painter(&windowPixmap);
- painter.drawPixmap(0, 0, pix);
- }
- return windowPixmap;
-}
-
static QCocoaIntegration::Options parseOptions(const QStringList &paramList)
{
QCocoaIntegration::Options options;
@@ -355,7 +142,7 @@ QCocoaIntegration::QCocoaIntegration(const QStringList &paramList)
// Move the application window to front to make it take focus, also when launching
// from the terminal. On 10.12+ this call has been moved to applicationDidFinishLauching
// to work around issues with loss of focus at startup.
- if (QSysInfo::macVersion() < QSysInfo::MV_10_12) {
+ if (QOperatingSystemVersion::current() < QOperatingSystemVersion::MacOSSierra) {
// Ignoring other apps is necessary (we must ignore the terminal), but makes
// Qt apps play slightly less nice with other apps when lanching from Finder
// (See the activateIgnoringOtherApps docs.)
@@ -392,6 +179,9 @@ QCocoaIntegration::QCocoaIntegration(const QStringList &paramList)
QMacInternalPasteboardMime::initializeMimeTypes();
QCocoaMimeTypes::initializeMimeTypes();
QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(false);
+
+ connect(qGuiApp, &QGuiApplication::focusWindowChanged,
+ this, &QCocoaIntegration::focusWindowChanged);
}
QCocoaIntegration::~QCocoaIntegration()
@@ -435,6 +225,8 @@ QCocoaIntegration::Options QCocoaIntegration::options() const
return mOptions;
}
+Q_LOGGING_CATEGORY(lcCocoaScreen, "qt.qpa.cocoa.screens");
+
/*!
\brief Synchronizes the screen list, adds new screens, removes deleted ones
*/
@@ -474,9 +266,11 @@ void QCocoaIntegration::updateScreens()
if (screen) {
remainingScreens.remove(screen);
screen->updateGeometry();
+ qCDebug(lcCocoaScreen) << "Updated properties of" << screen;
} else {
screen = new QCocoaScreen(i);
mScreens.append(screen);
+ qCDebug(lcCocoaScreen) << "Adding" << screen;
screenAdded(screen);
}
siblings << screen;
@@ -493,6 +287,7 @@ void QCocoaIntegration::updateScreens()
mScreens.removeOne(screen);
// Prevent stale references to NSScreen during destroy
screen->m_screenIndex = -1;
+ qCDebug(lcCocoaScreen) << "Removing" << screen;
destroyScreen(screen);
}
}
@@ -545,6 +340,24 @@ QPlatformWindow *QCocoaIntegration::createForeignWindow(QWindow *window, WId nat
return new QCocoaWindow(window, nativeHandle);
}
+class QCocoaOffscreenSurface : public QPlatformOffscreenSurface
+{
+public:
+ QCocoaOffscreenSurface(QOffscreenSurface *offscreenSurface) : QPlatformOffscreenSurface(offscreenSurface) {}
+
+ QSurfaceFormat format() const override
+ {
+ Q_ASSERT(offscreenSurface());
+ return offscreenSurface()->requestedFormat();
+ }
+ bool isValid() const override { return true; }
+};
+
+QPlatformOffscreenSurface *QCocoaIntegration::createPlatformOffscreenSurface(QOffscreenSurface *surface) const
+{
+ return new QCocoaOffscreenSurface(surface);
+}
+
#ifndef QT_NO_OPENGL
QPlatformOpenGLContext *QCocoaIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const
{
@@ -700,4 +513,33 @@ void QCocoaIntegration::beep() const
NSBeep();
}
+void QCocoaIntegration::focusWindowChanged(QWindow *focusWindow)
+{
+ // Don't revert icon just because we lost focus
+ if (!focusWindow)
+ return;
+
+ static bool hasDefaultApplicationIcon = [](){
+ NSImage *genericApplicationIcon = [[NSWorkspace sharedWorkspace]
+ iconForFileType:NSFileTypeForHFSTypeCode(kGenericApplicationIcon)];
+ NSImage *applicationIcon = [NSImage imageNamed:NSImageNameApplicationIcon];
+
+ NSRect rect = NSMakeRect(0, 0, 32, 32);
+ return [applicationIcon CGImageForProposedRect:&rect context:nil hints:nil]
+ == [genericApplicationIcon CGImageForProposedRect:&rect context:nil hints:nil];
+ }();
+
+ // Don't let the window icon override an explicit application icon set in the Info.plist
+ if (!hasDefaultApplicationIcon)
+ return;
+
+ // Or an explicit application icon set on QGuiApplication
+ if (!qGuiApp->windowIcon().isNull())
+ return;
+
+ setApplicationIcon(focusWindow->icon());
+}
+
+#include "moc_qcocoaintegration.cpp"
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/cocoa/qcocoakeymapper.h b/src/plugins/platforms/cocoa/qcocoakeymapper.h
index 4ba615efeb..a75e275077 100644
--- a/src/plugins/platforms/cocoa/qcocoakeymapper.h
+++ b/src/plugins/platforms/cocoa/qcocoakeymapper.h
@@ -91,8 +91,6 @@ public:
private:
QCFType<TISInputSourceRef> currentInputSource;
- QLocale keyboardInputLocale;
- Qt::LayoutDirection keyboardInputDirection;
enum { NullMode, UnicodeMode, OtherMode } keyboard_mode;
union {
const UCKeyboardLayout *unicode;
diff --git a/src/plugins/platforms/cocoa/qcocoakeymapper.mm b/src/plugins/platforms/cocoa/qcocoakeymapper.mm
index 1ef7f11011..80140505d1 100644
--- a/src/plugins/platforms/cocoa/qcocoakeymapper.mm
+++ b/src/plugins/platforms/cocoa/qcocoakeymapper.mm
@@ -72,14 +72,6 @@ static const Qt::KeyboardModifiers ModsTbl[] = {
bool qt_mac_eat_unicode_key = false;
-Q_GUI_EXPORT void qt_mac_secure_keyboard(bool b)
-{
- static bool secure = false;
- if (b != secure){
- b ? EnableSecureEventInput() : DisableSecureEventInput();
- secure = b;
- }
-}
/* key maps */
struct qt_mac_enum_mapper
@@ -384,18 +376,7 @@ bool QCocoaKeyMapper::updateKeyboard()
}
currentInputSource = source;
keyboard_dead = 0;
- CFStringRef iso639Code;
- CFArrayRef array = static_cast<CFArrayRef>(TISGetInputSourceProperty(currentInputSource, kTISPropertyInputSourceLanguages));
- iso639Code = static_cast<CFStringRef>(CFArrayGetValueAtIndex(array, 0)); // Actually a RFC3066bis, but it's close enough
-
- if (iso639Code) {
- keyboardInputLocale = QLocale(QString::fromCFString(iso639Code));
- keyboardInputDirection = keyboardInputLocale.textDirection();
- } else {
- keyboardInputLocale = QLocale::c();
- keyboardInputDirection = Qt::LeftToRight;
- }
return true;
}
diff --git a/src/plugins/platforms/cocoa/qcocoamenu.h b/src/plugins/platforms/cocoa/qcocoamenu.h
index 7baaf971f4..2b4cf0ef98 100644
--- a/src/plugins/platforms/cocoa/qcocoamenu.h
+++ b/src/plugins/platforms/cocoa/qcocoamenu.h
@@ -45,6 +45,8 @@
#include <qpa/qplatformmenu.h>
#include "qcocoamenuitem.h"
+Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(QCocoaNSMenu));
+
QT_BEGIN_NAMESPACE
class QCocoaMenuBar;
@@ -55,37 +57,31 @@ public:
QCocoaMenu();
~QCocoaMenu();
- void setTag(quintptr tag) Q_DECL_OVERRIDE
- { m_tag = tag; }
- quintptr tag() const Q_DECL_OVERRIDE
- { return m_tag; }
-
- void insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before) Q_DECL_OVERRIDE;
- void removeMenuItem(QPlatformMenuItem *menuItem) Q_DECL_OVERRIDE;
- void syncMenuItem(QPlatformMenuItem *menuItem) Q_DECL_OVERRIDE;
- void setEnabled(bool enabled) Q_DECL_OVERRIDE;
- bool isEnabled() const Q_DECL_OVERRIDE;
- void setVisible(bool visible) Q_DECL_OVERRIDE;
- void showPopup(const QWindow *parentWindow, const QRect &targetRect, const QPlatformMenuItem *item) Q_DECL_OVERRIDE;
- void dismiss() Q_DECL_OVERRIDE;
+ void insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before) override;
+ void removeMenuItem(QPlatformMenuItem *menuItem) override;
+ void syncMenuItem(QPlatformMenuItem *menuItem) override;
+ void setEnabled(bool enabled) override;
+ bool isEnabled() const override;
+ void setVisible(bool visible) override;
+ void showPopup(const QWindow *parentWindow, const QRect &targetRect, const QPlatformMenuItem *item) override;
+ void dismiss() override;
- void syncSeparatorsCollapsible(bool enable) Q_DECL_OVERRIDE;
+ void syncSeparatorsCollapsible(bool enable) override;
void propagateEnabledState(bool enabled);
- void setIcon(const QIcon &icon) Q_DECL_OVERRIDE { Q_UNUSED(icon) }
+ void setIcon(const QIcon &icon) override { Q_UNUSED(icon) }
- void setText(const QString &text) Q_DECL_OVERRIDE;
- void setMinimumWidth(int width) Q_DECL_OVERRIDE;
- void setFont(const QFont &font) Q_DECL_OVERRIDE;
+ void setText(const QString &text) override;
+ void setMinimumWidth(int width) override;
+ void setFont(const QFont &font) override;
- inline NSMenu *nsMenu() const
- { return m_nativeMenu; }
+ NSMenu *nsMenu() const;
inline bool isVisible() const { return m_visible; }
- QPlatformMenuItem *menuItemAt(int position) const Q_DECL_OVERRIDE;
- QPlatformMenuItem *menuItemForTag(quintptr tag) const Q_DECL_OVERRIDE;
+ QPlatformMenuItem *menuItemAt(int position) const override;
+ QPlatformMenuItem *menuItemForTag(quintptr tag) const override;
QList<QCocoaMenuItem *> items() const;
QList<QCocoaMenuItem *> merged() const;
@@ -96,7 +92,7 @@ public:
bool isOpen() const;
void setIsOpen(bool isOpen);
- void timerEvent(QTimerEvent *e) Q_DECL_OVERRIDE;
+ void timerEvent(QTimerEvent *e) override;
void syncMenuItem_helper(QPlatformMenuItem *menuItem, bool menubarUpdate);
@@ -106,9 +102,8 @@ private:
void scheduleUpdate();
QList<QCocoaMenuItem *> m_menuItems;
- NSMenu *m_nativeMenu;
+ QT_MANGLE_NAMESPACE(QCocoaNSMenu) *m_nativeMenu;
NSMenuItem *m_attachedItem;
- quintptr m_tag;
int m_updateTimer;
bool m_enabled:1;
bool m_parentEnabled:1;
diff --git a/src/plugins/platforms/cocoa/qcocoamenu.mm b/src/plugins/platforms/cocoa/qcocoamenu.mm
index e41c70b8ca..b3c2d5ae90 100644
--- a/src/plugins/platforms/cocoa/qcocoamenu.mm
+++ b/src/plugins/platforms/cocoa/qcocoamenu.mm
@@ -38,229 +38,22 @@
****************************************************************************/
#include "qcocoamenu.h"
+#include "qcocoansmenu.h"
#include "qcocoahelpers.h"
#include <QtCore/QtDebug>
-#include <QtCore/qmetaobject.h>
-#include <QtCore/private/qthread_p.h>
-#include <QtGui/private/qguiapplication_p.h>
#include "qcocoaapplication.h"
#include "qcocoaintegration.h"
#include "qcocoamenuloader.h"
#include "qcocoamenubar.h"
#include "qcocoawindow.h"
-#import "qnsview.h"
-
-QT_BEGIN_NAMESPACE
-
-NSString *qt_mac_removePrivateUnicode(NSString* string)
-{
- int len = [string length];
- if (len) {
- QVarLengthArray <unichar, 10> characters(len);
- bool changed = false;
- for (int i = 0; i<len; i++) {
- characters[i] = [string characterAtIndex:i];
- // check if they belong to key codes in private unicode range
- // currently we need to handle only the NSDeleteFunctionKey
- if (characters[i] == NSDeleteFunctionKey) {
- characters[i] = NSDeleteCharacter;
- changed = true;
- }
- }
- if (changed)
- return [NSString stringWithCharacters:characters.data() length:len];
- }
- return string;
-}
-
-QT_END_NAMESPACE
-
-@interface QT_MANGLE_NAMESPACE(QCocoaMenuDelegate) : NSObject <NSMenuDelegate> {
- QCocoaMenu *m_menu;
-}
-
-- (id) initWithMenu:(QCocoaMenu*) m;
-- (NSMenuItem *)findItem:(NSMenu *)menu forKey:(NSString *)key forModifiers:(NSUInteger)modifier;
-
-@end
-
-QT_NAMESPACE_ALIAS_OBJC_CLASS(QCocoaMenuDelegate);
-
-@implementation QCocoaMenuDelegate
-
-- (id) initWithMenu:(QCocoaMenu*) m
-{
- if ((self = [super init]))
- m_menu = m;
-
- return self;
-}
-
-- (NSInteger)numberOfItemsInMenu:(NSMenu *)menu
-{
- Q_ASSERT(m_menu->nsMenu() == menu);
- return menu.numberOfItems;
-}
-
-- (BOOL)menu:(NSMenu *)menu updateItem:(NSMenuItem *)item atIndex:(NSInteger)index shouldCancel:(BOOL)shouldCancel
-{
- Q_UNUSED(index);
- Q_ASSERT(m_menu->nsMenu() == menu);
- if (shouldCancel) {
- // TODO detach all submenus
- return NO;
- }
-
- QCocoaMenuItem *menuItem = reinterpret_cast<QCocoaMenuItem *>(item.tag);
- if (m_menu->items().contains(menuItem)) {
- if (QCocoaMenu *itemSubmenu = menuItem->menu())
- itemSubmenu->setAttachedItem(item);
- }
- return YES;
-}
-
-- (void)menu:(NSMenu*)menu willHighlightItem:(NSMenuItem*)item
-{
- Q_UNUSED(menu);
- if (item && [item tag]) {
- QCocoaMenuItem *cocoaItem = reinterpret_cast<QCocoaMenuItem *>([item tag]);
- cocoaItem->hovered();
- }
-}
-
-- (void) menuWillOpen:(NSMenu*)m
-{
- Q_UNUSED(m);
- m_menu->setIsOpen(true);
- emit m_menu->aboutToShow();
-}
-
-- (void) menuDidClose:(NSMenu*)m
-{
- Q_UNUSED(m);
- m_menu->setIsOpen(false);
- // wrong, but it's the best we can do
- emit m_menu->aboutToHide();
-}
-
-- (void) itemFired:(NSMenuItem*) item
-{
- QCocoaMenuItem *cocoaItem = reinterpret_cast<QCocoaMenuItem *>([item tag]);
- // Menu-holding items also get a target to play nicely
- // with NSMenuValidation but should not trigger.
- if (cocoaItem->menu())
- return;
- QScopedScopeLevelCounter scopeLevelCounter(QGuiApplicationPrivate::instance()->threadData);
- QGuiApplicationPrivate::modifier_buttons = [QNSView convertKeyModifiers:[NSEvent modifierFlags]];
- static QMetaMethod activatedSignal = QMetaMethod::fromSignal(&QCocoaMenuItem::activated);
- activatedSignal.invoke(cocoaItem, Qt::QueuedConnection);
-}
-
-- (BOOL)validateMenuItem:(NSMenuItem*)menuItem
-{
- QCocoaMenuItem *cocoaItem = reinterpret_cast<QCocoaMenuItem *>(menuItem.tag);
- // Menu-holding items are always enabled, as it's conventional in Cocoa
- if (!cocoaItem || cocoaItem->menu())
- return YES;
-
- return cocoaItem->isEnabled();
-}
-
-- (BOOL)menuHasKeyEquivalent:(NSMenu *)menu forEvent:(NSEvent *)event target:(id *)target action:(SEL *)action
-{
- /*
- Check if the menu actually has a keysequence defined for this key event.
- If it does, then we will first send the key sequence to the QWidget that has focus
- since (in Qt's eyes) it needs to a chance at the key event first (QEvent::ShortcutOverride).
- If the widget accepts the key event, we then return YES, but set the target and action to be nil,
- which means that the action should not be triggered, and instead dispatch the event ourselves.
- In every other case we return NO, which means that Cocoa can do as it pleases
- (i.e., fire the menu action).
- */
-
- // Change the private unicode keys to the ones used in setting the "Key Equivalents"
- NSString *characters = qt_mac_removePrivateUnicode([event characters]);
- // Interested only in Shift, Cmd, Ctrl & Alt Keys, so ignoring masks like, Caps lock, Num Lock ...
- const NSUInteger mask = NSShiftKeyMask | NSControlKeyMask | NSCommandKeyMask | NSAlternateKeyMask;
- if (NSMenuItem *menuItem = [self findItem:menu forKey:characters forModifiers:([event modifierFlags] & mask)]) {
- if (!menuItem.target) {
- // This item was modified by QCocoaMenuBar::redirectKnownMenuItemsToFirstResponder
- // and it looks like we're running a modal session for NSOpenPanel/NSSavePanel.
- // QCocoaFileDialogHelper is actually the only place we use this and we run NSOpenPanel modal
- // (modal sheet, window modal, application modal).
- // Whatever the current first responder is, let's give it a chance
- // and do not touch the Qt's focusObject (which is different from some native view
- // having a focus inside NSSave/OpenPanel.
- *target = nil;
- *action = menuItem.action;
- return YES;
- }
-
- QObject *object = qApp->focusObject();
- if (object) {
- QChar ch;
- int keyCode;
- ulong nativeModifiers = [event modifierFlags];
- Qt::KeyboardModifiers modifiers = [QNSView convertKeyModifiers: nativeModifiers];
- NSString *charactersIgnoringModifiers = [event charactersIgnoringModifiers];
- NSString *characters = [event characters];
-
- if ([charactersIgnoringModifiers length] > 0) { // convert the first character into a key code
- if ((modifiers & Qt::ControlModifier) && ([characters length] != 0)) {
- ch = QChar([characters characterAtIndex:0]);
- } else {
- ch = QChar([charactersIgnoringModifiers characterAtIndex:0]);
- }
- keyCode = qt_mac_cocoaKey2QtKey(ch);
- } else {
- // might be a dead key
- ch = QChar::ReplacementCharacter;
- keyCode = Qt::Key_unknown;
- }
-
- QKeyEvent accel_ev(QEvent::ShortcutOverride, (keyCode & (~Qt::KeyboardModifierMask)),
- Qt::KeyboardModifiers(modifiers & Qt::KeyboardModifierMask));
- accel_ev.ignore();
- QCoreApplication::sendEvent(object, &accel_ev);
- if (accel_ev.isAccepted()) {
- [[NSApp keyWindow] sendEvent: event];
- *target = nil;
- *action = nil;
- return YES;
- }
- }
- }
- return NO;
-}
-
-- (NSMenuItem *)findItem:(NSMenu *)menu forKey:(NSString *)key forModifiers:(NSUInteger)modifier
-{
- for (NSMenuItem *item in [menu itemArray]) {
- if (![item isEnabled] || [item isHidden] || [item isSeparatorItem])
- continue;
- if ([item hasSubmenu]) {
- if (NSMenuItem *nested = [self findItem:[item submenu] forKey:key forModifiers:modifier])
- return nested;
- }
-
- NSString *menuKey = [item keyEquivalent];
- if (menuKey
- && NSOrderedSame == [menuKey compare:key]
- && modifier == [item keyEquivalentModifierMask])
- return item;
- }
- return nil;
-}
-
-@end
+#include "qcocoascreen.h"
QT_BEGIN_NAMESPACE
QCocoaMenu::QCocoaMenu() :
m_attachedItem(0),
- m_tag(0),
m_updateTimer(0),
m_enabled(true),
m_parentEnabled(true),
@@ -269,9 +62,7 @@ QCocoaMenu::QCocoaMenu() :
{
QMacAutoReleasePool pool;
- m_nativeMenu = [[NSMenu alloc] initWithTitle:@"Untitled"];
- [m_nativeMenu setAutoenablesItems:YES];
- m_nativeMenu.delegate = [[QCocoaMenuDelegate alloc] initWithMenu:this];
+ m_nativeMenu = [[QCocoaNSMenu alloc] initWithQPAMenu:this];
}
QCocoaMenu::~QCocoaMenu()
@@ -281,10 +72,6 @@ QCocoaMenu::~QCocoaMenu()
item->setMenuParent(0);
}
- QMacAutoReleasePool pool;
- NSObject *delegate = m_nativeMenu.delegate;
- m_nativeMenu.delegate = nil;
- [delegate release];
[m_nativeMenu release];
}
@@ -309,6 +96,11 @@ void QCocoaMenu::setFont(const QFont &font)
}
}
+NSMenu *QCocoaMenu::nsMenu() const
+{
+ return static_cast<NSMenu *>(m_nativeMenu);
+}
+
void QCocoaMenu::insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before)
{
QMacAutoReleasePool pool;
diff --git a/src/plugins/platforms/cocoa/qcocoamenubar.h b/src/plugins/platforms/cocoa/qcocoamenubar.h
index a4ee531e91..6f3aca3a51 100644
--- a/src/plugins/platforms/cocoa/qcocoamenubar.h
+++ b/src/plugins/platforms/cocoa/qcocoamenubar.h
@@ -56,11 +56,11 @@ public:
QCocoaMenuBar();
~QCocoaMenuBar();
- void insertMenu(QPlatformMenu *menu, QPlatformMenu* before) Q_DECL_OVERRIDE;
- void removeMenu(QPlatformMenu *menu) Q_DECL_OVERRIDE;
- void syncMenu(QPlatformMenu *menuItem) Q_DECL_OVERRIDE;
- void handleReparent(QWindow *newParentWindow) Q_DECL_OVERRIDE;
- QPlatformMenu *menuForTag(quintptr tag) const Q_DECL_OVERRIDE;
+ void insertMenu(QPlatformMenu *menu, QPlatformMenu* before) override;
+ void removeMenu(QPlatformMenu *menu) override;
+ void syncMenu(QPlatformMenu *menuItem) override;
+ void handleReparent(QWindow *newParentWindow) override;
+ QPlatformMenu *menuForTag(quintptr tag) const override;
inline NSMenu *nsMenu() const
{ return m_nativeMenu; }
@@ -71,6 +71,7 @@ public:
QList<QCocoaMenuItem*> merged() const;
NSMenuItem *itemForRole(QPlatformMenuItem::MenuRole r);
+ QCocoaWindow *cocoaWindow() const;
void syncMenu_helper(QPlatformMenu *menu, bool menubarUpdate);
diff --git a/src/plugins/platforms/cocoa/qcocoamenubar.mm b/src/plugins/platforms/cocoa/qcocoamenubar.mm
index a4cd465dae..8091d00bda 100644
--- a/src/plugins/platforms/cocoa/qcocoamenubar.mm
+++ b/src/plugins/platforms/cocoa/qcocoamenubar.mm
@@ -449,7 +449,12 @@ NSMenuItem *QCocoaMenuBar::itemForRole(QPlatformMenuItem::MenuRole r)
foreach (QCocoaMenuItem *i, m->items())
if (i->effectiveRole() == r)
return i->nsItem();
- return Q_NULLPTR;
+ return nullptr;
+}
+
+QCocoaWindow *QCocoaMenuBar::cocoaWindow() const
+{
+ return m_window.data();
}
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/cocoa/qcocoamenuitem.h b/src/plugins/platforms/cocoa/qcocoamenuitem.h
index 23f788687c..2b598ee3a0 100644
--- a/src/plugins/platforms/cocoa/qcocoamenuitem.h
+++ b/src/plugins/platforms/cocoa/qcocoamenuitem.h
@@ -78,27 +78,22 @@ public:
QCocoaMenuItem();
~QCocoaMenuItem();
- void setTag(quintptr tag) Q_DECL_OVERRIDE
- { m_tag = tag; }
- quintptr tag() const Q_DECL_OVERRIDE
- { return m_tag; }
-
- void setText(const QString &text) Q_DECL_OVERRIDE;
- void setIcon(const QIcon &icon) Q_DECL_OVERRIDE;
- void setMenu(QPlatformMenu *menu) Q_DECL_OVERRIDE;
- void setVisible(bool isVisible) Q_DECL_OVERRIDE;
- void setIsSeparator(bool isSeparator) Q_DECL_OVERRIDE;
- void setFont(const QFont &font) Q_DECL_OVERRIDE;
- void setRole(MenuRole role) Q_DECL_OVERRIDE;
+ void setText(const QString &text) override;
+ void setIcon(const QIcon &icon) override;
+ void setMenu(QPlatformMenu *menu) override;
+ void setVisible(bool isVisible) override;
+ void setIsSeparator(bool isSeparator) override;
+ void setFont(const QFont &font) override;
+ void setRole(MenuRole role) override;
#ifndef QT_NO_SHORTCUT
- void setShortcut(const QKeySequence& shortcut) Q_DECL_OVERRIDE;
+ void setShortcut(const QKeySequence& shortcut) override;
#endif
- void setCheckable(bool checkable) Q_DECL_OVERRIDE { Q_UNUSED(checkable) }
- void setChecked(bool isChecked) Q_DECL_OVERRIDE;
- void setEnabled(bool isEnabled) Q_DECL_OVERRIDE;
- void setIconSize(int size) Q_DECL_OVERRIDE;
+ void setCheckable(bool checkable) override { Q_UNUSED(checkable) }
+ void setChecked(bool isChecked) override;
+ void setEnabled(bool isEnabled) override;
+ void setIconSize(int size) override;
- void setNativeContents(WId item) Q_DECL_OVERRIDE;
+ void setNativeContents(WId item) override;
inline QString text() const { return m_text; }
inline NSMenuItem * nsItem() { return m_native; }
@@ -129,7 +124,6 @@ private:
#ifndef QT_NO_SHORTCUT
QKeySequence m_shortcut;
#endif
- quintptr m_tag;
int m_iconSize;
bool m_textSynced:1;
bool m_isVisible:1;
diff --git a/src/plugins/platforms/cocoa/qcocoamenuitem.mm b/src/plugins/platforms/cocoa/qcocoamenuitem.mm
index 394e0fb8e4..f8f9648822 100644
--- a/src/plugins/platforms/cocoa/qcocoamenuitem.mm
+++ b/src/plugins/platforms/cocoa/qcocoamenuitem.mm
@@ -49,6 +49,7 @@
#include "qcocoaapplication.h" // for custom application category
#include "qcocoamenuloader.h"
#include <QtGui/private/qcoregraphics_p.h>
+#include <QtCore/qregularexpression.h>
#include <QtCore/QDebug>
@@ -95,7 +96,6 @@ QCocoaMenuItem::QCocoaMenuItem() :
m_itemView(nil),
m_menu(NULL),
m_role(NoRole),
- m_tag(0),
m_iconSize(16),
m_textSynced(false),
m_isVisible(true),
@@ -262,7 +262,8 @@ NSMenuItem *QCocoaMenuItem::sync()
m_detectedRole = detectMenuRole(m_text);
switch (m_detectedRole) {
case QPlatformMenuItem::AboutRole:
- if (m_text.indexOf(QRegExp(QString::fromLatin1("qt$"), Qt::CaseInsensitive)) == -1)
+ if (m_text.indexOf(QRegularExpression(QString::fromLatin1("qt$"),
+ QRegularExpression::CaseInsensitiveOption)) == -1)
mergeItem = [loader aboutMenuItem];
else
mergeItem = [loader aboutQtMenuItem];
diff --git a/src/plugins/platforms/cocoa/qcocoamenuloader.mm b/src/plugins/platforms/cocoa/qcocoamenuloader.mm
index b986833f6d..cd597da71c 100644
--- a/src/plugins/platforms/cocoa/qcocoamenuloader.mm
+++ b/src/plugins/platforms/cocoa/qcocoamenuloader.mm
@@ -57,8 +57,12 @@
static QCocoaMenuLoader *shared = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
- shared = [[self alloc] init];
- });
+ shared = [[self alloc] init];
+ atexit_b(^{
+ [shared release];
+ shared = nil;
+ });
+ });
return shared;
}
diff --git a/src/plugins/platforms/cocoa/qcocoamimetypes.mm b/src/plugins/platforms/cocoa/qcocoamimetypes.mm
index 093f86da6e..f7662a92a4 100644
--- a/src/plugins/platforms/cocoa/qcocoamimetypes.mm
+++ b/src/plugins/platforms/cocoa/qcocoamimetypes.mm
@@ -105,101 +105,9 @@ QList<QByteArray> QMacPasteboardMimeTraditionalMacPlainText::convertFromMime(con
return ret;
}
-class QMacPasteboardMimeTiff : public QMacInternalPasteboardMime {
-public:
- QMacPasteboardMimeTiff() : QMacInternalPasteboardMime(MIME_ALL) { }
- QString convertorName();
-
- QString flavorFor(const QString &mime);
- QString mimeFor(QString flav);
- bool canConvert(const QString &mime, QString flav);
- QVariant convertToMime(const QString &mime, QList<QByteArray> data, QString flav);
- QList<QByteArray> convertFromMime(const QString &mime, QVariant data, QString flav);
-};
-
-QString QMacPasteboardMimeTiff::convertorName()
-{
- return QLatin1String("Tiff");
-}
-
-QString QMacPasteboardMimeTiff::flavorFor(const QString &mime)
-{
- if (mime.startsWith(QLatin1String("application/x-qt-image")))
- return QLatin1String("public.tiff");
- return QString();
-}
-
-QString QMacPasteboardMimeTiff::mimeFor(QString flav)
-{
- if (flav == QLatin1String("public.tiff"))
- return QLatin1String("application/x-qt-image");
- return QString();
-}
-
-bool QMacPasteboardMimeTiff::canConvert(const QString &mime, QString flav)
-{
- return flav == QLatin1String("public.tiff") && mime == QLatin1String("application/x-qt-image");
-}
-
-QVariant QMacPasteboardMimeTiff::convertToMime(const QString &mime, QList<QByteArray> data, QString flav)
-{
- if (data.count() > 1)
- qWarning("QMacPasteboardMimeTiff: Cannot handle multiple member data");
- QVariant ret;
- if (!canConvert(mime, flav))
- return ret;
- const QByteArray &a = data.first();
- QCFType<CGImageRef> image;
- QCFType<CFDataRef> tiffData = CFDataCreateWithBytesNoCopy(0,
- reinterpret_cast<const UInt8 *>(a.constData()),
- a.size(), kCFAllocatorNull);
- QCFType<CGImageSourceRef> imageSource = CGImageSourceCreateWithData(tiffData, 0);
- image = CGImageSourceCreateImageAtIndex(imageSource, 0, 0);
- if (image != 0)
- ret = QVariant(qt_mac_toQImage(image));
- return ret;
-}
-
-QList<QByteArray> QMacPasteboardMimeTiff::convertFromMime(const QString &mime, QVariant variant, QString flav)
-{
- QList<QByteArray> ret;
- if (!canConvert(mime, flav))
- return ret;
-
- QImage img = qvariant_cast<QImage>(variant);
- QCFType<CGImageRef> cgimage = qt_mac_toCGImage(img);
-
- QCFType<CFMutableDataRef> data = CFDataCreateMutable(0, 0);
- QCFType<CGImageDestinationRef> imageDestination = CGImageDestinationCreateWithData(data, kUTTypeTIFF, 1, 0);
- if (imageDestination != 0) {
- CFTypeRef keys[2];
- QCFType<CFTypeRef> values[2];
- QCFType<CFDictionaryRef> options;
- keys[0] = kCGImagePropertyPixelWidth;
- keys[1] = kCGImagePropertyPixelHeight;
- int width = img.width();
- int height = img.height();
- values[0] = CFNumberCreate(0, kCFNumberIntType, &width);
- values[1] = CFNumberCreate(0, kCFNumberIntType, &height);
- options = CFDictionaryCreate(0, reinterpret_cast<const void **>(keys),
- reinterpret_cast<const void **>(values), 2,
- &kCFTypeDictionaryKeyCallBacks,
- &kCFTypeDictionaryValueCallBacks);
- CGImageDestinationAddImage(imageDestination, cgimage, options);
- CGImageDestinationFinalize(imageDestination);
- }
- QByteArray ar(CFDataGetLength(data), 0);
- CFDataGetBytes(data,
- CFRangeMake(0, ar.size()),
- reinterpret_cast<UInt8 *>(ar.data()));
- ret.append(ar);
- return ret;
-}
-
void QCocoaMimeTypes::initializeMimeTypes()
{
new QMacPasteboardMimeTraditionalMacPlainText;
- new QMacPasteboardMimeTiff;
}
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/cocoa/qcocoanativeinterface.h b/src/plugins/platforms/cocoa/qcocoanativeinterface.h
index 26fbe3e4bc..c78f1d5a7f 100644
--- a/src/plugins/platforms/cocoa/qcocoanativeinterface.h
+++ b/src/plugins/platforms/cocoa/qcocoanativeinterface.h
@@ -60,18 +60,18 @@ public:
QCocoaNativeInterface();
#ifndef QT_NO_OPENGL
- void *nativeResourceForContext(const QByteArray &resourceString, QOpenGLContext *context) Q_DECL_OVERRIDE;
+ void *nativeResourceForContext(const QByteArray &resourceString, QOpenGLContext *context) override;
#endif
- void *nativeResourceForWindow(const QByteArray &resourceString, QWindow *window) Q_DECL_OVERRIDE;
+ void *nativeResourceForWindow(const QByteArray &resourceString, QWindow *window) override;
- NativeResourceForIntegrationFunction nativeResourceFunctionForIntegration(const QByteArray &resource) Q_DECL_OVERRIDE;
+ NativeResourceForIntegrationFunction nativeResourceFunctionForIntegration(const QByteArray &resource) override;
#ifndef QT_NO_OPENGL
static void *cglContextForContext(QOpenGLContext *context);
static void *nsOpenGLContextForContext(QOpenGLContext* context);
#endif
- QFunctionPointer platformFunction(const QByteArray &function) const Q_DECL_OVERRIDE;
+ QFunctionPointer platformFunction(const QByteArray &function) const override;
public Q_SLOTS:
void onAppFocusWindowChanged(QWindow *window);
diff --git a/src/plugins/platforms/cocoa/qcocoanativeinterface.mm b/src/plugins/platforms/cocoa/qcocoanativeinterface.mm
index 5504c2427f..955b147bfd 100644
--- a/src/plugins/platforms/cocoa/qcocoanativeinterface.mm
+++ b/src/plugins/platforms/cocoa/qcocoanativeinterface.mm
@@ -103,7 +103,7 @@ void *QCocoaNativeInterface::nativeResourceForWindow(const QByteArray &resourceS
return static_cast<QCocoaWindow *>(window->handle())->currentContext()->nsOpenGLContext();
#endif
} else if (resourceString == "nswindow") {
- return static_cast<QCocoaWindow *>(window->handle())->m_nsWindow;
+ return static_cast<QCocoaWindow *>(window->handle())->nativeWindow();
}
return 0;
}
@@ -233,7 +233,7 @@ QFunctionPointer QCocoaNativeInterface::platformFunction(const QByteArray &funct
if (function == QCocoaWindowFunctions::bottomLeftClippedByNSWindowOffsetIdentifier())
return QFunctionPointer(QCocoaWindowFunctions::BottomLeftClippedByNSWindowOffset(QCocoaWindow::bottomLeftClippedByNSWindowOffsetStatic));
- return Q_NULLPTR;
+ return nullptr;
}
void QCocoaNativeInterface::addToMimeList(void *macPasteboardMime)
diff --git a/src/plugins/platforms/cocoa/qcocoansmenu.h b/src/plugins/platforms/cocoa/qcocoansmenu.h
new file mode 100644
index 0000000000..8fb0a26f27
--- /dev/null
+++ b/src/plugins/platforms/cocoa/qcocoansmenu.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOCOANSMENU_H
+#define QCOCOANSMENU_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#import <AppKit/AppKit.h>
+
+#include <QtCore/qpointer.h>
+#include <qcocoahelpers.h>
+
+QT_FORWARD_DECLARE_CLASS(QCocoaMenu);
+typedef QPointer<QCocoaMenu> QCocoaMenuPointer;
+
+
+@interface QT_MANGLE_NAMESPACE(QCocoaNSMenuDelegate) : NSObject <NSMenuDelegate>
+
++ (instancetype)sharedMenuDelegate;
+
+- (NSMenuItem *)findItemInMenu:(NSMenu *)menu
+ forKey:(NSString *)key
+ modifiers:(NSUInteger)modifiers;
+
+- (BOOL)validateMenuItem:(NSMenuItem *)item; // NSMenuValidation
+
+@end
+
+@interface QT_MANGLE_NAMESPACE(QCocoaNSMenu) : NSMenu
+
+@property (readonly, nonatomic) QCocoaMenuPointer qpaMenu;
+
+- (instancetype)initWithQPAMenu:(QCocoaMenu *)menu;
+
+@end
+
+QT_NAMESPACE_ALIAS_OBJC_CLASS(QCocoaNSMenu);
+QT_NAMESPACE_ALIAS_OBJC_CLASS(QCocoaNSMenuDelegate);
+
+#endif // QCOCOANSMENU_H
diff --git a/src/plugins/platforms/cocoa/qcocoansmenu.mm b/src/plugins/platforms/cocoa/qcocoansmenu.mm
new file mode 100644
index 0000000000..996a4ff194
--- /dev/null
+++ b/src/plugins/platforms/cocoa/qcocoansmenu.mm
@@ -0,0 +1,296 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#import "qcocoansmenu.h"
+#include "qcocoamenu.h"
+#include "qcocoamenuitem.h"
+#import "qnsview.h"
+
+#include <QtCore/qmetaobject.h>
+#include <QtCore/private/qthread_p.h>
+#include <QtGui/private/qguiapplication_p.h>
+
+static NSString *qt_mac_removePrivateUnicode(NSString* string)
+{
+ int len = [string length];
+ if (len) {
+ QVarLengthArray <unichar, 10> characters(len);
+ bool changed = false;
+ for (int i = 0; i<len; i++) {
+ characters[i] = [string characterAtIndex:i];
+ // check if they belong to key codes in private unicode range
+ // currently we need to handle only the NSDeleteFunctionKey
+ if (characters[i] == NSDeleteFunctionKey) {
+ characters[i] = NSDeleteCharacter;
+ changed = true;
+ }
+ }
+ if (changed)
+ return [NSString stringWithCharacters:characters.data() length:len];
+ }
+ return string;
+}
+
+@implementation QCocoaNSMenu
+
+- (instancetype)initWithQPAMenu:(QCocoaMenu *)menu
+{
+ if ((self = [super initWithTitle:@"Untitled"])) {
+ _qpaMenu = menu;
+ self.autoenablesItems = YES;
+ self.delegate = [QCocoaNSMenuDelegate sharedMenuDelegate];
+ }
+
+ return self;
+}
+
+@end
+
+#define CHECK_MENU_CLASS(menu) Q_ASSERT([menu isMemberOfClass:[QCocoaNSMenu class]])
+
+@implementation QCocoaNSMenuDelegate
+
++ (instancetype)sharedMenuDelegate
+{
+ static QCocoaNSMenuDelegate *shared = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ shared = [[self alloc] init];
+ atexit_b(^{
+ [shared release];
+ shared = nil;
+ });
+ });
+ return shared;
+}
+
+- (NSInteger)numberOfItemsInMenu:(NSMenu *)menu
+{
+ CHECK_MENU_CLASS(menu);
+ return menu.numberOfItems;
+}
+
+- (BOOL)menu:(NSMenu *)menu updateItem:(NSMenuItem *)item atIndex:(NSInteger)index shouldCancel:(BOOL)shouldCancel
+{
+ Q_UNUSED(index);
+ CHECK_MENU_CLASS(menu);
+
+ if (shouldCancel)
+ return NO;
+
+ const auto &qpaMenu = static_cast<QCocoaNSMenu *>(menu).qpaMenu;
+ if (qpaMenu.isNull())
+ return YES;
+
+ auto *menuItem = reinterpret_cast<QCocoaMenuItem *>(item.tag);
+ if (qpaMenu->items().contains(menuItem)) {
+ if (QCocoaMenu *itemSubmenu = menuItem->menu())
+ itemSubmenu->setAttachedItem(item);
+ }
+
+ return YES;
+}
+
+- (void)menu:(NSMenu *)menu willHighlightItem:(NSMenuItem *)item
+{
+ CHECK_MENU_CLASS(menu);
+ auto *qpaItem = reinterpret_cast<QCocoaMenuItem *>(item.tag);
+ if (qpaItem)
+ qpaItem->hovered();
+}
+
+- (void)menuWillOpen:(NSMenu *)menu
+{
+ CHECK_MENU_CLASS(menu);
+ const auto &qpaMenu = static_cast<QCocoaNSMenu *>(menu).qpaMenu;
+ if (qpaMenu.isNull())
+ return;
+
+ qpaMenu->setIsOpen(true);
+ emit qpaMenu->aboutToShow();
+}
+
+- (void)menuDidClose:(NSMenu *)menu
+{
+ CHECK_MENU_CLASS(menu);
+ const auto &qpaMenu = static_cast<QCocoaNSMenu *>(menu).qpaMenu;
+ if (qpaMenu.isNull())
+ return;
+
+ qpaMenu->setIsOpen(false);
+ // wrong, but it's the best we can do
+ emit qpaMenu->aboutToHide();
+}
+
+- (void)itemFired:(NSMenuItem *)item
+{
+ auto *qpaItem = reinterpret_cast<QCocoaMenuItem *>(item.tag);
+ // Menu-holding items also get a target to play nicely
+ // with NSMenuValidation but should not trigger.
+ if (!qpaItem || qpaItem->menu())
+ return;
+
+ QScopedScopeLevelCounter scopeLevelCounter(QGuiApplicationPrivate::instance()->threadData);
+ QGuiApplicationPrivate::modifier_buttons = [QNSView convertKeyModifiers:[NSEvent modifierFlags]];
+
+ static QMetaMethod activatedSignal = QMetaMethod::fromSignal(&QCocoaMenuItem::activated);
+ activatedSignal.invoke(qpaItem, Qt::QueuedConnection);
+}
+
+- (BOOL)validateMenuItem:(NSMenuItem*)item
+{
+ auto *qpaItem = reinterpret_cast<QCocoaMenuItem *>(item.tag);
+ // Menu-holding items are always enabled, as it's conventional in Cocoa
+ if (!qpaItem || qpaItem->menu())
+ return YES;
+
+ return qpaItem->isEnabled();
+}
+
+- (BOOL)menuHasKeyEquivalent:(NSMenu *)menu forEvent:(NSEvent *)event target:(id *)target action:(SEL *)action
+{
+ /*
+ Check if the menu actually has a keysequence defined for this key event.
+ If it does, then we will first send the key sequence to the QWidget that has focus
+ since (in Qt's eyes) it needs to a chance at the key event first (QEvent::ShortcutOverride).
+ If the widget accepts the key event, we then return YES, but set the target and action to be nil,
+ which means that the action should not be triggered, and instead dispatch the event ourselves.
+ In every other case we return NO, which means that Cocoa can do as it pleases
+ (i.e., fire the menu action).
+ */
+
+ CHECK_MENU_CLASS(menu);
+
+ // Interested only in Shift, Cmd, Ctrl & Alt Keys, so ignoring masks like, Caps lock, Num Lock ...
+ static const NSUInteger mask = NSShiftKeyMask | NSControlKeyMask | NSCommandKeyMask | NSAlternateKeyMask;
+
+ // Change the private unicode keys to the ones used in setting the "Key Equivalents"
+ NSString *characters = qt_mac_removePrivateUnicode(event.charactersIgnoringModifiers);
+ const auto modifiers = event.modifierFlags & mask;
+ NSMenuItem *keyEquivalentItem = [self findItemInMenu:menu
+ forKey:characters
+ modifiers:modifiers];
+ if (!keyEquivalentItem) {
+ // Maybe the modified character is what we're looking for after all
+ characters = qt_mac_removePrivateUnicode(event.characters);
+ keyEquivalentItem = [self findItemInMenu:menu
+ forKey:characters
+ modifiers:modifiers];
+ }
+
+ if (keyEquivalentItem) {
+ if (!keyEquivalentItem.target) {
+ // This item was modified by QCocoaMenuBar::redirectKnownMenuItemsToFirstResponder
+ // and it looks like we're running a modal session for NSOpenPanel/NSSavePanel.
+ // QCocoaFileDialogHelper is actually the only place we use this and we run NSOpenPanel modal
+ // (modal sheet, window modal, application modal).
+ // Whatever the current first responder is, let's give it a chance
+ // and do not touch the Qt's focusObject (which is different from some native view
+ // having a focus inside NSSave/OpenPanel.
+ *target = nil;
+ *action = keyEquivalentItem.action;
+ return YES;
+ }
+
+ QObject *object = qApp->focusObject();
+ if (object) {
+ QChar ch;
+ int keyCode;
+ ulong nativeModifiers = [event modifierFlags];
+ Qt::KeyboardModifiers modifiers = [QNSView convertKeyModifiers: nativeModifiers];
+ NSString *charactersIgnoringModifiers = [event charactersIgnoringModifiers];
+ NSString *characters = [event characters];
+
+ if ([charactersIgnoringModifiers length] > 0) { // convert the first character into a key code
+ if ((modifiers & Qt::ControlModifier) && ([characters length] != 0)) {
+ ch = QChar([characters characterAtIndex:0]);
+ } else {
+ ch = QChar([charactersIgnoringModifiers characterAtIndex:0]);
+ }
+ keyCode = qt_mac_cocoaKey2QtKey(ch);
+ } else {
+ // might be a dead key
+ ch = QChar::ReplacementCharacter;
+ keyCode = Qt::Key_unknown;
+ }
+
+ QKeyEvent accel_ev(QEvent::ShortcutOverride, (keyCode & (~Qt::KeyboardModifierMask)),
+ Qt::KeyboardModifiers(modifiers & Qt::KeyboardModifierMask));
+ accel_ev.ignore();
+ QCoreApplication::sendEvent(object, &accel_ev);
+ if (accel_ev.isAccepted()) {
+ [[NSApp keyWindow] sendEvent: event];
+ *target = nil;
+ *action = nil;
+ return YES;
+ }
+ }
+ }
+
+ return NO;
+}
+
+- (NSMenuItem *)findItemInMenu:(NSMenu *)menu
+ forKey:(NSString *)key
+ modifiers:(NSUInteger)modifiers
+{
+ // Find an item in 'menu' that has the same key equivalent as specified by
+ // 'key' and 'modifiers'. We ignore disabled, hidden and separator items.
+ // In a similar fashion, we don't need to recurse into submenus because their
+ // delegate will have [menuHasKeyEquivalent:...] invoked at some point.
+
+ for (NSMenuItem *item in menu.itemArray) {
+ if (!item.enabled || item.hidden || item.separatorItem)
+ continue;
+
+ if (item.hasSubmenu)
+ continue;
+
+ NSString *menuKey = item.keyEquivalent;
+ if (menuKey && NSOrderedSame == [menuKey compare:key]
+ && modifiers == item.keyEquivalentModifierMask)
+ return item;
+ }
+
+ return nil;
+}
+
+@end
+
+#undef CHECK_MENU_CLASS
diff --git a/src/plugins/platforms/cocoa/qcocoaprintdevice.h b/src/plugins/platforms/cocoa/qcocoaprintdevice.h
index c726cca4e5..20b27f3286 100644
--- a/src/plugins/platforms/cocoa/qcocoaprintdevice.h
+++ b/src/plugins/platforms/cocoa/qcocoaprintdevice.h
@@ -68,37 +68,37 @@ public:
explicit QCocoaPrintDevice(const QString &id);
virtual ~QCocoaPrintDevice();
- bool isValid() const Q_DECL_OVERRIDE;
- bool isDefault() const Q_DECL_OVERRIDE;
+ bool isValid() const override;
+ bool isDefault() const override;
- QPrint::DeviceState state() const Q_DECL_OVERRIDE;
+ QPrint::DeviceState state() const override;
- QPageSize defaultPageSize() const Q_DECL_OVERRIDE;
+ QPageSize defaultPageSize() const override;
QMarginsF printableMargins(const QPageSize &pageSize, QPageLayout::Orientation orientation,
- int resolution) const Q_DECL_OVERRIDE;
+ int resolution) const override;
- int defaultResolution() const Q_DECL_OVERRIDE;
+ int defaultResolution() const override;
- QPrint::InputSlot defaultInputSlot() const Q_DECL_OVERRIDE;
+ QPrint::InputSlot defaultInputSlot() const override;
- QPrint::OutputBin defaultOutputBin() const Q_DECL_OVERRIDE;
+ QPrint::OutputBin defaultOutputBin() const override;
- QPrint::DuplexMode defaultDuplexMode() const Q_DECL_OVERRIDE;
+ QPrint::DuplexMode defaultDuplexMode() const override;
- QPrint::ColorMode defaultColorMode() const Q_DECL_OVERRIDE;
+ QPrint::ColorMode defaultColorMode() const override;
PMPrinter macPrinter() const;
PMPaper macPaper(const QPageSize &pageSize) const;
protected:
- void loadPageSizes() const Q_DECL_OVERRIDE;
- void loadResolutions() const Q_DECL_OVERRIDE;
- void loadInputSlots() const Q_DECL_OVERRIDE;
- void loadOutputBins() const Q_DECL_OVERRIDE;
- void loadDuplexModes() const Q_DECL_OVERRIDE;
- void loadColorModes() const Q_DECL_OVERRIDE;
- void loadMimeTypes() const Q_DECL_OVERRIDE;
+ void loadPageSizes() const override;
+ void loadResolutions() const override;
+ void loadInputSlots() const override;
+ void loadOutputBins() const override;
+ void loadDuplexModes() const override;
+ void loadColorModes() const override;
+ void loadMimeTypes() const override;
private:
QPageSize createPageSize(const PMPaper &paper) const;
diff --git a/src/plugins/platforms/cocoa/qcocoaprintdevice.mm b/src/plugins/platforms/cocoa/qcocoaprintdevice.mm
index 39fcf285ed..bfe6cd09b6 100644
--- a/src/plugins/platforms/cocoa/qcocoaprintdevice.mm
+++ b/src/plugins/platforms/cocoa/qcocoaprintdevice.mm
@@ -46,6 +46,15 @@ QT_BEGIN_NAMESPACE
#ifndef QT_NO_PRINTER
+// The CUPS PPD APIs were deprecated in CUPS 1.6/macOS 10.8, but
+// as long as we're supporting RHEL 6, which still ships CUPS 1.4
+// we're not going to rewrite this, as we want to share the code
+// between macOS and Linux for the CUPS-bits. See discussion in
+// https://bugreports.qt.io/browse/QTBUG-56545
+#pragma message "Disabling CUPS PPD deprecation warnings. This should be fixed once we drop support for RHEL6 (QTBUG-56545)"
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
static QPrint::DuplexMode macToDuplexMode(const PMDuplexMode &mode)
{
if (mode == kPMDuplexTumble)
@@ -474,6 +483,8 @@ PMPaper QCocoaPrintDevice::macPaper(const QPageSize &pageSize) const
return paper;
}
+#pragma clang diagnostic pop
+
#endif // QT_NO_PRINTER
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/cocoa/qcocoaprintersupport.h b/src/plugins/platforms/cocoa/qcocoaprintersupport.h
index a07bf0ec1b..40a638207a 100644
--- a/src/plugins/platforms/cocoa/qcocoaprintersupport.h
+++ b/src/plugins/platforms/cocoa/qcocoaprintersupport.h
@@ -53,12 +53,12 @@ public:
QCocoaPrinterSupport();
~QCocoaPrinterSupport();
- QPrintEngine *createNativePrintEngine(QPrinter::PrinterMode printerMode, const QString &deviceId = QString()) Q_DECL_OVERRIDE;
- QPaintEngine *createPaintEngine(QPrintEngine *, QPrinter::PrinterMode printerMode) Q_DECL_OVERRIDE;
+ QPrintEngine *createNativePrintEngine(QPrinter::PrinterMode printerMode, const QString &deviceId = QString()) override;
+ QPaintEngine *createPaintEngine(QPrintEngine *, QPrinter::PrinterMode printerMode) override;
- QPrintDevice createPrintDevice(const QString &id) Q_DECL_OVERRIDE;
- QStringList availablePrintDeviceIds() const Q_DECL_OVERRIDE;
- QString defaultPrintDeviceId() const Q_DECL_OVERRIDE;
+ QPrintDevice createPrintDevice(const QString &id) override;
+ QStringList availablePrintDeviceIds() const override;
+ QString defaultPrintDeviceId() const override;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/cocoa/qcocoascreen.h b/src/plugins/platforms/cocoa/qcocoascreen.h
new file mode 100644
index 0000000000..3d59c3de79
--- /dev/null
+++ b/src/plugins/platforms/cocoa/qcocoascreen.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOCOASCREEN_H
+#define QCOCOASCREEN_H
+
+#include <AppKit/AppKit.h>
+
+#include "qcocoacursor.h"
+
+#include <qpa/qplatformintegration.h>
+
+QT_BEGIN_NAMESPACE
+
+class QCocoaScreen : public QPlatformScreen
+{
+public:
+ QCocoaScreen(int screenIndex);
+ ~QCocoaScreen();
+
+ // ----------------------------------------------------
+ // Virtual methods overridden from QPlatformScreen
+ QPixmap grabWindow(WId window, int x, int y, int width, int height) const override;
+ QRect geometry() const override { return m_geometry; }
+ QRect availableGeometry() const override { return m_availableGeometry; }
+ int depth() const override { return m_depth; }
+ QImage::Format format() const override { return m_format; }
+ qreal devicePixelRatio() const override;
+ QSizeF physicalSize() const override { return m_physicalSize; }
+ QDpi logicalDpi() const override { return m_logicalDpi; }
+ qreal refreshRate() const override { return m_refreshRate; }
+ QString name() const override { return m_name; }
+ QPlatformCursor *cursor() const override { return m_cursor; }
+ QWindow *topLevelAt(const QPoint &point) const override;
+ QList<QPlatformScreen *> virtualSiblings() const override { return m_siblings; }
+ QPlatformScreen::SubpixelAntialiasingType subpixelAntialiasingTypeHint() const override;
+
+ // ----------------------------------------------------
+ // Additional methods
+ void setVirtualSiblings(const QList<QPlatformScreen *> &siblings) { m_siblings = siblings; }
+ NSScreen *nativeScreen() const;
+ void updateGeometry();
+
+ static QCocoaScreen *primaryScreen();
+
+ static CGPoint mapToNative(const QPointF &pos, QCocoaScreen *screen = QCocoaScreen::primaryScreen());
+ static CGRect mapToNative(const QRectF &rect, QCocoaScreen *screen = QCocoaScreen::primaryScreen());
+ static QPointF mapFromNative(CGPoint pos, QCocoaScreen *screen = QCocoaScreen::primaryScreen());
+ static QRectF mapFromNative(CGRect rect, QCocoaScreen *screen = QCocoaScreen::primaryScreen());
+
+public:
+ int m_screenIndex;
+ QRect m_geometry;
+ QRect m_availableGeometry;
+ QDpi m_logicalDpi;
+ qreal m_refreshRate;
+ int m_depth;
+ QString m_name;
+ QImage::Format m_format;
+ QSizeF m_physicalSize;
+ QCocoaCursor *m_cursor;
+ QList<QPlatformScreen *> m_siblings;
+};
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug debug, const QCocoaScreen *screen);
+#endif
+
+QT_END_NAMESPACE
+
+#endif
+
diff --git a/src/plugins/platforms/cocoa/qcocoascreen.mm b/src/plugins/platforms/cocoa/qcocoascreen.mm
new file mode 100644
index 0000000000..ed1b19cd53
--- /dev/null
+++ b/src/plugins/platforms/cocoa/qcocoascreen.mm
@@ -0,0 +1,309 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcocoascreen.h"
+
+#include "qcocoawindow.h"
+#include "qcocoahelpers.h"
+
+#include <QtCore/qcoreapplication.h>
+#include <QtGui/private/qcoregraphics_p.h>
+
+#include <IOKit/graphics/IOGraphicsLib.h>
+
+QT_BEGIN_NAMESPACE
+
+class QCoreTextFontEngine;
+class QFontEngineFT;
+
+QCocoaScreen::QCocoaScreen(int screenIndex)
+ : QPlatformScreen(), m_screenIndex(screenIndex), m_refreshRate(60.0)
+{
+ updateGeometry();
+ m_cursor = new QCocoaCursor;
+}
+
+QCocoaScreen::~QCocoaScreen()
+{
+ delete m_cursor;
+}
+
+NSScreen *QCocoaScreen::nativeScreen() const
+{
+ NSArray *screens = [NSScreen screens];
+
+ // Stale reference, screen configuration has changed
+ if (m_screenIndex < 0 || (NSUInteger)m_screenIndex >= [screens count])
+ return nil;
+
+ return [screens objectAtIndex:m_screenIndex];
+}
+
+static QString displayName(CGDirectDisplayID displayID)
+{
+ QIOType<io_iterator_t> iterator;
+ if (IOServiceGetMatchingServices(kIOMasterPortDefault,
+ IOServiceMatching("IODisplayConnect"), &iterator))
+ return QString();
+
+ QIOType<io_service_t> display;
+ while ((display = IOIteratorNext(iterator)) != 0)
+ {
+ NSDictionary *info = [(__bridge NSDictionary*)IODisplayCreateInfoDictionary(
+ display, kIODisplayOnlyPreferredName) autorelease];
+
+ if ([[info objectForKey:@kDisplayVendorID] longValue] != CGDisplayVendorNumber(displayID))
+ continue;
+
+ if ([[info objectForKey:@kDisplayProductID] longValue] != CGDisplayModelNumber(displayID))
+ continue;
+
+ if ([[info objectForKey:@kDisplaySerialNumber] longValue] != CGDisplaySerialNumber(displayID))
+ continue;
+
+ NSDictionary *localizedNames = [info objectForKey:@kDisplayProductName];
+ if (![localizedNames count])
+ break; // Correct screen, but no name in dictionary
+
+ return QString::fromNSString([localizedNames objectForKey:[[localizedNames allKeys] objectAtIndex:0]]);
+ }
+
+ return QString();
+}
+
+void QCocoaScreen::updateGeometry()
+{
+ NSScreen *nsScreen = nativeScreen();
+ if (!nsScreen)
+ return;
+
+ // The reference screen for the geometry is always the primary screen
+ QRectF primaryScreenGeometry = QRectF::fromCGRect([[NSScreen screens] firstObject].frame);
+ m_geometry = qt_mac_flip(QRectF::fromCGRect(nsScreen.frame), primaryScreenGeometry).toRect();
+ m_availableGeometry = qt_mac_flip(QRectF::fromCGRect(nsScreen.visibleFrame), primaryScreenGeometry).toRect();
+
+ m_format = QImage::Format_RGB32;
+ m_depth = NSBitsPerPixelFromDepth([nsScreen depth]);
+
+ NSDictionary *devDesc = [nsScreen deviceDescription];
+ CGDirectDisplayID dpy = [[devDesc objectForKey:@"NSScreenNumber"] unsignedIntValue];
+ CGSize size = CGDisplayScreenSize(dpy);
+ m_physicalSize = QSizeF(size.width, size.height);
+ m_logicalDpi.first = 72;
+ m_logicalDpi.second = 72;
+ CGDisplayModeRef displayMode = CGDisplayCopyDisplayMode(dpy);
+ float refresh = CGDisplayModeGetRefreshRate(displayMode);
+ CGDisplayModeRelease(displayMode);
+ if (refresh > 0)
+ m_refreshRate = refresh;
+
+ m_name = displayName(dpy);
+
+ QWindowSystemInterface::handleScreenGeometryChange(screen(), geometry(), availableGeometry());
+ QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(screen(), m_logicalDpi.first, m_logicalDpi.second);
+ QWindowSystemInterface::handleScreenRefreshRateChange(screen(), m_refreshRate);
+}
+
+qreal QCocoaScreen::devicePixelRatio() const
+{
+ QMacAutoReleasePool pool;
+ NSScreen *nsScreen = nativeScreen();
+ return qreal(nsScreen ? [nsScreen backingScaleFactor] : 1.0);
+}
+
+QPlatformScreen::SubpixelAntialiasingType QCocoaScreen::subpixelAntialiasingTypeHint() const
+{
+ QPlatformScreen::SubpixelAntialiasingType type = QPlatformScreen::subpixelAntialiasingTypeHint();
+ if (type == QPlatformScreen::Subpixel_None) {
+ // Every OSX machine has RGB pixels unless a peculiar or rotated non-Apple screen is attached
+ type = QPlatformScreen::Subpixel_RGB;
+ }
+ return type;
+}
+
+QWindow *QCocoaScreen::topLevelAt(const QPoint &point) const
+{
+ NSPoint screenPoint = mapToNative(point);
+
+ // Search (hit test) for the top-level window. [NSWidow windowNumberAtPoint:
+ // belowWindowWithWindowNumber] may return windows that are not interesting
+ // to Qt. The search iterates until a suitable window or no window is found.
+ NSInteger topWindowNumber = 0;
+ QWindow *window = 0;
+ do {
+ // Get the top-most window, below any previously rejected window.
+ topWindowNumber = [NSWindow windowNumberAtPoint:screenPoint
+ belowWindowWithWindowNumber:topWindowNumber];
+
+ // Continue the search if the window does not belong to this process.
+ NSWindow *nsWindow = [NSApp windowWithWindowNumber:topWindowNumber];
+ if (nsWindow == 0)
+ continue;
+
+ // Continue the search if the window does not belong to Qt.
+ if (![nsWindow conformsToProtocol:@protocol(QNSWindowProtocol)])
+ continue;
+
+ id<QNSWindowProtocol> proto = static_cast<id<QNSWindowProtocol> >(nsWindow);
+ QCocoaWindow *cocoaWindow = proto.platformWindow;
+ if (!cocoaWindow)
+ continue;
+ window = cocoaWindow->window();
+
+ // Continue the search if the window is not a top-level window.
+ if (!window->isTopLevel())
+ continue;
+
+ // Stop searching. The current window is the correct window.
+ break;
+ } while (topWindowNumber > 0);
+
+ return window;
+}
+
+QPixmap QCocoaScreen::grabWindow(WId window, int x, int y, int width, int height) const
+{
+ // TODO window should be handled
+ Q_UNUSED(window)
+
+ const int maxDisplays = 128; // 128 displays should be enough for everyone.
+ CGDirectDisplayID displays[maxDisplays];
+ CGDisplayCount displayCount;
+ CGRect cgRect;
+
+ if (width < 0 || height < 0) {
+ // get all displays
+ cgRect = CGRectInfinite;
+ } else {
+ cgRect = CGRectMake(x, y, width, height);
+ }
+ const CGDisplayErr err = CGGetDisplaysWithRect(cgRect, maxDisplays, displays, &displayCount);
+
+ if (err && displayCount == 0)
+ return QPixmap();
+
+ // calculate pixmap size
+ QSize windowSize(width, height);
+ if (width < 0 || height < 0) {
+ QRect windowRect;
+ for (uint i = 0; i < displayCount; ++i) {
+ const CGRect cgRect = CGDisplayBounds(displays[i]);
+ QRect qRect(cgRect.origin.x, cgRect.origin.y, cgRect.size.width, cgRect.size.height);
+ windowRect = windowRect.united(qRect);
+ }
+ if (width < 0)
+ windowSize.setWidth(windowRect.width());
+ if (height < 0)
+ windowSize.setHeight(windowRect.height());
+ }
+
+ QPixmap windowPixmap(windowSize * devicePixelRatio());
+ windowPixmap.fill(Qt::transparent);
+
+ for (uint i = 0; i < displayCount; ++i) {
+ const CGRect bounds = CGDisplayBounds(displays[i]);
+ int w = (width < 0 ? bounds.size.width : width) * devicePixelRatio();
+ int h = (height < 0 ? bounds.size.height : height) * devicePixelRatio();
+ QRect displayRect = QRect(x, y, w, h);
+ displayRect = displayRect.translated(qRound(-bounds.origin.x), qRound(-bounds.origin.y));
+ QCFType<CGImageRef> image = CGDisplayCreateImageForRect(displays[i],
+ CGRectMake(displayRect.x(), displayRect.y(), displayRect.width(), displayRect.height()));
+ QPixmap pix(w, h);
+ pix.fill(Qt::transparent);
+ CGRect rect = CGRectMake(0, 0, w, h);
+ QMacCGContext ctx(&pix);
+ qt_mac_drawCGImage(ctx, &rect, image);
+
+ QPainter painter(&windowPixmap);
+ painter.drawPixmap(0, 0, pix);
+ }
+ return windowPixmap;
+}
+
+/*!
+ The screen used as a reference for global window geometry
+*/
+QCocoaScreen *QCocoaScreen::primaryScreen()
+{
+ return static_cast<QCocoaScreen *>(QGuiApplication::primaryScreen()->handle());
+}
+
+CGPoint QCocoaScreen::mapToNative(const QPointF &pos, QCocoaScreen *screen)
+{
+ Q_ASSERT(screen);
+ return qt_mac_flip(pos, screen->geometry()).toCGPoint();
+}
+
+CGRect QCocoaScreen::mapToNative(const QRectF &rect, QCocoaScreen *screen)
+{
+ Q_ASSERT(screen);
+ return qt_mac_flip(rect, screen->geometry()).toCGRect();
+}
+
+QPointF QCocoaScreen::mapFromNative(CGPoint pos, QCocoaScreen *screen)
+{
+ Q_ASSERT(screen);
+ return qt_mac_flip(QPointF::fromCGPoint(pos), screen->geometry());
+}
+
+QRectF QCocoaScreen::mapFromNative(CGRect rect, QCocoaScreen *screen)
+{
+ Q_ASSERT(screen);
+ return qt_mac_flip(QRectF::fromCGRect(rect), screen->geometry());
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug debug, const QCocoaScreen *screen)
+{
+ QDebugStateSaver saver(debug);
+ debug.nospace();
+ debug << "QCocoaScreen(" << (const void *)screen;
+ if (screen) {
+ debug << ", index=" << screen->m_screenIndex;
+ debug << ", native=" << screen->nativeScreen();
+ debug << ", geometry=" << screen->geometry();
+ debug << ", dpr=" << screen->devicePixelRatio();
+ debug << ", name=" << screen->name();
+ }
+ debug << ')';
+ return debug;
+}
+#endif // !QT_NO_DEBUG_STREAM
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/cocoa/qcocoaservices.h b/src/plugins/platforms/cocoa/qcocoaservices.h
index c2ceede0f0..62a8891f96 100644
--- a/src/plugins/platforms/cocoa/qcocoaservices.h
+++ b/src/plugins/platforms/cocoa/qcocoaservices.h
@@ -47,8 +47,8 @@ QT_BEGIN_NAMESPACE
class QCocoaServices : public QPlatformServices
{
public:
- bool openUrl(const QUrl &url) Q_DECL_OVERRIDE;
- bool openDocument(const QUrl &url) Q_DECL_OVERRIDE;
+ bool openUrl(const QUrl &url) override;
+ bool openDocument(const QUrl &url) override;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/cocoa/qcocoasystemsettings.mm b/src/plugins/platforms/cocoa/qcocoasystemsettings.mm
index 9ddad7fc7a..7c6f879b18 100644
--- a/src/plugins/platforms/cocoa/qcocoasystemsettings.mm
+++ b/src/plugins/platforms/cocoa/qcocoasystemsettings.mm
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
@@ -45,49 +45,8 @@
#include <QtGui/qfont.h>
#include <QtGui/private/qcoregraphics_p.h>
-#include <Carbon/Carbon.h>
-
QT_BEGIN_NAMESPACE
-QBrush qt_mac_brushForTheme(ThemeBrush brush)
-{
- QMacAutoReleasePool pool;
-
- QCFType<CGColorRef> cgClr = 0;
- HIThemeBrushCreateCGColor(brush, &cgClr);
- return qt_mac_toQBrush(cgClr);
-}
-
-QColor qt_mac_colorForThemeTextColor(ThemeTextColor themeColor)
-{
- // No GetThemeTextColor in 64-bit mode, use hardcoded values:
- switch (themeColor) {
- case kThemeTextColorAlertActive:
- case kThemeTextColorTabFrontActive:
- case kThemeTextColorBevelButtonActive:
- case kThemeTextColorListView:
- case kThemeTextColorPlacardActive:
- case kThemeTextColorPopupButtonActive:
- case kThemeTextColorPopupLabelActive:
- case kThemeTextColorPushButtonActive:
- return Qt::black;
- case kThemeTextColorAlertInactive:
- case kThemeTextColorDialogInactive:
- case kThemeTextColorPlacardInactive:
- case kThemeTextColorPopupButtonInactive:
- case kThemeTextColorPopupLabelInactive:
- case kThemeTextColorPushButtonInactive:
- case kThemeTextColorTabFrontInactive:
- case kThemeTextColorBevelButtonInactive:
- case kThemeTextColorMenuItemDisabled:
- return QColor(127, 127, 127, 255);
- case kThemeTextColorMenuItemSelected:
- return Qt::white;
- default:
- return QColor(0, 0, 0, 255); // ### TODO: Sample color like Qt 4.
- }
-}
-
QPalette * qt_mac_createSystemPalette()
{
QColor qc;
@@ -119,7 +78,7 @@ QPalette * qt_mac_createSystemPalette()
palette->setBrush(QPalette::Shadow, background.darker(170));
- qc = qt_mac_colorForThemeTextColor(kThemeTextColorDialogActive);
+ qc = qt_mac_toQColor([NSColor controlTextColor]);
palette->setColor(QPalette::Active, QPalette::Text, qc);
palette->setColor(QPalette::Active, QPalette::WindowText, qc);
palette->setColor(QPalette::Active, QPalette::HighlightedText, qc);
@@ -127,7 +86,7 @@ QPalette * qt_mac_createSystemPalette()
palette->setColor(QPalette::Inactive, QPalette::WindowText, qc);
palette->setColor(QPalette::Inactive, QPalette::HighlightedText, qc);
- qc = qt_mac_colorForThemeTextColor(kThemeTextColorDialogInactive);
+ qc = qt_mac_toQColor([NSColor disabledControlTextColor]);
palette->setColor(QPalette::Disabled, QPalette::Text, qc);
palette->setColor(QPalette::Disabled, QPalette::WindowText, qc);
palette->setColor(QPalette::Disabled, QPalette::HighlightedText, qc);
@@ -136,60 +95,66 @@ QPalette * qt_mac_createSystemPalette()
}
struct QMacPaletteMap {
- inline QMacPaletteMap(QPlatformTheme::Palette p, ThemeBrush a, ThemeBrush i) :
+ inline QMacPaletteMap(QPlatformTheme::Palette p, NSColor *a, NSColor *i) :
paletteRole(p), active(a), inactive(i) { }
QPlatformTheme::Palette paletteRole;
- ThemeBrush active, inactive;
+ NSColor *active, *inactive;
};
+#define MAC_PALETTE_ENTRY(pal, active, inactive) \
+ QMacPaletteMap(pal, [NSColor active], [NSColor inactive])
static QMacPaletteMap mac_widget_colors[] = {
- QMacPaletteMap(QPlatformTheme::ToolButtonPalette, kThemeTextColorBevelButtonActive, kThemeTextColorBevelButtonInactive),
- QMacPaletteMap(QPlatformTheme::ButtonPalette, kThemeTextColorPushButtonActive, kThemeTextColorPushButtonInactive),
- QMacPaletteMap(QPlatformTheme::HeaderPalette, kThemeTextColorPushButtonActive, kThemeTextColorPushButtonInactive),
- QMacPaletteMap(QPlatformTheme::ComboBoxPalette, kThemeTextColorPopupButtonActive, kThemeTextColorPopupButtonInactive),
- QMacPaletteMap(QPlatformTheme::ItemViewPalette, kThemeTextColorListView, kThemeTextColorDialogInactive),
- QMacPaletteMap(QPlatformTheme::MessageBoxLabelPalette, kThemeTextColorAlertActive, kThemeTextColorAlertInactive),
- QMacPaletteMap(QPlatformTheme::TabBarPalette, kThemeTextColorTabFrontActive, kThemeTextColorTabFrontInactive),
- QMacPaletteMap(QPlatformTheme::LabelPalette, kThemeTextColorPlacardActive, kThemeTextColorPlacardInactive),
- QMacPaletteMap(QPlatformTheme::GroupBoxPalette, kThemeTextColorPlacardActive, kThemeTextColorPlacardInactive),
- QMacPaletteMap(QPlatformTheme::MenuPalette, kThemeTextColorMenuItemActive, kThemeTextColorMenuItemDisabled),
- QMacPaletteMap(QPlatformTheme::MenuBarPalette, kThemeTextColorMenuItemActive, kThemeTextColorMenuItemDisabled),
- //### TODO: The zeros below gives white-on-black text.
- QMacPaletteMap(QPlatformTheme::TextEditPalette, 0, 0),
- QMacPaletteMap(QPlatformTheme::TextLineEditPalette, 0, 0),
- QMacPaletteMap(QPlatformTheme::NPalettes, 0, 0) };
+ MAC_PALETTE_ENTRY(QPlatformTheme::ToolButtonPalette, controlTextColor, disabledControlTextColor),
+ MAC_PALETTE_ENTRY(QPlatformTheme::ButtonPalette, controlTextColor, disabledControlTextColor),
+ MAC_PALETTE_ENTRY(QPlatformTheme::HeaderPalette, headerTextColor, disabledControlTextColor),
+ MAC_PALETTE_ENTRY(QPlatformTheme::ComboBoxPalette, controlTextColor, disabledControlTextColor),
+ MAC_PALETTE_ENTRY(QPlatformTheme::ItemViewPalette, textColor, disabledControlTextColor),
+ MAC_PALETTE_ENTRY(QPlatformTheme::MessageBoxLabelPalette, textColor, disabledControlTextColor),
+ MAC_PALETTE_ENTRY(QPlatformTheme::TabBarPalette, controlTextColor, disabledControlTextColor),
+ MAC_PALETTE_ENTRY(QPlatformTheme::LabelPalette, textColor, disabledControlTextColor),
+ MAC_PALETTE_ENTRY(QPlatformTheme::GroupBoxPalette, textColor, disabledControlTextColor),
+ MAC_PALETTE_ENTRY(QPlatformTheme::MenuPalette, controlTextColor, disabledControlTextColor),
+ MAC_PALETTE_ENTRY(QPlatformTheme::MenuBarPalette, controlTextColor, disabledControlTextColor),
+ MAC_PALETTE_ENTRY(QPlatformTheme::TextEditPalette, textColor, disabledControlTextColor),
+ MAC_PALETTE_ENTRY(QPlatformTheme::TextLineEditPalette, textColor, disabledControlTextColor)
+};
+#undef MAC_PALETTE_ENTRY
+
+static const int mac_widget_colors_count = sizeof(mac_widget_colors) / sizeof(mac_widget_colors[0]);
QHash<QPlatformTheme::Palette, QPalette*> qt_mac_createRolePalettes()
{
QHash<QPlatformTheme::Palette, QPalette*> palettes;
QColor qc;
- for (int i = 0; mac_widget_colors[i].paletteRole != QPlatformTheme::NPalettes; i++) {
+ for (int i = 0; i < mac_widget_colors_count; i++) {
QPalette &pal = *qt_mac_createSystemPalette();
if (mac_widget_colors[i].active != 0) {
- qc = qt_mac_colorForThemeTextColor(mac_widget_colors[i].active);
+ qc = qt_mac_toQColor(mac_widget_colors[i].active);
pal.setColor(QPalette::Active, QPalette::Text, qc);
pal.setColor(QPalette::Inactive, QPalette::Text, qc);
pal.setColor(QPalette::Active, QPalette::WindowText, qc);
pal.setColor(QPalette::Inactive, QPalette::WindowText, qc);
pal.setColor(QPalette::Active, QPalette::HighlightedText, qc);
pal.setColor(QPalette::Inactive, QPalette::HighlightedText, qc);
- qc = qt_mac_colorForThemeTextColor(mac_widget_colors[i].inactive);
+ qc = qt_mac_toQColor(mac_widget_colors[i].inactive);
pal.setColor(QPalette::Disabled, QPalette::Text, qc);
pal.setColor(QPalette::Disabled, QPalette::WindowText, qc);
pal.setColor(QPalette::Disabled, QPalette::HighlightedText, qc);
}
if (mac_widget_colors[i].paletteRole == QPlatformTheme::MenuPalette
|| mac_widget_colors[i].paletteRole == QPlatformTheme::MenuBarPalette) {
- pal.setBrush(QPalette::Background, qt_mac_brushForTheme(kThemeBrushMenuBackground));
- qc = qt_mac_colorForThemeTextColor(kThemeTextColorMenuItemActive);
+ pal.setBrush(QPalette::Highlight, qt_mac_toQColor([NSColor selectedMenuItemColor]));
+ qc = qt_mac_toQColor([NSColor labelColor]);
pal.setBrush(QPalette::ButtonText, qc);
- qc = qt_mac_colorForThemeTextColor(kThemeTextColorMenuItemSelected);
+ pal.setBrush(QPalette::Text, qc);
+ qc = qt_mac_toQColor([NSColor selectedMenuItemTextColor]);
pal.setBrush(QPalette::HighlightedText, qc);
- qc = qt_mac_colorForThemeTextColor(kThemeTextColorMenuItemDisabled);
+ qc = qt_mac_toQColor([NSColor disabledControlTextColor]);
pal.setBrush(QPalette::Disabled, QPalette::Text, qc);
} else if ((mac_widget_colors[i].paletteRole == QPlatformTheme::ButtonPalette)
- || (mac_widget_colors[i].paletteRole == QPlatformTheme::HeaderPalette)) {
+ || (mac_widget_colors[i].paletteRole == QPlatformTheme::HeaderPalette)
+ || (mac_widget_colors[i].paletteRole == QPlatformTheme::TabBarPalette)) {
pal.setColor(QPalette::Disabled, QPalette::ButtonText,
pal.color(QPalette::Disabled, QPalette::Text));
pal.setColor(QPalette::Inactive, QPalette::ButtonText,
diff --git a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.h b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.h
index d542a3cb8f..2f1a1e42a9 100644
--- a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.h
+++ b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.h
@@ -57,17 +57,17 @@ class Q_GUI_EXPORT QCocoaSystemTrayIcon : public QPlatformSystemTrayIcon
public:
QCocoaSystemTrayIcon() : m_sys(0) {}
- void init() Q_DECL_OVERRIDE;
- void cleanup() Q_DECL_OVERRIDE;
- void updateIcon(const QIcon &icon) Q_DECL_OVERRIDE;
- void updateToolTip(const QString &toolTip) Q_DECL_OVERRIDE;
- void updateMenu(QPlatformMenu *menu) Q_DECL_OVERRIDE;
- QRect geometry() const Q_DECL_OVERRIDE;
+ void init() override;
+ void cleanup() override;
+ void updateIcon(const QIcon &icon) override;
+ void updateToolTip(const QString &toolTip) override;
+ void updateMenu(QPlatformMenu *menu) override;
+ QRect geometry() const override;
void showMessage(const QString &title, const QString &msg,
- const QIcon& icon, MessageIcon iconType, int secs) Q_DECL_OVERRIDE;
+ const QIcon& icon, MessageIcon iconType, int secs) override;
- bool isSystemTrayAvailable() const Q_DECL_OVERRIDE;
- bool supportsMessages() const Q_DECL_OVERRIDE;
+ bool isSystemTrayAvailable() const override;
+ bool supportsMessages() const override;
private:
QSystemTrayIconSys *m_sys;
diff --git a/src/plugins/platforms/cocoa/qcocoatheme.h b/src/plugins/platforms/cocoa/qcocoatheme.h
index 27c071a8cd..69eaf8db56 100644
--- a/src/plugins/platforms/cocoa/qcocoatheme.h
+++ b/src/plugins/platforms/cocoa/qcocoatheme.h
@@ -56,25 +56,25 @@ public:
void reset();
- QPlatformMenuItem* createPlatformMenuItem() const Q_DECL_OVERRIDE;
- QPlatformMenu* createPlatformMenu() const Q_DECL_OVERRIDE;
- QPlatformMenuBar* createPlatformMenuBar() const Q_DECL_OVERRIDE;
+ QPlatformMenuItem* createPlatformMenuItem() const override;
+ QPlatformMenu* createPlatformMenu() const override;
+ QPlatformMenuBar* createPlatformMenuBar() const override;
#ifndef QT_NO_SYSTEMTRAYICON
- QPlatformSystemTrayIcon *createPlatformSystemTrayIcon() const Q_DECL_OVERRIDE;
+ QPlatformSystemTrayIcon *createPlatformSystemTrayIcon() const override;
#endif
- bool usePlatformNativeDialog(DialogType dialogType) const Q_DECL_OVERRIDE;
- QPlatformDialogHelper *createPlatformDialogHelper(DialogType dialogType) const Q_DECL_OVERRIDE;
+ bool usePlatformNativeDialog(DialogType dialogType) const override;
+ QPlatformDialogHelper *createPlatformDialogHelper(DialogType dialogType) const override;
- const QPalette *palette(Palette type = SystemPalette) const Q_DECL_OVERRIDE;
- const QFont *font(Font type = SystemFont) const Q_DECL_OVERRIDE;
- QPixmap standardPixmap(StandardPixmap sp, const QSizeF &size) const Q_DECL_OVERRIDE;
+ const QPalette *palette(Palette type = SystemPalette) const override;
+ const QFont *font(Font type = SystemFont) const override;
+ QPixmap standardPixmap(StandardPixmap sp, const QSizeF &size) const override;
QIcon fileIcon(const QFileInfo &fileInfo, QPlatformTheme::IconOptions options = 0) const override;
- QVariant themeHint(ThemeHint hint) const Q_DECL_OVERRIDE;
- QString standardButtonText(int button) const Q_DECL_OVERRIDE;
- QKeySequence standardButtonShortcut(int button) const Q_DECL_OVERRIDE;
+ QVariant themeHint(ThemeHint hint) const override;
+ QString standardButtonText(int button) const override;
+ QKeySequence standardButtonShortcut(int button) const override;
static const char *name;
diff --git a/src/plugins/platforms/cocoa/qcocoatheme.mm b/src/plugins/platforms/cocoa/qcocoatheme.mm
index a1c1b379f7..93f0400916 100644
--- a/src/plugins/platforms/cocoa/qcocoatheme.mm
+++ b/src/plugins/platforms/cocoa/qcocoatheme.mm
@@ -55,6 +55,7 @@
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/private/qcoregraphics_p.h>
#include <QtGui/qpainter.h>
+#include <QtGui/qtextformat.h>
#include <QtFontDatabaseSupport/private/qcoretextfontdatabase_p.h>
#include <QtThemeSupport/private/qabstractfileiconengine_p.h>
#include <qpa/qplatformdialoghelper.h>
@@ -97,7 +98,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QCocoaThemeNotificationReceiver);
{
Q_UNUSED(notification);
mPrivate->reset();
- QWindowSystemInterface::handleThemeChange(Q_NULLPTR);
+ QWindowSystemInterface::handleThemeChange(nullptr);
}
@end
@@ -126,7 +127,7 @@ QCocoaTheme::~QCocoaTheme()
void QCocoaTheme::reset()
{
delete m_systemPalette;
- m_systemPalette = Q_NULLPTR;
+ m_systemPalette = nullptr;
qDeleteAll(m_palettes);
m_palettes.clear();
}
@@ -274,7 +275,7 @@ QPixmap QCocoaTheme::standardPixmap(StandardPixmap sp, const QSizeF &size) const
}
if (iconType != 0) {
QPixmap pixmap;
- IconRef icon = Q_NULLPTR;
+ IconRef icon = nullptr;
GetIconRef(kOnSystemDisk, kSystemIconsCreator, iconType, &icon);
if (icon) {
@@ -341,9 +342,13 @@ QVariant QCocoaTheme::themeHint(ThemeHint hint) const
case IconPixmapSizes:
return QVariant::fromValue(QCocoaFileIconEngine::availableIconSizes());
case QPlatformTheme::PasswordMaskCharacter:
- return QVariant(QChar(kBulletUnicode));
+ return QVariant(QChar(0x2022));
case QPlatformTheme::UiEffects:
return QVariant(int(HoverEffect));
+ case QPlatformTheme::SpellCheckUnderlineStyle:
+ return QVariant(int(QTextCharFormat::DotLine));
+ case QPlatformTheme::UseFullScreenForPopupMenu:
+ return QVariant(bool([[NSApplication sharedApplication] presentationOptions] & NSApplicationPresentationFullScreen));
default:
break;
}
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.h b/src/plugins/platforms/cocoa/qcocoawindow.h
index aa8fffdf7e..fb91c53a7a 100644
--- a/src/plugins/platforms/cocoa/qcocoawindow.h
+++ b/src/plugins/platforms/cocoa/qcocoawindow.h
@@ -50,82 +50,15 @@
#include "qcocoaglcontext.h"
#endif
#include "qnsview.h"
+#include "qnswindow.h"
#include "qt_mac_p.h"
-QT_FORWARD_DECLARE_CLASS(QCocoaWindow)
-
-@class QT_MANGLE_NAMESPACE(QNSWindowHelper);
-
-// @compatibility_alias doesn't work with protocols
-#define QNSWindowProtocol QT_MANGLE_NAMESPACE(QNSWindowProtocol)
-
-@protocol QNSWindowProtocol
-
-@property (nonatomic, readonly) QT_MANGLE_NAMESPACE(QNSWindowHelper) *helper;
-
-- (void)superSendEvent:(NSEvent *)theEvent;
-- (void)closeAndRelease;
-
-@end
-
-typedef NSWindow<QNSWindowProtocol> QCocoaNSWindow;
-
-@interface QT_MANGLE_NAMESPACE(QNSWindowHelper) : NSObject
-{
- QCocoaNSWindow *_window;
- QPointer<QCocoaWindow> _platformWindow;
- BOOL _grabbingMouse;
- BOOL _releaseOnMouseUp;
-}
-
-@property (nonatomic, readonly) QCocoaNSWindow *window;
-@property (nonatomic, readonly) QCocoaWindow *platformWindow;
-@property (nonatomic) BOOL grabbingMouse;
-@property (nonatomic) BOOL releaseOnMouseUp;
-
-- (id)initWithNSWindow:(QCocoaNSWindow *)window platformWindow:(QCocoaWindow *)platformWindow;
-- (void)handleWindowEvent:(NSEvent *)theEvent;
-- (void) clearWindow;
-
-@end
-
-QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSWindowHelper);
-
-@interface QT_MANGLE_NAMESPACE(QNSWindow) : NSWindow<QNSWindowProtocol>
-{
- QNSWindowHelper *_helper;
-}
-
-@property (nonatomic, readonly) QNSWindowHelper *helper;
-
-- (id)initWithContentRect:(NSRect)contentRect
- screen:(NSScreen*)screen
- styleMask:(NSUInteger)windowStyle
- qPlatformWindow:(QCocoaWindow *)qpw;
-
-@end
-
-QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSWindow);
-
-@interface QT_MANGLE_NAMESPACE(QNSPanel) : NSPanel<QNSWindowProtocol>
-{
- QNSWindowHelper *_helper;
-}
-
-@property (nonatomic, readonly) QNSWindowHelper *helper;
-
-- (id)initWithContentRect:(NSRect)contentRect
- screen:(NSScreen*)screen
- styleMask:(NSUInteger)windowStyle
- qPlatformWindow:(QCocoaWindow *)qpw;
-
-@end
-
-QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSPanel);
+QT_BEGIN_NAMESPACE
-@class QT_MANGLE_NAMESPACE(QNSWindowDelegate);
+#ifndef QT_NO_DEBUG_STREAM
+class QDebug;
+#endif
-QT_BEGIN_NAMESPACE
// QCocoaWindow
//
// QCocoaWindow is an NSView (not an NSWindow!) in the sense
@@ -162,50 +95,51 @@ public:
QCocoaWindow(QWindow *tlw, WId nativeHandle = 0);
~QCocoaWindow();
- void setGeometry(const QRect &rect) Q_DECL_OVERRIDE;
- QRect geometry() const Q_DECL_OVERRIDE;
+ void initialize() override;
+
+ void setGeometry(const QRect &rect) override;
+ QRect geometry() const override;
void setCocoaGeometry(const QRect &rect);
- void clipChildWindows();
- void clipWindow(const NSRect &clipRect);
- void show(bool becauseOfAncestor = false);
- void hide(bool becauseOfAncestor = false);
- void setVisible(bool visible) Q_DECL_OVERRIDE;
- void setWindowFlags(Qt::WindowFlags flags) Q_DECL_OVERRIDE;
- void setWindowState(Qt::WindowState state) Q_DECL_OVERRIDE;
- void setWindowTitle(const QString &title) Q_DECL_OVERRIDE;
- void setWindowFilePath(const QString &filePath) Q_DECL_OVERRIDE;
- void setWindowIcon(const QIcon &icon) Q_DECL_OVERRIDE;
- void setAlertState(bool enabled) Q_DECL_OVERRIDE;
- bool isAlertState() const Q_DECL_OVERRIDE;
- void raise() Q_DECL_OVERRIDE;
- void lower() Q_DECL_OVERRIDE;
- bool isExposed() const Q_DECL_OVERRIDE;
+
+ void setVisible(bool visible) override;
+ void setWindowFlags(Qt::WindowFlags flags) override;
+ void setWindowState(Qt::WindowStates state) override;
+ void setWindowTitle(const QString &title) override;
+ void setWindowFilePath(const QString &filePath) override;
+ void setWindowIcon(const QIcon &icon) override;
+ void setAlertState(bool enabled) override;
+ bool isAlertState() const override;
+ void raise() override;
+ void lower() override;
+ bool isExposed() const override;
bool isOpaque() const;
- void propagateSizeHints() Q_DECL_OVERRIDE;
- void setOpacity(qreal level) Q_DECL_OVERRIDE;
- void setMask(const QRegion &region) Q_DECL_OVERRIDE;
- bool setKeyboardGrabEnabled(bool grab) Q_DECL_OVERRIDE;
- bool setMouseGrabEnabled(bool grab) Q_DECL_OVERRIDE;
- QMargins frameMargins() const Q_DECL_OVERRIDE;
- QSurfaceFormat format() const Q_DECL_OVERRIDE;
+ void propagateSizeHints() override;
+ void setOpacity(qreal level) override;
+ void setMask(const QRegion &region) override;
+ bool setKeyboardGrabEnabled(bool grab) override;
+ bool setMouseGrabEnabled(bool grab) override;
+ QMargins frameMargins() const override;
+ QSurfaceFormat format() const override;
- bool isForeignWindow() const Q_DECL_OVERRIDE;
+ bool isForeignWindow() const override;
- void requestActivateWindow() Q_DECL_OVERRIDE;
+ void requestUpdate() override;
+ void requestActivateWindow() override;
- WId winId() const Q_DECL_OVERRIDE;
- void setParent(const QPlatformWindow *window) Q_DECL_OVERRIDE;
+ WId winId() const override;
+ void setParent(const QPlatformWindow *window) override;
NSView *view() const;
NSWindow *nativeWindow() const;
void setEmbeddedInForeignView(bool subwindow);
+ Q_NOTIFICATION_HANDLER(NSViewFrameDidChangeNotification) void viewDidChangeFrame();
+ Q_NOTIFICATION_HANDLER(NSViewGlobalFrameDidChangeNotification) void viewDidChangeGlobalFrame();
+
Q_NOTIFICATION_HANDLER(NSWindowWillMoveNotification) void windowWillMove();
Q_NOTIFICATION_HANDLER(NSWindowDidMoveNotification) void windowDidMove();
Q_NOTIFICATION_HANDLER(NSWindowDidResizeNotification) void windowDidResize();
- Q_NOTIFICATION_HANDLER(NSViewFrameDidChangeNotification) void viewDidChangeFrame();
- Q_NOTIFICATION_HANDLER(NSViewGlobalFrameDidChangeNotification) void viewDidChangeGlobalFrame();
Q_NOTIFICATION_HANDLER(NSWindowDidEndLiveResizeNotification) void windowDidEndLiveResize();
Q_NOTIFICATION_HANDLER(NSWindowDidBecomeKeyNotification) void windowDidBecomeKey();
Q_NOTIFICATION_HANDLER(NSWindowDidResignKeyNotification) void windowDidResignKey();
@@ -215,8 +149,8 @@ public:
Q_NOTIFICATION_HANDLER(NSWindowDidEnterFullScreenNotification) void windowDidEnterFullScreen();
Q_NOTIFICATION_HANDLER(NSWindowWillExitFullScreenNotification) void windowWillExitFullScreen();
Q_NOTIFICATION_HANDLER(NSWindowDidExitFullScreenNotification) void windowDidExitFullScreen();
- Q_NOTIFICATION_HANDLER(NSWindowDidOrderOffScreenNotification) void windowDidOrderOffScreen();
Q_NOTIFICATION_HANDLER(NSWindowDidOrderOnScreenAndFinishAnimatingNotification) void windowDidOrderOnScreen();
+ Q_NOTIFICATION_HANDLER(NSWindowDidOrderOffScreenNotification) void windowDidOrderOffScreen();
Q_NOTIFICATION_HANDLER(NSWindowDidChangeOcclusionStateNotification) void windowDidChangeOcclusionState();
Q_NOTIFICATION_HANDLER(NSWindowDidChangeScreenNotification) void windowDidChangeScreen();
Q_NOTIFICATION_HANDLER(NSWindowWillCloseNotification) void windowWillClose();
@@ -224,11 +158,8 @@ public:
bool windowShouldClose();
bool windowIsPopupType(Qt::WindowType type = Qt::Widget) const;
- void reportCurrentWindowState(bool unconditionally = false);
-
NSInteger windowLevel(Qt::WindowFlags flags);
NSUInteger windowStyleMask(Qt::WindowFlags flags);
- void setWindowShadow(Qt::WindowFlags flags);
void setWindowZoomButton(Qt::WindowFlags flags);
#ifndef QT_NO_OPENGL
@@ -236,17 +167,15 @@ public:
QCocoaGLContext *currentContext() const;
#endif
- bool setWindowModified(bool modified) Q_DECL_OVERRIDE;
+ bool setWindowModified(bool modified) override;
- void setFrameStrutEventsEnabled(bool enabled) Q_DECL_OVERRIDE;
- bool frameStrutEventsEnabled() const Q_DECL_OVERRIDE
+ void setFrameStrutEventsEnabled(bool enabled) override;
+ bool frameStrutEventsEnabled() const override
{ return m_frameStrutEventsEnabled; }
void setMenubar(QCocoaMenuBar *mb);
QCocoaMenuBar *menubar() const;
- NSCursor *effectiveWindowCursor() const;
- void applyEffectiveWindowCursor();
void setWindowCursor(NSCursor *cursor);
void registerTouch(bool enable);
@@ -255,14 +184,10 @@ public:
void setContentBorderAreaEnabled(quintptr identifier, bool enable);
void setContentBorderEnabled(bool enable);
bool testContentBorderAreaPosition(int position) const;
- void applyContentBorderThickness(NSWindow *window);
+ void applyContentBorderThickness(NSWindow *window = nullptr);
void updateNSToolbar();
- qreal devicePixelRatio() const Q_DECL_OVERRIDE;
- bool isWindowExposable();
- void exposeWindow();
- void obscureWindow();
- void updateExposedGeometry();
+ qreal devicePixelRatio() const override;
QWindow *childWindowAt(QPoint windowPoint);
bool shouldRefuseKeyWindowAndFirstResponder();
@@ -274,7 +199,6 @@ public:
ParentChanged = 0x1,
MissingWindow = 0x2,
WindowModalityChanged = 0x4,
- ChildNSWindowChanged = 0x8,
ContentViewChanged = 0x10,
PanelChanged = 0x20,
};
@@ -282,20 +206,11 @@ public:
Q_FLAG(RecreationReasons)
protected:
- bool isChildNSWindow() const;
- bool isContentView() const;
-
- void foreachChildNSWindow(void (^block)(QCocoaWindow *));
-
void recreateWindowIfNeeded();
- QCocoaNSWindow *createNSWindow(bool shouldBeChildNSWindow, bool shouldBePanel);
-
- QRect nativeWindowGeometry() const;
- void reinsertChildWindow(QCocoaWindow *child);
- void removeChildWindow(QCocoaWindow *child);
+ QCocoaNSWindow *createNSWindow(bool shouldBePanel);
Qt::WindowState windowState() const;
- void applyWindowState(Qt::WindowState newState);
+ void applyWindowState(Qt::WindowStates newState);
void toggleMaximized();
void toggleFullScreen();
bool isTransitioningToFullScreen() const;
@@ -305,24 +220,34 @@ public: // for QNSView
friend class QCocoaBackingStore;
friend class QCocoaNativeInterface;
+ bool isContentView() const;
+
bool alwaysShowToolWindow() const;
void removeMonitor();
+ enum HandleFlags {
+ NoHandleFlags = 0,
+ HandleUnconditionally = 1
+ };
+
+ void handleGeometryChange();
+ void handleWindowStateChanged(HandleFlags flags = NoHandleFlags);
+ void handleExposeEvent(const QRegion &region);
+
NSView *m_view;
QCocoaNSWindow *m_nsWindow;
- QPointer<QCocoaWindow> m_forwardWindow;
// TODO merge to one variable if possible
bool m_viewIsEmbedded; // true if the m_view is actually embedded in a "foreign" NSView hiearchy
bool m_viewIsToBeEmbedded; // true if the m_view is intended to be embedded in a "foreign" NSView hiearchy
Qt::WindowFlags m_windowFlags;
- Qt::WindowState m_lastReportedWindowState;
+ Qt::WindowStates m_lastReportedWindowState;
Qt::WindowModality m_windowModality;
QPointer<QWindow> m_enterLeaveTargetWindow;
bool m_windowUnderMouse;
- bool m_inConstructor;
+ bool m_initialized;
bool m_inSetVisible;
bool m_inSetGeometry;
bool m_inSetStyleMask;
@@ -330,18 +255,14 @@ public: // for QNSView
QCocoaGLContext *m_glContext;
#endif
QCocoaMenuBar *m_menubar;
- NSCursor *m_windowCursor;
+
+ bool m_needsInvalidateShadow;
bool m_hasModalSession;
bool m_frameStrutEventsEnabled;
- bool m_geometryUpdateExposeAllowed;
- bool m_isExposed;
- QRect m_exposedGeometry;
- qreal m_exposedDevicePixelRatio;
+ QRect m_exposedRect;
int m_registerTouchCount;
bool m_resizableTransientParent;
- bool m_hiddenByClipping;
- bool m_hiddenByAncestor;
static const int NoAlertRequest;
NSInteger m_alertRequest;
@@ -362,10 +283,12 @@ public: // for QNSView
};
QHash<quintptr, BorderRange> m_contentBorderAreas; // identifer -> uppper/lower
QHash<quintptr, bool> m_enabledContentBorderAreas; // identifer -> enabled state (true/false)
-
- bool m_hasWindowFilePath;
};
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug debug, const QCocoaWindow *window);
+#endif
+
QT_END_NAMESPACE
#endif // QCOCOAWINDOW_H
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm
index aeeef55598..562872a300 100644
--- a/src/plugins/platforms/cocoa/qcocoawindow.mm
+++ b/src/plugins/platforms/cocoa/qcocoawindow.mm
@@ -38,6 +38,7 @@
****************************************************************************/
#include "qcocoawindow.h"
#include "qcocoaintegration.h"
+#include "qcocoascreen.h"
#include "qnswindowdelegate.h"
#include "qcocoaeventdispatcher.h"
#ifndef QT_NO_OPENGL
@@ -46,6 +47,7 @@
#include "qcocoahelpers.h"
#include "qcocoanativeinterface.h"
#include "qnsview.h"
+#include "qnswindow.h"
#include <QtCore/qfileinfo.h>
#include <QtCore/private/qcore_mac_p.h>
#include <qwindow.h>
@@ -53,34 +55,22 @@
#include <qpa/qwindowsysteminterface.h>
#include <qpa/qplatformscreen.h>
#include <QtGui/private/qcoregraphics_p.h>
+#include <QtGui/private/qhighdpiscaling_p.h>
#include <AppKit/AppKit.h>
+#include <QuartzCore/QuartzCore.h>
#include <QDebug>
#include <vector>
+QT_BEGIN_NAMESPACE
+
enum {
defaultWindowWidth = 160,
defaultWindowHeight = 160
};
-static bool isMouseEvent(NSEvent *ev)
-{
- switch ([ev type]) {
- case NSLeftMouseDown:
- case NSLeftMouseUp:
- case NSRightMouseDown:
- case NSRightMouseUp:
- case NSMouseMoved:
- case NSLeftMouseDragged:
- case NSRightMouseDragged:
- return true;
- default:
- return false;
- }
-}
-
static void qt_closePopups()
{
while (QCocoaWindow *popup = QCocoaIntegration::instance()->popPopupWindow()) {
@@ -89,377 +79,7 @@ static void qt_closePopups()
}
}
-
-// @compatibility_alias doesn't work with categories or their methods
-#define FullScreenProperty QT_MANGLE_NAMESPACE(FullScreenProperty)
-#define qt_fullScreen QT_MANGLE_NAMESPACE(qt_fullScreen)
-
-@interface NSWindow (FullScreenProperty)
-@property(readonly) BOOL qt_fullScreen;
-@end
-
-@implementation NSWindow (FullScreenProperty)
-
-+ (void)load
-{
- NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
- [center addObserverForName:NSWindowDidEnterFullScreenNotification object:nil queue:nil
- usingBlock:^(NSNotification *notification) {
- objc_setAssociatedObject(notification.object, @selector(qt_fullScreen),
- [NSNumber numberWithBool:YES], OBJC_ASSOCIATION_RETAIN);
- }
- ];
- [center addObserverForName:NSWindowDidExitFullScreenNotification object:nil queue:nil
- usingBlock:^(NSNotification *notification) {
- objc_setAssociatedObject(notification.object, @selector(qt_fullScreen),
- nil, OBJC_ASSOCIATION_RETAIN);
- }
- ];
-}
-
-- (BOOL)qt_fullScreen
-{
- NSNumber *number = objc_getAssociatedObject(self, @selector(qt_fullScreen));
- return [number boolValue];
-}
-@end
-
-@implementation QNSWindowHelper
-
-@synthesize window = _window;
-@synthesize grabbingMouse = _grabbingMouse;
-@synthesize releaseOnMouseUp = _releaseOnMouseUp;
-
-- (QCocoaWindow *)platformWindow
-{
- return _platformWindow.data();
-}
-
-- (id)initWithNSWindow:(QCocoaNSWindow *)window platformWindow:(QCocoaWindow *)platformWindow
-{
- self = [super init];
- if (self) {
- _window = window;
- _platformWindow = platformWindow;
-
- _window.delegate = [[QNSWindowDelegate alloc] initWithQCocoaWindow:_platformWindow];
-
- // Prevent Cocoa from releasing the window on close. Qt
- // handles the close event asynchronously and we want to
- // make sure that m_nsWindow stays valid until the
- // QCocoaWindow is deleted by Qt.
- [_window setReleasedWhenClosed:NO];
- }
-
- return self;
-}
-
-- (void)handleWindowEvent:(NSEvent *)theEvent
-{
- QCocoaWindow *pw = self.platformWindow;
- if (pw && pw->m_forwardWindow) {
- if (theEvent.type == NSLeftMouseUp || theEvent.type == NSLeftMouseDragged) {
- QNSView *forwardView = qnsview_cast(pw->view());
- if (theEvent.type == NSLeftMouseUp) {
- [forwardView mouseUp:theEvent];
- pw->m_forwardWindow.clear();
- } else {
- [forwardView mouseDragged:theEvent];
- }
- }
- if (pw->window()->isTopLevel() && theEvent.type == NSLeftMouseDown) {
- pw->m_forwardWindow.clear();
- }
- }
-
- if (theEvent.type == NSLeftMouseDown) {
- self.grabbingMouse = YES;
- } else if (theEvent.type == NSLeftMouseUp) {
- self.grabbingMouse = NO;
- if (self.releaseOnMouseUp) {
- [self detachFromPlatformWindow];
- [self.window release];
- return;
- }
- }
-
- // The call to -[NSWindow sendEvent] may result in the window being deleted
- // (e.g., when closing the window by pressing the title bar close button).
- [self retain];
- [self.window superSendEvent:theEvent];
- bool windowStillAlive = self.window != nil; // We need to read before releasing
- [self release];
- if (!windowStillAlive)
- return;
-
- if (!self.window.delegate)
- return; // Already detached, pending NSAppKitDefined event
-
- if (pw && pw->frameStrutEventsEnabled() && isMouseEvent(theEvent)) {
- NSPoint loc = [theEvent locationInWindow];
- NSRect windowFrame = [self.window convertRectFromScreen:[self.window frame]];
- NSRect contentFrame = [[self.window contentView] frame];
- if (NSMouseInRect(loc, windowFrame, NO) && !NSMouseInRect(loc, contentFrame, NO))
- [qnsview_cast(pw->view()) handleFrameStrutMouseEvent:theEvent];
- }
-}
-
-- (void)detachFromPlatformWindow
-{
- _platformWindow.clear();
- [self.window.delegate release];
- self.window.delegate = nil;
-}
-
-- (void)clearWindow
-{
- if (_window) {
- QCocoaEventDispatcher *cocoaEventDispatcher = qobject_cast<QCocoaEventDispatcher *>(QGuiApplication::instance()->eventDispatcher());
- if (cocoaEventDispatcher) {
- QCocoaEventDispatcherPrivate *cocoaEventDispatcherPrivate = static_cast<QCocoaEventDispatcherPrivate *>(QObjectPrivate::get(cocoaEventDispatcher));
- cocoaEventDispatcherPrivate->removeQueuedUserInputEvents([_window windowNumber]);
- }
-
- _window = nil;
- }
-}
-
-- (void)dealloc
-{
- _window = nil;
- _platformWindow.clear();
- [super dealloc];
-}
-
-@end
-
-@implementation QNSWindow
-
-@synthesize helper = _helper;
-
-- (id)initWithContentRect:(NSRect)contentRect
- screen:(NSScreen*)screen
- styleMask:(NSUInteger)windowStyle
- qPlatformWindow:(QCocoaWindow *)qpw
-{
- self = [super initWithContentRect:contentRect
- styleMask:windowStyle
- backing:NSBackingStoreBuffered
- defer:NO screen:screen]; // Deferring window creation breaks OpenGL (the GL context is
- // set up before the window is shown and needs a proper window)
-
- if (self) {
- _helper = [[QNSWindowHelper alloc] initWithNSWindow:self platformWindow:qpw];
- }
- return self;
-}
-
-- (BOOL)canBecomeKeyWindow
-{
- // Prevent child NSWindows from becoming the key window in
- // order keep the active apperance of the top-level window.
- QCocoaWindow *pw = self.helper.platformWindow;
- if (!pw || !pw->window()->isTopLevel())
- return NO;
-
- if (pw->shouldRefuseKeyWindowAndFirstResponder())
- return NO;
-
- // The default implementation returns NO for title-bar less windows,
- // override and return yes here to make sure popup windows such as
- // the combobox popup can become the key window.
- return YES;
-}
-
-- (BOOL)canBecomeMainWindow
-{
- BOOL canBecomeMain = YES; // By default, windows can become the main window
-
- // Windows with a transient parent (such as combobox popup windows)
- // cannot become the main window:
- QCocoaWindow *pw = self.helper.platformWindow;
- if (!pw || !pw->window()->isTopLevel() || pw->window()->transientParent())
- canBecomeMain = NO;
-
- return canBecomeMain;
-}
-
-- (void) sendEvent: (NSEvent*) theEvent
-{
- [self.helper handleWindowEvent:theEvent];
-}
-
-- (void)superSendEvent:(NSEvent *)theEvent
-{
- [super sendEvent:theEvent];
-}
-
-- (void)closeAndRelease
-{
- qCDebug(lcQpaCocoaWindow) << "closeAndRelease" << self;
-
- [self close];
-
- if (self.helper.grabbingMouse) {
- self.helper.releaseOnMouseUp = YES;
- } else {
- [self.helper detachFromPlatformWindow];
- [self release];
- }
-}
-
-- (void)dealloc
-{
- [_helper clearWindow];
- [_helper release];
- _helper = nil;
- [super dealloc];
-}
-
-@end
-
-@implementation QNSPanel
-
-@synthesize helper = _helper;
-
-+ (void)applicationActivationChanged:(NSNotification*)notification
-{
- const id sender = self;
- NSEnumerator<NSWindow*> *windowEnumerator = nullptr;
- NSApplication *application = [NSApplication sharedApplication];
-
-#if QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_12)
- if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSSierra) {
- // Unfortunately there's no NSWindowListOrderedBackToFront,
- // so we have to manually reverse the order using an array.
- NSMutableArray *windows = [[[NSMutableArray alloc] init] autorelease];
- [application enumerateWindowsWithOptions:NSWindowListOrderedFrontToBack
- usingBlock:^(NSWindow *window, BOOL *) {
- // For some reason AppKit will give us nil-windows, skip those
- if (!window)
- return;
-
- [(NSMutableArray*)windows addObject:window];
- }
- ];
-
- windowEnumerator = windows.reverseObjectEnumerator;
- } else
-#endif
- {
- // No way to get ordered list of windows, so fall back to unordered,
- // list, which typically corresponds to window creation order.
- windowEnumerator = application.windows.objectEnumerator;
- }
-
- for (NSWindow *window in windowEnumerator) {
- // We're meddling with normal and floating windows, so leave others alone
- if (!(window.level == NSNormalWindowLevel || window.level == NSFloatingWindowLevel))
- continue;
-
- // Windows that hide automatically will keep their NSFloatingWindowLevel,
- // and hence be on top of the window stack. We don't want to affect these
- // windows, as otherwise we might end up with key windows being ordered
- // behind these auto-hidden windows when activating the application by
- // clicking on a new tool window.
- if (window.hidesOnDeactivate)
- continue;
-
- if ([window conformsToProtocol:@protocol(QNSWindowProtocol)]) {
- QCocoaWindow *cocoaWindow = static_cast<id<QNSWindowProtocol>>(window).helper.platformWindow;
- window.level = notification.name == NSApplicationWillResignActiveNotification ?
- NSNormalWindowLevel : cocoaWindow->windowLevel(cocoaWindow->window()->flags());
- }
-
- // The documentation says that "when a window enters a new level, it’s ordered
- // in front of all its peers in that level", but that doesn't seem to be the
- // case in practice. To keep the order correct after meddling with the window
- // levels, we explicitly order each window to the front. Since we are iterating
- // the windows in back-to-front order, this is okey. The call also triggers AppKit
- // to re-evaluate the level in relation to windows from other applications,
- // working around an issue where our tool windows would stay on top of other
- // application windows if activation was transferred to another application by
- // clicking on it instead of via the application switcher or Dock. Finally, we
- // do this re-ordering for all windows (except auto-hiding ones), otherwise we would
- // end up triggering a bug in AppKit where the tool windows would disappear behind
- // the application window.
- [window orderFront:sender];
- }
-}
-
-- (id)initWithContentRect:(NSRect)contentRect
- screen:(NSScreen*)screen
- styleMask:(NSUInteger)windowStyle
- qPlatformWindow:(QCocoaWindow *)qpw
-{
- self = [super initWithContentRect:contentRect
- styleMask:windowStyle
- backing:NSBackingStoreBuffered
- defer:NO screen:screen]; // Deferring window creation breaks OpenGL (the GL context is
- // set up before the window is shown and needs a proper window)
-
- if (self) {
- _helper = [[QNSWindowHelper alloc] initWithNSWindow:self platformWindow:qpw];
-
- if (qpw->alwaysShowToolWindow()) {
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
- [center addObserver:[self class] selector:@selector(applicationActivationChanged:)
- name:NSApplicationWillResignActiveNotification object:nil];
- [center addObserver:[self class] selector:@selector(applicationActivationChanged:)
- name:NSApplicationWillBecomeActiveNotification object:nil];
- });
- }
- }
- return self;
-}
-
-- (BOOL)canBecomeKeyWindow
-{
- QCocoaWindow *pw = self.helper.platformWindow;
- if (!pw)
- return NO;
-
- if (pw->shouldRefuseKeyWindowAndFirstResponder())
- return NO;
-
- // Only tool or dialog windows should become key:
- Qt::WindowType type = pw->window()->type();
- if (type == Qt::Tool || type == Qt::Dialog)
- return YES;
-
- return NO;
-}
-
-- (void) sendEvent: (NSEvent*) theEvent
-{
- [self.helper handleWindowEvent:theEvent];
-}
-
-- (void)superSendEvent:(NSEvent *)theEvent
-{
- [super sendEvent:theEvent];
-}
-
-- (void)closeAndRelease
-{
- qCDebug(lcQpaCocoaWindow) << "closeAndRelease" << self;
-
- [self.helper detachFromPlatformWindow];
- [self close];
- [self release];
-}
-
-- (void)dealloc
-{
- [_helper clearWindow];
- [_helper release];
- _helper = nil;
- [super dealloc];
-}
-
-@end
+Q_LOGGING_CATEGORY(lcCocoaNotifications, "qt.qpa.cocoa.notifications");
static void qRegisterNotificationCallbacks()
{
@@ -480,38 +100,43 @@ static void qRegisterNotificationCallbacks()
[center addObserverForName:notificationName.toNSString() object:nil queue:nil
usingBlock:^(NSNotification *notification) {
- NSView *view = nullptr;
+ QVarLengthArray<QCocoaWindow *, 32> cocoaWindows;
if ([notification.object isKindOfClass:[NSWindow class]]) {
- NSWindow *window = notification.object;
- // Only top level NSWindows should notify their QNSViews
- if (window.parentWindow)
- return;
-
- if (!window.contentView)
- return;
-
- view = window.contentView;
+ NSWindow *nsWindow = notification.object;
+ for (const QWindow *window : QGuiApplication::allWindows()) {
+ if (QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow *>(window->handle()))
+ if (cocoaWindow->nativeWindow() == nsWindow)
+ cocoaWindows += cocoaWindow;
+ }
} else if ([notification.object isKindOfClass:[NSView class]]) {
- view = notification.object;
+ if (QNSView *qnsView = qnsview_cast(notification.object))
+ cocoaWindows += qnsView.platformWindow;
} else {
- qCWarning(lcQpaCocoaWindow) << "Unhandled notifcation"
+ qCWarning(lcCocoaNotifications) << "Unhandled notifcation"
<< notification.name << "for" << notification.object;
return;
}
- Q_ASSERT(view);
- QCocoaWindow *cocoaWindow = nullptr;
- if (QNSView *qnsView = qnsview_cast(view))
- cocoaWindow = qnsView.platformWindow;
+ if (lcCocoaNotifications().isDebugEnabled()) {
+ if (cocoaWindows.isEmpty()) {
+ qCDebug(lcCocoaNotifications) << "Could not find forwarding target for" <<
+ qPrintable(notificationName) << "from" << notification.object;
+ } else {
+ QVector<QCocoaWindow *> debugWindows;
+ for (QCocoaWindow *cocoaWindow : cocoaWindows)
+ debugWindows += cocoaWindow;
+ qCDebug(lcCocoaNotifications) << "Forwarding" << qPrintable(notificationName) <<
+ "to" << debugWindows;
+ }
+ }
// FIXME: Could be a foreign window, look up by iterating top level QWindows
- if (!cocoaWindow)
- return;
-
- if (!method.invoke(cocoaWindow, Qt::DirectConnection)) {
- qCWarning(lcQpaCocoaWindow) << "Failed to invoke NSNotification callback for"
- << notification.name << "on" << cocoaWindow;
+ for (QCocoaWindow *cocoaWindow : cocoaWindows) {
+ if (!method.invoke(cocoaWindow, Qt::DirectConnection)) {
+ qCWarning(lcQpaCocoaWindow) << "Failed to invoke NSNotification callback for"
+ << notification.name << "on" << cocoaWindow;
+ }
}
}];
}
@@ -520,8 +145,8 @@ Q_CONSTRUCTOR_FUNCTION(qRegisterNotificationCallbacks)
const int QCocoaWindow::NoAlertRequest = -1;
-QCocoaWindow::QCocoaWindow(QWindow *tlw, WId nativeHandle)
- : QPlatformWindow(tlw)
+QCocoaWindow::QCocoaWindow(QWindow *win, WId nativeHandle)
+ : QPlatformWindow(win)
, m_view(nil)
, m_nsWindow(0)
, m_viewIsEmbedded(false)
@@ -529,7 +154,7 @@ QCocoaWindow::QCocoaWindow(QWindow *tlw, WId nativeHandle)
, m_lastReportedWindowState(Qt::WindowNoState)
, m_windowModality(Qt::NonModal)
, m_windowUnderMouse(false)
- , m_inConstructor(true)
+ , m_initialized(false)
, m_inSetVisible(false)
, m_inSetGeometry(false)
, m_inSetStyleMask(false)
@@ -537,42 +162,45 @@ QCocoaWindow::QCocoaWindow(QWindow *tlw, WId nativeHandle)
, m_glContext(0)
#endif
, m_menubar(0)
- , m_windowCursor(0)
+ , m_needsInvalidateShadow(false)
, m_hasModalSession(false)
, m_frameStrutEventsEnabled(false)
- , m_geometryUpdateExposeAllowed(false)
- , m_isExposed(false)
, m_registerTouchCount(0)
, m_resizableTransientParent(false)
- , m_hiddenByClipping(false)
- , m_hiddenByAncestor(false)
, m_alertRequest(NoAlertRequest)
, monitor(nil)
, m_drawContentBorderGradient(false)
, m_topContentBorderThickness(0)
, m_bottomContentBorderThickness(0)
- , m_hasWindowFilePath(false)
{
qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::QCocoaWindow" << window();
- QMacAutoReleasePool pool;
-
if (nativeHandle) {
m_view = reinterpret_cast<NSView *>(nativeHandle);
[m_view retain];
- } else {
+ }
+}
+
+void QCocoaWindow::initialize()
+{
+ qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::initialize" << window();
+
+ QMacAutoReleasePool pool;
+
+ if (!m_view) {
m_view = [[QNSView alloc] initWithCocoaWindow:this];
// Enable high-dpi OpenGL for retina displays. Enabling has the side
// effect that Cocoa will start calling glViewport(0, 0, width, height),
// overriding any glViewport calls in application code. This is usually not a
// problem, except if the appilcation wants to have a "custom" viewport.
// (like the hellogl example)
- if (tlw->supportsOpenGL()) {
- BOOL enable = qt_mac_resolveOption(YES, tlw, "_q_mac_wantsBestResolutionOpenGLSurface",
+ if (window()->supportsOpenGL()) {
+ BOOL enable = qt_mac_resolveOption(YES, window(), "_q_mac_wantsBestResolutionOpenGLSurface",
"QT_MAC_WANTS_BEST_RESOLUTION_OPENGL_SURFACE");
[m_view setWantsBestResolutionOpenGLSurface:enable];
+ // See also QCocoaGLContext::makeCurrent for software renderer workarounds.
}
- BOOL enable = qt_mac_resolveOption(NO, tlw, "_q_mac_wantsLayer",
+ BOOL enable = qt_mac_resolveOption(NO, window(), "_q_mac_wantsLayer",
"QT_MAC_WANTS_LAYER");
[m_view setWantsLayer:enable];
}
@@ -580,10 +208,11 @@ QCocoaWindow::QCocoaWindow(QWindow *tlw, WId nativeHandle)
setGeometry(initialGeometry(window(), windowGeometry(), defaultWindowWidth, defaultWindowHeight));
recreateWindowIfNeeded();
- tlw->setGeometry(geometry());
- if (tlw->isTopLevel())
- setWindowIcon(tlw->icon());
- m_inConstructor = false;
+ window()->setGeometry(geometry());
+ if (window()->isTopLevel())
+ setWindowIcon(window()->icon());
+
+ m_initialized = true;
}
QCocoaWindow::~QCocoaWindow()
@@ -593,10 +222,7 @@ QCocoaWindow::~QCocoaWindow()
QMacAutoReleasePool pool;
[m_nsWindow makeFirstResponder:nil];
[m_nsWindow setContentView:nil];
- [m_nsWindow.helper detachFromPlatformWindow];
- if (m_view.window.parentWindow)
- [m_view.window.parentWindow removeChildWindow:m_view.window];
- else if ([m_view superview])
+ if ([m_view superview])
[m_view removeFromSuperview];
removeMonitor();
@@ -612,13 +238,8 @@ QCocoaWindow::~QCocoaWindow()
QCocoaIntegration::instance()->popupWindowStack()->removeAll(this);
}
- foreachChildNSWindow(^(QCocoaWindow *childWindow) {
- [m_nsWindow removeChildWindow:childWindow->m_nsWindow];
- });
-
[m_view release];
[m_nsWindow release];
- [m_windowCursor release];
}
QSurfaceFormat QCocoaWindow::format() const
@@ -666,7 +287,7 @@ QRect QCocoaWindow::geometry() const
NSPoint windowPoint = [m_view convertPoint:NSMakePoint(0, 0) toView:nil];
NSRect screenRect = [[m_view window] convertRectToScreen:NSMakeRect(windowPoint.x, windowPoint.y, 1, 1)];
NSPoint screenPoint = screenRect.origin;
- QPoint position = qt_mac_flipPoint(screenPoint).toPoint();
+ QPoint position = QCocoaScreen::mapFromNative(screenPoint).toPoint();
QSize size = QRectF::fromCGRect(NSRectToCGRect([m_view bounds])).toRect().size();
return QRect(position, size);
}
@@ -679,134 +300,29 @@ void QCocoaWindow::setCocoaGeometry(const QRect &rect)
qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::setCocoaGeometry" << window() << rect;
QMacAutoReleasePool pool;
+ QPlatformWindow::setGeometry(rect);
+
if (m_viewIsEmbedded) {
if (!isForeignWindow()) {
[m_view setFrame:NSMakeRect(0, 0, rect.width(), rect.height())];
- } else {
- QPlatformWindow::setGeometry(rect);
}
return;
}
- if (isChildNSWindow()) {
- QPlatformWindow::setGeometry(rect);
- NSWindow *parentNSWindow = m_view.window.parentWindow;
- NSRect parentWindowFrame = [parentNSWindow contentRectForFrameRect:parentNSWindow.frame];
- clipWindow(parentWindowFrame);
-
- // call this here: updateGeometry in qnsview.mm is a no-op for this case
- QWindowSystemInterface::handleGeometryChange(window(), rect);
- QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), rect.size()));
- } else if (m_nsWindow) {
- NSRect bounds = qt_mac_flipRect(rect);
- [m_nsWindow setFrame:[m_nsWindow frameRectForContentRect:bounds] display:YES animate:NO];
+ if (isContentView()) {
+ NSRect bounds = QCocoaScreen::mapToNative(rect);
+ [m_view.window setFrame:[m_view.window frameRectForContentRect:bounds] display:YES animate:NO];
} else {
[m_view setFrame:NSMakeRect(rect.x(), rect.y(), rect.width(), rect.height())];
}
- if (isForeignWindow())
- QPlatformWindow::setGeometry(rect);
-
// will call QPlatformWindow::setGeometry(rect) during resize confirmation (see qnsview.mm)
}
-void QCocoaWindow::clipChildWindows()
-{
- foreachChildNSWindow(^(QCocoaWindow *childWindow) {
- childWindow->clipWindow(m_nsWindow.frame);
- });
-}
-
-void QCocoaWindow::clipWindow(const NSRect &clipRect)
-{
- if (!isChildNSWindow())
- return;
-
- NSRect clippedWindowRect = NSZeroRect;
- if (!NSIsEmptyRect(clipRect)) {
- NSRect windowFrame = qt_mac_flipRect(QRect(window()->mapToGlobal(QPoint(0, 0)), geometry().size()));
- clippedWindowRect = NSIntersectionRect(windowFrame, clipRect);
- // Clipping top/left offsets the content. Move it back.
- NSPoint contentViewOffset = NSMakePoint(qMax(CGFloat(0), NSMinX(clippedWindowRect) - NSMinX(windowFrame)),
- qMax(CGFloat(0), NSMaxY(windowFrame) - NSMaxY(clippedWindowRect)));
- [m_view setBoundsOrigin:contentViewOffset];
- }
-
- if (NSIsEmptyRect(clippedWindowRect)) {
- if (!m_hiddenByClipping) {
- // We dont call hide() here as we will recurse further down
- [m_nsWindow orderOut:nil];
- m_hiddenByClipping = true;
- }
- } else {
- [m_nsWindow setFrame:clippedWindowRect display:YES animate:NO];
- if (m_hiddenByClipping) {
- m_hiddenByClipping = false;
- if (!m_hiddenByAncestor) {
- [m_nsWindow orderFront:nil];
- static_cast<QCocoaWindow *>(QPlatformWindow::parent())->reinsertChildWindow(this);
- }
- }
- }
-
- // recurse
- foreachChildNSWindow(^(QCocoaWindow *childWindow) {
- childWindow->clipWindow(clippedWindowRect);
- });
-}
-
-void QCocoaWindow::hide(bool becauseOfAncestor)
-{
- bool visible = [m_nsWindow isVisible];
-
- if (!m_hiddenByAncestor && !visible) // Already explicitly hidden
- return;
- if (m_hiddenByAncestor && becauseOfAncestor) // Trying to hide some child again
- return;
-
- m_hiddenByAncestor = becauseOfAncestor;
-
- if (!visible) // Could have been clipped before
- return;
-
- foreachChildNSWindow(^(QCocoaWindow *childWindow) {
- childWindow->hide(true);
- });
-
- [m_nsWindow orderOut:nil];
-}
-
-void QCocoaWindow::show(bool becauseOfAncestor)
-{
- if ([m_nsWindow isVisible])
- return;
-
- if (m_view.window.parentWindow && !m_view.window.parentWindow.visible) {
- m_hiddenByAncestor = true; // Parent still hidden, don't show now
- } else if ((becauseOfAncestor == m_hiddenByAncestor) // Was NEITHER explicitly hidden
- && !m_hiddenByClipping) { // ... NOR clipped
- if (isChildNSWindow()) {
- m_hiddenByAncestor = false;
- setCocoaGeometry(windowGeometry());
- }
- if (!m_hiddenByClipping) { // setCocoaGeometry() can change the clipping status
- [m_nsWindow orderFront:nil];
- if (isChildNSWindow())
- static_cast<QCocoaWindow *>(QPlatformWindow::parent())->reinsertChildWindow(this);
- foreachChildNSWindow(^(QCocoaWindow *childWindow) {
- childWindow->show(true);
- });
- }
- }
-}
-
void QCocoaWindow::setVisible(bool visible)
{
qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::setVisible" << window() << visible;
- if (isChildNSWindow() && m_hiddenByClipping)
- return;
-
m_inSetVisible = true;
QMacAutoReleasePool pool;
@@ -818,6 +334,12 @@ void QCocoaWindow::setVisible(bool visible)
// We need to recreate if the modality has changed as the style mask will need updating
recreateWindowIfNeeded();
+ // We didn't send geometry changes during creation, as that would have confused
+ // Qt, which expects a show-event to be sent before any resize events. But now
+ // that the window is made visible, we know that the show-event has been sent
+ // so we can send the geometry change. FIXME: Get rid of this workaround.
+ handleGeometryChange();
+
// Register popup windows. The Cocoa platform plugin will forward mouse events
// to them and close them when needed.
if (window()->type() == Qt::Popup || window()->type() == Qt::ToolTip)
@@ -831,34 +353,28 @@ void QCocoaWindow::setVisible(bool visible)
if (window()->type() == Qt::Popup) {
// QTBUG-30266: a window should not be resizable while a transient popup is open
// Since this isn't a native popup, the window manager doesn't close the popup when you click outside
- NSUInteger parentStyleMask = [parentCocoaWindow->m_nsWindow styleMask];
+ NSWindow *nativeParentWindow = parentCocoaWindow->nativeWindow();
+ NSUInteger parentStyleMask = nativeParentWindow.styleMask;
if ((m_resizableTransientParent = (parentStyleMask & NSResizableWindowMask))
- && !([parentCocoaWindow->m_nsWindow styleMask] & NSFullScreenWindowMask))
- [parentCocoaWindow->m_nsWindow setStyleMask:parentStyleMask & ~NSResizableWindowMask];
+ && !(nativeParentWindow.styleMask & NSFullScreenWindowMask))
+ nativeParentWindow.styleMask &= ~NSResizableWindowMask;
}
}
- // This call is here to handle initial window show correctly:
- // - top-level windows need to have backing store content ready when the
- // window is shown, sendin the expose event here makes that more likely.
- // - QNSViews for child windows are initialy not hidden and won't get the
- // viewDidUnhide message.
- exposeWindow();
-
- if (m_nsWindow) {
+ if (isContentView()) {
QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);
// setWindowState might have been called while the window was hidden and
// will not change the NSWindow state in that case. Sync up here:
- applyWindowState(window()->windowState());
+ applyWindowState(window()->windowStates());
if (window()->windowState() != Qt::WindowMinimized) {
if ((window()->modality() == Qt::WindowModal
|| window()->type() == Qt::Sheet)
&& parentCocoaWindow) {
// show the window as a sheet
- [parentCocoaWindow->m_nsWindow beginSheet:m_nsWindow completionHandler:nil];
+ [parentCocoaWindow->nativeWindow() beginSheet:m_view.window completionHandler:nil];
} else if (window()->modality() != Qt::NonModal) {
// show the window as application modal
QCocoaEventDispatcher *cocoaEventDispatcher = qobject_cast<QCocoaEventDispatcher *>(QGuiApplication::instance()->eventDispatcher());
@@ -866,32 +382,28 @@ void QCocoaWindow::setVisible(bool visible)
QCocoaEventDispatcherPrivate *cocoaEventDispatcherPrivate = static_cast<QCocoaEventDispatcherPrivate *>(QObjectPrivate::get(cocoaEventDispatcher));
cocoaEventDispatcherPrivate->beginModalSession(window());
m_hasModalSession = true;
- } else if ([m_nsWindow canBecomeKeyWindow]) {
+ } else if ([m_view.window canBecomeKeyWindow]) {
QCocoaEventDispatcher *cocoaEventDispatcher = qobject_cast<QCocoaEventDispatcher *>(QGuiApplication::instance()->eventDispatcher());
QCocoaEventDispatcherPrivate *cocoaEventDispatcherPrivate = 0;
if (cocoaEventDispatcher)
cocoaEventDispatcherPrivate = static_cast<QCocoaEventDispatcherPrivate *>(QObjectPrivate::get(cocoaEventDispatcher));
if (cocoaEventDispatcherPrivate && cocoaEventDispatcherPrivate->cocoaModalSessionStack.isEmpty())
- [m_nsWindow makeKeyAndOrderFront:nil];
+ [m_view.window makeKeyAndOrderFront:nil];
else
- [m_nsWindow orderFront:nil];
-
- foreachChildNSWindow(^(QCocoaWindow *childWindow) {
- childWindow->show(true);
- });
+ [m_view.window orderFront:nil];
} else {
- show();
+ [m_view.window orderFront:nil];
}
// We want the events to properly reach the popup, dialog, and tool
if ((window()->type() == Qt::Popup || window()->type() == Qt::Dialog || window()->type() == Qt::Tool)
- && [m_nsWindow isKindOfClass:[NSPanel class]]) {
- [(NSPanel *)m_nsWindow setWorksWhenModal:YES];
+ && [m_view.window isKindOfClass:[NSPanel class]]) {
+ ((NSPanel *)m_view.window).worksWhenModal = YES;
if (!(parentCocoaWindow && window()->transientParent()->isActive()) && window()->type() == Qt::Popup) {
removeMonitor();
monitor = [NSEvent addGlobalMonitorForEventsMatchingMask:NSLeftMouseDownMask|NSRightMouseDownMask|NSOtherMouseDownMask|NSMouseMovedMask handler:^(NSEvent *e) {
- QPointF localPoint = qt_mac_flipPoint([NSEvent mouseLocation]);
+ QPointF localPoint = QCocoaScreen::mapFromNative([NSEvent mouseLocation]);
QWindowSystemInterface::handleMouseEvent(window(), window()->mapFromGlobal(localPoint.toPoint()), localPoint,
cocoaButton2QtButton([e buttonNumber]));
}];
@@ -914,20 +426,21 @@ void QCocoaWindow::setVisible(bool visible)
QCocoaEventDispatcherPrivate *cocoaEventDispatcherPrivate = 0;
if (cocoaEventDispatcher)
cocoaEventDispatcherPrivate = static_cast<QCocoaEventDispatcherPrivate *>(QObjectPrivate::get(cocoaEventDispatcher));
- if (m_nsWindow) {
+ if (isContentView()) {
if (m_hasModalSession) {
if (cocoaEventDispatcherPrivate)
cocoaEventDispatcherPrivate->endModalSession(window());
m_hasModalSession = false;
} else {
- if ([m_nsWindow isSheet]) {
+ if ([m_view.window isSheet]) {
Q_ASSERT_X(parentCocoaWindow, "QCocoaWindow", "Window modal dialog has no transient parent.");
- [parentCocoaWindow->m_nsWindow endSheet:m_nsWindow];
+ [parentCocoaWindow->nativeWindow() endSheet:m_view.window];
}
}
- hide();
- if (m_nsWindow == [NSApp keyWindow]
+ [m_view.window orderOut:nil];
+
+ if (m_view.window == [NSApp keyWindow]
&& !(cocoaEventDispatcherPrivate && cocoaEventDispatcherPrivate->currentModalSession())) {
// Probably because we call runModalSession: outside [NSApp run] in QCocoaEventDispatcher
// (e.g., when show()-ing a modal QDialog instead of exec()-ing it), it can happen that
@@ -946,10 +459,11 @@ void QCocoaWindow::setVisible(bool visible)
QCocoaIntegration::instance()->popupWindowStack()->removeAll(this);
if (parentCocoaWindow && window()->type() == Qt::Popup) {
+ NSWindow *nativeParentWindow = parentCocoaWindow->nativeWindow();
if (m_resizableTransientParent
- && !([parentCocoaWindow->m_nsWindow styleMask] & NSFullScreenWindowMask))
- // QTBUG-30266: a window should not be resizable while a transient popup is open
- [parentCocoaWindow->m_nsWindow setStyleMask:[parentCocoaWindow->m_nsWindow styleMask] | NSResizableWindowMask];
+ && !(nativeParentWindow.styleMask & NSFullScreenWindowMask))
+ // A window should not be resizable while a transient popup is open
+ nativeParentWindow.styleMask |= NSResizableWindowMask;
}
}
@@ -979,7 +493,7 @@ NSInteger QCocoaWindow::windowLevel(Qt::WindowFlags flags)
const QWindow * const transientParent = window()->transientParent();
const QCocoaWindow * const transientParentWindow = transientParent ? static_cast<QCocoaWindow *>(transientParent->handle()) : 0;
if (transientParentWindow)
- windowLevel = qMax([transientParentWindow->m_nsWindow level], windowLevel);
+ windowLevel = qMax([transientParentWindow->nativeWindow() level], windowLevel);
}
return windowLevel;
@@ -987,70 +501,46 @@ NSInteger QCocoaWindow::windowLevel(Qt::WindowFlags flags)
NSUInteger QCocoaWindow::windowStyleMask(Qt::WindowFlags flags)
{
- Qt::WindowType type = static_cast<Qt::WindowType>(int(flags & Qt::WindowType_Mask));
- NSInteger styleMask = NSBorderlessWindowMask;
- if (flags & Qt::FramelessWindowHint)
- return styleMask;
- if ((type & Qt::Popup) == Qt::Popup) {
- if (!windowIsPopupType(type)) {
- styleMask = NSUtilityWindowMask | NSResizableWindowMask;
- if (!(flags & Qt::CustomizeWindowHint)) {
- styleMask |= NSClosableWindowMask | NSMiniaturizableWindowMask | NSTitledWindowMask;
- } else {
- if (flags & Qt::WindowTitleHint)
- styleMask |= NSTitledWindowMask;
- if (flags & Qt::WindowCloseButtonHint)
- styleMask |= NSClosableWindowMask;
- if (flags & Qt::WindowMinimizeButtonHint)
- styleMask |= NSMiniaturizableWindowMask;
- }
- }
+ const Qt::WindowType type = static_cast<Qt::WindowType>(int(flags & Qt::WindowType_Mask));
+ const bool frameless = (flags & Qt::FramelessWindowHint) || windowIsPopupType(type);
+
+ // Select base window type.
+ NSUInteger styleMask = frameless ? NSBorderlessWindowMask : NSResizableWindowMask;
+
+ if (frameless) {
+ // No further customizations for frameless since there are no window decorations.
+ } else if (flags & Qt::CustomizeWindowHint) {
+ if (flags & Qt::WindowTitleHint)
+ styleMask |= NSTitledWindowMask;
+ if (flags & Qt::WindowCloseButtonHint)
+ styleMask |= NSClosableWindowMask;
+ if (flags & Qt::WindowMinimizeButtonHint)
+ styleMask |= NSMiniaturizableWindowMask;
} else {
- if (type == Qt::Window && !(flags & Qt::CustomizeWindowHint)) {
- styleMask = (NSResizableWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSTitledWindowMask);
- } else if (type == Qt::Dialog) {
- if (flags & Qt::CustomizeWindowHint) {
- if (flags & Qt::WindowMaximizeButtonHint)
- styleMask = NSResizableWindowMask;
- if (flags & Qt::WindowTitleHint)
- styleMask |= NSTitledWindowMask;
- if (flags & Qt::WindowCloseButtonHint)
- styleMask |= NSClosableWindowMask;
- if (flags & Qt::WindowMinimizeButtonHint)
- styleMask |= NSMiniaturizableWindowMask;
- } else {
- styleMask = NSResizableWindowMask | NSClosableWindowMask | NSTitledWindowMask;
- }
- } else {
- if (flags & Qt::WindowMaximizeButtonHint)
- styleMask |= NSResizableWindowMask;
- if (flags & Qt::WindowTitleHint)
- styleMask |= NSTitledWindowMask;
- if (flags & Qt::WindowCloseButtonHint)
- styleMask |= NSClosableWindowMask;
- if (flags & Qt::WindowMinimizeButtonHint)
- styleMask |= NSMiniaturizableWindowMask;
- }
+ styleMask |= NSClosableWindowMask | NSTitledWindowMask;
+
+ if (type != Qt::Dialog)
+ styleMask |= NSMiniaturizableWindowMask;
}
+ if (type == Qt::Tool)
+ styleMask |= NSUtilityWindowMask;
+
if (m_drawContentBorderGradient)
styleMask |= NSTexturedBackgroundWindowMask;
// Don't wipe fullscreen state
- if (m_nsWindow.styleMask & NSFullScreenWindowMask)
+ if (m_view.window.styleMask & NSFullScreenWindowMask)
styleMask |= NSFullScreenWindowMask;
return styleMask;
}
-void QCocoaWindow::setWindowShadow(Qt::WindowFlags flags)
-{
- bool keepShadow = !(flags & Qt::NoDropShadowWindowHint);
- [m_nsWindow setHasShadow:(keepShadow ? YES : NO)];
-}
-
void QCocoaWindow::setWindowZoomButton(Qt::WindowFlags flags)
{
+ if (!isContentView())
+ return;
+
// Disable the zoom (maximize) button for fixed-sized windows and customized
// no-WindowMaximizeButtonHint windows. From a Qt perspective it migth be expected
// that the button would be removed in the latter case, but disabling it is more
@@ -1059,28 +549,27 @@ void QCocoaWindow::setWindowZoomButton(Qt::WindowFlags flags)
&& windowMinimumSize() == windowMaximumSize());
bool customizeNoZoom = ((flags & Qt::CustomizeWindowHint)
&& !(flags & (Qt::WindowMaximizeButtonHint | Qt::WindowFullscreenButtonHint)));
- [[m_nsWindow standardWindowButton:NSWindowZoomButton] setEnabled:!(fixedSizeNoZoom || customizeNoZoom)];
+ [[m_view.window standardWindowButton:NSWindowZoomButton] setEnabled:!(fixedSizeNoZoom || customizeNoZoom)];
}
void QCocoaWindow::setWindowFlags(Qt::WindowFlags flags)
{
- if (m_nsWindow && !isChildNSWindow()) {
- NSUInteger styleMask = windowStyleMask(flags);
- NSInteger level = this->windowLevel(flags);
- // While setting style mask we can have -updateGeometry calls on a content
+ if (isContentView()) {
+ // While setting style mask we can have handleGeometryChange calls on a content
// view with null geometry, reporting an invalid coordinates as a result.
m_inSetStyleMask = true;
- [m_nsWindow setStyleMask:styleMask];
+ m_view.window.styleMask = windowStyleMask(flags);
m_inSetStyleMask = false;
- [m_nsWindow setLevel:level];
- setWindowShadow(flags);
- if (!(flags & Qt::FramelessWindowHint)) {
+ m_view.window.level = this->windowLevel(flags);
+
+ m_view.window.hasShadow = !(flags & Qt::NoDropShadowWindowHint);
+
+ if (!(flags & Qt::FramelessWindowHint))
setWindowTitle(window()->title());
- }
Qt::WindowType type = window()->type();
if ((type & Qt::Popup) != Qt::Popup && (type & Qt::Dialog) != Qt::Dialog) {
- NSWindowCollectionBehavior behavior = [m_nsWindow collectionBehavior];
+ NSWindowCollectionBehavior behavior = m_view.window.collectionBehavior;
if (flags & Qt::WindowFullscreenButtonHint) {
behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary;
@@ -1088,28 +577,26 @@ void QCocoaWindow::setWindowFlags(Qt::WindowFlags flags)
behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
}
- [m_nsWindow setCollectionBehavior:behavior];
+ m_view.window.collectionBehavior = behavior;
}
setWindowZoomButton(flags);
- }
- // Make window ignore mouse events if WindowTransparentForInput is set.
- // Note that ignoresMouseEvents has a special initial state where events
- // are ignored (passed through) based on window transparency, and that
- // setting the property to false does not return us to that state. Instead,
- // this makes the window capture all mouse events. Take care to only
- // set the property if needed. FIXME: recreate window if needed or find
- // some other way to implement WindowTransparentForInput.
- if (m_nsWindow) {
+ // Make window ignore mouse events if WindowTransparentForInput is set.
+ // Note that ignoresMouseEvents has a special initial state where events
+ // are ignored (passed through) based on window transparency, and that
+ // setting the property to false does not return us to that state. Instead,
+ // this makes the window capture all mouse events. Take care to only
+ // set the property if needed. FIXME: recreate window if needed or find
+ // some other way to implement WindowTransparentForInput.
bool ignoreMouse = flags & Qt::WindowTransparentForInput;
- if (m_nsWindow.ignoresMouseEvents != ignoreMouse)
- m_nsWindow.ignoresMouseEvents = ignoreMouse;
+ if (m_view.window.ignoresMouseEvents != ignoreMouse)
+ m_view.window.ignoresMouseEvents = ignoreMouse;
}
m_windowFlags = flags;
}
-void QCocoaWindow::setWindowState(Qt::WindowState state)
+void QCocoaWindow::setWindowState(Qt::WindowStates state)
{
if (window()->isVisible())
applyWindowState(state); // Window state set for hidden windows take effect when show() is called
@@ -1117,45 +604,54 @@ void QCocoaWindow::setWindowState(Qt::WindowState state)
void QCocoaWindow::setWindowTitle(const QString &title)
{
- QMacAutoReleasePool pool;
- if (!m_nsWindow)
+ if (!isContentView())
return;
- CFStringRef windowTitle = title.toCFString();
- [m_nsWindow setTitle: const_cast<NSString *>(reinterpret_cast<const NSString *>(windowTitle))];
- CFRelease(windowTitle);
+ QMacAutoReleasePool pool;
+ m_view.window.title = title.toNSString();
+
+ if (title.isEmpty() && !window()->filePath().isEmpty()) {
+ // Clearing the title should restore the default filename
+ setWindowFilePath(window()->filePath());
+ }
}
void QCocoaWindow::setWindowFilePath(const QString &filePath)
{
- QMacAutoReleasePool pool;
- if (!m_nsWindow)
+ if (!isContentView())
return;
- QFileInfo fi(filePath);
- [m_nsWindow setRepresentedFilename:fi.exists() ? filePath.toNSString() : @""];
- m_hasWindowFilePath = fi.exists();
+ QMacAutoReleasePool pool;
+
+ if (window()->title().isNull())
+ [m_view.window setTitleWithRepresentedFilename:filePath.toNSString()];
+ else
+ m_view.window.representedFilename = filePath.toNSString();
+
+ // Changing the file path may affect icon visibility
+ setWindowIcon(window()->icon());
}
void QCocoaWindow::setWindowIcon(const QIcon &icon)
{
- QMacAutoReleasePool pool;
+ if (!isContentView())
+ return;
- NSButton *iconButton = [m_nsWindow standardWindowButton:NSWindowDocumentIconButton];
- if (iconButton == nil) {
- if (icon.isNull())
- return;
- NSString *title = window()->title().toNSString();
- [m_nsWindow setRepresentedURL:[NSURL fileURLWithPath:title]];
- iconButton = [m_nsWindow standardWindowButton:NSWindowDocumentIconButton];
+ NSButton *iconButton = [m_view.window standardWindowButton:NSWindowDocumentIconButton];
+ if (!iconButton) {
+ // Window icons are only supported on macOS in combination with a document filePath
+ return;
}
+
+ QMacAutoReleasePool pool;
+
if (icon.isNull()) {
- [iconButton setImage:nil];
+ NSWorkspace *workspace = [NSWorkspace sharedWorkspace];
+ [iconButton setImage:[workspace iconForFile:m_view.window.representedFilename]];
} else {
QPixmap pixmap = icon.pixmap(QSize(22, 22));
NSImage *image = static_cast<NSImage *>(qt_mac_create_nsimage(pixmap));
- [iconButton setImage:image];
- [image release];
+ [iconButton setImage:[image autorelease]];
}
}
@@ -1179,34 +675,22 @@ void QCocoaWindow::raise()
qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::raise" << window();
// ### handle spaces (see Qt 4 raise_sys in qwidget_mac.mm)
- if (!m_nsWindow)
+ if (!isContentView())
return;
- if (isChildNSWindow()) {
- if (m_hiddenByClipping)
- return;
- }
- if ([m_nsWindow isVisible]) {
- if (isChildNSWindow()) {
- // -[NSWindow orderFront:] doesn't work with attached windows.
- // The only solution is to remove and add the child window.
- // This will place it on top of all the other NSWindows.
- NSWindow *parentNSWindow = m_view.window.parentWindow;
- [parentNSWindow removeChildWindow:m_nsWindow];
- [parentNSWindow addChildWindow:m_nsWindow ordered:NSWindowAbove];
- } else {
- {
- // Clean up autoreleased temp objects from orderFront immediately.
- // Failure to do so has been observed to cause leaks also beyond any outer
- // autorelease pool (for example around a complete QWindow
- // construct-show-raise-hide-delete cyle), counter to expected autoreleasepool
- // behavior.
- QMacAutoReleasePool pool;
- [m_nsWindow orderFront: m_nsWindow];
- }
- static bool raiseProcess = qt_mac_resolveOption(true, "QT_MAC_SET_RAISE_PROCESS");
- if (raiseProcess) {
- [NSApp activateIgnoringOtherApps:YES];
- }
+
+ if (m_view.window.visible) {
+ {
+ // Clean up autoreleased temp objects from orderFront immediately.
+ // Failure to do so has been observed to cause leaks also beyond any outer
+ // autorelease pool (for example around a complete QWindow
+ // construct-show-raise-hide-delete cyle), counter to expected autoreleasepool
+ // behavior.
+ QMacAutoReleasePool pool;
+ [m_view.window orderFront:m_view.window];
+ }
+ static bool raiseProcess = qt_mac_resolveOption(true, "QT_MAC_SET_RAISE_PROCESS");
+ if (raiseProcess) {
+ [NSApp activateIgnoringOtherApps:YES];
}
}
}
@@ -1214,34 +698,16 @@ void QCocoaWindow::raise()
void QCocoaWindow::lower()
{
qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::lower" << window();
- if (!m_nsWindow)
+ if (!isContentView())
return;
- if (isChildNSWindow()) {
- if (m_hiddenByClipping)
- return;
- }
- if ([m_nsWindow isVisible]) {
- if (isChildNSWindow()) {
- // -[NSWindow orderBack:] doesn't work with attached windows.
- // The only solution is to remove and add all the child windows except this one.
- // This will keep the current window at the bottom while adding the others on top of it,
- // hopefully in the same order (this is not documented anywhere in the Cocoa documentation).
- NSWindow *parentNSWindow = m_view.window.parentWindow;
- NSArray *children = [parentNSWindow.childWindows copy];
- for (NSWindow *child in children)
- if (m_nsWindow != child) {
- [parentNSWindow removeChildWindow:child];
- [parentNSWindow addChildWindow:child ordered:NSWindowAbove];
- }
- } else {
- [m_nsWindow orderBack: m_nsWindow];
- }
- }
+
+ if (m_view.window.visible)
+ [m_view.window orderBack:m_view.window];
}
bool QCocoaWindow::isExposed() const
{
- return m_isExposed;
+ return !m_exposedRect.isEmpty();
}
bool QCocoaWindow::isOpaque() const
@@ -1252,7 +718,7 @@ bool QCocoaWindow::isOpaque() const
bool translucent = window()->format().alphaBufferSize() > 0
|| window()->opacity() < 1
- || [qnsview_cast(m_view) hasMask]
+ || !window()->mask().isEmpty()
|| (surface()->supportsOpenGL() && openglSourfaceOrder == -1);
return !translucent;
}
@@ -1260,24 +726,24 @@ bool QCocoaWindow::isOpaque() const
void QCocoaWindow::propagateSizeHints()
{
QMacAutoReleasePool pool;
- if (!m_nsWindow)
+ if (!isContentView())
return;
- qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::propagateSizeHints" << window() << "\n"
- << " min/max" << windowMinimumSize() << windowMaximumSize()
- << "size increment" << windowSizeIncrement()
- << " basesize" << windowBaseSize()
- << " geometry" << windowGeometry();
+ qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::propagateSizeHints" << window()
+ << "min:" << windowMinimumSize() << "max:" << windowMaximumSize()
+ << "increment:" << windowSizeIncrement()
+ << "base:" << windowBaseSize();
+
+ const NSWindow *window = m_view.window;
// Set the minimum content size.
- const QSize minimumSize = windowMinimumSize();
+ QSize minimumSize = windowMinimumSize();
if (!minimumSize.isValid()) // minimumSize is (-1, -1) when not set. Make that (0, 0) for Cocoa.
- [m_nsWindow setContentMinSize : NSMakeSize(0.0, 0.0)];
- [m_nsWindow setContentMinSize : NSMakeSize(minimumSize.width(), minimumSize.height())];
+ minimumSize = QSize(0, 0);
+ window.contentMinSize = NSSizeFromCGSize(minimumSize.toCGSize());
// Set the maximum content size.
- const QSize maximumSize = windowMaximumSize();
- [m_nsWindow setContentMaxSize : NSMakeSize(maximumSize.width(), maximumSize.height())];
+ window.contentMaxSize = NSSizeFromCGSize(windowMaximumSize().toCGSize());
// The window may end up with a fixed size; in this case the zoom button should be disabled.
setWindowZoomButton(m_windowFlags);
@@ -1287,42 +753,65 @@ void QCocoaWindow::propagateSizeHints()
QSize sizeIncrement = windowSizeIncrement();
if (sizeIncrement.isEmpty())
sizeIncrement = QSize(1, 1);
- [m_nsWindow setResizeIncrements:NSSizeFromCGSize(sizeIncrement.toCGSize())];
+ window.resizeIncrements = NSSizeFromCGSize(sizeIncrement.toCGSize());
QRect rect = geometry();
QSize baseSize = windowBaseSize();
- if (!baseSize.isNull() && baseSize.isValid()) {
- [m_nsWindow setFrame:NSMakeRect(rect.x(), rect.y(), baseSize.width(), baseSize.height()) display:YES];
- }
+ if (!baseSize.isNull() && baseSize.isValid())
+ [window setFrame:NSMakeRect(rect.x(), rect.y(), baseSize.width(), baseSize.height()) display:YES];
}
void QCocoaWindow::setOpacity(qreal level)
{
qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::setOpacity" << level;
- if (m_nsWindow) {
- [m_nsWindow setAlphaValue:level];
- [m_nsWindow setOpaque: isOpaque()];
- }
+ if (!isContentView())
+ return;
+
+ m_view.window.alphaValue = level;
}
void QCocoaWindow::setMask(const QRegion &region)
{
qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::setMask" << window() << region;
- if (m_nsWindow)
- [m_nsWindow setBackgroundColor:[NSColor clearColor]];
- [qnsview_cast(m_view) setMaskRegion:&region];
- [m_nsWindow setOpaque:isOpaque()];
+ if (m_view.layer) {
+ if (!region.isEmpty()) {
+ QCFType<CGMutablePathRef> maskPath = CGPathCreateMutable();
+ for (const QRect &r : region)
+ CGPathAddRect(maskPath, nullptr, r.toCGRect());
+ CAShapeLayer *maskLayer = [CAShapeLayer layer];
+ maskLayer.path = maskPath;
+ m_view.layer.mask = maskLayer;
+ } else {
+ m_view.layer.mask = nil;
+ }
+ }
+
+ if (isContentView()) {
+ // Setting the mask requires invalidating the NSWindow shadow, but that needs
+ // to happen after the backingstore has been redrawn, so that AppKit can pick
+ // up the new window shape based on the backingstore content. Doing a display
+ // directly here is not an option, as the window might not be exposed at this
+ // time, and so would not result in an updated backingstore.
+ m_needsInvalidateShadow = true;
+ [m_view setNeedsDisplay:YES];
+
+ // FIXME: [NSWindow invalidateShadow] has no effect when in layer-backed mode,
+ // so if the mask is changed after the initial mask is applied, it will not
+ // result in any visual change to the shadow. This is an Apple bug, and there
+ // may be ways to work around it, such as calling setFrame on the window to
+ // trigger some internal invalidation, but that needs more research.
+ }
}
bool QCocoaWindow::setKeyboardGrabEnabled(bool grab)
{
qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::setKeyboardGrabEnabled" << window() << grab;
- if (!m_nsWindow)
+ if (!isContentView())
return false;
- if (grab && ![m_nsWindow isKeyWindow])
- [m_nsWindow makeKeyWindow];
+ if (grab && ![m_view.window isKeyWindow])
+ [m_view.window makeKeyWindow];
return true;
}
@@ -1330,11 +819,11 @@ bool QCocoaWindow::setKeyboardGrabEnabled(bool grab)
bool QCocoaWindow::setMouseGrabEnabled(bool grab)
{
qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::setMouseGrabEnabled" << window() << grab;
- if (!m_nsWindow)
+ if (!isContentView())
return false;
- if (grab && ![m_nsWindow isKeyWindow])
- [m_nsWindow makeKeyWindow];
+ if (grab && ![m_view.window isKeyWindow])
+ [m_view.window makeKeyWindow];
return true;
}
@@ -1363,7 +852,7 @@ NSView *QCocoaWindow::view() const
NSWindow *QCocoaWindow::nativeWindow() const
{
- return m_nsWindow;
+ return m_view.window;
}
void QCocoaWindow::setEmbeddedInForeignView(bool embedded)
@@ -1374,8 +863,32 @@ void QCocoaWindow::setEmbeddedInForeignView(bool embedded)
m_nsWindow = 0;
}
+// ----------------------- NSView notifications -----------------------
+
+void QCocoaWindow::viewDidChangeFrame()
+{
+ handleGeometryChange();
+}
+
+/*!
+ Callback for NSViewGlobalFrameDidChangeNotification.
+
+ Posted whenever an NSView object that has attached surfaces (that is,
+ NSOpenGLContext objects) moves to a different screen, or other cases
+ where the NSOpenGLContext object needs to be updated.
+*/
+void QCocoaWindow::viewDidChangeGlobalFrame()
+{
+ [m_view setNeedsDisplay:YES];
+}
+
// ----------------------- NSWindow notifications -----------------------
+// Note: The following notifications are delivered to every QCocoaWindow
+// that is a child of the NSWindow that triggered the notification. Each
+// callback should make sure to filter out notifications if they do not
+// apply to that QCocoaWindow, e.g. if the window is not a content view.
+
void QCocoaWindow::windowWillMove()
{
// Close any open popups on window move
@@ -1384,54 +897,39 @@ void QCocoaWindow::windowWillMove()
void QCocoaWindow::windowDidMove()
{
- if (isChildNSWindow())
+ if (!isContentView())
return;
- [qnsview_cast(m_view) updateGeometry];
+ handleGeometryChange();
// Moving a window might bring it out of maximized state
- reportCurrentWindowState();
+ handleWindowStateChanged();
}
void QCocoaWindow::windowDidResize()
{
- if (!m_nsWindow)
+ if (!isContentView())
return;
- if (isChildNSWindow())
- return;
-
- clipChildWindows();
- [qnsview_cast(m_view) updateGeometry];
+ handleGeometryChange();
if (!m_view.inLiveResize)
- reportCurrentWindowState();
-}
-
-void QCocoaWindow::viewDidChangeFrame()
-{
- [qnsview_cast(m_view) updateGeometry];
-}
-
-/*!
- Callback for NSViewGlobalFrameDidChangeNotification.
-
- Posted whenever an NSView object that has attached surfaces (that is,
- NSOpenGLContext objects) moves to a different screen, or other cases
- where the NSOpenGLContext object needs to be updated.
-*/
-void QCocoaWindow::viewDidChangeGlobalFrame()
-{
- updateExposedGeometry();
+ handleWindowStateChanged();
}
void QCocoaWindow::windowDidEndLiveResize()
{
- reportCurrentWindowState();
+ if (!isContentView())
+ return;
+
+ handleWindowStateChanged();
}
void QCocoaWindow::windowDidBecomeKey()
{
+ if (!isContentView())
+ return;
+
if (isForeignWindow())
return;
@@ -1448,6 +946,9 @@ void QCocoaWindow::windowDidBecomeKey()
void QCocoaWindow::windowDidResignKey()
{
+ if (!isContentView())
+ return;
+
if (isForeignWindow())
return;
@@ -1464,43 +965,61 @@ void QCocoaWindow::windowDidResignKey()
void QCocoaWindow::windowDidMiniaturize()
{
- reportCurrentWindowState();
+ if (!isContentView())
+ return;
+
+ handleWindowStateChanged();
}
void QCocoaWindow::windowDidDeminiaturize()
{
- reportCurrentWindowState();
+ if (!isContentView())
+ return;
+
+ handleWindowStateChanged();
}
void QCocoaWindow::windowWillEnterFullScreen()
{
+ if (!isContentView())
+ return;
+
// The NSWindow needs to be resizable, otherwise we'll end up with
// the normal window geometry, centered in the middle of the screen
// on a black background. The styleMask will be reset below.
- m_nsWindow.styleMask |= NSResizableWindowMask;
+ m_view.window.styleMask |= NSResizableWindowMask;
}
void QCocoaWindow::windowDidEnterFullScreen()
{
- Q_ASSERT_X(m_nsWindow.qt_fullScreen, "QCocoaWindow",
+ if (!isContentView())
+ return;
+
+ Q_ASSERT_X(m_view.window.qt_fullScreen, "QCocoaWindow",
"FullScreen category processes window notifications first");
// Reset to original styleMask
setWindowFlags(m_windowFlags);
- reportCurrentWindowState();
+ handleWindowStateChanged();
}
void QCocoaWindow::windowWillExitFullScreen()
{
+ if (!isContentView())
+ return;
+
// The NSWindow needs to be resizable, otherwise we'll end up with
// a weird zoom animation. The styleMask will be reset below.
- m_nsWindow.styleMask |= NSResizableWindowMask;
+ m_view.window.styleMask |= NSResizableWindowMask;
}
void QCocoaWindow::windowDidExitFullScreen()
{
- Q_ASSERT_X(!m_nsWindow.qt_fullScreen, "QCocoaWindow",
+ if (!isContentView())
+ return;
+
+ Q_ASSERT_X(!m_view.window.qt_fullScreen, "QCocoaWindow",
"FullScreen category processes window notifications first");
// Reset to original styleMask
@@ -1509,7 +1028,7 @@ void QCocoaWindow::windowDidExitFullScreen()
Qt::WindowState requestedState = window()->windowState();
// Deliver update of QWindow state
- reportCurrentWindowState();
+ handleWindowStateChanged();
if (requestedState != windowState() && requestedState != Qt::WindowFullScreen) {
// We were only going out of full screen as an intermediate step before
@@ -1518,30 +1037,22 @@ void QCocoaWindow::windowDidExitFullScreen()
}
}
-void QCocoaWindow::windowDidOrderOffScreen()
+void QCocoaWindow::windowDidOrderOnScreen()
{
- obscureWindow();
+ [m_view setNeedsDisplay:YES];
}
-void QCocoaWindow::windowDidOrderOnScreen()
+void QCocoaWindow::windowDidOrderOffScreen()
{
- exposeWindow();
+ handleExposeEvent(QRegion());
}
void QCocoaWindow::windowDidChangeOcclusionState()
{
- // Several unit tests expect paint and/or expose events for windows that are
- // sometimes (unpredictably) occluded and some unit tests depend on QWindow::isExposed.
- // Don't send Expose/Obscure events when running under QTestLib.
- static const bool onTestLib = qt_mac_resolveOption(false, "QT_QTESTLIB_RUNNING");
- if (!onTestLib) {
- if ((NSUInteger)[m_view.window occlusionState] & NSWindowOcclusionStateVisible) {
- exposeWindow();
- } else {
- // Send Obscure events on window occlusion to stop animations.
- obscureWindow();
- }
- }
+ if (m_view.window.occlusionState & NSWindowOcclusionStateVisible)
+ [m_view setNeedsDisplay:YES];
+ else
+ handleExposeEvent(QRegion());
}
void QCocoaWindow::windowDidChangeScreen()
@@ -1551,8 +1062,6 @@ void QCocoaWindow::windowDidChangeScreen()
if (QCocoaScreen *cocoaScreen = QCocoaIntegration::instance()->screenForNSScreen(m_view.window.screen))
QWindowSystemInterface::handleWindowScreenChanged(window(), cocoaScreen->screen());
-
- updateExposedGeometry();
}
void QCocoaWindow::windowWillClose()
@@ -1577,6 +1086,111 @@ bool QCocoaWindow::windowShouldClose()
return accepted;
}
+// ----------------------------- QPA forwarding -----------------------------
+
+void QCocoaWindow::handleGeometryChange()
+{
+ // Prevent geometry change during initialization, as that will result
+ // in a resize event, and Qt expects those to come after the show event.
+ // FIXME: Remove once we've clarified the Qt behavior for this.
+ if (!m_initialized)
+ return;
+
+ // Don't send the geometry change if the QWindow is designated to be
+ // embedded in a foreign view hierarchy but has not actually been
+ // embedded yet - it's too early.
+ if (m_viewIsToBeEmbedded && !m_viewIsEmbedded)
+ return;
+
+ // It can happen that the current NSWindow is nil (if we are changing styleMask
+ // from/to borderless, and the content view is being re-parented), which results
+ // in invalid coordinates.
+ if (m_inSetStyleMask && !m_view.window)
+ return;
+
+ const bool isEmbedded = m_viewIsToBeEmbedded || m_viewIsEmbedded;
+
+ QRect newGeometry;
+ if (isContentView() && !isEmbedded) {
+ // Content views are positioned at (0, 0) in the window, so we resolve via the window
+ CGRect contentRect = [m_view.window contentRectForFrameRect:m_view.window.frame];
+
+ // The result above is in native screen coordinates, so remap to the Qt coordinate system
+ newGeometry = QCocoaScreen::mapFromNative(contentRect).toRect();
+ } else {
+ // QNSView has isFlipped set, so no need to remap the geometry
+ newGeometry = QRectF::fromCGRect(m_view.frame).toRect();
+ }
+
+ qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::handleGeometryChange" << window()
+ << "current" << geometry() << "new" << newGeometry;
+
+ QWindowSystemInterface::handleGeometryChange(window(), newGeometry);
+
+ // Guard against processing window system events during QWindow::setGeometry
+ // calls, which Qt and Qt applications do not expect.
+ if (!m_inSetGeometry)
+ QWindowSystemInterface::flushWindowSystemEvents();
+}
+
+void QCocoaWindow::handleExposeEvent(const QRegion &region)
+{
+ const QRect previouslyExposedRect = m_exposedRect;
+
+ // Ideally we'd implement isExposed() in terms of these properties,
+ // plus the occlusionState of the NSWindow, and let the expose event
+ // pull the exposed state out when needed. However, when the window
+ // is first shown we receive a drawRect call where the occlusionState
+ // of the window is still hidden, but we still want to prepare the
+ // window for display by issuing an expose event to Qt. To work around
+ // this we don't use the occlusionState directly, but instead base
+ // the exposed state on the region we get in, which in the case of
+ // a window being obscured is an empty region, and in the case of
+ // a drawRect call is a non-null region, even if occlusionState
+ // is still hidden. This ensures the window is prepared for display.
+ if (m_view.window.visible && m_view.window.screen
+ && !geometry().size().isEmpty() && !region.isEmpty()
+ && !m_view.hiddenOrHasHiddenAncestor) {
+ m_exposedRect = region.boundingRect();
+ } else {
+ m_exposedRect = QRect();
+ }
+
+ QWindowPrivate *windowPrivate = qt_window_private(window());
+ if (windowPrivate->updateRequestPending) {
+ // We can only deliver update request events when the window is exposed,
+ // and we also have to make sure we deliver any change to the exposed
+ // rect as a real expose event (including going from non-exposed to
+ // exposed). FIXME: Should this logic live in QGuiApplication?
+ if (isExposed() && m_exposedRect == previouslyExposedRect) {
+ qCDebug(lcQpaCocoaDrawing) << "QCocoaWindow::handleExposeEvent" << window() << region << "as update request";
+ windowPrivate->deliverUpdateRequest();
+ return;
+ } else {
+ // Since updateRequestPending is still set, we will issue a deferred setNeedsDisplay
+ // from drawRect and get back into this code on the next display cycle, delivering
+ // the pending update request.
+ }
+ }
+
+ qCDebug(lcQpaCocoaDrawing) << "QCocoaWindow::handleExposeEvent" << window() << region << "isExposed" << isExposed();
+ QWindowSystemInterface::handleExposeEvent<QWindowSystemInterface::SynchronousDelivery>(window(), region);
+}
+
+void QCocoaWindow::handleWindowStateChanged(HandleFlags flags)
+{
+ Qt::WindowState currentState = windowState();
+ if (!(flags & HandleUnconditionally) && currentState == m_lastReportedWindowState)
+ return;
+
+ qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::handleWindowStateChanged" <<
+ m_lastReportedWindowState << "-->" << currentState;
+
+ QWindowSystemInterface::handleWindowStateChanged<QWindowSystemInterface::SynchronousDelivery>(
+ window(), currentState, m_lastReportedWindowState);
+ m_lastReportedWindowState = currentState;
+}
+
// --------------------------------------------------------------------------
bool QCocoaWindow::windowIsPopupType(Qt::WindowType type) const
@@ -1602,26 +1216,13 @@ QCocoaGLContext *QCocoaWindow::currentContext() const
#endif
/*!
- Checks if the window is a non-top level QWindow with a NSWindow.
-
- \sa _q_platform_MacUseNSWindow, QT_MAC_USE_NSWINDOW
-*/
-bool QCocoaWindow::isChildNSWindow() const
-{
- return m_view.window.parentWindow != nil;
-}
-
-/*!
Checks if the window is the content view of its immediate NSWindow.
Being the content view of a NSWindow means the QWindow is
the highest accessible NSView object in the window's view
hierarchy.
- This can only happen in two cases, either if the QWindow is
- itself a top level window, or if it's a child NSWindow.
-
- \sa isChildNSWindow
+ This is the case if the QWindow is a top level window.
*/
bool QCocoaWindow::isContentView() const
{
@@ -1629,33 +1230,16 @@ bool QCocoaWindow::isContentView() const
}
/*!
- Iterates child NSWindows that have a corresponding QCocoaWindow.
-*/
-void QCocoaWindow::foreachChildNSWindow(void (^block)(QCocoaWindow *))
-{
- NSArray *windows = m_view.window.childWindows;
- [windows enumerateObjectsUsingBlock:^(NSWindow *window, NSUInteger index, BOOL *stop) {
- Q_UNUSED(index);
- Q_UNUSED(stop);
- if (QNSView *view = qnsview_cast(window.contentView))
- block(view.platformWindow);
- }];
-}
-
-/*!
Recreates (or removes) the NSWindow for this QWindow, if needed.
- A QWindow may need a corresponding NSWindow, depending on whether
- or not it's a top level or not (or explicitly set to be a child
- NSWindow), whether it is a NSPanel or not, etc.
+ A QWindow may need a corresponding NSWindow/NSPanel, depending on
+ whether or not it's a top level or not, window flags, etc.
*/
void QCocoaWindow::recreateWindowIfNeeded()
{
QMacAutoReleasePool pool;
QPlatformWindow *parentWindow = QPlatformWindow::parent();
- qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::recreateWindowIfNeeded" << window()
- << "parent" << (parentWindow ? parentWindow->window() : 0);
RecreationReasons recreateReason = RecreationNotNeeded;
@@ -1673,79 +1257,56 @@ void QCocoaWindow::recreateWindowIfNeeded()
if (m_windowModality != window()->modality())
recreateReason |= WindowModalityChanged;
- const bool shouldBeChildNSWindow = parentWindow && qt_mac_resolveOption(NO,
- window(), "_q_platform_MacUseNSWindow", "QT_MAC_USE_NSWINDOW");
-
- if (isChildNSWindow() != shouldBeChildNSWindow)
- recreateReason |= ChildNSWindowChanged;
-
- const bool shouldBeContentView = (!parentWindow && !m_viewIsEmbedded) || shouldBeChildNSWindow;
+ const bool shouldBeContentView = !parentWindow && !(m_viewIsToBeEmbedded || m_viewIsEmbedded);
if (isContentView() != shouldBeContentView)
recreateReason |= ContentViewChanged;
Qt::WindowType type = window()->type();
const bool isPanel = isContentView() && [m_view.window isKindOfClass:[QNSPanel class]];
- const bool shouldBePanel = shouldBeContentView && !shouldBeChildNSWindow &&
+ const bool shouldBePanel = shouldBeContentView &&
((type & Qt::Popup) == Qt::Popup || (type & Qt::Dialog) == Qt::Dialog);
if (isPanel != shouldBePanel)
recreateReason |= PanelChanged;
- if (recreateReason == RecreationNotNeeded) {
- qCDebug(lcQpaCocoaWindow) << "No need to recreate NSWindow";
- return;
- }
+ qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::recreateWindowIfNeeded" << window() << recreateReason;
- qCDebug(lcQpaCocoaWindow) << "Reconfiguring NSWindow due to" << recreateReason;
+ if (recreateReason == RecreationNotNeeded)
+ return;
QCocoaWindow *parentCocoaWindow = static_cast<QCocoaWindow *>(parentWindow);
- if (shouldBeChildNSWindow) {
- QWindow *parentQWindow = parentWindow->window();
- // Ensure that all parents in the hierarchy are also child NSWindows
- if (!parentQWindow->property("_q_platform_MacUseNSWindow").toBool()) {
- parentQWindow->setProperty("_q_platform_MacUseNSWindow", QVariant(true));
- parentCocoaWindow->recreateWindowIfNeeded();
- }
- }
-
// Remove current window (if any)
if ((isContentView() && !shouldBeContentView) || (recreateReason & PanelChanged)) {
- qCDebug(lcQpaCocoaWindow) << "Getting rid of existing window" << m_nsWindow;
- [m_nsWindow closeAndRelease];
- if (isChildNSWindow())
- [m_view.window.parentWindow removeChildWindow:m_view.window];
- if (isContentView()) {
- // We explicitly disassociate m_view from the window's contentView,
- // as AppKit does not automatically do this in response to removing
- // the view from the NSThemeFrame subview list, so we might end up
- // with a NSWindow contentView pointing to a deallocated NSView.
- m_view.window.contentView = nil;
+ if (m_nsWindow) {
+ qCDebug(lcQpaCocoaWindow) << "Getting rid of existing window" << m_nsWindow;
+ [m_nsWindow closeAndRelease];
+ if (isContentView()) {
+ // We explicitly disassociate m_view from the window's contentView,
+ // as AppKit does not automatically do this in response to removing
+ // the view from the NSThemeFrame subview list, so we might end up
+ // with a NSWindow contentView pointing to a deallocated NSView.
+ m_view.window.contentView = nil;
+ }
+ m_nsWindow = 0;
}
- m_nsWindow = 0;
}
if (shouldBeContentView) {
bool noPreviousWindow = m_nsWindow == 0;
+ QCocoaNSWindow *newWindow = nullptr;
if (noPreviousWindow)
- m_nsWindow = createNSWindow(shouldBeChildNSWindow, shouldBePanel);
-
- if (m_view.window.parentWindow) {
- if (!shouldBeChildNSWindow || (recreateReason & ParentChanged))
- [m_view.window.parentWindow removeChildWindow:m_view.window];
- m_forwardWindow = oldParentCocoaWindow;
- }
+ newWindow = createNSWindow(shouldBePanel);
// Move view to new NSWindow if needed
- if (m_nsWindow.contentView != m_view) {
- qCDebug(lcQpaCocoaWindow) << "Ensuring that view is content view for" << m_nsWindow;
+ if (newWindow) {
+ qCDebug(lcQpaCocoaWindow) << "Ensuring that" << m_view << "is content view for" << newWindow;
[m_view setPostsFrameChangedNotifications:NO];
- [m_view retain];
- if (m_view.superview) // m_view comes from another NSWindow
- [m_view removeFromSuperview];
- [m_nsWindow setContentView:m_view];
- [m_view release];
+ [newWindow setContentView:m_view];
[m_view setPostsFrameChangedNotifications:YES];
+
+ m_nsWindow = newWindow;
+ Q_ASSERT(m_view.window == m_nsWindow);
}
}
@@ -1756,21 +1317,10 @@ void QCocoaWindow::recreateWindowIfNeeded()
propagateSizeHints();
setWindowFlags(window()->flags());
setWindowTitle(window()->title());
+ setWindowFilePath(window()->filePath());
setWindowState(window()->windowState());
- } else if (shouldBeChildNSWindow) {
- if (!m_hiddenByClipping) {
- [parentCocoaWindow->m_nsWindow addChildWindow:m_nsWindow ordered:NSWindowAbove];
- parentCocoaWindow->reinsertChildWindow(this);
- }
-
- // Set properties after the window has been made a child NSWindow
- setCocoaGeometry(windowGeometry());
- setWindowFlags(window()->flags());
} else {
// Child windows have no NSWindow, link the NSViews instead.
- if ([m_view superview])
- [m_view removeFromSuperview];
-
[parentCocoaWindow->m_view addSubview:m_view];
QRect rect = windowGeometry();
// Prevent setting a (0,0) window size; causes opengl context
@@ -1786,29 +1336,18 @@ void QCocoaWindow::recreateWindowIfNeeded()
if (!qFuzzyCompare(opacity, qreal(1.0)))
setOpacity(opacity);
+ setMask(QHighDpi::toNativeLocalRegion(window()->mask(), window()));
+
// top-level QWindows may have an attached NSToolBar, call
// update function which will attach to the NSWindow.
if (!parentWindow)
updateNSToolbar();
}
-void QCocoaWindow::reinsertChildWindow(QCocoaWindow *child)
+void QCocoaWindow::requestUpdate()
{
- const QObjectList &childWindows = window()->children();
- int childIndex = childWindows.indexOf(child->window());
- Q_ASSERT(childIndex != -1);
-
- for (int i = childIndex; i < childWindows.size(); ++i) {
- QWindow *window = static_cast<QWindow *>(childWindows.at(i));
- QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow *>(window->handle());
- if (!cocoaWindow)
- continue;
-
- NSWindow *nsChild = cocoaWindow->m_nsWindow;
- if (i != childIndex)
- [m_nsWindow removeChildWindow:nsChild];
- [m_nsWindow addChildWindow:nsChild ordered:NSWindowAbove];
- }
+ qCDebug(lcQpaCocoaDrawing) << "QCocoaWindow::requestUpdate" << window();
+ [m_view setNeedsDisplay:YES];
}
void QCocoaWindow::requestActivateWindow()
@@ -1818,10 +1357,8 @@ void QCocoaWindow::requestActivateWindow()
[window makeKeyWindow];
}
-QCocoaNSWindow *QCocoaWindow::createNSWindow(bool shouldBeChildNSWindow, bool shouldBePanel)
+QCocoaNSWindow *QCocoaWindow::createNSWindow(bool shouldBePanel)
{
- qCDebug(lcQpaCocoaWindow) << "createNSWindow" << shouldBeChildNSWindow << shouldBePanel;
-
QMacAutoReleasePool pool;
QRect rect = geometry();
@@ -1841,7 +1378,7 @@ QCocoaNSWindow *QCocoaWindow::createNSWindow(bool shouldBeChildNSWindow, bool sh
rect.translate(-targetScreen->geometry().topLeft());
QCocoaScreen *cocoaScreen = static_cast<QCocoaScreen *>(targetScreen->handle());
- NSRect frame = NSRectFromCGRect(cocoaScreen->mapToNative(rect).toCGRect());
+ NSRect frame = QCocoaScreen::mapToNative(rect, cocoaScreen);
// Note: The macOS window manager has a bug, where if a screen is rotated, it will not allow
// a window to be created within the area of the screen that has a Y coordinate (I quadrant)
@@ -1853,44 +1390,58 @@ QCocoaNSWindow *QCocoaWindow::createNSWindow(bool shouldBeChildNSWindow, bool sh
// Create NSWindow
Class windowClass = shouldBePanel ? [QNSPanel class] : [QNSWindow class];
- NSUInteger styleMask = shouldBeChildNSWindow ? NSBorderlessWindowMask : windowStyleMask(flags);
- QCocoaNSWindow *window = [[windowClass alloc] initWithContentRect:frame
- screen:cocoaScreen->nativeScreen() styleMask:styleMask qPlatformWindow:this];
-
- window.restorable = NO;
- window.level = shouldBeChildNSWindow ? NSNormalWindowLevel : windowLevel(flags);
-
- if (!isOpaque()) {
- window.backgroundColor = [NSColor clearColor];
- window.opaque = NO;
+ QCocoaNSWindow *nsWindow = [[windowClass alloc] initWithContentRect:frame
+ styleMask:windowStyleMask(flags)
+ // Deferring window creation breaks OpenGL (the GL context is
+ // set up before the window is shown and needs a proper window)
+ backing:NSBackingStoreBuffered defer:NO
+ screen:cocoaScreen->nativeScreen()];
+
+ Q_ASSERT_X(nsWindow.screen == cocoaScreen->nativeScreen(), "QCocoaWindow",
+ "Resulting NSScreen should match the requested NSScreen");
+
+ nsWindow.delegate = [[QNSWindowDelegate alloc] initWithQCocoaWindow:this];
+
+ // Prevent Cocoa from releasing the window on close. Qt
+ // handles the close event asynchronously and we want to
+ // make sure that NSWindow stays valid until the
+ // QCocoaWindow is deleted by Qt.
+ [nsWindow setReleasedWhenClosed:NO];
+
+ if (alwaysShowToolWindow()) {
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
+ [center addObserver:[QNSWindow class] selector:@selector(applicationActivationChanged:)
+ name:NSApplicationWillResignActiveNotification object:nil];
+ [center addObserver:[QNSWindow class] selector:@selector(applicationActivationChanged:)
+ name:NSApplicationWillBecomeActiveNotification object:nil];
+ });
}
- Q_ASSERT(!(shouldBePanel && shouldBeChildNSWindow));
+ if (targetScreen != window()->screen())
+ QWindowSystemInterface::handleWindowScreenChanged(window(), targetScreen);
+
+ nsWindow.restorable = NO;
+ nsWindow.level = windowLevel(flags);
if (shouldBePanel) {
// Qt::Tool windows hide on app deactivation, unless Qt::WA_MacAlwaysShowToolWindow is set
- window.hidesOnDeactivate = ((type & Qt::Tool) == Qt::Tool) && !alwaysShowToolWindow();
+ nsWindow.hidesOnDeactivate = ((type & Qt::Tool) == Qt::Tool) && !alwaysShowToolWindow();
// Make popup windows show on the same desktop as the parent full-screen window
- window.collectionBehavior = NSWindowCollectionBehaviorFullScreenAuxiliary;
+ nsWindow.collectionBehavior = NSWindowCollectionBehaviorFullScreenAuxiliary;
if ((type & Qt::Popup) == Qt::Popup) {
- window.hasShadow = YES;
- window.animationBehavior = NSWindowAnimationBehaviorUtilityWindow;
+ nsWindow.hasShadow = YES;
+ nsWindow.animationBehavior = NSWindowAnimationBehaviorUtilityWindow;
}
- } else if (shouldBeChildNSWindow) {
- window.collectionBehavior =
- NSWindowCollectionBehaviorManaged
- | NSWindowCollectionBehaviorIgnoresCycle
- | NSWindowCollectionBehaviorFullScreenAuxiliary;
- window.hasShadow = NO;
- window.animationBehavior = NSWindowAnimationBehaviorNone;
}
// Persist modality so we can detect changes later on
m_windowModality = QPlatformWindow::window()->modality();
- applyContentBorderThickness(window);
+ applyContentBorderThickness(nsWindow);
// Prevent CoreGraphics RGB32 -> RGB64 backing store conversions on deep color
// displays by forcing 8-bit components, unless a deep color format has been
@@ -1903,11 +1454,11 @@ QCocoaNSWindow *QCocoaWindow::createNSWindow(bool shouldBeChildNSWindow, bool sh
surfaceFormat.blueBufferSize() > 8;
bool usesLayer = view().layer;
if (usesCoreGraphics && !usesDeepColor && !usesLayer) {
- [window setDynamicDepthLimit:NO];
- [window setDepthLimit:NSWindowDepthTwentyfourBitRGB];
+ [nsWindow setDynamicDepthLimit:NO];
+ [nsWindow setDepthLimit:NSWindowDepthTwentyfourBitRGB];
}
- return window;
+ return nsWindow;
}
bool QCocoaWindow::alwaysShowToolWindow() const
@@ -1923,19 +1474,6 @@ void QCocoaWindow::removeMonitor()
monitor = nil;
}
-// Returns the current global screen geometry for the nswindow associated with this window.
-QRect QCocoaWindow::nativeWindowGeometry() const
-{
- if (!m_nsWindow || isChildNSWindow())
- return geometry();
-
- NSRect rect = [m_nsWindow frame];
- QPlatformScreen *onScreen = QPlatformScreen::platformScreenForWindow(window());
- int flippedY = onScreen->geometry().height() - rect.origin.y - rect.size.height; // account for nswindow inverted y.
- QRect qRect = QRect(rect.origin.x, flippedY, rect.size.width, rect.size.height);
- return qRect;
-}
-
/*!
Applies the given state to the NSWindow, going in/out of minimize/zoomed/fullscreen
@@ -1943,13 +1481,15 @@ QRect QCocoaWindow::nativeWindowGeometry() const
updated yet, so window()->windowState() will reflect the previous state that was
reported to QtGui.
*/
-void QCocoaWindow::applyWindowState(Qt::WindowState newState)
+void QCocoaWindow::applyWindowState(Qt::WindowStates requestedState)
{
- const Qt::WindowState currentState = windowState();
- if (newState == currentState)
+ if (!isContentView())
return;
- if (!m_nsWindow)
+ const Qt::WindowState currentState = windowState();
+ const Qt::WindowState newState = QWindowPrivate::effectiveState(requestedState);
+
+ if (newState == currentState)
return;
const NSSize contentSize = m_view.frame.size;
@@ -1957,23 +1497,25 @@ void QCocoaWindow::applyWindowState(Qt::WindowState newState)
// If content view width or height is 0 then the window animations will crash so
// do nothing. We report the current state back to reflect the failed operation.
qWarning("invalid window content view size, check your window geometry");
- reportCurrentWindowState(true);
+ handleWindowStateChanged(HandleUnconditionally);
return;
}
- if (m_nsWindow.styleMask & NSUtilityWindowMask) {
- // Utility panels cannot be fullscreen
- qWarning() << window()->type() << "windows can not be made full screen";
- reportCurrentWindowState(true);
+ const NSWindow *nsWindow = m_view.window;
+
+ if (nsWindow.styleMask & NSUtilityWindowMask
+ && newState & (Qt::WindowMinimized | Qt::WindowFullScreen)) {
+ qWarning() << window()->type() << "windows can not be made" << newState;
+ handleWindowStateChanged(HandleUnconditionally);
return;
}
- const id sender = m_nsWindow;
+ const id sender = nsWindow;
// First we need to exit states that can't transition directly to other states
switch (currentState) {
case Qt::WindowMinimized:
- [m_nsWindow deminiaturize:sender];
+ [nsWindow deminiaturize:sender];
Q_ASSERT_X(windowState() != Qt::WindowMinimized, "QCocoaWindow",
"[NSWindow deminiaturize:] is synchronous");
break;
@@ -1999,7 +1541,7 @@ void QCocoaWindow::applyWindowState(Qt::WindowState newState)
toggleMaximized();
break;
case Qt::WindowMinimized:
- [m_nsWindow miniaturize:sender];
+ [nsWindow miniaturize:sender];
break;
case Qt::WindowNoState:
if (windowState() == Qt::WindowMaximized)
@@ -2012,27 +1554,31 @@ void QCocoaWindow::applyWindowState(Qt::WindowState newState)
void QCocoaWindow::toggleMaximized()
{
+ const NSWindow *window = m_view.window;
+
// The NSWindow needs to be resizable, otherwise the window will
// not be possible to zoom back to non-zoomed state.
- const bool wasResizable = m_nsWindow.styleMask & NSResizableWindowMask;
- m_nsWindow.styleMask |= NSResizableWindowMask;
+ const bool wasResizable = window.styleMask & NSResizableWindowMask;
+ window.styleMask |= NSResizableWindowMask;
- const id sender = m_nsWindow;
- [m_nsWindow zoom:sender];
+ const id sender = window;
+ [window zoom:sender];
if (!wasResizable)
- m_nsWindow.styleMask &= ~NSResizableWindowMask;
+ window.styleMask &= ~NSResizableWindowMask;
}
void QCocoaWindow::toggleFullScreen()
{
+ const NSWindow *window = m_view.window;
+
// The window needs to have the correct collection behavior for the
// toggleFullScreen call to have an effect. The collection behavior
// will be reset in windowDidEnterFullScreen/windowDidLeaveFullScreen.
- m_nsWindow.collectionBehavior |= NSWindowCollectionBehaviorFullScreenPrimary;
+ window.collectionBehavior |= NSWindowCollectionBehaviorFullScreenPrimary;
- const id sender = m_nsWindow;
- [m_nsWindow toggleFullScreen:sender];
+ const id sender = window;
+ [window toggleFullScreen:sender];
}
bool QCocoaWindow::isTransitioningToFullScreen() const
@@ -2060,22 +1606,12 @@ Qt::WindowState QCocoaWindow::windowState() const
return Qt::WindowNoState;
}
-void QCocoaWindow::reportCurrentWindowState(bool unconditionally)
-{
- Qt::WindowState currentState = windowState();
- if (!unconditionally && currentState == m_lastReportedWindowState)
- return;
-
- QWindowSystemInterface::handleWindowStateChanged<QWindowSystemInterface::SynchronousDelivery>(
- window(), currentState, m_lastReportedWindowState);
- m_lastReportedWindowState = currentState;
-}
-
bool QCocoaWindow::setWindowModified(bool modified)
{
- if (!m_nsWindow)
+ if (!isContentView())
return false;
- [m_nsWindow setDocumentEdited:(modified?YES:NO)];
+
+ m_view.window.documentEdited = modified;
return true;
}
@@ -2089,51 +1625,19 @@ QCocoaMenuBar *QCocoaWindow::menubar() const
return m_menubar;
}
-// Finds the effective cursor for this window by walking up the
-// ancestor chain (including this window) until a set cursor is
-// found. Returns nil if there is not set cursor.
-NSCursor *QCocoaWindow::effectiveWindowCursor() const
-{
-
- if (m_windowCursor)
- return m_windowCursor;
- if (!QPlatformWindow::parent())
- return nil;
- return static_cast<QCocoaWindow *>(QPlatformWindow::parent())->effectiveWindowCursor();
-}
-
-// Applies the cursor as returned by effectiveWindowCursor(), handles
-// the special no-cursor-set case by setting the arrow cursor.
-void QCocoaWindow::applyEffectiveWindowCursor()
-{
- NSCursor *effectiveCursor = effectiveWindowCursor();
- if (effectiveCursor) {
- [effectiveCursor set];
- } else {
- // We wold like to _unset_ the cursor here; but there is no such
- // API. Fall back to setting the default arrow cursor.
- [[NSCursor arrowCursor] set];
- }
-}
-
void QCocoaWindow::setWindowCursor(NSCursor *cursor)
{
- if (m_windowCursor == cursor)
+ // Setting a cursor in a foreign view is not supported
+ if (isForeignWindow())
return;
- // Setting a cursor in a foregin view is not supported.
- if (isForeignWindow())
+ QNSView *view = qnsview_cast(m_view);
+ if (cursor == view.cursor)
return;
- [m_windowCursor release];
- m_windowCursor = cursor;
- [m_windowCursor retain];
+ view.cursor = cursor;
- // The installed view tracking area (see QNSView updateTrackingAreas) will
- // handle cursor updates on mouse enter/leave. Handle the case where the
- // mouse is on the this window by changing the cursor immediately.
- if (m_windowUnderMouse)
- applyEffectiveWindowCursor();
+ [m_view.window invalidateCursorRectsForView:m_view];
}
void QCocoaWindow::registerTouch(bool enable)
@@ -2152,35 +1656,38 @@ void QCocoaWindow::setContentBorderThickness(int topThickness, int bottomThickne
bool enable = (topThickness > 0 || bottomThickness > 0);
m_drawContentBorderGradient = enable;
- applyContentBorderThickness(m_nsWindow);
+ applyContentBorderThickness();
}
void QCocoaWindow::registerContentBorderArea(quintptr identifier, int upper, int lower)
{
m_contentBorderAreas.insert(identifier, BorderRange(identifier, upper, lower));
- applyContentBorderThickness(m_nsWindow);
+ applyContentBorderThickness();
}
void QCocoaWindow::setContentBorderAreaEnabled(quintptr identifier, bool enable)
{
m_enabledContentBorderAreas.insert(identifier, enable);
- applyContentBorderThickness(m_nsWindow);
+ applyContentBorderThickness();
}
void QCocoaWindow::setContentBorderEnabled(bool enable)
{
m_drawContentBorderGradient = enable;
- applyContentBorderThickness(m_nsWindow);
+ applyContentBorderThickness();
}
void QCocoaWindow::applyContentBorderThickness(NSWindow *window)
{
+ if (!window && isContentView())
+ window = m_view.window;
+
if (!window)
return;
if (!m_drawContentBorderGradient) {
- [window setStyleMask:[window styleMask] & ~NSTexturedBackgroundWindowMask];
- [[[window contentView] superview] setNeedsDisplay:YES];
+ window.styleMask = window.styleMask & ~NSTexturedBackgroundWindowMask;
+ [window.contentView.superview setNeedsDisplay:YES];
window.titlebarAppearsTransparent = NO;
return;
}
@@ -2219,22 +1726,23 @@ void QCocoaWindow::applyContentBorderThickness(NSWindow *window)
void QCocoaWindow::updateNSToolbar()
{
- if (!m_nsWindow)
+ if (!isContentView())
return;
NSToolbar *toolbar = QCocoaIntegration::instance()->toolbar(window());
+ const NSWindow *window = m_view.window;
- if ([m_nsWindow toolbar] == toolbar)
+ if (window.toolbar == toolbar)
return;
- [m_nsWindow setToolbar: toolbar];
- [m_nsWindow setShowsToolbarButton:YES];
+ window.toolbar = toolbar;
+ window.showsToolbarButton = YES;
}
bool QCocoaWindow::testContentBorderAreaPosition(int position) const
{
- return m_nsWindow && m_drawContentBorderGradient &&
- 0 <= position && position < [m_nsWindow contentBorderThicknessForEdge: NSMaxYEdge];
+ return isContentView() && m_drawContentBorderGradient &&
+ 0 <= position && position < [m_view.window contentBorderThicknessForEdge:NSMaxYEdge];
}
qreal QCocoaWindow::devicePixelRatio() const
@@ -2248,85 +1756,6 @@ qreal QCocoaWindow::devicePixelRatio() const
return backingSize.height;
}
-// Returns whether the window can be expose, which it can
-// if it is on screen and has a valid geometry.
-bool QCocoaWindow::isWindowExposable()
-{
- QSize size = geometry().size();
- bool validGeometry = (size.width() > 0 && size.height() > 0);
- bool validScreen = ([[m_view window] screen] != 0);
- bool nonHiddenSuperView = ![[m_view superview] isHidden];
- return (validGeometry && validScreen && nonHiddenSuperView);
-}
-
-// Exposes the window by posting an expose event to QWindowSystemInterface
-void QCocoaWindow::exposeWindow()
-{
- m_geometryUpdateExposeAllowed = true;
-
- if (!isWindowExposable())
- return;
-
- if (window()->isTopLevel()) {
- // Update the QWindow's screen property. This property is set
- // to QGuiApplication::primaryScreen() at QWindow construciton
- // time, and we won't get a NSWindowDidChangeScreenNotification
- // on show. The case where the window is initially displayed
- // on a non-primary screen needs special handling here.
- if (QCocoaScreen *cocoaScreen = QCocoaIntegration::instance()->screenForNSScreen(m_nsWindow.screen))
- window()->setScreen(cocoaScreen->screen());
- }
-
- if (!m_isExposed) {
- m_isExposed = true;
- m_exposedGeometry = geometry();
- m_exposedDevicePixelRatio = devicePixelRatio();
- QRect geometry(QPoint(0, 0), m_exposedGeometry.size());
- qCDebug(lcQpaCocoaWindow) << "QCocoaWindow: exposeWindow" << window() << geometry;
- QWindowSystemInterface::handleExposeEvent(window(), geometry);
- }
-}
-
-// Obscures the window by posting an empty expose event to QWindowSystemInterface
-void QCocoaWindow::obscureWindow()
-{
- if (m_isExposed) {
- m_geometryUpdateExposeAllowed = false;
- m_isExposed = false;
-
- qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::obscureWindow" << window();
- QWindowSystemInterface::handleExposeEvent(window(), QRegion());
- }
-}
-
-// Updates window geometry by posting an expose event to QWindowSystemInterface
-void QCocoaWindow::updateExposedGeometry()
-{
- // updateExposedGeometry is not allowed to send the initial expose. If you want
- // that call exposeWindow();
- if (!m_geometryUpdateExposeAllowed)
- return;
-
- // Do not send incorrect exposes in case the window is not even visible yet.
- // We might get here as a result of a resize() from QWidget's show(), for instance.
- if (!window()->isVisible())
- return;
-
- if (!isWindowExposable())
- return;
-
- if (m_exposedGeometry.size() == geometry().size() && m_exposedDevicePixelRatio == devicePixelRatio())
- return;
-
- m_isExposed = true;
- m_exposedGeometry = geometry();
- m_exposedDevicePixelRatio = devicePixelRatio();
-
- QRect geometry(QPoint(0, 0), m_exposedGeometry.size());
- qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::updateExposedGeometry" << window() << geometry;
- QWindowSystemInterface::handleExposeEvent(window(), geometry);
-}
-
QWindow *QCocoaWindow::childWindowAt(QPoint windowPoint)
{
QWindow *targetWindow = window();
@@ -2376,8 +1805,11 @@ QPoint QCocoaWindow::bottomLeftClippedByNSWindowOffset() const
QMargins QCocoaWindow::frameMargins() const
{
- NSRect frameW = [m_nsWindow frame];
- NSRect frameC = [m_nsWindow contentRectForFrameRect:frameW];
+ if (!isContentView())
+ return QMargins();
+
+ NSRect frameW = m_view.window.frame;
+ NSRect frameC = [m_view.window contentRectForFrameRect:frameW];
return QMargins(frameW.origin.x - frameC.origin.x,
(frameW.origin.y + frameW.size.height) - (frameC.origin.y + frameC.size.height),
@@ -2390,4 +1822,19 @@ void QCocoaWindow::setFrameStrutEventsEnabled(bool enabled)
m_frameStrutEventsEnabled = enabled;
}
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug debug, const QCocoaWindow *window)
+{
+ QDebugStateSaver saver(debug);
+ debug.nospace();
+ debug << "QCocoaWindow(" << (const void *)window;
+ if (window)
+ debug << ", window=" << window->window();
+ debug << ')';
+ return debug;
+}
+#endif // !QT_NO_DEBUG_STREAM
+
#include "moc_qcocoawindow.cpp"
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/cocoa/qmultitouch_mac.mm b/src/plugins/platforms/cocoa/qmultitouch_mac.mm
index f0ea3b1e66..9eca4d2d43 100644
--- a/src/plugins/platforms/cocoa/qmultitouch_mac.mm
+++ b/src/plugins/platforms/cocoa/qmultitouch_mac.mm
@@ -39,10 +39,15 @@
#include "qmultitouch_mac_p.h"
#include "qcocoahelpers.h"
+#include "qcocoascreen.h"
+#include <private/qtouchdevice_p.h>
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(lcInputDevices, "qt.qpa.input.devices")
+
QHash<qint64, QCocoaTouch*> QCocoaTouch::_currentTouches;
+QHash<quint64, QTouchDevice*> QCocoaTouch::_touchDevices;
QPointF QCocoaTouch::_screenReferencePos;
QPointF QCocoaTouch::_trackpadReferencePos;
int QCocoaTouch::_idAssignmentCount = 0;
@@ -79,7 +84,7 @@ void QCocoaTouch::updateTouchData(NSTouch *nstouch, NSTouchPhase phase)
if (_touchPoint.id == 0 && phase == NSTouchPhaseBegan) {
_trackpadReferencePos = qnpos;
- _screenReferencePos = qt_mac_flipPoint([NSEvent mouseLocation]);
+ _screenReferencePos = QCocoaScreen::mapFromNative([NSEvent mouseLocation]);
}
QPointF screenPos = _screenReferencePos;
@@ -209,4 +214,19 @@ QCocoaTouch::getCurrentTouchPointList(NSEvent *event, bool acceptSingleTouch)
return touchPoints.values();
}
+QTouchDevice *QCocoaTouch::getTouchDevice(QTouchDevice::DeviceType type, quint64 id)
+{
+ QTouchDevice *ret = _touchDevices.value(id);
+ if (!ret) {
+ ret = new QTouchDevice;
+ ret->setType(type);
+ ret->setCapabilities(QTouchDevice::Position | QTouchDevice::NormalizedPosition | QTouchDevice::MouseEmulation);
+ QWindowSystemInterface::registerTouchDevice(ret);
+ _touchDevices.insert(id, ret);
+ qCDebug(lcInputDevices) << "touch device" << id << "of type" << type
+ << "registered as Qt device" << QTouchDevicePrivate::get(ret)->id;
+ }
+ return ret;
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/cocoa/qmultitouch_mac_p.h b/src/plugins/platforms/cocoa/qmultitouch_mac_p.h
index 77af86c9c7..044bcd1882 100644
--- a/src/plugins/platforms/cocoa/qmultitouch_mac_p.h
+++ b/src/plugins/platforms/cocoa/qmultitouch_mac_p.h
@@ -66,8 +66,10 @@ class QCocoaTouch
public:
static QList<QWindowSystemInterface::TouchPoint> getCurrentTouchPointList(NSEvent *event, bool acceptSingleTouch);
static void setMouseInDraggingState(bool inDraggingState);
+ static QTouchDevice *getTouchDevice(QTouchDevice::DeviceType type, quint64 id);
private:
+ static QHash<quint64, QTouchDevice*> _touchDevices;
static QHash<qint64, QCocoaTouch*> _currentTouches;
static QPointF _screenReferencePos;
static QPointF _trackpadReferencePos;
diff --git a/src/plugins/platforms/cocoa/qnsview.h b/src/plugins/platforms/cocoa/qnsview.h
index 384f14ba3a..f8903725a6 100644
--- a/src/plugins/platforms/cocoa/qnsview.h
+++ b/src/plugins/platforms/cocoa/qnsview.h
@@ -51,19 +51,12 @@
QT_BEGIN_NAMESPACE
class QCocoaWindow;
-class QCocoaBackingStore;
class QCocoaGLContext;
QT_END_NAMESPACE
Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(QNSViewMouseMoveHelper));
@interface QT_MANGLE_NAMESPACE(QNSView) : NSView <NSTextInputClient> {
- QCocoaBackingStore* m_backingStore;
- QPoint m_backingStoreOffset;
- QRegion m_maskRegion;
- CGImageRef m_maskImage;
- uchar *m_maskData;
- bool m_shouldInvalidateWindowShadow;
QPointer<QCocoaWindow> m_platformWindow;
NSTrackingArea *m_trackingArea;
Qt::MouseButtons m_buttons;
@@ -73,6 +66,7 @@ Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(QNSViewMouseMoveHelper));
QPointer<QObject> m_composingFocusObject;
bool m_sendKeyEvent;
QStringList *currentCustomDragTypes;
+ bool m_dontOverrideCtrlLMB;
bool m_sendUpAsRightButton;
Qt::KeyboardModifiers currentWheelModifiers;
#ifndef QT_NO_OPENGL
@@ -89,28 +83,22 @@ Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(QNSViewMouseMoveHelper));
QSet<quint32> m_acceptedKeyDowns;
}
+@property (nonatomic, retain) NSCursor *cursor;
+
- (id)init;
- (id)initWithCocoaWindow:(QCocoaWindow *)platformWindow;
#ifndef QT_NO_OPENGL
- (void)setQCocoaGLContext:(QCocoaGLContext *)context;
#endif
-- (void)flushBackingStore:(QCocoaBackingStore *)backingStore region:(const QRegion &)region offset:(QPoint)offset;
-- (void)clearBackingStore:(QCocoaBackingStore *)backingStore;
-- (void)setMaskRegion:(const QRegion *)region;
-- (void)invalidateWindowShadowIfNeeded;
- (void)drawRect:(NSRect)dirtyRect;
-- (void)drawBackingStoreUsingCoreGraphics:(NSRect)dirtyRect;
-- (void)updateGeometry;
- (void)textInputContextKeyboardSelectionDidChangeNotification : (NSNotification *) textInputContextKeyboardSelectionDidChangeNotification;
- (void)viewDidHide;
-- (void)viewDidUnhide;
- (void)removeFromSuperview;
- (void)cancelComposingText;
- (BOOL)isFlipped;
- (BOOL)acceptsFirstResponder;
- (BOOL)becomeFirstResponder;
-- (BOOL)hasMask;
- (BOOL)isOpaque;
- (void)convertFromScreen:(NSPoint)mouseLocation toWindowPoint:(QPointF *)qtWindowPoint andScreenPoint:(QPointF *)qtScreenPoint;
diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm
index 78287b482c..216b2f1287 100644
--- a/src/plugins/platforms/cocoa/qnsview.mm
+++ b/src/plugins/platforms/cocoa/qnsview.mm
@@ -42,6 +42,7 @@
#include "qnsview.h"
#include "qcocoawindow.h"
#include "qcocoahelpers.h"
+#include "qcocoascreen.h"
#include "qmultitouch_mac_p.h"
#include "qcocoadrag.h"
#include "qcocoainputcontext.h"
@@ -53,6 +54,7 @@
#include <QtCore/qsysinfo.h>
#include <private/qguiapplication_p.h>
#include <private/qcoregraphics_p.h>
+#include <private/qwindow_p.h>
#include "qcocoabackingstore.h"
#ifndef QT_NO_OPENGL
#include "qcocoaglcontext.h"
@@ -69,16 +71,6 @@ Q_LOGGING_CATEGORY(lcQpaGestures, "qt.qpa.input.gestures")
#endif
Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet")
-static QTouchDevice *touchDevice = 0;
-
-static bool _q_dontOverrideCtrlLMB = false;
-
-@interface NSEvent (Qt_Compile_Leopard_DeviceDelta)
- - (CGFloat)deviceDeltaX;
- - (CGFloat)deviceDeltaY;
- - (CGFloat)deviceDeltaZ;
-@end
-
@interface QT_MANGLE_NAMESPACE(QNSViewMouseMoveHelper) : NSObject
{
QNSView *view;
@@ -121,7 +113,7 @@ static bool _q_dontOverrideCtrlLMB = false;
- (void)cursorUpdate:(NSEvent *)theEvent
{
- [self cursorUpdate:theEvent];
+ [view cursorUpdate:theEvent];
}
@end
@@ -133,17 +125,9 @@ static bool _q_dontOverrideCtrlLMB = false;
@implementation QT_MANGLE_NAMESPACE(QNSView)
-+ (void)initialize
-{
- _q_dontOverrideCtrlLMB = qt_mac_resolveOption(false, "QT_MAC_DONT_OVERRIDE_CTRL_LMB");
-}
-
- (id) init
{
if (self = [super initWithFrame:NSZeroRect]) {
- m_backingStore = 0;
- m_maskImage = 0;
- m_shouldInvalidateWindowShadow = false;
m_buttons = Qt::NoButton;
m_acceptedMouseDowns = Qt::NoButton;
m_frameStrutButtons = Qt::NoButton;
@@ -153,6 +137,7 @@ static bool _q_dontOverrideCtrlLMB = false;
m_shouldSetGLContextinDrawRect = false;
#endif
currentCustomDragTypes = 0;
+ m_dontOverrideCtrlLMB = false;
m_sendUpAsRightButton = false;
m_inputSource = 0;
m_mouseMoveHelper = [[QT_MANGLE_NAMESPACE(QNSViewMouseMoveHelper) alloc] initWithView:self];
@@ -160,28 +145,19 @@ static bool _q_dontOverrideCtrlLMB = false;
m_scrolling = false;
m_updatingDrag = false;
m_currentlyInterpretedKeyEvent = 0;
-
- if (!touchDevice) {
- touchDevice = new QTouchDevice;
- touchDevice->setType(QTouchDevice::TouchPad);
- touchDevice->setCapabilities(QTouchDevice::Position | QTouchDevice::NormalizedPosition | QTouchDevice::MouseEmulation);
- QWindowSystemInterface::registerTouchDevice(touchDevice);
- }
-
m_isMenuView = false;
self.focusRingType = NSFocusRingTypeNone;
+ self.cursor = nil;
}
return self;
}
- (void)dealloc
{
- CGImageRelease(m_maskImage);
if (m_trackingArea) {
[self removeTrackingArea:m_trackingArea];
[m_trackingArea release];
}
- m_maskImage = 0;
[m_inputSource release];
[[NSNotificationCenter defaultCenter] removeObserver:self];
[m_mouseMoveHelper release];
@@ -199,6 +175,7 @@ static bool _q_dontOverrideCtrlLMB = false;
m_platformWindow = platformWindow;
m_sendKeyEvent = false;
+ m_dontOverrideCtrlLMB = qt_mac_resolveOption(false, platformWindow->window(), "_q_platform_MacDontOverrideCtrlLMB", "QT_MAC_DONT_OVERRIDE_CTRL_LMB");
m_trackingArea = nil;
#ifdef QT_COCOA_ENABLE_ACCESSIBILITY_INSPECTOR
@@ -226,6 +203,22 @@ static bool _q_dontOverrideCtrlLMB = false;
return self;
}
+- (NSString *)description
+{
+ NSMutableString *description = [NSMutableString stringWithString:[super description]];
+
+#ifndef QT_NO_DEBUG_STREAM
+ QString platformWindowDescription;
+ QDebug debug(&platformWindowDescription);
+ debug.nospace() << "; " << m_platformWindow << ">";
+
+ NSRange lastCharacter = [description rangeOfComposedCharacterSequenceAtIndex:description.length - 1];
+ [description replaceCharactersInRange:lastCharacter withString:platformWindowDescription.toNSString()];
+#endif
+
+ return description;
+}
+
#ifndef QT_NO_OPENGL
- (void) setQCocoaGLContext:(QCocoaGLContext *)context
{
@@ -249,18 +242,13 @@ static bool _q_dontOverrideCtrlLMB = false;
if ([self superview]) {
m_platformWindow->m_viewIsEmbedded = true;
QWindowSystemInterface::handleGeometryChange(m_platformWindow->window(), m_platformWindow->geometry());
- m_platformWindow->updateExposedGeometry();
+ [self setNeedsDisplay:YES];
QWindowSystemInterface::flushWindowSystemEvents();
} else {
m_platformWindow->m_viewIsEmbedded = false;
}
}
-- (void)viewDidMoveToWindow
-{
- m_backingStore = Q_NULLPTR;
-}
-
- (QWindow *)topLevelWindow
{
if (!m_platformWindow)
@@ -279,77 +267,6 @@ static bool _q_dontOverrideCtrlLMB = false;
return focusWindow;
}
-- (void)updateGeometry
-{
- if (!m_platformWindow)
- return;
-
- QRect geometry;
-
- if (self.window.parentWindow) {
- return;
-#if 0
- //geometry = QRectF::fromCGRect([self frame]).toRect();
- qDebug() << "nsview updateGeometry" << m_platformWindow->window();
- QRect screenRect = QRectF::fromCGRect([m_platformWindow->m_nsWindow convertRectToScreen:[self frame]]).toRect();
- qDebug() << "screenRect" << screenRect;
-
- screenRect.moveTop(qt_mac_flipYCoordinate(screenRect.y() + screenRect.height()));
- geometry = QRect(m_platformWindow->window()->parent()->mapFromGlobal(screenRect.topLeft()), screenRect.size());
- qDebug() << "geometry" << geometry;
-#endif
- //geometry = QRect(screenRect.origin.x, qt_mac_flipYCoordinate(screenRect.origin.y + screenRect.size.height), screenRect.size.width, screenRect.size.height);
- } else if (m_platformWindow->m_nsWindow) {
- // top level window, get window rect and flip y.
- NSRect rect = [self frame];
- NSRect windowRect = [[self window] frame];
- geometry = QRect(windowRect.origin.x, qt_mac_flipYCoordinate(windowRect.origin.y + rect.size.height), rect.size.width, rect.size.height);
- } else if (m_platformWindow->m_viewIsToBeEmbedded) {
- // embedded child window, use the frame rect ### merge with case below
- geometry = QRectF::fromCGRect(NSRectToCGRect([self bounds])).toRect();
- } else {
- // child window, use the frame rect
- geometry = QRectF::fromCGRect(NSRectToCGRect([self frame])).toRect();
- }
-
- if (m_platformWindow->m_nsWindow && geometry == m_platformWindow->geometry())
- return;
-
- const bool isResize = geometry.size() != m_platformWindow->geometry().size();
-
- // It can happen that self.window is nil (if we are changing
- // styleMask from/to borderless and content view is being re-parented)
- // - this results in an invalid coordinates.
- if (m_platformWindow->m_inSetStyleMask && !self.window)
- return;
-
- qCDebug(lcQpaCocoaWindow) << "[QNSView udpateGeometry:]" << m_platformWindow->window()
- << "current" << m_platformWindow->geometry() << "new" << geometry;
-
- // Call setGeometry on QPlatformWindow. (not on QCocoaWindow,
- // doing that will initiate a geometry change it and possibly create
- // an infinite loop when this notification is triggered again.)
- m_platformWindow->QPlatformWindow::setGeometry(geometry);
-
- // Don't send the geometry change if the QWindow is designated to be
- // embedded in a foreign view hiearchy but has not actually been
- // embedded yet - it's too early.
- if (m_platformWindow->m_viewIsToBeEmbedded && !m_platformWindow->m_viewIsEmbedded)
- return;
-
- // Send a geometry change event to Qt, if it's ready to handle events
- if (!m_platformWindow->m_inConstructor) {
- QWindowSystemInterface::handleGeometryChange(m_platformWindow->window(), geometry);
- m_platformWindow->updateExposedGeometry();
- // Guard against processing window system events during QWindow::setGeometry
- // calles, which Qt and Qt applications do not excpect.
- if (!m_platformWindow->m_inSetGeometry)
- QWindowSystemInterface::flushWindowSystemEvents();
- else if (isResize)
- m_backingStore = 0;
- }
-}
-
- (void)textInputContextKeyboardSelectionDidChangeNotification : (NSNotification *) textInputContextKeyboardSelectionDidChangeNotification
{
Q_UNUSED(textInputContextKeyboardSelectionDidChangeNotification)
@@ -361,12 +278,13 @@ static bool _q_dontOverrideCtrlLMB = false;
- (void)viewDidHide
{
- m_platformWindow->obscureWindow();
-}
+ if (!m_platformWindow->isExposed())
+ return;
-- (void)viewDidUnhide
-{
- m_platformWindow->exposeWindow();
+ m_platformWindow->handleExposeEvent(QRegion());
+
+ // Note: setNeedsDisplay is automatically called for
+ // viewDidUnhide so no reason to override it here.
}
- (void)removeFromSuperview
@@ -375,35 +293,6 @@ static bool _q_dontOverrideCtrlLMB = false;
[super removeFromSuperview];
}
-- (void) flushBackingStore:(QCocoaBackingStore *)backingStore region:(const QRegion &)region offset:(QPoint)offset
-{
- qCDebug(lcQpaCocoaWindow) << "[QNSView flushBackingStore:]" << m_platformWindow->window() << region.rectCount() << region.boundingRect() << offset;
-
- m_backingStore = backingStore;
- m_backingStoreOffset = offset * m_backingStore->paintDevice()->devicePixelRatio();
-
- // Prevent buildup of NSDisplayCycle objects during setNeedsDisplayInRect, which
- // would normally be released as part of the root runloop's autorelease pool, but
- // can be kept alive during repeated painting which starve the root runloop.
- // FIXME: Move this to the event dispatcher, to cover more cases of starvation.
- // FIXME: Figure out if there's a way to detect and/or prevent runloop starvation.
- QMacAutoReleasePool pool;
-
- for (const QRect &rect : region)
- [self setNeedsDisplayInRect:NSMakeRect(rect.x(), rect.y(), rect.width(), rect.height())];
-}
-
-- (void)clearBackingStore:(QCocoaBackingStore *)backingStore
-{
- if (backingStore == m_backingStore)
- m_backingStore = 0;
-}
-
-- (BOOL) hasMask
-{
- return !m_maskRegion.isEmpty();
-}
-
- (BOOL) isOpaque
{
if (!m_platformWindow)
@@ -411,49 +300,21 @@ static bool _q_dontOverrideCtrlLMB = false;
return m_platformWindow->isOpaque();
}
-- (void) setMaskRegion:(const QRegion *)region
-{
- m_shouldInvalidateWindowShadow = true;
- m_maskRegion = *region;
- if (m_maskImage)
- CGImageRelease(m_maskImage);
- if (region->isEmpty()) {
- m_maskImage = 0;
- return;
- }
-
- const QRect &rect = region->boundingRect();
- QImage tmp(rect.size(), QImage::Format_RGB32);
- tmp.fill(Qt::white);
- QPainter p(&tmp);
- p.setClipRegion(*region);
- p.fillRect(rect, Qt::black);
- p.end();
- QImage maskImage = QImage(rect.size(), QImage::Format_Indexed8);
- for (int y=0; y<rect.height(); ++y) {
- const uint *src = (const uint *) tmp.constScanLine(y);
- uchar *dst = maskImage.scanLine(y);
- for (int x=0; x<rect.width(); ++x) {
- dst[x] = src[x] & 0xff;
- }
- }
- m_maskImage = qt_mac_toCGImageMask(maskImage);
-}
-
-- (void)invalidateWindowShadowIfNeeded
-{
- if (m_shouldInvalidateWindowShadow && m_platformWindow->m_nsWindow) {
- [m_platformWindow->m_nsWindow invalidateShadow];
- m_shouldInvalidateWindowShadow = false;
- }
-}
-
- (void)drawRect:(NSRect)dirtyRect
{
+ Q_UNUSED(dirtyRect);
+
if (!m_platformWindow)
return;
- qCDebug(lcQpaCocoaWindow) << "[QNSView drawRect:]" << m_platformWindow->window() << QRectF::fromCGRect(NSRectToCGRect(dirtyRect));
+ QRegion exposedRegion;
+ const NSRect *dirtyRects;
+ NSInteger numDirtyRects;
+ [self getRectsBeingDrawn:&dirtyRects count:&numDirtyRects];
+ for (int i = 0; i < numDirtyRects; ++i)
+ exposedRegion += QRectF::fromCGRect(dirtyRects[i]).toRect();
+
+ qCDebug(lcQpaCocoaDrawing) << "[QNSView drawRect:]" << m_platformWindow->window() << exposedRegion;
#ifndef QT_NO_OPENGL
if (m_glContext && m_shouldSetGLContextinDrawRect) {
@@ -462,85 +323,44 @@ static bool _q_dontOverrideCtrlLMB = false;
}
#endif
- if (m_platformWindow->m_drawContentBorderGradient)
- NSDrawWindowBackground(dirtyRect);
-
- if (m_backingStore)
- [self drawBackingStoreUsingCoreGraphics:dirtyRect];
+ m_platformWindow->handleExposeEvent(exposedRegion);
+
+ if (qt_window_private(m_platformWindow->window())->updateRequestPending) {
+ // A call to QWindow::requestUpdate was issued during the expose event, or we
+ // had to deliver a real expose event and still need to deliver the update.
+ // But AppKit will reset the needsDisplay state of the view after completing
+ // the current display cycle, so we need to defer the request to redisplay.
+ // FIXME: Perhaps this should be a trigger to enable CADisplayLink?
+ qCDebug(lcQpaCocoaDrawing) << "[QNSView drawRect:] issuing deferred setNeedsDisplay due to pending update request";
+ dispatch_async(dispatch_get_main_queue (), ^{
+ [self setNeedsDisplay:YES];
+ });
+ }
+}
- [self invalidateWindowShadowIfNeeded];
+- (BOOL)wantsUpdateLayer
+{
+ return YES;
}
-// Draws the backing store content to the QNSView using Core Graphics.
-// This function assumes that the QNSView is in a configuration that
-// supports Core Graphics, such as "classic" mode or layer mode with
-// the default layer.
-- (void)drawBackingStoreUsingCoreGraphics:(NSRect)dirtyRect
+- (void)updateLayer
{
- if (!m_backingStore)
+ if (!m_platformWindow)
return;
- // Calculate source and target rects. The target rect is the dirtyRect:
- CGRect dirtyWindowRect = NSRectToCGRect(dirtyRect);
-
- // The backing store source rect will be larger on retina displays.
- // Scale dirtyRect by the device pixel ratio:
- const qreal devicePixelRatio = m_backingStore->paintDevice()->devicePixelRatio();
- CGRect dirtyBackingRect = CGRectMake(dirtyRect.origin.x * devicePixelRatio,
- dirtyRect.origin.y * devicePixelRatio,
- dirtyRect.size.width * devicePixelRatio,
- dirtyRect.size.height * devicePixelRatio);
-
- NSGraphicsContext *nsGraphicsContext = [NSGraphicsContext currentContext];
- CGContextRef cgContext = (CGContextRef) [nsGraphicsContext graphicsPort];
-
- // Translate coordiate system from CoreGraphics (bottom-left) to NSView (top-left):
- CGContextSaveGState(cgContext);
- int dy = dirtyWindowRect.origin.y + CGRectGetMaxY(dirtyWindowRect);
-
- CGContextTranslateCTM(cgContext, 0, dy);
- CGContextScaleCTM(cgContext, 1, -1);
-
- // If a mask is set, modify the sub image accordingly:
- CGImageRef subMask = 0;
- if (m_maskImage) {
- subMask = CGImageCreateWithImageInRect(m_maskImage, dirtyWindowRect);
- CGContextClipToMask(cgContext, dirtyWindowRect, subMask);
- }
-
- // Clip out and draw the correct sub image from the (shared) backingstore:
- CGRect backingStoreRect = CGRectMake(
- dirtyBackingRect.origin.x + m_backingStoreOffset.x(),
- dirtyBackingRect.origin.y + m_backingStoreOffset.y(),
- dirtyBackingRect.size.width,
- dirtyBackingRect.size.height
- );
- CGImageRef bsCGImage = qt_mac_toCGImage(m_backingStore->toImage());
-
- // Prevent potentially costly color conversion by assiging the display
- // color space to the backingstore image.
- CGImageRef displayColorSpaceImage = CGImageCreateCopyWithColorSpace(bsCGImage,
- self.window.screen.colorSpace.CGColorSpace);
-
- CGImageRef cleanImg = CGImageCreateWithImageInRect(displayColorSpaceImage, backingStoreRect);
-
- // Optimization: Copy frame buffer content instead of blending for
- // top-level windows where Qt fills the entire window content area.
- // (But don't overpaint the title-bar gradient)
- if (m_platformWindow->m_nsWindow && !m_platformWindow->m_drawContentBorderGradient)
- CGContextSetBlendMode(cgContext, kCGBlendModeCopy);
+ qCDebug(lcQpaCocoaDrawing) << "[QNSView updateLayer]" << m_platformWindow->window();
- CGContextDrawImage(cgContext, dirtyWindowRect, cleanImg);
+ // FIXME: Find out if there's a way to resolve the dirty rect like in drawRect:
+ m_platformWindow->handleExposeEvent(QRectF::fromCGRect(self.bounds).toRect());
+}
- // Clean-up:
- CGContextRestoreGState(cgContext);
- CGImageRelease(cleanImg);
- CGImageRelease(subMask);
- CGImageRelease(bsCGImage);
- CGImageRelease(displayColorSpaceImage);
+- (void)viewDidChangeBackingProperties
+{
+ if (self.layer)
+ self.layer.contentsScale = self.window.backingScaleFactor;
}
-- (BOOL) isFlipped
+- (BOOL)isFlipped
{
return YES;
}
@@ -623,8 +443,7 @@ static bool _q_dontOverrideCtrlLMB = false;
nsWindowPoint = windowRect.origin; // NSWindow coordinates
NSPoint nsViewPoint = [self convertPoint: nsWindowPoint fromView: nil]; // NSView/QWindow coordinates
*qtWindowPoint = QPointF(nsViewPoint.x, nsViewPoint.y); // NSView/QWindow coordinates
-
- *qtScreenPoint = QPointF(mouseLocation.x, qt_mac_flipYCoordinate(mouseLocation.y)); // Qt screen coordinates
+ *qtScreenPoint = QCocoaScreen::mapFromNative(mouseLocation);
}
- (void)resetMouseButtons
@@ -661,12 +480,6 @@ static bool _q_dontOverrideCtrlLMB = false;
QPointF qtWindowPoint;
QPointF qtScreenPoint;
QNSView *targetView = self;
- if (m_platformWindow && m_platformWindow->m_forwardWindow) {
- if (theEvent.type == NSLeftMouseDragged || theEvent.type == NSLeftMouseUp)
- targetView = qnsview_cast(m_platformWindow->m_forwardWindow->view());
- else
- m_platformWindow->m_forwardWindow.clear();
- }
if (!targetView.platformWindow)
return;
@@ -741,7 +554,7 @@ static bool _q_dontOverrideCtrlLMB = false;
NSPoint nsViewPoint = [self convertPoint: windowPoint fromView: nil];
QPoint qtWindowPoint = QPoint(nsViewPoint.x, titleBarHeight + nsViewPoint.y);
NSPoint screenPoint = [window convertRectToScreen:NSMakeRect(windowPoint.x, windowPoint.y, 0, 0)].origin;
- QPoint qtScreenPoint = QPoint(screenPoint.x, qt_mac_flipYCoordinate(screenPoint.y));
+ QPoint qtScreenPoint = QCocoaScreen::mapFromNative(screenPoint).toPoint();
ulong timestamp = [theEvent timestamp] * 1000;
QWindowSystemInterface::handleFrameStrutMouseEvent(m_platformWindow->window(), timestamp, qtWindowPoint, qtScreenPoint, m_frameStrutButtons);
@@ -760,7 +573,8 @@ static bool _q_dontOverrideCtrlLMB = false;
Q_UNUSED(qtScreenPoint);
// Maintain masked state for the button for use by MouseDragged and MouseUp.
- const bool masked = [self hasMask] && !m_maskRegion.contains(qtWindowPoint.toPoint());
+ QRegion mask = m_platformWindow->window()->mask();
+ const bool masked = !mask.isEmpty() && !mask.contains(qtWindowPoint.toPoint());
if (masked)
m_acceptedMouseDowns &= ~button;
else
@@ -832,7 +646,7 @@ static bool _q_dontOverrideCtrlLMB = false;
if (!popups->isEmpty()) {
// Check if the click is outside all popups.
bool inside = false;
- QPointF qtScreenPoint = qt_mac_flipPoint([self screenMousePoint:theEvent]);
+ QPointF qtScreenPoint = QCocoaScreen::mapFromNative([self screenMousePoint:theEvent]);
for (QList<QCocoaWindow *>::const_iterator it = popups->begin(); it != popups->end(); ++it) {
if ((*it)->geometry().contains(qtScreenPoint.toPoint())) {
inside = true;
@@ -858,8 +672,8 @@ static bool _q_dontOverrideCtrlLMB = false;
[self convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint:&qtWindowPoint andScreenPoint:&qtScreenPoint];
Q_UNUSED(qtScreenPoint);
- const bool masked = [self hasMask] && !m_maskRegion.contains(qtWindowPoint.toPoint());
-
+ QRegion mask = m_platformWindow->window()->mask();
+ const bool masked = !mask.isEmpty() && !mask.contains(qtWindowPoint.toPoint());
// Maintain masked state for the button for use by MouseDragged and Up.
if (masked)
m_acceptedMouseDowns &= ~Qt::LeftButton;
@@ -875,7 +689,8 @@ static bool _q_dontOverrideCtrlLMB = false;
if ([self hasMarkedText]) {
[[NSTextInputContext currentInputContext] handleEvent:theEvent];
} else {
- if (!_q_dontOverrideCtrlLMB && [QNSView convertKeyModifiers:[theEvent modifierFlags]] & Qt::MetaModifier) {
+ auto ctrlOrMetaModifier = qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta) ? Qt::ControlModifier : Qt::MetaModifier;
+ if (!m_dontOverrideCtrlLMB && [QNSView convertKeyModifiers:[theEvent modifierFlags]] & ctrlOrMetaModifier) {
m_buttons |= Qt::RightButton;
m_sendUpAsRightButton = true;
} else {
@@ -972,8 +787,16 @@ static bool _q_dontOverrideCtrlLMB = false;
- (void)cursorUpdate:(NSEvent *)theEvent
{
- Q_UNUSED(theEvent);
- m_platformWindow->applyEffectiveWindowCursor();
+ qCDebug(lcQpaCocoaMouse) << "[QNSView cursorUpdate:]" << self.cursor;
+
+ // Note: We do not get this callback when moving from a subview that
+ // uses the legacy cursorRect API, so the cursor is reset to the arrow
+ // cursor. See rdar://34183708
+
+ if (self.cursor)
+ [self.cursor set];
+ else
+ [super cursorUpdate:theEvent];
}
- (void)mouseMovedImpl:(NSEvent *)theEvent
@@ -994,7 +817,7 @@ static bool _q_dontOverrideCtrlLMB = false;
// the time of the leave. This is dificult to accomplish by
// handling mouseEnter and mouseLeave envents, since they are sent
// individually to different views.
- if (m_platformWindow->m_nsWindow && childWindow) {
+ if (m_platformWindow->isContentView() && childWindow) {
if (childWindow != m_platformWindow->m_enterLeaveTargetWindow) {
QWindowSystemInterface::handleEnterLeaveEvent(childWindow, m_platformWindow->m_enterLeaveTargetWindow, windowPoint, screenPoint);
m_platformWindow->m_enterLeaveTargetWindow = childWindow;
@@ -1021,7 +844,7 @@ static bool _q_dontOverrideCtrlLMB = false;
return;
// Top-level windows generate enter events for sub-windows.
- if (!m_platformWindow->m_nsWindow)
+ if (!m_platformWindow->isContentView())
return;
QPointF windowPoint;
@@ -1043,7 +866,7 @@ static bool _q_dontOverrideCtrlLMB = false;
return;
// Top-level windows generate leave events for sub-windows.
- if (!m_platformWindow->m_nsWindow)
+ if (!m_platformWindow->isContentView())
return;
QWindowSystemInterface::handleLeaveEvent(m_platformWindow->m_enterLeaveTargetWindow);
@@ -1243,8 +1066,8 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent)
const NSTimeInterval timestamp = [event timestamp];
const QList<QWindowSystemInterface::TouchPoint> points = QCocoaTouch::getCurrentTouchPointList(event, [self shouldSendSingleTouch]);
- qCDebug(lcQpaTouch) << "touchesBeganWithEvent" << points;
- QWindowSystemInterface::handleTouchEvent(m_platformWindow->window(), timestamp * 1000, touchDevice, points);
+ qCDebug(lcQpaTouch) << "touchesBeganWithEvent" << points << "from device" << hex << [event deviceID];
+ QWindowSystemInterface::handleTouchEvent(m_platformWindow->window(), timestamp * 1000, QCocoaTouch::getTouchDevice(QTouchDevice::TouchPad, [event deviceID]), points);
}
- (void)touchesMovedWithEvent:(NSEvent *)event
@@ -1254,8 +1077,8 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent)
const NSTimeInterval timestamp = [event timestamp];
const QList<QWindowSystemInterface::TouchPoint> points = QCocoaTouch::getCurrentTouchPointList(event, [self shouldSendSingleTouch]);
- qCDebug(lcQpaTouch) << "touchesMovedWithEvent" << points;
- QWindowSystemInterface::handleTouchEvent(m_platformWindow->window(), timestamp * 1000, touchDevice, points);
+ qCDebug(lcQpaTouch) << "touchesMovedWithEvent" << points << "from device" << hex << [event deviceID];
+ QWindowSystemInterface::handleTouchEvent(m_platformWindow->window(), timestamp * 1000, QCocoaTouch::getTouchDevice(QTouchDevice::TouchPad, [event deviceID]), points);
}
- (void)touchesEndedWithEvent:(NSEvent *)event
@@ -1265,8 +1088,8 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent)
const NSTimeInterval timestamp = [event timestamp];
const QList<QWindowSystemInterface::TouchPoint> points = QCocoaTouch::getCurrentTouchPointList(event, [self shouldSendSingleTouch]);
- qCDebug(lcQpaTouch) << "touchesEndedWithEvent" << points;
- QWindowSystemInterface::handleTouchEvent(m_platformWindow->window(), timestamp * 1000, touchDevice, points);
+ qCDebug(lcQpaTouch) << "touchesEndedWithEvent" << points << "from device" << hex << [event deviceID];
+ QWindowSystemInterface::handleTouchEvent(m_platformWindow->window(), timestamp * 1000, QCocoaTouch::getTouchDevice(QTouchDevice::TouchPad, [event deviceID]), points);
}
- (void)touchesCancelledWithEvent:(NSEvent *)event
@@ -1276,8 +1099,8 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent)
const NSTimeInterval timestamp = [event timestamp];
const QList<QWindowSystemInterface::TouchPoint> points = QCocoaTouch::getCurrentTouchPointList(event, [self shouldSendSingleTouch]);
- qCDebug(lcQpaTouch) << "touchesCancelledWithEvent" << points;
- QWindowSystemInterface::handleTouchEvent(m_platformWindow->window(), timestamp * 1000, touchDevice, points);
+ qCDebug(lcQpaTouch) << "touchesCancelledWithEvent" << points << "from device" << hex << [event deviceID];
+ QWindowSystemInterface::handleTouchEvent(m_platformWindow->window(), timestamp * 1000, QCocoaTouch::getTouchDevice(QTouchDevice::TouchPad, [event deviceID]), points);
}
#ifndef QT_NO_GESTURES
@@ -1307,12 +1130,12 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent)
if ([self handleGestureAsBeginEnd:event])
return;
- qCDebug(lcQpaGestures) << "magnifyWithEvent" << [event magnification];
+ qCDebug(lcQpaGestures) << "magnifyWithEvent" << [event magnification] << "from device" << hex << [event deviceID];
const NSTimeInterval timestamp = [event timestamp];
QPointF windowPoint;
QPointF screenPoint;
[self convertFromScreen:[self screenMousePoint:event] toWindowPoint:&windowPoint andScreenPoint:&screenPoint];
- QWindowSystemInterface::handleGestureEventWithRealValue(m_platformWindow->window(), timestamp, Qt::ZoomNativeGesture,
+ QWindowSystemInterface::handleGestureEventWithRealValue(m_platformWindow->window(), QCocoaTouch::getTouchDevice(QTouchDevice::TouchPad, [event deviceID]), timestamp, Qt::ZoomNativeGesture,
[event magnification], windowPoint, screenPoint);
}
@@ -1322,12 +1145,12 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent)
return;
static bool zoomIn = true;
- qCDebug(lcQpaGestures) << "smartMagnifyWithEvent" << zoomIn;
+ qCDebug(lcQpaGestures) << "smartMagnifyWithEvent" << zoomIn << "from device" << hex << [event deviceID];
const NSTimeInterval timestamp = [event timestamp];
QPointF windowPoint;
QPointF screenPoint;
[self convertFromScreen:[self screenMousePoint:event] toWindowPoint:&windowPoint andScreenPoint:&screenPoint];
- QWindowSystemInterface::handleGestureEventWithRealValue(m_platformWindow->window(), timestamp, Qt::SmartZoomNativeGesture,
+ QWindowSystemInterface::handleGestureEventWithRealValue(m_platformWindow->window(), QCocoaTouch::getTouchDevice(QTouchDevice::TouchPad, [event deviceID]), timestamp, Qt::SmartZoomNativeGesture,
zoomIn ? 1.0f : 0.0f, windowPoint, screenPoint);
zoomIn = !zoomIn;
}
@@ -1344,7 +1167,7 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent)
QPointF windowPoint;
QPointF screenPoint;
[self convertFromScreen:[self screenMousePoint:event] toWindowPoint:&windowPoint andScreenPoint:&screenPoint];
- QWindowSystemInterface::handleGestureEventWithRealValue(m_platformWindow->window(), timestamp, Qt::RotateNativeGesture,
+ QWindowSystemInterface::handleGestureEventWithRealValue(m_platformWindow->window(), QCocoaTouch::getTouchDevice(QTouchDevice::TouchPad, [event deviceID]), timestamp, Qt::RotateNativeGesture,
-[event rotation], windowPoint, screenPoint);
}
@@ -1353,7 +1176,7 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent)
if (!m_platformWindow)
return;
- qCDebug(lcQpaGestures) << "swipeWithEvent" << [event deltaX] << [event deltaY];
+ qCDebug(lcQpaGestures) << "swipeWithEvent" << [event deltaX] << [event deltaY] << "from device" << hex << [event deviceID];
const NSTimeInterval timestamp = [event timestamp];
QPointF windowPoint;
QPointF screenPoint;
@@ -1369,7 +1192,7 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent)
else if ([event deltaY] == -1)
angle = 270.0f;
- QWindowSystemInterface::handleGestureEventWithRealValue(m_platformWindow->window(), timestamp, Qt::SwipeNativeGesture,
+ QWindowSystemInterface::handleGestureEventWithRealValue(m_platformWindow->window(), QCocoaTouch::getTouchDevice(QTouchDevice::TouchPad, [event deviceID]), timestamp, Qt::SwipeNativeGesture,
angle, windowPoint, screenPoint);
}
@@ -1382,8 +1205,8 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent)
QPointF windowPoint;
QPointF screenPoint;
[self convertFromScreen:[self screenMousePoint:event] toWindowPoint:&windowPoint andScreenPoint:&screenPoint];
- qCDebug(lcQpaGestures) << "beginGestureWithEvent @" << windowPoint;
- QWindowSystemInterface::handleGestureEvent(m_platformWindow->window(), timestamp, Qt::BeginNativeGesture,
+ qCDebug(lcQpaGestures) << "beginGestureWithEvent @" << windowPoint << "from device" << hex << [event deviceID];
+ QWindowSystemInterface::handleGestureEvent(m_platformWindow->window(), QCocoaTouch::getTouchDevice(QTouchDevice::TouchPad, [event deviceID]), timestamp, Qt::BeginNativeGesture,
windowPoint, screenPoint);
}
@@ -1392,12 +1215,12 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent)
if (!m_platformWindow)
return;
- qCDebug(lcQpaGestures) << "endGestureWithEvent";
+ qCDebug(lcQpaGestures) << "endGestureWithEvent" << "from device" << hex << [event deviceID];
const NSTimeInterval timestamp = [event timestamp];
QPointF windowPoint;
QPointF screenPoint;
[self convertFromScreen:[self screenMousePoint:event] toWindowPoint:&windowPoint andScreenPoint:&screenPoint];
- QWindowSystemInterface::handleGestureEvent(m_platformWindow->window(), timestamp, Qt::EndNativeGesture,
+ QWindowSystemInterface::handleGestureEvent(m_platformWindow->window(), QCocoaTouch::getTouchDevice(QTouchDevice::TouchPad, [event deviceID]), timestamp, Qt::EndNativeGesture,
windowPoint, screenPoint);
}
#endif // QT_NO_GESTURES
@@ -1492,15 +1315,16 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent)
+ (Qt::KeyboardModifiers) convertKeyModifiers : (ulong)modifierFlags
{
+ const bool dontSwapCtrlAndMeta = qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta);
Qt::KeyboardModifiers qtMods =Qt::NoModifier;
if (modifierFlags & NSShiftKeyMask)
qtMods |= Qt::ShiftModifier;
if (modifierFlags & NSControlKeyMask)
- qtMods |= Qt::MetaModifier;
+ qtMods |= dontSwapCtrlAndMeta ? Qt::ControlModifier : Qt::MetaModifier;
if (modifierFlags & NSAlternateKeyMask)
qtMods |= Qt::AltModifier;
if (modifierFlags & NSCommandKeyMask)
- qtMods |= Qt::ControlModifier;
+ qtMods |= dontSwapCtrlAndMeta ? Qt::MetaModifier : Qt::ControlModifier;
if (modifierFlags & NSNumericPadKeyMask)
qtMods |= Qt::KeypadModifier;
return qtMods;
@@ -1533,7 +1357,8 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent)
// ALT+E to be used as a shortcut with an English keyboard even though
// pressing ALT+E will give a dead key while doing normal text input.
if ([characters length] != 0 || [charactersIgnoringModifiers length] != 0) {
- if (((modifiers & Qt::MetaModifier) || (modifiers & Qt::AltModifier)) && ([charactersIgnoringModifiers length] != 0))
+ auto ctrlOrMetaModifier = qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta) ? Qt::ControlModifier : Qt::MetaModifier;
+ if (((modifiers & ctrlOrMetaModifier) || (modifiers & Qt::AltModifier)) && ([charactersIgnoringModifiers length] != 0))
ch = QChar([charactersIgnoringModifiers characterAtIndex:0]);
else if ([characters length] != 0)
ch = QChar([characters characterAtIndex:0]);
@@ -1682,10 +1507,17 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent)
if ((delta & mac_mask) == 0u)
continue;
+ Qt::Key qtCode = modifier_key_symbols[i].qt_code;
+ if (qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) {
+ if (qtCode == Qt::Key_Meta)
+ qtCode = Qt::Key_Control;
+ else if (qtCode == Qt::Key_Control)
+ qtCode = Qt::Key_Meta;
+ }
QWindowSystemInterface::handleKeyEvent(m_platformWindow->window(),
timestamp,
(lastKnownModifiers & mac_mask) ? QEvent::KeyRelease : QEvent::KeyPress,
- modifier_key_symbols[i].qt_code,
+ qtCode,
qmodifiers ^ [QNSView convertKeyModifiers:mac_mask]);
}
}
@@ -1917,14 +1749,8 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent)
// The returned rect is always based on the internal cursor.
QRect mr = qApp->inputMethod()->cursorRectangle().toRect();
- QPoint mp = m_platformWindow->window()->mapToGlobal(mr.bottomLeft());
-
- NSRect rect;
- rect.origin.x = mp.x();
- rect.origin.y = qt_mac_flipYCoordinate(mp.y());
- rect.size.width = mr.width();
- rect.size.height = mr.height();
- return rect;
+ mr.moveBottomLeft(m_platformWindow->window()->mapToGlobal(mr.bottomLeft()));
+ return QCocoaScreen::mapToNative(mr);
}
- (NSUInteger)characterIndexForPoint:(NSPoint)aPoint
@@ -2129,7 +1955,7 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin
QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag();
if (nativeDrag->currentDrag()) {
// The drag was started from within the application
- response = QWindowSystemInterface::handleDrag(target, nativeDrag->platformDropData(), mapWindowCoordinates(m_platformWindow->window(), target, qt_windowPoint), qtAllowed);
+ response = QWindowSystemInterface::handleDrag(target, nativeDrag->dragMimeData(), mapWindowCoordinates(m_platformWindow->window(), target, qt_windowPoint), qtAllowed);
[self updateCursorFromDragResponse:response drag:nativeDrag];
} else {
QCocoaDropData mimeData([sender draggingPasteboard]);
@@ -2173,7 +1999,7 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin
QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag();
if (nativeDrag->currentDrag()) {
// The drag was started from within the application
- response = QWindowSystemInterface::handleDrop(target, nativeDrag->platformDropData(), mapWindowCoordinates(m_platformWindow->window(), target, qt_windowPoint), qtAllowed);
+ response = QWindowSystemInterface::handleDrop(target, nativeDrag->dragMimeData(), mapWindowCoordinates(m_platformWindow->window(), target, qt_windowPoint), qtAllowed);
} else {
QCocoaDropData mimeData([sender draggingPasteboard]);
response = QWindowSystemInterface::handleDrop(target, &mimeData, mapWindowCoordinates(m_platformWindow->window(), target, qt_windowPoint), qtAllowed);
@@ -2210,8 +2036,7 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin
NSPoint windowPoint = [self.window convertRectFromScreen:NSMakeRect(screenPoint.x, screenPoint.y, 1, 1)].origin;
NSPoint nsViewPoint = [self convertPoint: windowPoint fromView: nil]; // NSView/QWindow coordinates
QPoint qtWindowPoint(nsViewPoint.x, nsViewPoint.y);
-
- QPoint qtScreenPoint = QPoint(screenPoint.x, qt_mac_flipYCoordinate(screenPoint.y));
+ QPoint qtScreenPoint = QCocoaScreen::mapFromNative(screenPoint).toPoint();
QWindowSystemInterface::handleMouseEvent(target, mapWindowCoordinates(m_platformWindow->window(), target, qtWindowPoint), qtScreenPoint, m_buttons);
}
diff --git a/src/plugins/platforms/cocoa/qnswindow.h b/src/plugins/platforms/cocoa/qnswindow.h
new file mode 100644
index 0000000000..ea690b69e3
--- /dev/null
+++ b/src/plugins/platforms/cocoa/qnswindow.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNSWINDOW_H
+#define QNSWINDOW_H
+
+#include <qglobal.h>
+#include <QPointer>
+#include "qt_mac_p.h"
+
+#include <AppKit/AppKit.h>
+
+QT_FORWARD_DECLARE_CLASS(QCocoaWindow)
+
+// @compatibility_alias doesn't work with categories or their methods
+#define FullScreenProperty QT_MANGLE_NAMESPACE(FullScreenProperty)
+#define qt_fullScreen QT_MANGLE_NAMESPACE(qt_fullScreen)
+
+@interface NSWindow (FullScreenProperty)
+@property(readonly) BOOL qt_fullScreen;
+@end
+
+// @compatibility_alias doesn't work with protocols
+#define QNSWindowProtocol QT_MANGLE_NAMESPACE(QNSWindowProtocol)
+
+@protocol QNSWindowProtocol
+@optional
+- (BOOL)canBecomeKeyWindow;
+- (void)sendEvent:(NSEvent*)theEvent;
+- (void)closeAndRelease;
+- (void)dealloc;
+- (BOOL)isOpaque;
+- (NSColor *)backgroundColor;
+@property (nonatomic, readonly) QCocoaWindow *platformWindow;
+@end
+
+typedef NSWindow<QNSWindowProtocol> QCocoaNSWindow;
+
+@interface QT_MANGLE_NAMESPACE(QNSWindow) : NSWindow<QNSWindowProtocol> @end
+QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSWindow);
+
+@interface QT_MANGLE_NAMESPACE(QNSPanel) : NSPanel<QNSWindowProtocol> @end
+QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSPanel);
+
+#endif // QNSWINDOW_H
diff --git a/src/plugins/platforms/cocoa/qnswindow.mm b/src/plugins/platforms/cocoa/qnswindow.mm
new file mode 100644
index 0000000000..cb13b7d184
--- /dev/null
+++ b/src/plugins/platforms/cocoa/qnswindow.mm
@@ -0,0 +1,331 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnswindow.h"
+#include "qnswindowdelegate.h"
+#include "qcocoawindow.h"
+#include "qcocoahelpers.h"
+#include "qcocoaeventdispatcher.h"
+
+#include <qpa/qwindowsysteminterface.h>
+#include <qoperatingsystemversion.h>
+
+Q_LOGGING_CATEGORY(lcCocoaEvents, "qt.qpa.cocoa.events");
+
+static bool isMouseEvent(NSEvent *ev)
+{
+ switch ([ev type]) {
+ case NSLeftMouseDown:
+ case NSLeftMouseUp:
+ case NSRightMouseDown:
+ case NSRightMouseUp:
+ case NSMouseMoved:
+ case NSLeftMouseDragged:
+ case NSRightMouseDragged:
+ return true;
+ default:
+ return false;
+ }
+}
+
+@implementation NSWindow (FullScreenProperty)
+
++ (void)load
+{
+ NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
+ [center addObserverForName:NSWindowDidEnterFullScreenNotification object:nil queue:nil
+ usingBlock:^(NSNotification *notification) {
+ objc_setAssociatedObject(notification.object, @selector(qt_fullScreen),
+ [NSNumber numberWithBool:YES], OBJC_ASSOCIATION_RETAIN);
+ }
+ ];
+ [center addObserverForName:NSWindowDidExitFullScreenNotification object:nil queue:nil
+ usingBlock:^(NSNotification *notification) {
+ objc_setAssociatedObject(notification.object, @selector(qt_fullScreen),
+ nil, OBJC_ASSOCIATION_RETAIN);
+ }
+ ];
+}
+
+- (BOOL)qt_fullScreen
+{
+ NSNumber *number = objc_getAssociatedObject(self, @selector(qt_fullScreen));
+ return [number boolValue];
+}
+@end
+
+#define super USE_qt_objcDynamicSuper_INSTEAD
+
+@implementation QNSWindow
+
++ (void)load
+{
+ const Class windowClass = [self class];
+ const Class panelClass = [QNSPanel class];
+
+ unsigned int protocolCount;
+ Protocol **protocols = class_copyProtocolList(windowClass, &protocolCount);
+ for (unsigned int i = 0; i < protocolCount; ++i) {
+ Protocol *protocol = protocols[i];
+
+ unsigned int methodDescriptionsCount;
+ objc_method_description *methods = protocol_copyMethodDescriptionList(
+ protocol, NO, YES, &methodDescriptionsCount);
+
+ for (unsigned int j = 0; j < methodDescriptionsCount; ++j) {
+ objc_method_description method = methods[j];
+ class_addMethod(panelClass, method.name,
+ class_getMethodImplementation(windowClass, method.name),
+ method.types);
+ }
+ free(methods);
+ }
+
+ free(protocols);
+}
+
+- (QCocoaWindow *)platformWindow
+{
+ return qnsview_cast(self.contentView).platformWindow;
+}
+
+- (NSString *)description
+{
+ NSMutableString *description = [NSMutableString stringWithString:qt_objcDynamicSuper()];
+
+#ifndef QT_NO_DEBUG_STREAM
+ QString contentViewDescription;
+ QDebug debug(&contentViewDescription);
+ debug.nospace() << "; contentView=" << qnsview_cast(self.contentView) << ">";
+
+ NSRange lastCharacter = [description rangeOfComposedCharacterSequenceAtIndex:description.length - 1];
+ [description replaceCharactersInRange:lastCharacter withString:contentViewDescription.toNSString()];
+#endif
+
+ return description;
+}
+
+- (BOOL)canBecomeKeyWindow
+{
+ QCocoaWindow *pw = self.platformWindow;
+ if (!pw)
+ return NO;
+
+ if (pw->shouldRefuseKeyWindowAndFirstResponder())
+ return NO;
+
+ if ([self isKindOfClass:[QNSPanel class]]) {
+ // Only tool or dialog windows should become key:
+ Qt::WindowType type = pw->window()->type();
+ if (type == Qt::Tool || type == Qt::Dialog)
+ return YES;
+
+ return NO;
+ } else {
+ // The default implementation returns NO for title-bar less windows,
+ // override and return yes here to make sure popup windows such as
+ // the combobox popup can become the key window.
+ return YES;
+ }
+}
+
+- (BOOL)canBecomeMainWindow
+{
+ BOOL canBecomeMain = YES; // By default, windows can become the main window
+
+ // Windows with a transient parent (such as combobox popup windows)
+ // cannot become the main window:
+ QCocoaWindow *pw = self.platformWindow;
+ if (!pw || pw->window()->transientParent())
+ canBecomeMain = NO;
+
+ return canBecomeMain;
+}
+
+- (BOOL)isOpaque
+{
+ return self.platformWindow ?
+ self.platformWindow->isOpaque() : qt_objcDynamicSuper();
+}
+
+/*!
+ Borderless windows need a transparent background
+
+ Technically windows with NSTexturedBackgroundWindowMask (such
+ as windows with unified toolbars) need to draw the textured
+ background of the NSWindow, and can't have a transparent
+ background, but as NSBorderlessWindowMask is 0, you can't
+ have a window with NSTexturedBackgroundWindowMask that is
+ also borderless.
+*/
+- (NSColor *)backgroundColor
+{
+ return self.styleMask == NSBorderlessWindowMask
+ ? [NSColor clearColor] : qt_objcDynamicSuper();
+}
+
+- (void)sendEvent:(NSEvent*)theEvent
+{
+ qCDebug(lcCocoaEvents) << "Sending" << theEvent << "to" << self;
+
+ // We might get events for a NSWindow after the corresponding platform
+ // window has been deleted, as the NSWindow can outlive the QCocoaWindow
+ // e.g. if being retained by other parts of AppKit, or in an auto-release
+ // pool. We guard against this in QNSView as well, as not all callbacks
+ // come via events, but if they do there's no point in propagating them.
+ if (!self.platformWindow)
+ return;
+
+ // Prevent deallocation of this NSWindow during event delivery, as we
+ // have logic further below that depends on the window being alive.
+ [[self retain] autorelease];
+
+ const char *eventType = object_getClassName(theEvent);
+ if (QWindowSystemInterface::handleNativeEvent(self.platformWindow->window(),
+ QByteArray::fromRawData(eventType, qstrlen(eventType)), theEvent, nullptr)) {
+ return;
+ }
+
+ qt_objcDynamicSuper(theEvent);
+
+ if (!self.platformWindow)
+ return; // Platform window went away while processing event
+
+ QCocoaWindow *pw = self.platformWindow;
+ if (pw->frameStrutEventsEnabled() && isMouseEvent(theEvent)) {
+ NSPoint loc = [theEvent locationInWindow];
+ NSRect windowFrame = [self convertRectFromScreen:self.frame];
+ NSRect contentFrame = self.contentView.frame;
+ if (NSMouseInRect(loc, windowFrame, NO) && !NSMouseInRect(loc, contentFrame, NO))
+ [qnsview_cast(pw->view()) handleFrameStrutMouseEvent:theEvent];
+ }
+}
+
+- (void)closeAndRelease
+{
+ qCDebug(lcQpaCocoaWindow) << "closeAndRelease" << self;
+
+ [self.delegate release];
+ self.delegate = nil;
+
+ [self close];
+ [self release];
+}
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wobjc-missing-super-calls"
+- (void)dealloc
+{
+ qCDebug(lcQpaCocoaWindow) << "dealloc" << self;
+ qt_objcDynamicSuper();
+}
+#pragma clang diagnostic pop
+
++ (void)applicationActivationChanged:(NSNotification*)notification
+{
+ const id sender = self;
+ NSEnumerator<NSWindow*> *windowEnumerator = nullptr;
+ NSApplication *application = [NSApplication sharedApplication];
+
+#if QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_12)
+ if (__builtin_available(macOS 10.12, *)) {
+ // Unfortunately there's no NSWindowListOrderedBackToFront,
+ // so we have to manually reverse the order using an array.
+ NSMutableArray *windows = [[[NSMutableArray alloc] init] autorelease];
+ [application enumerateWindowsWithOptions:NSWindowListOrderedFrontToBack
+ usingBlock:^(NSWindow *window, BOOL *) {
+ // For some reason AppKit will give us nil-windows, skip those
+ if (!window)
+ return;
+
+ [(NSMutableArray*)windows addObject:window];
+ }
+ ];
+
+ windowEnumerator = windows.reverseObjectEnumerator;
+ } else
+#endif
+ {
+ // No way to get ordered list of windows, so fall back to unordered,
+ // list, which typically corresponds to window creation order.
+ windowEnumerator = application.windows.objectEnumerator;
+ }
+
+ for (NSWindow *window in windowEnumerator) {
+ // We're meddling with normal and floating windows, so leave others alone
+ if (!(window.level == NSNormalWindowLevel || window.level == NSFloatingWindowLevel))
+ continue;
+
+ // Windows that hide automatically will keep their NSFloatingWindowLevel,
+ // and hence be on top of the window stack. We don't want to affect these
+ // windows, as otherwise we might end up with key windows being ordered
+ // behind these auto-hidden windows when activating the application by
+ // clicking on a new tool window.
+ if (window.hidesOnDeactivate)
+ continue;
+
+ if ([window conformsToProtocol:@protocol(QNSWindowProtocol)]) {
+ QCocoaWindow *cocoaWindow = static_cast<QCocoaNSWindow *>(window).platformWindow;
+ window.level = notification.name == NSApplicationWillResignActiveNotification ?
+ NSNormalWindowLevel : cocoaWindow->windowLevel(cocoaWindow->window()->flags());
+ }
+
+ // The documentation says that "when a window enters a new level, it’s ordered
+ // in front of all its peers in that level", but that doesn't seem to be the
+ // case in practice. To keep the order correct after meddling with the window
+ // levels, we explicitly order each window to the front. Since we are iterating
+ // the windows in back-to-front order, this is okey. The call also triggers AppKit
+ // to re-evaluate the level in relation to windows from other applications,
+ // working around an issue where our tool windows would stay on top of other
+ // application windows if activation was transferred to another application by
+ // clicking on it instead of via the application switcher or Dock. Finally, we
+ // do this re-ordering for all windows (except auto-hiding ones), otherwise we would
+ // end up triggering a bug in AppKit where the tool windows would disappear behind
+ // the application window.
+ [window orderFront:sender];
+ }
+}
+
+@end
+
+@implementation QNSPanel
+// Implementation shared with QNSWindow, see +[QNSWindow load] above
+@end
+
+#undef super
diff --git a/src/plugins/platforms/cocoa/qnswindowdelegate.mm b/src/plugins/platforms/cocoa/qnswindowdelegate.mm
index 1224d138d9..6e5623d679 100644
--- a/src/plugins/platforms/cocoa/qnswindowdelegate.mm
+++ b/src/plugins/platforms/cocoa/qnswindowdelegate.mm
@@ -44,6 +44,8 @@
#include <qpa/qplatformscreen.h>
#include <qpa/qwindowsysteminterface.h>
+static QRegExp whitespaceRegex = QRegExp(QStringLiteral("\\s*"));
+
@implementation QNSWindowDelegate
- (id)initWithQCocoaWindow:(QCocoaWindow *)cocoaWindow
@@ -78,27 +80,14 @@
return NSRectFromCGRect(m_cocoaWindow->screen()->availableGeometry().toCGRect());
}
-#if QT_MACOS_DEPLOYMENT_TARGET_BELOW(__MAC_10_11)
-/*
- AppKit on OS X 10.10 wrongly calls windowWillUseStandardFrame:defaultFrame
- from -[NSWindow _frameForFullScreenMode] when going into fullscreen, resulting
- in black bars on top and bottom of the window. By implementing the following
- method, AppKit will choose that instead, and resolve the right fullscreen
- geometry.
-*/
-- (NSSize)window:(NSWindow *)window willUseFullScreenContentSize:(NSSize)proposedSize
-{
- Q_UNUSED(proposedSize);
- Q_ASSERT(window == m_cocoaWindow->nativeWindow());
- return NSSizeFromCGSize(m_cocoaWindow->screen()->geometry().size().toCGSize());
-}
-#endif
-
- (BOOL)window:(NSWindow *)window shouldPopUpDocumentPathMenu:(NSMenu *)menu
{
Q_UNUSED(window);
Q_UNUSED(menu);
- return m_cocoaWindow && m_cocoaWindow->m_hasWindowFilePath;
+
+ // Only pop up document path if the filename is non-empty. We allow whitespace, to
+ // allow faking a window icon by setting the file path to a single space character.
+ return !whitespaceRegex.exactMatch(m_cocoaWindow->window()->filePath());
}
- (BOOL)window:(NSWindow *)window shouldDragDocumentWithEvent:(NSEvent *)event from:(NSPoint)dragImageLocation withPasteboard:(NSPasteboard *)pasteboard
@@ -107,6 +96,9 @@
Q_UNUSED(event);
Q_UNUSED(dragImageLocation);
Q_UNUSED(pasteboard);
- return m_cocoaWindow && m_cocoaWindow->m_hasWindowFilePath;
+
+ // Only allow drag if the filename is non-empty. We allow whitespace, to
+ // allow faking a window icon by setting the file path to a single space.
+ return !whitespaceRegex.exactMatch(m_cocoaWindow->window()->filePath());
}
@end
diff --git a/src/plugins/platforms/cocoa/qpaintengine_mac.mm b/src/plugins/platforms/cocoa/qpaintengine_mac.mm
index 7e241e3ae6..3f363b62d5 100644
--- a/src/plugins/platforms/cocoa/qpaintengine_mac.mm
+++ b/src/plugins/platforms/cocoa/qpaintengine_mac.mm
@@ -108,7 +108,7 @@ CGAffineTransform qt_mac_convert_transform_to_cg(const QTransform &t) {
return CGAffineTransformMake(t.m11(), t.m12(), t.m21(), t.m22(), t.dx(), t.dy());
}
-inline static QCFType<CGColorRef> cgColorForQColor(const QColor &col, QPaintDevice *pdev)
+inline static QCFType<CGColorRef> cgColorForQColor(const QColor &col)
{
CGFloat components[] = {
qt_mac_convert_color_to_cg(col.red()),
@@ -116,7 +116,8 @@ inline static QCFType<CGColorRef> cgColorForQColor(const QColor &col, QPaintDevi
qt_mac_convert_color_to_cg(col.blue()),
qt_mac_convert_color_to_cg(col.alpha())
};
- return CGColorCreate(qt_mac_colorSpaceForDeviceType(pdev), components);
+ QCFType<CGColorSpaceRef> colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
+ return CGColorCreate(colorSpace, components);
}
// There's architectural problems with using native gradients
@@ -239,81 +240,6 @@ static CGMutablePathRef qt_mac_compose_path(const QPainterPath &p, float off=0)
return ret;
}
-CGColorSpaceRef QCoreGraphicsPaintEngine::m_genericColorSpace = 0;
-QHash<CGDirectDisplayID, CGColorSpaceRef> QCoreGraphicsPaintEngine::m_displayColorSpaceHash;
-bool QCoreGraphicsPaintEngine::m_postRoutineRegistered = false;
-
-CGColorSpaceRef QCoreGraphicsPaintEngine::macGenericColorSpace()
-{
-#if 0
- if (!m_genericColorSpace) {
- if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) {
- m_genericColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
- } else
- {
- m_genericColorSpace = CGColorSpaceCreateDeviceRGB();
- }
- if (!m_postRoutineRegistered) {
- m_postRoutineRegistered = true;
- qAddPostRoutine(QCoreGraphicsPaintEngine::cleanUpMacColorSpaces);
- }
- }
- return m_genericColorSpace;
-#else
- // Just return the main display colorspace for the moment.
- return macDisplayColorSpace();
-#endif
-}
-
-/*
- Ideally, we should pass the widget in here, and use CGGetDisplaysWithRect() etc.
- to support multiple displays correctly.
-*/
-CGColorSpaceRef QCoreGraphicsPaintEngine::macDisplayColorSpace(const QWidget *widget)
-{
- CGColorSpaceRef colorSpace;
-
- CGDirectDisplayID displayID;
- if (widget == 0) {
- displayID = CGMainDisplayID();
- } else {
- const QRect &qrect = widget->window()->geometry();
- CGRect rect = CGRectMake(qrect.x(), qrect.y(), qrect.width(), qrect.height());
- CGDisplayCount throwAway;
- CGDisplayErr dErr = CGGetDisplaysWithRect(rect, 1, &displayID, &throwAway);
- if (dErr != kCGErrorSuccess)
- return macDisplayColorSpace(0); // fall back on main display
- }
- if ((colorSpace = m_displayColorSpaceHash.value(displayID)))
- return colorSpace;
-
- colorSpace = CGDisplayCopyColorSpace(displayID);
- if (colorSpace == 0)
- colorSpace = CGColorSpaceCreateDeviceRGB();
-
- m_displayColorSpaceHash.insert(displayID, colorSpace);
- if (!m_postRoutineRegistered) {
- m_postRoutineRegistered = true;
- qAddPostRoutine(QCoreGraphicsPaintEngine::cleanUpMacColorSpaces);
- }
- return colorSpace;
-}
-
-void QCoreGraphicsPaintEngine::cleanUpMacColorSpaces()
-{
- if (m_genericColorSpace) {
- CFRelease(m_genericColorSpace);
- m_genericColorSpace = 0;
- }
- QHash<CGDirectDisplayID, CGColorSpaceRef>::const_iterator it = m_displayColorSpaceHash.constBegin();
- while (it != m_displayColorSpaceHash.constEnd()) {
- if (it.value())
- CFRelease(it.value());
- ++it;
- }
- m_displayColorSpaceHash.clear();
-}
-
//pattern handling (tiling)
#if 1
# define QMACPATTERN_MASK_MULTIPLIER 32
@@ -377,7 +303,7 @@ static void qt_mac_draw_pattern(void *info, CGContextRef c)
QPixmap pm(w*QMACPATTERN_MASK_MULTIPLIER, h*QMACPATTERN_MASK_MULTIPLIER);
pm.fill(c0);
QMacCGContext pm_ctx(&pm);
- CGContextSetFillColorWithColor(c, cgColorForQColor(c1, pat->pdev));
+ CGContextSetFillColorWithColor(c, cgColorForQColor(c1));
CGRect rect = CGRectMake(0, 0, w, h);
for (int x = 0; x < QMACPATTERN_MASK_MULTIPLIER; ++x) {
rect.origin.x = x * w;
@@ -409,7 +335,7 @@ static void qt_mac_draw_pattern(void *info, CGContextRef c)
bool needRestore = false;
if (CGImageIsMask(pat->image)) {
CGContextSaveGState(c);
- CGContextSetFillColorWithColor(c, cgColorForQColor(pat->foreground, pat->pdev));
+ CGContextSetFillColorWithColor(c, cgColorForQColor(pat->foreground));
}
CGRect rect = CGRectMake(0, 0, w, h);
qt_mac_drawCGImage(c, &rect, pat->image);
@@ -871,7 +797,7 @@ void QCoreGraphicsPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, co
d->saveGraphicsState();
const QColor &col = d->current.pen.color();
- CGContextSetFillColorWithColor(d->hd, cgColorForQColor(col, d->pdev));
+ CGContextSetFillColorWithColor(d->hd, cgColorForQColor(col));
image = qt_mac_create_imagemask(pm, sr);
} else if (differentSize) {
QCFType<CGImageRef> img = qt_mac_toCGImage(pm.toImage());
@@ -1233,7 +1159,7 @@ QCoreGraphicsPaintEnginePrivate::setStrokePen(const QPen &pen)
CGContextSetLineDash(hd, pen.dashOffset() * cglinewidth, linedashes.data(), linedashes.size());
// color
- CGContextSetStrokeColorWithColor(hd, cgColorForQColor(pen.color(), pdev));
+ CGContextSetStrokeColorWithColor(hd, cgColorForQColor(pen.color()));
}
// Add our own patterns here to deal with the fact that the coordinate system
@@ -1276,7 +1202,7 @@ void QCoreGraphicsPaintEnginePrivate::setFillBrush(const QPointF &offset)
CGFunctionRef fill_func = CGFunctionCreate(reinterpret_cast<void *>(&current.brush),
1, domain, 4, 0, &callbacks);
- CGColorSpaceRef colorspace = qt_mac_colorSpaceForDeviceType(pdev);
+ CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB)
if (bs == Qt::LinearGradientPattern) {
const QLinearGradient *linearGrad = static_cast<const QLinearGradient *>(grad);
const QPointF start(linearGrad->start());
@@ -1315,7 +1241,7 @@ void QCoreGraphicsPaintEnginePrivate::setFillBrush(const QPointF &offset)
components[0] = qt_mac_convert_color_to_cg(col.red());
components[1] = qt_mac_convert_color_to_cg(col.green());
components[2] = qt_mac_convert_color_to_cg(col.blue());
- base_colorspace = QCoreGraphicsPaintEngine::macGenericColorSpace();
+ base_colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
}
} else {
qpattern->as_mask = true;
@@ -1325,7 +1251,7 @@ void QCoreGraphicsPaintEnginePrivate::setFillBrush(const QPointF &offset)
components[0] = qt_mac_convert_color_to_cg(col.red());
components[1] = qt_mac_convert_color_to_cg(col.green());
components[2] = qt_mac_convert_color_to_cg(col.blue());
- base_colorspace = QCoreGraphicsPaintEngine::macGenericColorSpace();
+ base_colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
}
int width = qpattern->width(), height = qpattern->height();
qpattern->foreground = current.brush.color();
@@ -1346,10 +1272,12 @@ void QCoreGraphicsPaintEnginePrivate::setFillBrush(const QPointF &offset)
!base_colorspace, &callbks);
CGContextSetFillPattern(hd, fill_pattern, components);
+
CGPatternRelease(fill_pattern);
+ CGColorSpaceRelease(base_colorspace);
CGColorSpaceRelease(fill_colorspace);
} else if (bs != Qt::NoBrush) {
- CGContextSetFillColorWithColor(hd, cgColorForQColor(current.brush.color(), pdev));
+ CGContextSetFillColorWithColor(hd, cgColorForQColor(current.brush.color()));
}
}
diff --git a/src/plugins/platforms/cocoa/qpaintengine_mac_p.h b/src/plugins/platforms/cocoa/qpaintengine_mac_p.h
index 57d985c399..c9519ac827 100644
--- a/src/plugins/platforms/cocoa/qpaintengine_mac_p.h
+++ b/src/plugins/platforms/cocoa/qpaintengine_mac_p.h
@@ -73,8 +73,6 @@ public:
bool begin(QPaintDevice *pdev);
bool end();
- static CGColorSpaceRef macGenericColorSpace();
- static CGColorSpaceRef macDisplayColorSpace(const QWidget *widget = 0);
void updateState(const QPaintEngineState &state);
@@ -126,10 +124,6 @@ protected:
QCoreGraphicsPaintEngine(QPaintEnginePrivate &dptr);
private:
- static bool m_postRoutineRegistered;
- static CGColorSpaceRef m_genericColorSpace;
- static QHash<CGDirectDisplayID, CGColorSpaceRef> m_displayColorSpaceHash;
- static void cleanUpMacColorSpaces();
Q_DISABLE_COPY(QCoreGraphicsPaintEngine)
};
diff --git a/src/plugins/platforms/cocoa/qprintengine_mac.mm b/src/plugins/platforms/cocoa/qprintengine_mac.mm
index c39af870d4..b3d48c1ec3 100644
--- a/src/plugins/platforms/cocoa/qprintengine_mac.mm
+++ b/src/plugins/platforms/cocoa/qprintengine_mac.mm
@@ -247,7 +247,7 @@ void QMacPrintEnginePrivate::initialize()
QList<int> resolutions = m_printDevice->supportedResolutions();
if (!resolutions.isEmpty() && mode != QPrinter::ScreenResolution) {
- qSort(resolutions);
+ std::sort(resolutions.begin(), resolutions.end());
if (resolutions.count() > 1 && mode == QPrinter::HighResolution)
resolution.hRes = resolution.vRes = resolutions.last();
else
diff --git a/src/plugins/platforms/cocoa/qprintengine_mac_p.h b/src/plugins/platforms/cocoa/qprintengine_mac_p.h
index 2d46a250d5..9514f3e691 100644
--- a/src/plugins/platforms/cocoa/qprintengine_mac_p.h
+++ b/src/plugins/platforms/cocoa/qprintengine_mac_p.h
@@ -150,8 +150,8 @@ public:
PMPrintSession session() const { return static_cast<PMPrintSession>([printInfo PMPrintSession]); }
PMPrintSettings settings() const { return static_cast<PMPrintSettings>([printInfo PMPrintSettings]); }
- QPaintEngine *aggregateEngine() Q_DECL_OVERRIDE { return paintEngine; }
- Qt::HANDLE nativeHandle() Q_DECL_OVERRIDE { return q_func()->handle(); }
+ QPaintEngine *aggregateEngine() override { return paintEngine; }
+ Qt::HANDLE nativeHandle() override { return q_func()->handle(); }
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/direct2d/direct2d.pro b/src/plugins/platforms/direct2d/direct2d.pro
index 3fe0d5e660..3bfd02bdc8 100644
--- a/src/plugins/platforms/direct2d/direct2d.pro
+++ b/src/plugins/platforms/direct2d/direct2d.pro
@@ -6,8 +6,9 @@ QT += \
fontdatabase_support-private theme_support-private
qtConfig(accessibility): QT += accessibility_support-private
+qtConfig(vulkan): QT += vulkan_support-private
-LIBS += -ldwmapi -ld2d1 -ld3d11 -ldwrite -lVersion -lgdi32
+LIBS += -ldwmapi -ld2d1 -ld3d11 -ldwrite -lversion -lgdi32
include(../windows/windows.pri)
diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dbackingstore.h b/src/plugins/platforms/direct2d/qwindowsdirect2dbackingstore.h
index 670c4e9840..f72ea2b038 100644
--- a/src/plugins/platforms/direct2d/qwindowsdirect2dbackingstore.h
+++ b/src/plugins/platforms/direct2d/qwindowsdirect2dbackingstore.h
@@ -54,12 +54,12 @@ public:
QWindowsDirect2DBackingStore(QWindow *window);
~QWindowsDirect2DBackingStore();
- void beginPaint(const QRegion &) Q_DECL_OVERRIDE;
- void endPaint() Q_DECL_OVERRIDE;
+ void beginPaint(const QRegion &) override;
+ void endPaint() override;
- QPaintDevice *paintDevice() Q_DECL_OVERRIDE;
- void flush(QWindow *targetWindow, const QRegion &region, const QPoint &offset) Q_DECL_OVERRIDE;
- void resize(const QSize &size, const QRegion &staticContents) Q_DECL_OVERRIDE;
+ QPaintDevice *paintDevice() override;
+ void flush(QWindow *targetWindow, const QRegion &region, const QPoint &offset) override;
+ void resize(const QSize &size, const QRegion &staticContents) override;
QImage toImage() const override;
};
diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2ddevicecontext.cpp b/src/plugins/platforms/direct2d/qwindowsdirect2ddevicecontext.cpp
index 643ae877d0..d578a58982 100644
--- a/src/plugins/platforms/direct2d/qwindowsdirect2ddevicecontext.cpp
+++ b/src/plugins/platforms/direct2d/qwindowsdirect2ddevicecontext.cpp
@@ -161,7 +161,7 @@ void QWindowsDirect2DDeviceContextSuspender::resume()
{
if (m_dc) {
m_dc->resume();
- m_dc = Q_NULLPTR;
+ m_dc = nullptr;
}
}
diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dintegration.cpp b/src/plugins/platforms/direct2d/qwindowsdirect2dintegration.cpp
index ea51135583..6d98da5be5 100644
--- a/src/plugins/platforms/direct2d/qwindowsdirect2dintegration.cpp
+++ b/src/plugins/platforms/direct2d/qwindowsdirect2dintegration.cpp
@@ -48,6 +48,7 @@
#include <qplatformdefs.h>
#include <QtCore/QCoreApplication>
+#include <QtCore/QVersionNumber>
#include <QtGui/private/qpixmap_raster_p.h>
#include <QtGui/qpa/qwindowsysteminterface.h>
#include <QtEventDispatcherSupport/private/qwindowsguieventdispatcher_p.h>
@@ -73,127 +74,60 @@ public:
QWindowsDirect2DContext m_d2dContext;
};
-class Direct2DVersion
+static QVersionNumber systemD2DVersion()
{
-private:
- Direct2DVersion() = default;
-
- Direct2DVersion(int one, int two, int three, int four)
- : partOne(one)
- , partTwo(two)
- , partThree(three)
- , partFour(four)
- {}
+ static const int bufSize = 512;
+ TCHAR filename[bufSize];
+
+ UINT i = GetSystemDirectory(filename, bufSize);
+ if (i > 0 && i < bufSize) {
+ if (_tcscat_s(filename, bufSize, __TEXT("\\d2d1.dll")) == 0) {
+ DWORD versionInfoSize = GetFileVersionInfoSize(filename, NULL);
+ if (versionInfoSize) {
+ QVarLengthArray<BYTE> info(static_cast<int>(versionInfoSize));
+ if (GetFileVersionInfo(filename, 0, versionInfoSize, info.data())) {
+ UINT size;
+ DWORD *fi;
+
+ if (VerQueryValue(info.constData(), __TEXT("\\"),
+ reinterpret_cast<void **>(&fi), &size) && size) {
+ const VS_FIXEDFILEINFO *verInfo = reinterpret_cast<const VS_FIXEDFILEINFO *>(fi);
+ return QVersionNumber{HIWORD(verInfo->dwFileVersionMS), LOWORD(verInfo->dwFileVersionMS),
+ HIWORD(verInfo->dwFileVersionLS), LOWORD(verInfo->dwFileVersionLS)};
+ }
+ }
+ }
+ }
+ }
+ return QVersionNumber();
+}
-public:
+static QVersionNumber minimumD2DVersion()
+{
// 6.2.9200.16492 corresponds to Direct2D 1.1 on Windows 7 SP1 with Platform Update
- enum {
+ enum : int {
D2DMinVersionPart1 = 6,
D2DMinVersionPart2 = 2,
D2DMinVersionPart3 = 9200,
D2DMinVersionPart4 = 16492
};
- static Direct2DVersion systemVersion() {
- static const int bufSize = 512;
- TCHAR filename[bufSize];
-
- UINT i = GetSystemDirectory(filename, bufSize);
- if (i > 0 && i < bufSize) {
- if (_tcscat_s(filename, bufSize, __TEXT("\\d2d1.dll")) == 0) {
- DWORD versionInfoSize = GetFileVersionInfoSize(filename, NULL);
- if (versionInfoSize) {
- QVarLengthArray<BYTE> info(static_cast<int>(versionInfoSize));
- if (GetFileVersionInfo(filename, 0, versionInfoSize, info.data())) {
- UINT size;
- DWORD *fi;
-
- if (VerQueryValue(info.constData(), __TEXT("\\"),
- reinterpret_cast<void **>(&fi), &size) && size) {
- const VS_FIXEDFILEINFO *verInfo = reinterpret_cast<const VS_FIXEDFILEINFO *>(fi);
- return Direct2DVersion(HIWORD(verInfo->dwFileVersionMS),
- LOWORD(verInfo->dwFileVersionMS),
- HIWORD(verInfo->dwFileVersionLS),
- LOWORD(verInfo->dwFileVersionLS));
- }
- }
- }
- }
- }
-
- return Direct2DVersion();
- }
-
- static Direct2DVersion minimumVersion() {
- return Direct2DVersion(D2DMinVersionPart1,
- D2DMinVersionPart2,
- D2DMinVersionPart3,
- D2DMinVersionPart4);
- }
-
- bool isValid() const {
- return partOne || partTwo || partThree || partFour;
- }
-
- bool operator<(const Direct2DVersion &other) {
- int c = cmp(partOne, other.partOne);
- if (c > 0)
- return false;
- if (c < 0)
- return true;
-
- c = cmp(partTwo, other.partTwo);
- if (c > 0)
- return false;
- if (c < 0)
- return true;
-
- c = cmp(partThree, other.partThree);
- if (c > 0)
- return false;
- if (c < 0)
- return true;
-
- c = cmp(partFour, other.partFour);
- if (c > 0)
- return false;
- if (c < 0)
- return true;
-
- return false;
- }
-
- static Q_DECL_CONSTEXPR int cmp(int a, int b) {
- return a - b;
- }
-
- int partOne = 0;
- int partTwo = 0;
- int partThree = 0;
- int partFour = 0;
-};
+ return QVersionNumber{D2DMinVersionPart1, D2DMinVersionPart2, D2DMinVersionPart3, D2DMinVersionPart4};
+}
QWindowsDirect2DIntegration *QWindowsDirect2DIntegration::create(const QStringList &paramList)
{
- Direct2DVersion systemVersion = Direct2DVersion::systemVersion();
-
- if (systemVersion.isValid() && systemVersion < Direct2DVersion::minimumVersion()) {
+ const QVersionNumber systemVersion = systemD2DVersion();
+ const QVersionNumber minimumVersion = minimumD2DVersion();
+ if (!systemVersion.isNull() && systemVersion < minimumVersion) {
QString msg = QCoreApplication::translate("QWindowsDirect2DIntegration",
"Qt cannot load the direct2d platform plugin because " \
"the Direct2D version on this system is too old. The " \
"minimum system requirement for this platform plugin " \
"is Windows 7 SP1 with Platform Update.\n\n" \
- "The minimum Direct2D version required is %1.%2.%3.%4. " \
- "The Direct2D version on this system is %5.%6.%7.%8.");
-
- msg = msg.arg(Direct2DVersion::D2DMinVersionPart1)
- .arg(Direct2DVersion::D2DMinVersionPart2)
- .arg(Direct2DVersion::D2DMinVersionPart3)
- .arg(Direct2DVersion::D2DMinVersionPart4)
- .arg(systemVersion.partOne)
- .arg(systemVersion.partTwo)
- .arg(systemVersion.partThree)
- .arg(systemVersion.partFour);
+ "The minimum Direct2D version required is %1. " \
+ "The Direct2D version on this system is %2.")
+ .arg(minimumVersion.toString(), systemVersion.toString());
QString caption = QCoreApplication::translate("QWindowsDirect2DIntegration",
"Cannot load direct2d platform plugin");
@@ -203,7 +137,7 @@ QWindowsDirect2DIntegration *QWindowsDirect2DIntegration::create(const QStringLi
caption.toStdWString().c_str(),
MB_OK | MB_ICONERROR);
- return Q_NULLPTR;
+ return nullptr;
}
QWindowsDirect2DIntegration *integration = new QWindowsDirect2DIntegration(paramList);
diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dintegration.h b/src/plugins/platforms/direct2d/qwindowsdirect2dintegration.h
index 43f2a08745..39ca1d0dbf 100644
--- a/src/plugins/platforms/direct2d/qwindowsdirect2dintegration.h
+++ b/src/plugins/platforms/direct2d/qwindowsdirect2dintegration.h
@@ -58,15 +58,15 @@ public:
static QWindowsDirect2DIntegration *instance();
- QPlatformNativeInterface *nativeInterface() const Q_DECL_OVERRIDE;
- QPlatformPixmap *createPlatformPixmap(QPlatformPixmap::PixelType type) const Q_DECL_OVERRIDE;
- QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const Q_DECL_OVERRIDE;
- QAbstractEventDispatcher *createEventDispatcher() const Q_DECL_OVERRIDE;
+ QPlatformNativeInterface *nativeInterface() const override;
+ QPlatformPixmap *createPlatformPixmap(QPlatformPixmap::PixelType type) const override;
+ QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const override;
+ QAbstractEventDispatcher *createEventDispatcher() const override;
QWindowsDirect2DContext *direct2DContext() const;
protected:
- QWindowsWindow *createPlatformWindowHelper(QWindow *window, const QWindowsWindowData &) const Q_DECL_OVERRIDE;
+ QWindowsWindow *createPlatformWindowHelper(QWindow *window, const QWindowsWindowData &) const override;
private:
explicit QWindowsDirect2DIntegration(const QStringList &paramList);
diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dnativeinterface.h b/src/plugins/platforms/direct2d/qwindowsdirect2dnativeinterface.h
index d57136129b..fce6ff9969 100644
--- a/src/plugins/platforms/direct2d/qwindowsdirect2dnativeinterface.h
+++ b/src/plugins/platforms/direct2d/qwindowsdirect2dnativeinterface.h
@@ -48,7 +48,7 @@ class QWindowsDirect2DNativeInterface : public QWindowsNativeInterface
{
Q_OBJECT
public:
- void *nativeResourceForBackingStore(const QByteArray &resource, QBackingStore *bs) Q_DECL_OVERRIDE;
+ void *nativeResourceForBackingStore(const QByteArray &resource, QBackingStore *bs) override;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintdevice.h b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintdevice.h
index f434ef993c..1f23d604f5 100644
--- a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintdevice.h
+++ b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintdevice.h
@@ -58,11 +58,11 @@ public:
QWindowsDirect2DPaintEngine::Flags paintFlags = QWindowsDirect2DPaintEngine::NoFlag);
~QWindowsDirect2DPaintDevice();
- QPaintEngine *paintEngine() const Q_DECL_OVERRIDE;
- int devType() const Q_DECL_OVERRIDE;
+ QPaintEngine *paintEngine() const override;
+ int devType() const override;
protected:
- int metric(PaintDeviceMetric metric) const Q_DECL_OVERRIDE;
+ int metric(PaintDeviceMetric metric) const override;
private:
QScopedPointer<QWindowsDirect2DPaintDevicePrivate> d_ptr;
diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp
index 164429ba30..95fbd37247 100644
--- a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp
+++ b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp
@@ -804,7 +804,7 @@ public:
const bool alias = !q->antiAliasingEnabled();
QVectorPath::CacheEntry *cacheEntry = path.isCacheable() ? path.lookupCacheData(q)
- : Q_NULLPTR;
+ : nullptr;
if (cacheEntry) {
D2DVectorPathCache *e = static_cast<D2DVectorPathCache *>(cacheEntry->data);
diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.h b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.h
index a8f63af5d5..b9616acd6a 100644
--- a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.h
+++ b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.h
@@ -65,45 +65,45 @@ public:
QWindowsDirect2DPaintEngine(QWindowsDirect2DBitmap *bitmap, Flags flags);
- bool begin(QPaintDevice *pdev) Q_DECL_OVERRIDE;
- bool end() Q_DECL_OVERRIDE;
+ bool begin(QPaintDevice *pdev) override;
+ bool end() override;
- Type type() const Q_DECL_OVERRIDE;
+ Type type() const override;
- void setState(QPainterState *s) Q_DECL_OVERRIDE;
+ void setState(QPainterState *s) override;
- void draw(const QVectorPath &path) Q_DECL_OVERRIDE;
+ void draw(const QVectorPath &path) override;
- void fill(const QVectorPath &path, const QBrush &brush) Q_DECL_OVERRIDE;
+ void fill(const QVectorPath &path, const QBrush &brush) override;
void fill(ID2D1Geometry *geometry, const QBrush &brush);
- void stroke(const QVectorPath &path, const QPen &pen) Q_DECL_OVERRIDE;
+ void stroke(const QVectorPath &path, const QPen &pen) override;
void stroke(ID2D1Geometry *geometry, const QPen &pen);
- void clip(const QVectorPath &path, Qt::ClipOperation op) Q_DECL_OVERRIDE;
+ void clip(const QVectorPath &path, Qt::ClipOperation op) override;
- void clipEnabledChanged() Q_DECL_OVERRIDE;
- void penChanged() Q_DECL_OVERRIDE;
- void brushChanged() Q_DECL_OVERRIDE;
- void brushOriginChanged() Q_DECL_OVERRIDE;
- void opacityChanged() Q_DECL_OVERRIDE;
- void compositionModeChanged() Q_DECL_OVERRIDE;
- void renderHintsChanged() Q_DECL_OVERRIDE;
- void transformChanged() Q_DECL_OVERRIDE;
+ void clipEnabledChanged() override;
+ void penChanged() override;
+ void brushChanged() override;
+ void brushOriginChanged() override;
+ void opacityChanged() override;
+ void compositionModeChanged() override;
+ void renderHintsChanged() override;
+ void transformChanged() override;
- void fillRect(const QRectF &rect, const QBrush &brush) Q_DECL_OVERRIDE;
+ void fillRect(const QRectF &rect, const QBrush &brush) override;
- void drawRects(const QRect *rects, int rectCount) Q_DECL_OVERRIDE;
- void drawRects(const QRectF *rects, int rectCount) Q_DECL_OVERRIDE;
+ void drawRects(const QRect *rects, int rectCount) override;
+ void drawRects(const QRectF *rects, int rectCount) override;
- void drawEllipse(const QRectF &r) Q_DECL_OVERRIDE;
- void drawEllipse(const QRect &r) Q_DECL_OVERRIDE;
+ void drawEllipse(const QRectF &r) override;
+ void drawEllipse(const QRect &r) override;
- void drawImage(const QRectF &rectangle, const QImage &image, const QRectF &sr, Qt::ImageConversionFlags flags = Qt::AutoColor) Q_DECL_OVERRIDE;
- void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) Q_DECL_OVERRIDE;
+ void drawImage(const QRectF &rectangle, const QImage &image, const QRectF &sr, Qt::ImageConversionFlags flags = Qt::AutoColor) override;
+ void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) override;
- void drawStaticTextItem(QStaticTextItem *staticTextItem) Q_DECL_OVERRIDE;
- void drawTextItem(const QPointF &p, const QTextItem &textItem) Q_DECL_OVERRIDE;
+ void drawStaticTextItem(QStaticTextItem *staticTextItem) override;
+ void drawTextItem(const QPointF &p, const QTextItem &textItem) override;
private:
void ensureBrush();
diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dplatformpixmap.h b/src/plugins/platforms/direct2d/qwindowsdirect2dplatformpixmap.h
index 5f65a2313a..0448613a95 100644
--- a/src/plugins/platforms/direct2d/qwindowsdirect2dplatformpixmap.h
+++ b/src/plugins/platforms/direct2d/qwindowsdirect2dplatformpixmap.h
@@ -59,21 +59,21 @@ public:
QWindowsDirect2DPlatformPixmap(PixelType pixelType, QWindowsDirect2DPaintEngine::Flags flags, QWindowsDirect2DBitmap *bitmap);
~QWindowsDirect2DPlatformPixmap();
- void resize(int width, int height) Q_DECL_OVERRIDE;
- void fromImage(const QImage &image, Qt::ImageConversionFlags flags) Q_DECL_OVERRIDE;
+ void resize(int width, int height) override;
+ void fromImage(const QImage &image, Qt::ImageConversionFlags flags) override;
- int metric(QPaintDevice::PaintDeviceMetric metric) const Q_DECL_OVERRIDE;
- void fill(const QColor &color) Q_DECL_OVERRIDE;
+ int metric(QPaintDevice::PaintDeviceMetric metric) const override;
+ void fill(const QColor &color) override;
- bool hasAlphaChannel() const Q_DECL_OVERRIDE;
+ bool hasAlphaChannel() const override;
- QImage toImage() const Q_DECL_OVERRIDE;
- QImage toImage(const QRect &rect) const Q_DECL_OVERRIDE;
+ QImage toImage() const override;
+ QImage toImage(const QRect &rect) const override;
- QPaintEngine* paintEngine() const Q_DECL_OVERRIDE;
+ QPaintEngine* paintEngine() const override;
- qreal devicePixelRatio() const Q_DECL_OVERRIDE;
- void setDevicePixelRatio(qreal scaleFactor) Q_DECL_OVERRIDE;
+ qreal devicePixelRatio() const override;
+ void setDevicePixelRatio(qreal scaleFactor) override;
QWindowsDirect2DBitmap *bitmap() const;
diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dwindow.cpp b/src/plugins/platforms/direct2d/qwindowsdirect2dwindow.cpp
index 21294cfb15..f81182e0b1 100644
--- a/src/plugins/platforms/direct2d/qwindowsdirect2dwindow.cpp
+++ b/src/plugins/platforms/direct2d/qwindowsdirect2dwindow.cpp
@@ -209,7 +209,7 @@ void QWindowsDirect2DWindow::resizeSwapChain(const QSize &size)
{
m_pixmap.reset();
m_bitmap.reset();
- m_deviceContext->SetTarget(Q_NULLPTR);
+ m_deviceContext->SetTarget(nullptr);
m_needsFullFlush = true;
if (!m_swapChain)
@@ -241,7 +241,7 @@ QSharedPointer<QWindowsDirect2DBitmap> QWindowsDirect2DWindow::copyBackBuffer()
dpiX, // FLOAT dpiX;
dpiY, // FLOAT dpiY;
D2D1_BITMAP_OPTIONS_TARGET, // D2D1_BITMAP_OPTIONS bitmapOptions;
- Q_NULLPTR // _Field_size_opt_(1) ID2D1ColorContext *colorContext;
+ nullptr // _Field_size_opt_(1) ID2D1ColorContext *colorContext;
};
ComPtr<ID2D1Bitmap1> copy;
HRESULT hr = m_deviceContext.Get()->CreateBitmap(size, NULL, 0, properties, &copy);
@@ -257,7 +257,7 @@ QSharedPointer<QWindowsDirect2DBitmap> QWindowsDirect2DWindow::copyBackBuffer()
return null_result;
}
- return QSharedPointer<QWindowsDirect2DBitmap>(new QWindowsDirect2DBitmap(copy.Get(), Q_NULLPTR));
+ return QSharedPointer<QWindowsDirect2DBitmap>(new QWindowsDirect2DBitmap(copy.Get(), nullptr));
}
void QWindowsDirect2DWindow::setupBitmap()
diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dwindow.h b/src/plugins/platforms/direct2d/qwindowsdirect2dwindow.h
index 156d4660d1..3ac532d938 100644
--- a/src/plugins/platforms/direct2d/qwindowsdirect2dwindow.h
+++ b/src/plugins/platforms/direct2d/qwindowsdirect2dwindow.h
@@ -56,7 +56,7 @@ public:
QWindowsDirect2DWindow(QWindow *window, const QWindowsWindowData &data);
~QWindowsDirect2DWindow();
- void setWindowFlags(Qt::WindowFlags flags) Q_DECL_OVERRIDE;
+ void setWindowFlags(Qt::WindowFlags flags) override;
QPixmap *pixmap();
void flush(QWindowsDirect2DBitmap *bitmap, const QRegion &region, const QPoint &offset);
diff --git a/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp b/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp
index e411ea55e9..1b8f50a1c6 100644
--- a/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp
+++ b/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp
@@ -103,7 +103,7 @@ QStringList QEglFSDeviceIntegrationFactory::keys(const QString &pluginPath)
QEglFSDeviceIntegration *QEglFSDeviceIntegrationFactory::create(const QString &key, const QString &pluginPath)
{
- QEglFSDeviceIntegration *integration = Q_NULLPTR;
+ QEglFSDeviceIntegration *integration = nullptr;
#if QT_CONFIG(library)
if (!pluginPath.isEmpty()) {
QCoreApplication::addLibraryPath(pluginPath);
@@ -351,9 +351,28 @@ bool QEglFSDeviceIntegration::supportsSurfacelessContexts() const
return true;
}
+QFunctionPointer QEglFSDeviceIntegration::platformFunction(const QByteArray &function) const
+{
+ Q_UNUSED(function);
+ return nullptr;
+}
+
+void *QEglFSDeviceIntegration::nativeResourceForIntegration(const QByteArray &name)
+{
+ Q_UNUSED(name);
+ return nullptr;
+}
+
+void *QEglFSDeviceIntegration::nativeResourceForScreen(const QByteArray &resource, QScreen *screen)
+{
+ Q_UNUSED(resource);
+ Q_UNUSED(screen);
+ return nullptr;
+}
+
void *QEglFSDeviceIntegration::wlDisplay() const
{
- return Q_NULLPTR;
+ return nullptr;
}
EGLConfig QEglFSDeviceIntegration::chooseConfig(EGLDisplay display, const QSurfaceFormat &format)
diff --git a/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration_p.h b/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration_p.h
index 4335554912..71ffb4c69a 100644
--- a/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration_p.h
+++ b/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration_p.h
@@ -103,7 +103,9 @@ public:
virtual int framebufferIndex() const;
virtual bool supportsPBuffers() const;
virtual bool supportsSurfacelessContexts() const;
-
+ virtual QFunctionPointer platformFunction(const QByteArray &function) const;
+ virtual void *nativeResourceForIntegration(const QByteArray &name);
+ virtual void *nativeResourceForScreen(const QByteArray &resource, QScreen *screen);
virtual void *wlDisplay() const;
static EGLConfig chooseConfig(EGLDisplay display, const QSurfaceFormat &format);
diff --git a/src/plugins/platforms/eglfs/api/qeglfsintegration.cpp b/src/plugins/platforms/eglfs/api/qeglfsintegration.cpp
index 9a0be489a8..33878a5f50 100644
--- a/src/plugins/platforms/eglfs/api/qeglfsintegration.cpp
+++ b/src/plugins/platforms/eglfs/api/qeglfsintegration.cpp
@@ -324,13 +324,14 @@ void *QEglFSIntegration::nativeResourceForIntegration(const QByteArray &resource
result = qt_egl_device_integration()->wlDisplay();
break;
default:
+ result = qt_egl_device_integration()->nativeResourceForIntegration(resource);
break;
}
return result;
}
-void *QEglFSIntegration::nativeResourceForScreen(const QByteArray &resource, QScreen *)
+void *QEglFSIntegration::nativeResourceForScreen(const QByteArray &resource, QScreen *screen)
{
void *result = 0;
@@ -341,6 +342,7 @@ void *QEglFSIntegration::nativeResourceForScreen(const QByteArray &resource, QSc
result = reinterpret_cast<void*>(nativeDisplay());
break;
default:
+ result = qt_egl_device_integration()->nativeResourceForScreen(resource, screen);
break;
}
@@ -427,11 +429,9 @@ QFunctionPointer QEglFSIntegration::platformFunction(const QByteArray &function)
#if QT_CONFIG(evdev)
if (function == QEglFSFunctions::loadKeymapTypeIdentifier())
return QFunctionPointer(loadKeymapStatic);
-#else
- Q_UNUSED(function)
#endif
- return 0;
+ return qt_egl_device_integration()->platformFunction(function);
}
void QEglFSIntegration::loadKeymapStatic(const QString &filename)
diff --git a/src/plugins/platforms/eglfs/api/qeglfswindow.cpp b/src/plugins/platforms/eglfs/api/qeglfswindow.cpp
index 17e4aa1df8..29cfd4ea79 100644
--- a/src/plugins/platforms/eglfs/api/qeglfswindow.cpp
+++ b/src/plugins/platforms/eglfs/api/qeglfswindow.cpp
@@ -41,6 +41,7 @@
#include <qpa/qwindowsysteminterface.h>
#include <qpa/qplatformintegration.h>
#include <private/qguiapplication_p.h>
+#include <private/qwindow_p.h>
#ifndef QT_NO_OPENGL
# include <QtGui/private/qopenglcontext_p.h>
# include <QtGui/QOpenGLContext>
@@ -99,7 +100,6 @@ void QEglFSWindow::create()
if (window()->type() == Qt::Desktop) {
QRect fullscreenRect(QPoint(), screen()->availableGeometry().size());
- QPlatformWindow::setGeometry(fullscreenRect);
QWindowSystemInterface::handleGeometryChange(window(), fullscreenRect);
return;
}
@@ -235,21 +235,16 @@ void QEglFSWindow::setVisible(bool visible)
void QEglFSWindow::setGeometry(const QRect &r)
{
- QRect rect;
- bool forceFullscreen = m_flags.testFlag(HasNativeWindow);
- if (forceFullscreen)
+ QRect rect = r;
+ if (m_flags.testFlag(HasNativeWindow))
rect = screen()->availableGeometry();
- else
- rect = r;
- const bool changed = rect != QPlatformWindow::geometry();
QPlatformWindow::setGeometry(rect);
- // if we corrected the size, trigger a resize event
- if (rect != r)
- QWindowSystemInterface::handleGeometryChange(window(), rect, r);
+ QWindowSystemInterface::handleGeometryChange(window(), rect);
- if (changed)
+ const QRect lastReportedGeometry = qt_window_private(window())->geometry;
+ if (rect != lastReportedGeometry)
QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), rect.size()));
}
diff --git a/src/plugins/platforms/eglfs/api/qeglfswindow_p.h b/src/plugins/platforms/eglfs/api/qeglfswindow_p.h
index 6bda262523..c61f04f569 100644
--- a/src/plugins/platforms/eglfs/api/qeglfswindow_p.h
+++ b/src/plugins/platforms/eglfs/api/qeglfswindow_p.h
@@ -95,7 +95,7 @@ public:
EGLNativeWindowType eglWindow() const;
EGLSurface surface() const;
- QEglFSScreen *screen() const;
+ QEglFSScreen *screen() const override;
bool hasNativeWindow() const { return m_flags.testFlag(HasNativeWindow); }
diff --git a/src/plugins/platforms/eglfs/deviceintegration/deviceintegration.pro b/src/plugins/platforms/eglfs/deviceintegration/deviceintegration.pro
index 92ee83fd8f..919ecd01f6 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/deviceintegration.pro
+++ b/src/plugins/platforms/eglfs/deviceintegration/deviceintegration.pro
@@ -4,6 +4,7 @@ QT_FOR_CONFIG += gui-private
qtConfig(egl_x11): SUBDIRS += eglfs_x11
qtConfig(eglfs_gbm): SUBDIRS *= eglfs_kms_support eglfs_kms
qtConfig(eglfs_egldevice): SUBDIRS *= eglfs_kms_support eglfs_kms_egldevice
+qtConfig(eglfs_vsp2): SUBDIRS += eglfs_kms_vsp2
qtConfig(eglfs_brcm): SUBDIRS += eglfs_brcm
qtConfig(eglfs_mali): SUBDIRS += eglfs_mali
qtConfig(eglfs_viv): SUBDIRS += eglfs_viv
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/qeglfsemulatorintegration.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/qeglfsemulatorintegration.h
index 513a5063fb..be5524cba3 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/qeglfsemulatorintegration.h
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/qeglfsemulatorintegration.h
@@ -45,7 +45,7 @@
#include <QtCore/QLoggingCategory>
#include <QtCore/QFunctionPointer>
-typedef const char *(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC) ();
+typedef QByteArray (EGLAPIENTRYP PFNQGSGETDISPLAYSPROC) ();
typedef void (EGLAPIENTRYP PFNQGSSETDISPLAYPROC) (uint screen);
QT_BEGIN_NAMESPACE
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/qeglfsemulatorscreen.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/qeglfsemulatorscreen.cpp
index 4546088327..7654034f85 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/qeglfsemulatorscreen.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/qeglfsemulatorscreen.cpp
@@ -110,6 +110,11 @@ uint QEglFSEmulatorScreen::id() const
return m_id;
}
+QString QEglFSEmulatorScreen::name() const
+{
+ return m_description;
+}
+
void QEglFSEmulatorScreen::initFromJsonObject(const QJsonObject &description)
{
QJsonValue value;
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/qeglfsemulatorscreen.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/qeglfsemulatorscreen.h
index 3e5113c9c2..c4994720fa 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/qeglfsemulatorscreen.h
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/qeglfsemulatorscreen.h
@@ -62,6 +62,7 @@ public:
qreal refreshRate() const override;
Qt::ScreenOrientation nativeOrientation() const override;
Qt::ScreenOrientation orientation() const override;
+ QString name() const override;
uint id() const;
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/eglfs_kms.pro b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/eglfs_kms.pro
index e522c0ee1b..43170a3875 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/eglfs_kms.pro
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/eglfs_kms.pro
@@ -4,7 +4,7 @@ PLUGIN_TYPE = egldeviceintegrations
PLUGIN_CLASS_NAME = QEglFSKmsGbmIntegrationPlugin
load(qt_plugin)
-QT += core-private gui-private eglfsdeviceintegration-private eglfs_kms_support-private kms_support-private
+QT += core-private gui-private eglfsdeviceintegration-private eglfs_kms_support-private kms_support-private edid_support-private
INCLUDEPATH += $$PWD/../../api $$PWD/../eglfs_kms_support
@@ -19,11 +19,13 @@ SOURCES += $$PWD/qeglfskmsgbmmain.cpp \
$$PWD/qeglfskmsgbmintegration.cpp \
$$PWD/qeglfskmsgbmdevice.cpp \
$$PWD/qeglfskmsgbmscreen.cpp \
- $$PWD/qeglfskmsgbmcursor.cpp
+ $$PWD/qeglfskmsgbmcursor.cpp \
+ $$PWD/qeglfskmsgbmwindow.cpp
HEADERS += $$PWD/qeglfskmsgbmintegration.h \
$$PWD/qeglfskmsgbmdevice.h \
$$PWD/qeglfskmsgbmscreen.h \
- $$PWD/qeglfskmsgbmcursor.h
+ $$PWD/qeglfskmsgbmcursor.h \
+ $$PWD/qeglfskmsgbmwindow.h
OTHER_FILES += $$PWD/eglfs_kms.json
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmcursor.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmcursor.cpp
index 19790e5c45..9bd7fee1fb 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmcursor.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmcursor.cpp
@@ -68,7 +68,7 @@ Q_DECLARE_LOGGING_CATEGORY(qLcEglfsKmsDebug)
QEglFSKmsGbmCursor::QEglFSKmsGbmCursor(QEglFSKmsGbmScreen *screen)
: m_screen(screen)
, m_cursorSize(64, 64) // 64x64 is the old standard size, we now try to query the real size below
- , m_bo(Q_NULLPTR)
+ , m_bo(nullptr)
, m_cursorImage(0, 0, 0, 0, 0, 0)
, m_state(CursorPendingVisible)
{
@@ -118,7 +118,7 @@ QEglFSKmsGbmCursor::~QEglFSKmsGbmCursor()
if (m_bo) {
gbm_bo_destroy(m_bo);
- m_bo = Q_NULLPTR;
+ m_bo = nullptr;
}
}
@@ -132,7 +132,7 @@ void QEglFSKmsGbmCursor::updateMouseStatus()
m_state = visible ? CursorPendingVisible : CursorPendingHidden;
#ifndef QT_NO_CURSOR
- changeCursor(Q_NULLPTR, m_screen->topLevelAt(pos()));
+ changeCursor(nullptr, m_screen->topLevelAt(pos()));
#endif
}
@@ -204,7 +204,7 @@ void QEglFSKmsGbmCursor::changeCursor(QCursor *windowCursor, QWindow *window)
painter.drawImage(0, 0, *m_cursorImage.image());
painter.end();
- gbm_bo_write(m_bo, cursorImage.constBits(), cursorImage.byteCount());
+ gbm_bo_write(m_bo, cursorImage.constBits(), cursorImage.sizeInBytes());
uint32_t handle = gbm_bo_get_handle(m_bo).u32;
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.cpp
index e218d580a2..20127ae7f7 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.cpp
@@ -53,28 +53,17 @@ QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(qLcEglfsKmsDebug)
-void QEglFSKmsGbmDevice::pageFlipHandler(int fd, unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec, void *user_data)
-{
- Q_UNUSED(fd);
- Q_UNUSED(sequence);
- Q_UNUSED(tv_sec);
- Q_UNUSED(tv_usec);
-
- QEglFSKmsScreen *screen = static_cast<QEglFSKmsScreen *>(user_data);
- screen->flipFinished();
-}
-
QEglFSKmsGbmDevice::QEglFSKmsGbmDevice(QKmsScreenConfig *screenConfig, const QString &path)
: QEglFSKmsDevice(screenConfig, path)
- , m_gbm_device(Q_NULLPTR)
- , m_globalCursor(Q_NULLPTR)
+ , m_gbm_device(nullptr)
+ , m_globalCursor(nullptr)
{
}
bool QEglFSKmsGbmDevice::open()
{
Q_ASSERT(fd() == -1);
- Q_ASSERT(m_gbm_device == Q_NULLPTR);
+ Q_ASSERT(m_gbm_device == nullptr);
int fd = qt_safe_open(devicePath().toLocal8Bit().constData(), O_RDWR | O_CLOEXEC);
if (fd == -1) {
@@ -103,7 +92,7 @@ void QEglFSKmsGbmDevice::close()
if (m_gbm_device) {
gbm_device_destroy(m_gbm_device);
- m_gbm_device = Q_NULLPTR;
+ m_gbm_device = nullptr;
}
if (fd() != -1) {
@@ -134,24 +123,13 @@ void QEglFSKmsGbmDevice::destroyGlobalCursor()
if (m_globalCursor) {
qCDebug(qLcEglfsKmsDebug, "Destroying global GBM mouse cursor");
delete m_globalCursor;
- m_globalCursor = Q_NULLPTR;
+ m_globalCursor = nullptr;
}
}
-void QEglFSKmsGbmDevice::handleDrmEvent()
-{
- drmEventContext drmEvent;
- memset(&drmEvent, 0, sizeof(drmEvent));
- drmEvent.version = 2;
- drmEvent.vblank_handler = nullptr;
- drmEvent.page_flip_handler = pageFlipHandler;
-
- drmHandleEvent(fd(), &drmEvent);
-}
-
QPlatformScreen *QEglFSKmsGbmDevice::createScreen(const QKmsOutput &output)
{
- QEglFSKmsGbmScreen *screen = new QEglFSKmsGbmScreen(this, output);
+ QEglFSKmsGbmScreen *screen = new QEglFSKmsGbmScreen(this, output, false);
if (!m_globalCursor && screenConfig()->hwCursor()) {
qCDebug(qLcEglfsKmsDebug, "Creating new global GBM mouse cursor");
@@ -161,4 +139,20 @@ QPlatformScreen *QEglFSKmsGbmDevice::createScreen(const QKmsOutput &output)
return screen;
}
+QPlatformScreen *QEglFSKmsGbmDevice::createHeadlessScreen()
+{
+ return new QEglFSKmsGbmScreen(this, QKmsOutput(), true);
+}
+
+void QEglFSKmsGbmDevice::registerScreenCloning(QPlatformScreen *screen,
+ QPlatformScreen *screenThisScreenClones,
+ const QVector<QPlatformScreen *> &screensCloningThisScreen)
+{
+ if (!screenThisScreenClones && screensCloningThisScreen.isEmpty())
+ return;
+
+ QEglFSKmsGbmScreen *gbmScreen = static_cast<QEglFSKmsGbmScreen *>(screen);
+ gbmScreen->initCloning(screenThisScreenClones, screensCloningThisScreen);
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.h
index 08ca28d48e..518e2ce58b 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.h
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.h
@@ -65,9 +65,11 @@ public:
QPlatformCursor *globalCursor() const;
void destroyGlobalCursor();
- void handleDrmEvent();
-
QPlatformScreen *createScreen(const QKmsOutput &output) override;
+ QPlatformScreen *createHeadlessScreen() override;
+ void registerScreenCloning(QPlatformScreen *screen,
+ QPlatformScreen *screenThisScreenClones,
+ const QVector<QPlatformScreen *> &screensCloningThisScreen) override;
private:
Q_DISABLE_COPY(QEglFSKmsGbmDevice)
@@ -75,12 +77,6 @@ private:
gbm_device *m_gbm_device;
QEglFSKmsGbmCursor *m_globalCursor;
-
- static void pageFlipHandler(int fd,
- unsigned int sequence,
- unsigned int tv_sec,
- unsigned int tv_usec,
- void *user_data);
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmintegration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmintegration.cpp
index b6cdcf92b6..402338197d 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmintegration.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmintegration.cpp
@@ -43,19 +43,13 @@
#include "qeglfskmsgbmdevice.h"
#include "qeglfskmsgbmscreen.h"
#include "qeglfskmsgbmcursor.h"
+#include "qeglfskmsgbmwindow.h"
#include "private/qeglfscursor_p.h"
-#include <QtDeviceDiscoverySupport/private/qdevicediscovery_p.h>
#include <QtCore/QLoggingCategory>
-#include <QtCore/QJsonDocument>
-#include <QtCore/QJsonObject>
-#include <QtCore/QJsonArray>
-#include <QtGui/qpa/qplatformwindow.h>
-#include <QtGui/qpa/qplatformcursor.h>
#include <QtGui/QScreen>
+#include <QtDeviceDiscoverySupport/private/qdevicediscovery_p.h>
-#include <xf86drm.h>
-#include <xf86drmMode.h>
#include <gbm.h>
QT_BEGIN_NAMESPACE
@@ -67,20 +61,35 @@ QEglFSKmsGbmIntegration::QEglFSKmsGbmIntegration()
qCDebug(qLcEglfsKmsDebug, "New DRM/KMS via GBM integration created");
}
-EGLNativeWindowType QEglFSKmsGbmIntegration::createNativeWindow(QPlatformWindow *platformWindow,
- const QSize &size,
- const QSurfaceFormat &format)
+#ifndef EGL_EXT_platform_base
+typedef EGLDisplay (EGLAPIENTRYP PFNEGLGETPLATFORMDISPLAYEXTPROC) (EGLenum platform, void *native_display, const EGLint *attrib_list);
+typedef EGLSurface (EGLAPIENTRYP PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC) (EGLDisplay dpy, EGLConfig config, void *native_window, const EGLint *attrib_list);
+#endif
+
+#ifndef EGL_PLATFORM_GBM_KHR
+#define EGL_PLATFORM_GBM_KHR 0x31D7
+#endif
+
+EGLDisplay QEglFSKmsGbmIntegration::createDisplay(EGLNativeDisplayType nativeDisplay)
{
- Q_UNUSED(size);
- Q_UNUSED(format);
+ qCDebug(qLcEglfsKmsDebug, "Querying EGLDisplay");
+ EGLDisplay display;
+
+ PFNEGLGETPLATFORMDISPLAYEXTPROC getPlatformDisplay = nullptr;
+ const char *extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
+ if (extensions && (strstr(extensions, "EGL_KHR_platform_gbm") || strstr(extensions, "EGL_MESA_platform_gbm"))) {
+ getPlatformDisplay = reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>(
+ eglGetProcAddress("eglGetPlatformDisplayEXT"));
+ }
- QEglFSKmsGbmScreen *screen = static_cast<QEglFSKmsGbmScreen *>(platformWindow->screen());
- if (screen->surface()) {
- qWarning("Only single window per screen supported!");
- return 0;
+ if (getPlatformDisplay) {
+ display = getPlatformDisplay(EGL_PLATFORM_GBM_KHR, nativeDisplay, nullptr);
+ } else {
+ qCDebug(qLcEglfsKmsDebug, "No eglGetPlatformDisplay for GBM, falling back to eglGetDisplay");
+ display = eglGetDisplay(nativeDisplay);
}
- return reinterpret_cast<EGLNativeWindowType>(screen->createSurface());
+ return display;
}
EGLNativeWindowType QEglFSKmsGbmIntegration::createNativeOffscreenWindow(const QSurfaceFormat &format)
@@ -88,7 +97,6 @@ EGLNativeWindowType QEglFSKmsGbmIntegration::createNativeOffscreenWindow(const Q
Q_UNUSED(format);
Q_ASSERT(device());
- qCDebug(qLcEglfsKmsDebug) << "Creating native off screen window";
gbm_surface *surface = gbm_surface_create(static_cast<QEglFSKmsGbmDevice *>(device())->gbmDevice(),
1, 1,
GBM_FORMAT_XRGB8888,
@@ -117,8 +125,7 @@ QPlatformCursor *QEglFSKmsGbmIntegration::createCursor(QPlatformScreen *screen)
void QEglFSKmsGbmIntegration::presentBuffer(QPlatformSurface *surface)
{
QWindow *window = static_cast<QWindow *>(surface->surface());
- QEglFSKmsScreen *screen = static_cast<QEglFSKmsScreen *>(window->screen()->handle());
-
+ QEglFSKmsGbmScreen *screen = static_cast<QEglFSKmsGbmScreen *>(window->screen()->handle());
screen->flip();
}
@@ -143,4 +150,9 @@ QKmsDevice *QEglFSKmsGbmIntegration::createDevice()
return new QEglFSKmsGbmDevice(screenConfig(), path);
}
+QEglFSWindow *QEglFSKmsGbmIntegration::createWindow(QWindow *window) const
+{
+ return new QEglFSKmsGbmWindow(window, this);
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmintegration.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmintegration.h
index 38f132d72e..71f232abf9 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmintegration.h
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmintegration.h
@@ -55,14 +55,13 @@ class QEglFSKmsGbmIntegration : public QEglFSKmsIntegration
public:
QEglFSKmsGbmIntegration();
- EGLNativeWindowType createNativeWindow(QPlatformWindow *platformWindow,
- const QSize &size,
- const QSurfaceFormat &format) override;
+ EGLDisplay createDisplay(EGLNativeDisplayType nativeDisplay) override;
EGLNativeWindowType createNativeOffscreenWindow(const QSurfaceFormat &format) override;
void destroyNativeWindow(EGLNativeWindowType window) override;
QPlatformCursor *createCursor(QPlatformScreen *screen) const override;
void presentBuffer(QPlatformSurface *surface) override;
+ QEglFSWindow *createWindow(QWindow *window) const override;
protected:
QKmsDevice *createDevice() override;
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp
index 87fb3146c7..4742143121 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp
@@ -1,7 +1,7 @@
/****************************************************************************
**
+** Copyright (C) 2017 The Qt Company Ltd.
** Copyright (C) 2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
-** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2016 Pelagicore AG
** Contact: https://www.qt.io/licensing/
**
@@ -55,6 +55,18 @@ QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(qLcEglfsKmsDebug)
+static inline uint32_t drmFormatToGbmFormat(uint32_t drmFormat)
+{
+ Q_ASSERT(DRM_FORMAT_XRGB8888 == GBM_FORMAT_XRGB8888);
+ return drmFormat;
+}
+
+static inline uint32_t gbmFormatToDrmFormat(uint32_t gbmFormat)
+{
+ Q_ASSERT(DRM_FORMAT_XRGB8888 == GBM_FORMAT_XRGB8888);
+ return gbmFormat;
+}
+
void QEglFSKmsGbmScreen::bufferDestroyedHandler(gbm_bo *bo, void *data)
{
FrameBuffer *fb = static_cast<FrameBuffer *>(data);
@@ -77,29 +89,34 @@ QEglFSKmsGbmScreen::FrameBuffer *QEglFSKmsGbmScreen::framebufferForBufferObject(
uint32_t width = gbm_bo_get_width(bo);
uint32_t height = gbm_bo_get_height(bo);
- uint32_t stride = gbm_bo_get_stride(bo);
- uint32_t handle = gbm_bo_get_handle(bo).u32;
+ uint32_t handles[4] = { gbm_bo_get_handle(bo).u32 };
+ uint32_t strides[4] = { gbm_bo_get_stride(bo) };
+ uint32_t offsets[4] = { 0 };
+ uint32_t pixelFormat = gbmFormatToDrmFormat(gbm_bo_get_format(bo));
QScopedPointer<FrameBuffer> fb(new FrameBuffer);
+ qCDebug(qLcEglfsKmsDebug, "Adding FB, size %ux%u, DRM format 0x%x", width, height, pixelFormat);
- int ret = drmModeAddFB(device()->fd(), width, height, 24, 32,
- stride, handle, &fb->fb);
+ int ret = drmModeAddFB2(device()->fd(), width, height, pixelFormat,
+ handles, strides, offsets, &fb->fb, 0);
if (ret) {
qWarning("Failed to create KMS FB!");
- return Q_NULLPTR;
+ return nullptr;
}
gbm_bo_set_user_data(bo, fb.data(), bufferDestroyedHandler);
return fb.take();
}
-QEglFSKmsGbmScreen::QEglFSKmsGbmScreen(QKmsDevice *device, const QKmsOutput &output)
- : QEglFSKmsScreen(device, output)
- , m_gbm_surface(Q_NULLPTR)
- , m_gbm_bo_current(Q_NULLPTR)
- , m_gbm_bo_next(Q_NULLPTR)
- , m_cursor(Q_NULLPTR)
+QEglFSKmsGbmScreen::QEglFSKmsGbmScreen(QKmsDevice *device, const QKmsOutput &output, bool headless)
+ : QEglFSKmsScreen(device, output, headless)
+ , m_gbm_surface(nullptr)
+ , m_gbm_bo_current(nullptr)
+ , m_gbm_bo_next(nullptr)
+ , m_flipPending(false)
+ , m_cursor(nullptr)
+ , m_cloneSource(nullptr)
{
}
@@ -114,6 +131,8 @@ QEglFSKmsGbmScreen::~QEglFSKmsGbmScreen()
QPlatformCursor *QEglFSKmsGbmScreen::cursor() const
{
QKmsScreenConfig *config = device()->screenConfig();
+ if (config->headless())
+ return nullptr;
if (config->hwCursor()) {
if (!config->separateScreens())
return static_cast<QEglFSKmsGbmDevice *>(device())->globalCursor();
@@ -132,47 +151,112 @@ QPlatformCursor *QEglFSKmsGbmScreen::cursor() const
gbm_surface *QEglFSKmsGbmScreen::createSurface()
{
if (!m_gbm_surface) {
- qCDebug(qLcEglfsKmsDebug) << "Creating window for screen" << name();
+ uint32_t gbmFormat = drmFormatToGbmFormat(m_output.drm_format);
+ qCDebug(qLcEglfsKmsDebug, "Creating gbm_surface for screen %s with format 0x%x", qPrintable(name()), gbmFormat);
m_gbm_surface = gbm_surface_create(static_cast<QEglFSKmsGbmDevice *>(device())->gbmDevice(),
rawGeometry().width(),
rawGeometry().height(),
- GBM_FORMAT_XRGB8888,
+ gbmFormat,
GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
}
- return m_gbm_surface;
+ return m_gbm_surface; // not owned, gets destroyed in QEglFSKmsGbmIntegration::destroyNativeWindow() via QEglFSKmsGbmWindow::invalidateSurface()
}
-void QEglFSKmsGbmScreen::destroySurface()
+void QEglFSKmsGbmScreen::resetSurface()
{
- if (m_gbm_bo_current) {
- gbm_bo_destroy(m_gbm_bo_current);
- m_gbm_bo_current = Q_NULLPTR;
- }
+ m_gbm_surface = nullptr;
+}
- if (m_gbm_bo_next) {
- gbm_bo_destroy(m_gbm_bo_next);
- m_gbm_bo_next = Q_NULLPTR;
+void QEglFSKmsGbmScreen::initCloning(QPlatformScreen *screenThisScreenClones,
+ const QVector<QPlatformScreen *> &screensCloningThisScreen)
+{
+ // clone destinations need to know the clone source
+ const bool clonesAnother = screenThisScreenClones != nullptr;
+ if (clonesAnother && !screensCloningThisScreen.isEmpty()) {
+ qWarning("QEglFSKmsGbmScreen %s cannot be clone source and destination at the same time", qPrintable(name()));
+ return;
}
+ if (clonesAnother)
+ m_cloneSource = static_cast<QEglFSKmsGbmScreen *>(screenThisScreenClones);
+
+ // clone sources need to know their additional destinations
+ for (QPlatformScreen *s : screensCloningThisScreen) {
+ CloneDestination d;
+ d.screen = static_cast<QEglFSKmsGbmScreen *>(s);
+ m_cloneDests.append(d);
+ }
+}
+
+void QEglFSKmsGbmScreen::ensureModeSet(uint32_t fb)
+{
+ QKmsOutput &op(output());
+ const int fd = device()->fd();
+
+ if (!op.mode_set) {
+ op.mode_set = true;
+
+ bool doModeSet = true;
+ drmModeCrtcPtr currentMode = drmModeGetCrtc(fd, op.crtc_id);
+ const bool alreadySet = currentMode && !memcmp(&currentMode->mode, &op.modes[op.mode], sizeof(drmModeModeInfo));
+ if (currentMode)
+ drmModeFreeCrtc(currentMode);
+ if (alreadySet) {
+ static bool alwaysDoSet = qEnvironmentVariableIntValue("QT_QPA_EGLFS_ALWAYS_SET_MODE");
+ if (!alwaysDoSet) {
+ qCDebug(qLcEglfsKmsDebug, "Mode already set, skipping modesetting for screen %s", qPrintable(name()));
+ doModeSet = false;
+ }
+ }
- if (m_gbm_surface) {
- gbm_surface_destroy(m_gbm_surface);
- m_gbm_surface = Q_NULLPTR;
+ if (doModeSet) {
+ qCDebug(qLcEglfsKmsDebug, "Setting mode for screen %s", qPrintable(name()));
+ int ret = drmModeSetCrtc(fd,
+ op.crtc_id,
+ fb,
+ 0, 0,
+ &op.connector_id, 1,
+ &op.modes[op.mode]);
+
+ if (ret == 0)
+ setPowerState(PowerStateOn);
+ else
+ qErrnoWarning(errno, "Could not set DRM mode for screen %s", qPrintable(name()));
+ }
}
}
void QEglFSKmsGbmScreen::waitForFlip()
{
+ if (m_headless || m_cloneSource)
+ return;
+
// Don't lock the mutex unless we actually need to
if (!m_gbm_bo_next)
return;
QMutexLocker lock(&m_waitForFlipMutex);
- while (m_gbm_bo_next)
- static_cast<QEglFSKmsGbmDevice *>(device())->handleDrmEvent();
+ while (m_gbm_bo_next) {
+ drmEventContext drmEvent;
+ memset(&drmEvent, 0, sizeof(drmEvent));
+ drmEvent.version = 2;
+ drmEvent.vblank_handler = nullptr;
+ drmEvent.page_flip_handler = pageFlipHandler;
+ drmHandleEvent(device()->fd(), &drmEvent);
+ }
}
void QEglFSKmsGbmScreen::flip()
{
+ // For headless screen just return silently. It is not necessarily an error
+ // to end up here, so show no warnings.
+ if (m_headless)
+ return;
+
+ if (m_cloneSource) {
+ qWarning("Screen %s clones another screen. swapBuffers() not allowed.", qPrintable(name()));
+ return;
+ }
+
if (!m_gbm_surface) {
qWarning("Cannot sync before platform init!");
return;
@@ -185,60 +269,92 @@ void QEglFSKmsGbmScreen::flip()
}
FrameBuffer *fb = framebufferForBufferObject(m_gbm_bo_next);
+ ensureModeSet(fb->fb);
QKmsOutput &op(output());
const int fd = device()->fd();
- const uint32_t w = op.modes[op.mode].hdisplay;
- const uint32_t h = op.modes[op.mode].vdisplay;
-
- if (!op.mode_set) {
- int ret = drmModeSetCrtc(fd,
- op.crtc_id,
- fb->fb,
- 0, 0,
- &op.connector_id, 1,
- &op.modes[op.mode]);
-
- if (ret == -1) {
- qErrnoWarning(errno, "Could not set DRM mode!");
- } else {
- op.mode_set = true;
- setPowerState(PowerStateOn);
-
- if (!op.plane_set) {
- op.plane_set = true;
- if (op.wants_plane) {
- int ret = drmModeSetPlane(fd, op.plane_id, op.crtc_id,
- uint32_t(-1), 0,
- 0, 0, w, h,
- 0 << 16, 0 << 16, w << 16, h << 16);
- if (ret == -1)
- qErrnoWarning(errno, "drmModeSetPlane failed");
- }
- }
- }
- }
-
+ m_flipPending = true;
int ret = drmModePageFlip(fd,
op.crtc_id,
fb->fb,
DRM_MODE_PAGE_FLIP_EVENT,
this);
if (ret) {
- qErrnoWarning("Could not queue DRM page flip!");
+ qErrnoWarning("Could not queue DRM page flip on screen %s", qPrintable(name()));
+ m_flipPending = false;
gbm_surface_release_buffer(m_gbm_surface, m_gbm_bo_next);
- m_gbm_bo_next = Q_NULLPTR;
+ m_gbm_bo_next = nullptr;
+ return;
+ }
+
+ for (CloneDestination &d : m_cloneDests) {
+ if (d.screen != this) {
+ d.screen->ensureModeSet(fb->fb);
+ d.cloneFlipPending = true;
+ int ret = drmModePageFlip(fd,
+ d.screen->output().crtc_id,
+ fb->fb,
+ DRM_MODE_PAGE_FLIP_EVENT,
+ d.screen);
+ if (ret) {
+ qErrnoWarning("Could not queue DRM page flip for clone screen %s", qPrintable(name()));
+ d.cloneFlipPending = false;
+ }
+ }
}
}
+void QEglFSKmsGbmScreen::pageFlipHandler(int fd, unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec, void *user_data)
+{
+ Q_UNUSED(fd);
+ Q_UNUSED(sequence);
+ Q_UNUSED(tv_sec);
+ Q_UNUSED(tv_usec);
+
+ QEglFSKmsGbmScreen *screen = static_cast<QEglFSKmsGbmScreen *>(user_data);
+ screen->flipFinished();
+}
+
void QEglFSKmsGbmScreen::flipFinished()
{
+ if (m_cloneSource) {
+ m_cloneSource->cloneDestFlipFinished(this);
+ return;
+ }
+
+ m_flipPending = false;
+ updateFlipStatus();
+}
+
+void QEglFSKmsGbmScreen::cloneDestFlipFinished(QEglFSKmsGbmScreen *cloneDestScreen)
+{
+ for (CloneDestination &d : m_cloneDests) {
+ if (d.screen == cloneDestScreen) {
+ d.cloneFlipPending = false;
+ break;
+ }
+ }
+ updateFlipStatus();
+}
+
+void QEglFSKmsGbmScreen::updateFlipStatus()
+{
+ Q_ASSERT(!m_cloneSource);
+
+ if (m_flipPending)
+ return;
+
+ for (const CloneDestination &d : m_cloneDests) {
+ if (d.cloneFlipPending)
+ return;
+ }
+
if (m_gbm_bo_current)
gbm_surface_release_buffer(m_gbm_surface,
m_gbm_bo_current);
m_gbm_bo_current = m_gbm_bo_next;
- m_gbm_bo_next = Q_NULLPTR;
+ m_gbm_bo_next = nullptr;
}
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.h
index 341cc95bbe..f5a2122723 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.h
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.h
@@ -54,34 +54,54 @@ class QEglFSKmsGbmCursor;
class QEglFSKmsGbmScreen : public QEglFSKmsScreen
{
public:
- QEglFSKmsGbmScreen(QKmsDevice *device, const QKmsOutput &output);
+ QEglFSKmsGbmScreen(QKmsDevice *device, const QKmsOutput &output, bool headless);
~QEglFSKmsGbmScreen();
QPlatformCursor *cursor() const override;
- gbm_surface *surface() const { return m_gbm_surface; }
gbm_surface *createSurface();
- void destroySurface();
+ void resetSurface();
+
+ void initCloning(QPlatformScreen *screenThisScreenClones,
+ const QVector<QPlatformScreen *> &screensCloningThisScreen);
void waitForFlip() override;
- void flip() override;
- void flipFinished() override;
+
+ void flip();
private:
+ void flipFinished();
+ void ensureModeSet(uint32_t fb);
+ void cloneDestFlipFinished(QEglFSKmsGbmScreen *cloneDestScreen);
+ void updateFlipStatus();
+
+ static void pageFlipHandler(int fd,
+ unsigned int sequence,
+ unsigned int tv_sec,
+ unsigned int tv_usec,
+ void *user_data);
+
gbm_surface *m_gbm_surface;
gbm_bo *m_gbm_bo_current;
gbm_bo *m_gbm_bo_next;
+ bool m_flipPending;
QScopedPointer<QEglFSKmsGbmCursor> m_cursor;
struct FrameBuffer {
- FrameBuffer() : fb(0) {}
- uint32_t fb;
+ uint32_t fb = 0;
};
static void bufferDestroyedHandler(gbm_bo *bo, void *data);
FrameBuffer *framebufferForBufferObject(gbm_bo *bo);
+ QEglFSKmsGbmScreen *m_cloneSource;
+ struct CloneDestination {
+ QEglFSKmsGbmScreen *screen = nullptr;
+ bool cloneFlipPending = false;
+ };
+ QVector<CloneDestination> m_cloneDests;
+
static QMutex m_waitForFlipMutex;
};
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmwindow.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmwindow.cpp
new file mode 100644
index 0000000000..110a592dec
--- /dev/null
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmwindow.cpp
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qeglfskmsgbmwindow.h"
+#include "qeglfskmsgbmintegration.h"
+#include "qeglfskmsgbmscreen.h"
+
+#include <QtEglSupport/private/qeglconvenience_p.h>
+
+QT_BEGIN_NAMESPACE
+
+void QEglFSKmsGbmWindow::resetSurface()
+{
+ QEglFSKmsGbmScreen *gbmScreen = static_cast<QEglFSKmsGbmScreen *>(screen());
+ EGLDisplay display = gbmScreen->display();
+ QSurfaceFormat platformFormat = m_integration->surfaceFormatFor(window()->requestedFormat());
+ m_config = QEglFSDeviceIntegration::chooseConfig(display, platformFormat);
+ m_format = q_glFormatFromConfig(display, m_config, platformFormat);
+ // One fullscreen window per screen -> the native window is simply the gbm_surface the screen created.
+ m_window = reinterpret_cast<EGLNativeWindowType>(gbmScreen->createSurface());
+
+ PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC createPlatformWindowSurface = nullptr;
+ const char *extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
+ if (extensions && (strstr(extensions, "EGL_KHR_platform_gbm") || strstr(extensions, "EGL_MESA_platform_gbm"))) {
+ createPlatformWindowSurface = reinterpret_cast<PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC>(
+ eglGetProcAddress("eglCreatePlatformWindowSurfaceEXT"));
+ }
+
+ if (createPlatformWindowSurface) {
+ m_surface = createPlatformWindowSurface(display, m_config, reinterpret_cast<void *>(m_window), nullptr);
+ } else {
+ qCDebug(qLcEglfsKmsDebug, "No eglCreatePlatformWindowSurface for GBM, falling back to eglCreateWindowSurface");
+ m_surface = eglCreateWindowSurface(display, m_config, m_window, nullptr);
+ }
+}
+
+void QEglFSKmsGbmWindow::invalidateSurface()
+{
+ QEglFSKmsGbmScreen *gbmScreen = static_cast<QEglFSKmsGbmScreen *>(screen());
+ QEglFSWindow::invalidateSurface();
+ gbmScreen->resetSurface();
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/windows/accessible/comutils.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmwindow.h
index b1e6183a0f..a19cf7e8bc 100644
--- a/src/plugins/platforms/windows/accessible/comutils.h
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmwindow.h
@@ -1,6 +1,8 @@
/****************************************************************************
**
+** Copyright (C) 2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2016 Pelagicore AG
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
@@ -36,29 +38,30 @@
** $QT_END_LICENSE$
**
****************************************************************************/
-#ifndef COMUTILS_H
-#define COMUTILS_H
-#if !defined(_WINDOWS_) && !defined(_WINDOWS_H) && !defined(__WINDOWS__)
-#error Must include windows.h first!
-#endif
+#ifndef QEGLFSKMSGBMWINDOW_H
+#define QEGLFSKMSGBMWINDOW_H
-#include <ocidl.h>
-#include <QtCore/qstring.h>
+#include "private/qeglfswindow_p.h"
QT_BEGIN_NAMESPACE
-class QVariant;
+class QEglFSKmsGbmIntegration;
-// Originally QVariantToVARIANT copied from ActiveQt - renamed to avoid conflicts in static builds.
-bool QVariant2VARIANT(const QVariant &var, VARIANT &arg, const QByteArray &typeName, bool out);
-
-inline BSTR QStringToBSTR(const QString &str)
+class QEglFSKmsGbmWindow : public QEglFSWindow
{
- return SysAllocStringLen(reinterpret_cast<const OLECHAR *>(str.unicode()), UINT(str.length()));
-}
+public:
+ QEglFSKmsGbmWindow(QWindow *w, const QEglFSKmsGbmIntegration *integration)
+ : QEglFSWindow(w),
+ m_integration(integration)
+ { }
+ void resetSurface() override;
+ void invalidateSurface() override;
-QT_END_NAMESPACE
+private:
+ const QEglFSKmsGbmIntegration *m_integration;
+};
-#endif // COMUTILS_H
+QT_END_NAMESPACE
+#endif // QEGLFSKMSGBMWINDOW_H
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/eglfs_kms_egldevice.pro b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/eglfs_kms_egldevice.pro
index a2dc9c4a50..36f037ac6c 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/eglfs_kms_egldevice.pro
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/eglfs_kms_egldevice.pro
@@ -1,6 +1,6 @@
TARGET = qeglfs-kms-egldevice-integration
-QT += core-private gui-private eglfsdeviceintegration-private eglfs_kms_support-private kms_support-private
+QT += core-private gui-private eglfsdeviceintegration-private eglfs_kms_support-private kms_support-private edid_support-private
INCLUDEPATH += $$PWD/../../api $$PWD/../eglfs_kms_support
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevice.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevice.cpp
index 0a66a897a1..8c8e6260f1 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevice.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevice.cpp
@@ -58,7 +58,7 @@ bool QEglFSKmsEglDevice::open()
{
Q_ASSERT(fd() == -1);
- int fd = drmOpen(devicePath().toLocal8Bit().constData(), Q_NULLPTR);
+ int fd = drmOpen(devicePath().toLocal8Bit().constData(), nullptr);
if (Q_UNLIKELY(fd < 0))
qFatal("Could not open DRM (NV) device");
@@ -77,9 +77,9 @@ void QEglFSKmsEglDevice::close()
setFd(-1);
}
-EGLNativeDisplayType QEglFSKmsEglDevice::nativeDisplay() const
+void *QEglFSKmsEglDevice::nativeDisplay() const
{
- return reinterpret_cast<EGLNativeDisplayType>(m_devInt->eglDevice());
+ return m_devInt->eglDevice();
}
QPlatformScreen *QEglFSKmsEglDevice::createScreen(const QKmsOutput &output)
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldeviceintegration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldeviceintegration.cpp
index 3af21d768e..a67457a6a5 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldeviceintegration.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldeviceintegration.cpp
@@ -51,7 +51,7 @@ QT_BEGIN_NAMESPACE
QEglFSKmsEglDeviceIntegration::QEglFSKmsEglDeviceIntegration()
: m_egl_device(EGL_NO_DEVICE_EXT)
- , m_funcs(Q_NULLPTR)
+ , m_funcs(nullptr)
{
qCDebug(qLcEglfsKmsDebug, "New DRM/KMS on EGLDevice integration created");
}
@@ -75,7 +75,7 @@ EGLDisplay QEglFSKmsEglDeviceIntegration::createDisplay(EGLNativeDisplayType nat
EGLDisplay display;
if (m_funcs->has_egl_platform_device) {
- display = m_funcs->get_platform_display(EGL_PLATFORM_DEVICE_EXT, nativeDisplay, Q_NULLPTR);
+ display = m_funcs->get_platform_display(EGL_PLATFORM_DEVICE_EXT, nativeDisplay, nullptr);
} else {
qWarning("EGL_EXT_platform_device not available, falling back to legacy path!");
display = eglGetDisplay(nativeDisplay);
@@ -162,7 +162,7 @@ void QEglFSKmsEglDeviceWindow::resetSurface()
qCDebug(qLcEglfsKmsDebug, "Could not query number of EGLStream FIFO frames");
}
- if (!m_integration->m_funcs->get_output_layers(display, Q_NULLPTR, Q_NULLPTR, 0, &count) || count == 0) {
+ if (!m_integration->m_funcs->get_output_layers(display, nullptr, nullptr, 0, &count) || count == 0) {
qWarning("No output layers found");
return;
}
@@ -172,7 +172,7 @@ void QEglFSKmsEglDeviceWindow::resetSurface()
QVector<EGLOutputLayerEXT> layers;
layers.resize(count);
EGLint actualCount;
- if (!m_integration->m_funcs->get_output_layers(display, Q_NULLPTR, layers.data(), count, &actualCount)) {
+ if (!m_integration->m_funcs->get_output_layers(display, nullptr, layers.data(), count, &actualCount)) {
qWarning("Failed to get layers");
return;
}
@@ -180,7 +180,7 @@ void QEglFSKmsEglDeviceWindow::resetSurface()
QEglFSKmsEglDeviceScreen *cur_screen = static_cast<QEglFSKmsEglDeviceScreen *>(screen());
Q_ASSERT(cur_screen);
QKmsOutput &output(cur_screen->output());
- const uint32_t wantedId = !output.wants_plane ? output.crtc_id : output.plane_id;
+ const uint32_t wantedId = !output.wants_forced_plane ? output.crtc_id : output.forced_plane_id;
qCDebug(qLcEglfsKmsDebug, "Searching for id: %d", wantedId);
EGLOutputLayerEXT layer = EGL_NO_OUTPUT_LAYER_EXT;
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevicescreen.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevicescreen.cpp
index a27c89faab..531b73d1dc 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevicescreen.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevicescreen.cpp
@@ -105,12 +105,12 @@ void QEglFSKmsEglDeviceScreen::waitForFlip()
qErrnoWarning(errno, "drmModeSetCrtc failed");
}
- if (!op.plane_set) {
- op.plane_set = true;
+ if (!op.forced_plane_set) {
+ op.forced_plane_set = true;
- if (op.wants_plane) {
- qCDebug(qLcEglfsKmsDebug, "Setting plane %u", op.plane_id);
- int ret = drmModeSetPlane(fd, op.plane_id, op.crtc_id, uint32_t(-1), 0,
+ if (op.wants_forced_plane) {
+ qCDebug(qLcEglfsKmsDebug, "Setting plane %u", op.forced_plane_id);
+ int ret = drmModeSetPlane(fd, op.forced_plane_id, op.crtc_id, uint32_t(-1), 0,
0, 0, w, h,
0 << 16, 0 << 16, w << 16, h << 16);
if (ret == -1)
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/eglfs_kms_support.pro b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/eglfs_kms_support.pro
index 3c0a0ce30f..4d1321079c 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/eglfs_kms_support.pro
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/eglfs_kms_support.pro
@@ -2,7 +2,7 @@ TARGET = QtEglFsKmsSupport
CONFIG += no_module_headers internal_module
load(qt_module)
-QT += core-private gui-private eglfsdeviceintegration-private kms_support-private
+QT += core-private gui-private eglfsdeviceintegration-private kms_support-private edid_support-private
INCLUDEPATH += $$PWD/../../api
@@ -19,4 +19,5 @@ SOURCES += $$PWD/qeglfskmsintegration.cpp \
HEADERS += $$PWD/qeglfskmsintegration.h \
$$PWD/qeglfskmsdevice.h \
- $$PWD/qeglfskmsscreen.h
+ $$PWD/qeglfskmsscreen.h \
+ $$PWD/qeglfskmshelpers.h
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmshelpers.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmshelpers.h
new file mode 100644
index 0000000000..c9e5f04a7b
--- /dev/null
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmshelpers.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QEGLFSKMSHELPERS_H
+#define QEGLFSKMSHELPERS_H
+
+#include <QString>
+
+QT_BEGIN_NAMESPACE
+
+inline QString q_fourccToString(uint code)
+{
+ QString s;
+ s.reserve(4);
+ s.append(code & 0xff);
+ s.append((code >> 8) & 0xff);
+ s.append((code >> 16) & 0xff);
+ s.append((code >> 24) & 0xff);
+ return s;
+}
+
+QT_END_NAMESPACE
+
+#endif // QEGLFSKMSHELPERS_H
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsintegration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsintegration.cpp
index c77151181e..06bc272050 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsintegration.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsintegration.cpp
@@ -55,7 +55,7 @@ QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(qLcEglfsKmsDebug, "qt.qpa.eglfs.kms")
QEglFSKmsIntegration::QEglFSKmsIntegration()
- : m_device(Q_NULLPTR),
+ : m_device(nullptr),
m_screenConfig(new QKmsScreenConfig)
{
}
@@ -78,7 +78,7 @@ void QEglFSKmsIntegration::platformDestroy()
qCDebug(qLcEglfsKmsDebug, "platformDestroy: Closing DRM device");
m_device->close();
delete m_device;
- m_device = Q_NULLPTR;
+ m_device = nullptr;
}
EGLNativeDisplayType QEglFSKmsIntegration::platformDisplay() const
@@ -133,6 +133,24 @@ bool QEglFSKmsIntegration::supportsPBuffers() const
return m_screenConfig->supportsPBuffers();
}
+void *QEglFSKmsIntegration::nativeResourceForIntegration(const QByteArray &name)
+{
+ if (name == QByteArrayLiteral("dri_fd") && m_device)
+ return (void *) (qintptr) m_device->fd();
+
+ return nullptr;
+}
+
+void *QEglFSKmsIntegration::nativeResourceForScreen(const QByteArray &resource, QScreen *screen)
+{
+ QEglFSKmsScreen *s = static_cast<QEglFSKmsScreen *>(screen->handle());
+ if (s) {
+ if (resource == QByteArrayLiteral("dri_crtcid"))
+ return (void *) (qintptr) s->output().crtc_id;
+ }
+ return nullptr;
+}
+
QKmsDevice *QEglFSKmsIntegration::device() const
{
return m_device;
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsintegration.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsintegration.h
index 9955616919..e2c37f60fc 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsintegration.h
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsintegration.h
@@ -69,6 +69,8 @@ public:
bool hasCapability(QPlatformIntegration::Capability cap) const override;
void waitForVSync(QPlatformSurface *surface) const override;
bool supportsPBuffers() const override;
+ void *nativeResourceForIntegration(const QByteArray &name) override;
+ void *nativeResourceForScreen(const QByteArray &resource, QScreen *screen) override;
QKmsDevice *device() const;
QKmsScreenConfig *screenConfig() const;
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.cpp
index 3951f46a82..5e45b42abe 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.cpp
@@ -1,7 +1,7 @@
/****************************************************************************
**
-** Copyright (C) 2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2017 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
** Copyright (C) 2016 Pelagicore AG
** Contact: https://www.qt.io/licensing/
**
@@ -68,14 +68,31 @@ private:
QEglFSKmsScreen *m_screen;
};
-QEglFSKmsScreen::QEglFSKmsScreen(QKmsDevice *device, const QKmsOutput &output)
- : QEglFSScreen(eglGetDisplay((EGLNativeDisplayType) device->nativeDisplay()))
+QEglFSKmsScreen::QEglFSKmsScreen(QKmsDevice *device, const QKmsOutput &output, bool headless)
+ : QEglFSScreen(static_cast<QEglFSIntegration *>(QGuiApplicationPrivate::platformIntegration())->display())
, m_device(device)
, m_output(output)
, m_powerState(PowerStateOn)
, m_interruptHandler(new QEglFSKmsInterruptHandler(this))
+ , m_headless(headless)
{
m_siblings << this; // gets overridden later
+
+ if (m_output.edid_blob) {
+ QByteArray edid(reinterpret_cast<const char *>(m_output.edid_blob->data), m_output.edid_blob->length);
+ if (m_edid.parse(edid))
+ qCDebug(qLcEglfsKmsDebug, "EDID data for output \"%s\": identifier '%s', manufacturer '%s', model '%s', serial '%s', physical size: %.2fx%.2f",
+ name().toLatin1().constData(),
+ m_edid.identifier.toLatin1().constData(),
+ m_edid.manufacturer.toLatin1().constData(),
+ m_edid.model.toLatin1().constData(),
+ m_edid.serialNumber.toLatin1().constData(),
+ m_edid.physicalSize.width(), m_edid.physicalSize.height());
+ else
+ qCDebug(qLcEglfsKmsDebug) << "Failed to parse EDID data for output" << name(); // keep this debug, not warning
+ } else {
+ qCDebug(qLcEglfsKmsDebug) << "No EDID data for output" << name();
+ }
}
QEglFSKmsScreen::~QEglFSKmsScreen()
@@ -93,6 +110,9 @@ void QEglFSKmsScreen::setVirtualPosition(const QPoint &pos)
// geometry() calls rawGeometry() and may apply additional transforms.
QRect QEglFSKmsScreen::rawGeometry() const
{
+ if (m_headless)
+ return QRect(QPoint(0, 0), m_device->screenConfig()->headlessSize());
+
const int mode = m_output.mode;
return QRect(m_pos.x(), m_pos.y(),
m_output.modes[mode].hdisplay,
@@ -101,12 +121,30 @@ QRect QEglFSKmsScreen::rawGeometry() const
int QEglFSKmsScreen::depth() const
{
- return 32;
+ return format() == QImage::Format_RGB16 ? 16 : 32;
}
QImage::Format QEglFSKmsScreen::format() const
{
- return QImage::Format_RGB32;
+ // the result can be slightly incorrect, it won't matter in practice
+ switch (m_output.drm_format) {
+ case DRM_FORMAT_ARGB8888:
+ case DRM_FORMAT_ABGR8888:
+ return QImage::Format_ARGB32;
+ case DRM_FORMAT_RGB565:
+ case DRM_FORMAT_BGR565:
+ return QImage::Format_RGB16;
+ case DRM_FORMAT_XRGB2101010:
+ return QImage::Format_RGB30;
+ case DRM_FORMAT_XBGR2101010:
+ return QImage::Format_BGR30;
+ case DRM_FORMAT_ARGB2101010:
+ return QImage::Format_A2RGB30_Premultiplied;
+ case DRM_FORMAT_ABGR2101010:
+ return QImage::Format_A2BGR30_Premultiplied;
+ default:
+ return QImage::Format_RGB32;
+ }
}
QSizeF QEglFSKmsScreen::physicalSize() const
@@ -143,22 +181,25 @@ Qt::ScreenOrientation QEglFSKmsScreen::orientation() const
QString QEglFSKmsScreen::name() const
{
- return m_output.name;
+ return !m_headless ? m_output.name : QStringLiteral("qt_Headless");
}
-void QEglFSKmsScreen::destroySurface()
+QString QEglFSKmsScreen::manufacturer() const
{
+ return m_edid.manufacturer;
}
-void QEglFSKmsScreen::waitForFlip()
+QString QEglFSKmsScreen::model() const
{
+ return m_edid.model.isEmpty() ? m_edid.identifier : m_edid.model;
}
-void QEglFSKmsScreen::flip()
+QString QEglFSKmsScreen::serialNumber() const
{
+ return m_edid.serialNumber;
}
-void QEglFSKmsScreen::flipFinished()
+void QEglFSKmsScreen::waitForFlip()
{
}
@@ -169,10 +210,35 @@ void QEglFSKmsScreen::restoreMode()
qreal QEglFSKmsScreen::refreshRate() const
{
+ if (m_headless)
+ return 60;
+
quint32 refresh = m_output.modes[m_output.mode].vrefresh;
return refresh > 0 ? refresh : 60;
}
+QVector<QPlatformScreen::Mode> QEglFSKmsScreen::modes() const
+{
+ QVector<QPlatformScreen::Mode> list;
+ list.reserve(m_output.modes.size());
+
+ for (const drmModeModeInfo &info : qAsConst(m_output.modes))
+ list.append({QSize(info.hdisplay, info.vdisplay),
+ qreal(info.vrefresh > 0 ? info.vrefresh : 60)});
+
+ return list;
+}
+
+int QEglFSKmsScreen::currentMode() const
+{
+ return m_output.mode;
+}
+
+int QEglFSKmsScreen::preferredMode() const
+{
+ return m_output.preferred_mode;
+}
+
QPlatformScreen::SubpixelAntialiasingType QEglFSKmsScreen::subpixelAntialiasingTypeHint() const
{
return m_output.subpixelAntialiasingTypeHint();
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.h
index 80bbb0c7f1..7f395aacb7 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.h
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.h
@@ -47,6 +47,7 @@
#include <QtCore/QMutex>
#include <QtKmsSupport/private/qkmsdevice_p.h>
+#include <QtEdidSupport/private/qedidparser_p.h>
QT_BEGIN_NAMESPACE
@@ -55,7 +56,7 @@ class QEglFSKmsInterruptHandler;
class Q_EGLFS_EXPORT QEglFSKmsScreen : public QEglFSScreen
{
public:
- QEglFSKmsScreen(QKmsDevice *device, const QKmsOutput &output);
+ QEglFSKmsScreen(QKmsDevice *device, const QKmsOutput &output, bool headless = false);
~QEglFSKmsScreen();
void setVirtualPosition(const QPoint &pos);
@@ -72,18 +73,23 @@ public:
QString name() const override;
+ QString manufacturer() const override;
+ QString model() const override;
+ QString serialNumber() const override;
+
qreal refreshRate() const override;
QList<QPlatformScreen *> virtualSiblings() const override { return m_siblings; }
void setVirtualSiblings(QList<QPlatformScreen *> sl) { m_siblings = sl; }
- QKmsDevice *device() const { return m_device; }
+ QVector<QPlatformScreen::Mode> modes() const override;
- void destroySurface();
+ int currentMode() const override;
+ int preferredMode() const override;
+
+ QKmsDevice *device() const { return m_device; }
virtual void waitForFlip();
- virtual void flip();
- virtual void flipFinished();
QKmsOutput &output() { return m_output; }
void restoreMode();
@@ -97,6 +103,7 @@ protected:
QKmsDevice *m_device;
QKmsOutput m_output;
+ QEdidParser m_edid;
QPoint m_pos;
QList<QPlatformScreen *> m_siblings;
@@ -104,6 +111,8 @@ protected:
PowerState m_powerState;
QEglFSKmsInterruptHandler *m_interruptHandler;
+
+ bool m_headless;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/eglfs_kms_vsp2.json b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/eglfs_kms_vsp2.json
new file mode 100644
index 0000000000..b898dc27cb
--- /dev/null
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/eglfs_kms_vsp2.json
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "eglfs_kms_vsp2" ]
+}
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/eglfs_kms_vsp2.pro b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/eglfs_kms_vsp2.pro
new file mode 100644
index 0000000000..826c2f989f
--- /dev/null
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/eglfs_kms_vsp2.pro
@@ -0,0 +1,31 @@
+TARGET = qeglfs-kms-vsp2-integration
+
+PLUGIN_TYPE = egldeviceintegrations
+PLUGIN_CLASS_NAME = QEglFSKmsVsp2IntegrationPlugin
+load(qt_plugin)
+
+QT += core-private gui-private eglfsdeviceintegration-private eglfs_kms_support-private kms_support-private edid_support-private
+
+INCLUDEPATH += $$PWD/../../api $$PWD/../eglfs_kms_support
+
+# Avoid X11 header collision, use generic EGL native types
+DEFINES += QT_EGL_NO_X11
+
+QMAKE_USE += gbm drm v4l2
+CONFIG += egl
+QMAKE_LFLAGS += $$QMAKE_LFLAGS_NOUNDEF
+
+SOURCES += $$PWD/qeglfskmsvsp2main.cpp \
+ $$PWD/qeglfskmsvsp2integration.cpp \
+ $$PWD/qeglfskmsvsp2device.cpp \
+ $$PWD/qeglfskmsvsp2screen.cpp \
+ $$PWD/qlinuxmediadevice.cpp \
+ $$PWD/qvsp2blendingdevice.cpp
+
+HEADERS += $$PWD/qeglfskmsvsp2integration.h \
+ $$PWD/qeglfskmsvsp2device.h \
+ $$PWD/qeglfskmsvsp2screen.h \
+ $$PWD/qlinuxmediadevice.h \
+ $$PWD/qvsp2blendingdevice.h
+
+OTHER_FILES += $$PWD/eglfs_kms.json
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2device.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2device.cpp
new file mode 100644
index 0000000000..44f855910c
--- /dev/null
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2device.cpp
@@ -0,0 +1,136 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
+** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2016 Pelagicore AG
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qeglfskmsvsp2device.h"
+#include "qeglfskmsvsp2screen.h"
+
+#include "qeglfsintegration_p.h"
+
+#include <QtCore/QLoggingCategory>
+#include <QtCore/private/qcore_unix_p.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(qLcEglfsKmsDebug)
+
+QEglFSKmsVsp2Device::QEglFSKmsVsp2Device(QKmsScreenConfig *screenConfig, const QString &path)
+ : QEglFSKmsDevice(screenConfig, path)
+{
+}
+
+bool QEglFSKmsVsp2Device::open()
+{
+ Q_ASSERT(fd() == -1);
+ Q_ASSERT(m_gbm_device == nullptr);
+
+ int fd = qt_safe_open(devicePath().toLocal8Bit().constData(), O_RDWR | O_CLOEXEC);
+ if (fd == -1) {
+ qErrnoWarning("Could not open DRM device %s", qPrintable(devicePath()));
+ return false;
+ }
+
+ qCDebug(qLcEglfsKmsDebug) << "Creating GBM device for file descriptor" << fd
+ << "obtained from" << devicePath();
+ m_gbm_device = gbm_create_device(fd);
+ if (!m_gbm_device) {
+ qErrnoWarning("Could not create GBM device");
+ qt_safe_close(fd);
+ fd = -1;
+ return false;
+ }
+
+ setFd(fd);
+
+ return true;
+}
+
+void QEglFSKmsVsp2Device::close()
+{
+ // Note: screens are gone at this stage.
+
+ if (m_gbm_device) {
+ gbm_device_destroy(m_gbm_device);
+ m_gbm_device = nullptr;
+ }
+
+ if (fd() != -1) {
+ qt_safe_close(fd());
+ setFd(-1);
+ }
+}
+
+void *QEglFSKmsVsp2Device::nativeDisplay() const
+{
+ return m_gbm_device;
+}
+
+gbm_device * QEglFSKmsVsp2Device::gbmDevice() const
+{
+ return m_gbm_device;
+}
+
+QPlatformScreen *QEglFSKmsVsp2Device::createScreen(const QKmsOutput &output)
+{
+ auto *screen = new QEglFSKmsVsp2Screen(this, output);
+
+ return screen;
+}
+
+QPlatformScreen *QEglFSKmsVsp2Device::createHeadlessScreen()
+{
+ qWarning() << Q_FUNC_INFO << "Not implemented yet";
+ return nullptr;
+}
+
+void QEglFSKmsVsp2Device::registerScreenCloning(QPlatformScreen *screen,
+ QPlatformScreen *screenThisScreenClones,
+ const QVector<QPlatformScreen *> &screensCloningThisScreen)
+{
+ Q_UNUSED(screen);
+ qWarning() << Q_FUNC_INFO << "Not implemented yet";
+ if (!screenThisScreenClones && screensCloningThisScreen.isEmpty())
+ return;
+
+// auto *vsp2Screen = static_cast<QEglFSKmsVsp2Screen *>(screen);
+// vsp2Screen->initCloning(screenThisScreenClones, screensCloningThisScreen);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2device.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2device.h
new file mode 100644
index 0000000000..c795fa4005
--- /dev/null
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2device.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
+** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2016 Pelagicore AG
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QEGLFSKMSVSP2DEVICE_H
+#define QEGLFSKMSVSP2DEVICE_H
+
+#include <qeglfskmsdevice.h>
+
+#include <gbm.h>
+
+QT_BEGIN_NAMESPACE
+
+class QEglFSKmsScreen;
+
+class QEglFSKmsVsp2Device: public QEglFSKmsDevice
+{
+public:
+ QEglFSKmsVsp2Device(QKmsScreenConfig *screenConfig, const QString &path);
+
+ bool open() override;
+ void close() override;
+
+ void *nativeDisplay() const override;
+ gbm_device *gbmDevice() const;
+
+ QPlatformScreen *createScreen(const QKmsOutput &output) override;
+ QPlatformScreen *createHeadlessScreen() override;
+ void registerScreenCloning(QPlatformScreen *screen,
+ QPlatformScreen *screenThisScreenClones,
+ const QVector<QPlatformScreen *> &screensCloningThisScreen) override;
+
+private:
+ Q_DISABLE_COPY(QEglFSKmsVsp2Device)
+
+ gbm_device *m_gbm_device = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QEGLFSKMSVSP2DEVICE_H
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2integration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2integration.cpp
new file mode 100644
index 0000000000..0d9b6b6290
--- /dev/null
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2integration.cpp
@@ -0,0 +1,250 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
+** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2016 Pelagicore AG
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qeglfskmsvsp2integration.h"
+#include "qeglfskmsvsp2device.h"
+#include "qeglfskmsvsp2screen.h"
+#include "private/qeglfswindow_p.h"
+
+#include <QtDeviceDiscoverySupport/private/qdevicediscovery_p.h>
+#include <QtEglSupport/private/qeglconvenience_p.h>
+#include <QtCore/QLoggingCategory>
+#include <QtCore/QJsonDocument>
+#include <QtCore/QJsonObject>
+#include <QtCore/QJsonArray>
+#include <QtGui/qpa/qplatformwindow.h>
+#include <QtGui/QScreen>
+#include <QtPlatformHeaders/qeglfsfunctions.h>
+
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+#include <gbm.h>
+
+QT_BEGIN_NAMESPACE
+
+QEglFSKmsVsp2Integration::QEglFSKmsVsp2Integration()
+{
+ qCDebug(qLcEglfsKmsDebug, "New DRM/KMS via Vsp2 integration created");
+}
+
+#ifndef EGL_EXT_platform_base
+typedef EGLDisplay (EGLAPIENTRYP PFNEGLGETPLATFORMDISPLAYEXTPROC) (EGLenum platform, void *native_display, const EGLint *attrib_list);
+typedef EGLSurface (EGLAPIENTRYP PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC) (EGLDisplay dpy, EGLConfig config, void *native_window, const EGLint *attrib_list);
+#endif
+
+#ifndef EGL_PLATFORM_GBM_KHR
+#define EGL_PLATFORM_GBM_KHR 0x31D7
+#endif
+
+EGLDisplay QEglFSKmsVsp2Integration::createDisplay(EGLNativeDisplayType nativeDisplay)
+{
+ qCDebug(qLcEglfsKmsDebug, "Querying EGLDisplay");
+ EGLDisplay display;
+
+ PFNEGLGETPLATFORMDISPLAYEXTPROC getPlatformDisplay = nullptr;
+ const char *extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
+ if (extensions && (strstr(extensions, "EGL_KHR_platform_gbm") || strstr(extensions, "EGL_MESA_platform_gbm"))) {
+ getPlatformDisplay = reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>(
+ eglGetProcAddress("eglGetPlatformDisplayEXT"));
+ }
+
+ if (getPlatformDisplay) {
+ display = getPlatformDisplay(EGL_PLATFORM_GBM_KHR, nativeDisplay, nullptr);
+ } else {
+ qCDebug(qLcEglfsKmsDebug, "No eglGetPlatformDisplay for GBM, falling back to eglGetDisplay");
+ display = eglGetDisplay(nativeDisplay);
+ }
+
+ return display;
+}
+
+EGLNativeWindowType QEglFSKmsVsp2Integration::createNativeOffscreenWindow(const QSurfaceFormat &format)
+{
+ Q_UNUSED(format);
+ Q_ASSERT(device());
+
+ gbm_surface *surface = gbm_surface_create(static_cast<QEglFSKmsVsp2Device *>(device())->gbmDevice(),
+ 1, 1,
+ GBM_FORMAT_XRGB8888,
+ GBM_BO_USE_RENDERING);
+
+ return reinterpret_cast<EGLNativeWindowType>(surface);
+}
+
+void QEglFSKmsVsp2Integration::destroyNativeWindow(EGLNativeWindowType window)
+{
+ gbm_surface *surface = reinterpret_cast<gbm_surface *>(window);
+ //TODO call screen destroysurface instead
+ gbm_surface_destroy(surface);
+}
+
+void QEglFSKmsVsp2Integration::presentBuffer(QPlatformSurface *surface)
+{
+ QWindow *window = static_cast<QWindow *>(surface->surface());
+ auto *screen = static_cast<QEglFSKmsVsp2Screen *>(window->screen()->handle());
+ screen->flip();
+}
+
+QFunctionPointer QEglFSKmsVsp2Integration::platformFunction(const QByteArray &function) const
+{
+ if (function == QEglFSFunctions::vsp2AddLayerTypeIdentifier())
+ return QFunctionPointer(addLayerStatic);
+ if (function == QEglFSFunctions::vsp2RemoveLayerTypeIdentifier())
+ return QFunctionPointer(removeLayerStatic);
+ if (function == QEglFSFunctions::vsp2SetLayerBufferTypeIdentifier())
+ return QFunctionPointer(setLayerBufferStatic);
+ if (function == QEglFSFunctions::vsp2SetLayerPositionTypeIdentifier())
+ return QFunctionPointer(setLayerPositionStatic);
+ if (function == QEglFSFunctions::vsp2SetLayerAlphaTypeIdentifier())
+ return QFunctionPointer(setLayerAlphaStatic);
+ if (function == QEglFSFunctions::vsp2AddBlendListenerTypeIdentifier())
+ return QFunctionPointer(addBlendListenerStatic);
+
+ return nullptr;
+}
+
+QKmsDevice *QEglFSKmsVsp2Integration::createDevice()
+{
+ QString path = screenConfig()->devicePath();
+ if (!path.isEmpty()) {
+ qCDebug(qLcEglfsKmsDebug) << "VSP2: Using DRM device" << path << "specified in config file";
+ } else {
+ QDeviceDiscovery *d = QDeviceDiscovery::create(QDeviceDiscovery::Device_VideoMask);
+ const QStringList devices = d->scanConnectedDevices();
+ qCDebug(qLcEglfsKmsDebug) << "Found the following video devices:" << devices;
+ d->deleteLater();
+
+ if (Q_UNLIKELY(devices.isEmpty()))
+ qFatal("Could not find DRM device!");
+
+ path = devices.first();
+ qCDebug(qLcEglfsKmsDebug) << "Using" << path;
+ }
+
+ return new QEglFSKmsVsp2Device(screenConfig(), path);
+}
+
+int QEglFSKmsVsp2Integration::addLayerStatic(const QScreen *screen, int dmabufFd, const QSize &size, const QPoint &position, uint pixelFormat, uint bytesPerLine)
+{
+ auto vsp2Screen = static_cast<QEglFSKmsVsp2Screen *>(screen->handle());
+ return vsp2Screen->addLayer(dmabufFd, size, position, pixelFormat, bytesPerLine);
+}
+
+bool QEglFSKmsVsp2Integration::removeLayerStatic(const QScreen *screen, int id)
+{
+ auto vsp2Screen = static_cast<QEglFSKmsVsp2Screen *>(screen->handle());
+ return vsp2Screen->removeLayer(id);
+}
+
+void QEglFSKmsVsp2Integration::setLayerBufferStatic(const QScreen *screen, int id, int dmabufFd)
+{
+ auto vsp2Screen = static_cast<QEglFSKmsVsp2Screen *>(screen->handle());
+ vsp2Screen->setLayerBuffer(id, dmabufFd);
+}
+
+void QEglFSKmsVsp2Integration::setLayerPositionStatic(const QScreen *screen, int id, const QPoint &position)
+{
+ auto vsp2Screen = static_cast<QEglFSKmsVsp2Screen *>(screen->handle());
+ vsp2Screen->setLayerPosition(id, position);
+}
+
+void QEglFSKmsVsp2Integration::setLayerAlphaStatic(const QScreen *screen, int id, qreal alpha)
+{
+ auto vsp2Screen = static_cast<QEglFSKmsVsp2Screen *>(screen->handle());
+ vsp2Screen->setLayerAlpha(id, alpha);
+}
+
+void QEglFSKmsVsp2Integration::addBlendListenerStatic(const QScreen *screen, void(*callback)())
+{
+ auto vsp2Screen = static_cast<QEglFSKmsVsp2Screen *>(screen->handle());
+ vsp2Screen->addBlendListener(callback);
+}
+
+class QEglFSKmsVsp2Window : public QEglFSWindow
+{
+public:
+ QEglFSKmsVsp2Window(QWindow *w, const QEglFSKmsVsp2Integration *integration)
+ : QEglFSWindow(w)
+ , m_integration(integration)
+ {}
+ void resetSurface() override;
+ void invalidateSurface() override;
+ const QEglFSKmsVsp2Integration *m_integration;
+};
+
+void QEglFSKmsVsp2Window::resetSurface()
+{
+ auto *vsp2Screen = static_cast<QEglFSKmsVsp2Screen *>(screen());
+ EGLDisplay display = vsp2Screen->display();
+ QSurfaceFormat platformFormat = m_integration->surfaceFormatFor(window()->requestedFormat());
+ m_config = QEglFSDeviceIntegration::chooseConfig(display, platformFormat);
+ m_format = q_glFormatFromConfig(display, m_config, platformFormat);
+ // One fullscreen window per screen -> the native window is simply the gbm_surface the screen created.
+ m_window = reinterpret_cast<EGLNativeWindowType>(vsp2Screen->createSurface());
+
+ PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC createPlatformWindowSurface = nullptr;
+ const char *extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
+ if (extensions && (strstr(extensions, "EGL_KHR_platform_gbm") || strstr(extensions, "EGL_MESA_platform_gbm"))) {
+ createPlatformWindowSurface = reinterpret_cast<PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC>(
+ eglGetProcAddress("eglCreatePlatformWindowSurfaceEXT"));
+ }
+
+ if (createPlatformWindowSurface) {
+ m_surface = createPlatformWindowSurface(display, m_config, reinterpret_cast<void *>(m_window), nullptr);
+ } else {
+ qCDebug(qLcEglfsKmsDebug, "No eglCreatePlatformWindowSurface for GBM, falling back to eglCreateWindowSurface");
+ m_surface = eglCreateWindowSurface(display, m_config, m_window, nullptr);
+ }
+}
+
+void QEglFSKmsVsp2Window::invalidateSurface()
+{
+ auto *vsp2Screen = static_cast<QEglFSKmsVsp2Screen *>(screen());
+ QEglFSWindow::invalidateSurface();
+ vsp2Screen->resetSurface();
+}
+
+QEglFSWindow *QEglFSKmsVsp2Integration::createWindow(QWindow *window) const
+{
+ return new QEglFSKmsVsp2Window(window, this);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2integration.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2integration.h
new file mode 100644
index 0000000000..b1a8a2edf3
--- /dev/null
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2integration.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
+** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2016 Pelagicore AG
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QEGLFSKMSVSP2INTEGRATION_H
+#define QEGLFSKMSVSP2INTEGRATION_H
+
+#include "qeglfskmsintegration.h"
+#include <QtCore/QMap>
+#include <QtCore/QVariant>
+
+QT_BEGIN_NAMESPACE
+
+class QEglFSKmsDevice;
+
+class QEglFSKmsVsp2Integration : public QEglFSKmsIntegration
+{
+public:
+ QEglFSKmsVsp2Integration();
+
+ EGLDisplay createDisplay(EGLNativeDisplayType nativeDisplay) override;
+ EGLNativeWindowType createNativeOffscreenWindow(const QSurfaceFormat &format) override;
+ void destroyNativeWindow(EGLNativeWindowType window) override;
+
+ void presentBuffer(QPlatformSurface *surface) override;
+ QEglFSWindow *createWindow(QWindow *window) const override;
+
+ QFunctionPointer platformFunction(const QByteArray &function) const override;
+
+protected:
+ QKmsDevice *createDevice() override;
+
+private:
+ static int addLayerStatic(const QScreen *screen, int dmabufFd, const QSize &size, const QPoint &position, uint pixelFormat, uint bytesPerLine);
+ static bool removeLayerStatic(const QScreen *screen, int id);
+ static void setLayerBufferStatic(const QScreen *screen, int id, int dmabufFd);
+ static void setLayerPositionStatic(const QScreen *screen, int id, const QPoint &position);
+ static void setLayerAlphaStatic(const QScreen *screen, int id, qreal alpha);
+ static void addBlendListenerStatic(const QScreen *screen, void(*callback)());
+};
+
+QT_END_NAMESPACE
+
+#endif // QEGLFSKMSVSP2INTEGRATION_H
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2main.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2main.cpp
new file mode 100644
index 0000000000..1345d38359
--- /dev/null
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2main.cpp
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the qmake spec of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "private/qeglfsdeviceintegration_p.h"
+#include "qeglfskmsvsp2integration.h"
+
+QT_BEGIN_NAMESPACE
+
+class QEglFSKmsVsp2IntegrationPlugin : public QEglFSDeviceIntegrationPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QEglFSDeviceIntegrationFactoryInterface_iid FILE "eglfs_kms_vsp2.json")
+
+public:
+ QEglFSDeviceIntegration *create() override { return new QEglFSKmsVsp2Integration; }
+};
+
+QT_END_NAMESPACE
+
+#include "qeglfskmsvsp2main.moc"
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2screen.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2screen.cpp
new file mode 100644
index 0000000000..88b401c920
--- /dev/null
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2screen.cpp
@@ -0,0 +1,365 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
+** Copyright (C) 2016 Pelagicore AG
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qeglfskmsvsp2screen.h"
+#include "qeglfskmsvsp2device.h"
+#include <qeglfskmshelpers.h>
+
+#include <QtCore/QLoggingCategory>
+#include <QtGui/private/qguiapplication_p.h>
+
+#include <drm_fourcc.h>
+#include <xf86drm.h>
+#include <fcntl.h>
+
+//TODO move to qlinuxmediadevice?
+#include <cstdlib> //this needs to go before mediactl/mediactl.h because it uses size_t without including it
+extern "C" {
+#include <mediactl/mediactl.h>
+#include <mediactl/v4l2subdev.h> //needed in header for default arguments
+}
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(qLcEglfsKmsDebug)
+
+static inline uint32_t drmFormatToGbmFormat(uint32_t drmFormat)
+{
+ Q_ASSERT(DRM_FORMAT_XRGB8888 == GBM_FORMAT_XRGB8888);
+ return drmFormat;
+}
+
+static inline uint32_t gbmFormatToDrmFormat(uint32_t gbmFormat) //TODO: is this needed?
+{
+ Q_ASSERT(DRM_FORMAT_XRGB8888 == GBM_FORMAT_XRGB8888);
+ return gbmFormat;
+}
+
+void QEglFSKmsVsp2Screen::dmaBufferDestroyedHandler(gbm_bo *gbmBo, void *data)
+{
+ Q_UNUSED(gbmBo); //TODO: do we need to do something with it after all?
+ auto dmabuf = static_cast<DmaBuffer *>(data);
+ //TODO: need some extra cleanup here?
+ delete dmabuf;
+}
+
+QEglFSKmsVsp2Screen::DmaBuffer *QEglFSKmsVsp2Screen::dmaBufferForGbmBuffer(gbm_bo *gbmBo)
+{
+ auto existingBuffer = static_cast<DmaBuffer *>(gbm_bo_get_user_data(gbmBo));
+ if (existingBuffer)
+ return existingBuffer;
+
+ uint32_t handle = gbm_bo_get_handle(gbmBo).u32;
+ QScopedPointer<DmaBuffer> fb(new DmaBuffer);
+ int ret = drmPrimeHandleToFD(device()->fd(), handle, DRM_CLOEXEC, &fb->dmabufFd);
+ if (ret) {
+ qWarning("Failed to create dmabuf file descriptor for buffer with drmPrimeHandleToFd");
+ return nullptr;
+ }
+
+ gbm_bo_set_user_data(gbmBo, fb.data(), dmaBufferDestroyedHandler);
+ return fb.take();
+}
+
+QEglFSKmsVsp2Screen::QEglFSKmsVsp2Screen(QKmsDevice *device, const QKmsOutput &output)
+ : QEglFSKmsScreen(device, output)
+ , m_blender(new Blender(this))
+{
+}
+
+gbm_surface *QEglFSKmsVsp2Screen::createSurface()
+{
+ if (!m_gbmSurface) {
+ uint32_t gbmFormat = drmFormatToGbmFormat(m_output.drm_format);
+ qCDebug(qLcEglfsKmsDebug, "Creating gbm_surface for screen %s with format 0x%x", qPrintable(name()), gbmFormat);
+ Q_ASSERT(rawGeometry().isValid());
+ m_gbmSurface = gbm_surface_create(static_cast<QEglFSKmsVsp2Device *>(device())->gbmDevice(),
+ uint(rawGeometry().width()),
+ uint(rawGeometry().height()),
+ gbmFormat,
+ GBM_BO_USE_RENDERING);
+ }
+
+ if (!m_blendDevice)
+ initVsp2();
+
+ if (m_frameBuffers[m_backFb].dmabufFd == -1)
+ initDumbFrameBuffers();
+
+ return m_gbmSurface; // not owned, gets destroyed in QEglFSKmsGbmIntegration::destroyNativeWindow()
+}
+
+void QEglFSKmsVsp2Screen::resetSurface()
+{
+ m_gbmSurface = nullptr;
+}
+
+void QEglFSKmsVsp2Screen::initDumbFrameBuffers()
+{
+ for (auto &fb : m_frameBuffers)
+ initDumbFrameBuffer(fb);
+}
+
+void QEglFSKmsVsp2Screen::initVsp2()
+{
+ qCDebug(qLcEglfsKmsDebug, "Initializing Vsp2 hardware");
+ const QSize screenSize = rawGeometry().size();
+ m_blendDevice.reset(new QVsp2BlendingDevice(screenSize));
+
+ // Enable input for main buffer drawn by the compositor (always on)
+ const uint bytesPerLine = uint(screenSize.width()) * 4; //TODO: is this ok?
+ bool formatSet = m_blendDevice->enableInput(m_qtLayer, QRect(QPoint(), screenSize), m_output.drm_format, bytesPerLine);
+ if (!formatSet) {
+ const uint32_t fallbackFormat = DRM_FORMAT_ARGB8888;
+ qWarning() << "Failed to set format" << q_fourccToString(m_output.drm_format)
+ << "falling back to" << q_fourccToString(fallbackFormat);
+ formatSet = m_blendDevice->enableInput(m_qtLayer, QRect(QPoint(), screenSize), fallbackFormat, bytesPerLine);
+ if (!formatSet)
+ qFatal("Failed to set vsp2 blending format");
+ }
+}
+
+int QEglFSKmsVsp2Screen::addLayer(int dmabufFd, const QSize &size, const QPoint &position, uint drmPixelFormat, uint bytesPerLine)
+{
+ int index = m_blendDevice->enableInput(QRect(position, size), drmPixelFormat, bytesPerLine);
+ if (index != -1) {
+ m_blendDevice->setInputBuffer(index, dmabufFd);
+ int id = index; //TODO: maybe make id something independent of layer index?
+ qCDebug(qLcEglfsKmsDebug) << "Enabled extra layer for vsp input" << index;
+ return id;
+ }
+ qWarning() << "Failed to add layer";
+ return -1;
+}
+
+void QEglFSKmsVsp2Screen::setLayerBuffer(int id, int dmabufFd)
+{
+ int layerIndex = id;
+ m_blendDevice->setInputBuffer(layerIndex, dmabufFd);
+ if (!m_blendScheduled) {
+ m_blendScheduled = true;
+ QCoreApplication::postEvent(m_blender.data(), new QEvent(QEvent::User));
+ }
+}
+
+void QEglFSKmsVsp2Screen::setLayerPosition(int id, const QPoint &position)
+{
+ int layerIndex = id;
+ m_blendDevice->setInputPosition(layerIndex, position);
+}
+
+void QEglFSKmsVsp2Screen::setLayerAlpha(int id, qreal alpha)
+{
+ int layerIndex = id;
+ m_blendDevice->setInputAlpha(layerIndex, alpha);
+}
+
+bool QEglFSKmsVsp2Screen::removeLayer(int id)
+{
+ int layerIndex = id;
+ m_blendDevice->disableInput(layerIndex);
+ return true;
+}
+
+void QEglFSKmsVsp2Screen::addBlendListener(void (*callback)())
+{
+ m_blendFinishedCallbacks.append(callback);
+}
+
+void QEglFSKmsVsp2Screen::flip()
+{
+ if (!m_gbmSurface) {
+ qWarning("Cannot sync before platform init!");
+ return;
+ }
+
+ if (!m_blendScheduled && !m_nextGbmBo) {
+ m_nextGbmBo = gbm_surface_lock_front_buffer(m_gbmSurface);
+
+ if (!m_nextGbmBo) {
+ qWarning("Could not lock GBM surface front buffer!");
+ return;
+ }
+
+ m_blendScheduled = true;
+ QCoreApplication::postEvent(m_blender.data(), new QEvent(QEvent::User));
+ }
+}
+
+void QEglFSKmsVsp2Screen::ensureModeSet()
+{
+ const int driFd = device()->fd();
+ QKmsOutput &op(output());
+ if (!op.mode_set) {
+ int ret = drmModeSetCrtc(driFd,
+ op.crtc_id,
+ m_frameBuffers[m_backFb].drmBufferId,
+ 0, 0,
+ &op.connector_id, 1,
+ &op.modes[op.mode]);
+
+ if (ret == -1) {
+ qErrnoWarning(errno, "Could not set DRM mode!");
+ } else {
+ op.mode_set = true;
+ setPowerState(PowerStateOn);
+ }
+ }
+}
+
+void QEglFSKmsVsp2Screen::doDrmFlip()
+{
+ QKmsOutput &op(output());
+ const int driFd = device()->fd();
+
+ int ret = drmModePageFlip(driFd,
+ op.crtc_id,
+ m_frameBuffers[m_backFb].drmBufferId,
+ 0,
+ this);
+
+ if (ret)
+ qErrnoWarning("Could not queue DRM page flip on screen %s", qPrintable(name()));
+
+ m_backFb = (m_backFb + 1) % 2;
+}
+
+void QEglFSKmsVsp2Screen::blendAndFlipDrm()
+{
+ m_blendScheduled = false;
+ if (!m_nextGbmBo && !m_blendDevice->isDirty())
+ return;
+
+ FrameBuffer &backBuffer = m_frameBuffers[m_backFb];
+ if (backBuffer.dmabufFd == -1)
+ initDumbFrameBuffers();
+
+ if (m_nextGbmBo) {
+ Q_ASSERT(m_nextGbmBo != m_currentGbmBo);
+ int compositorBackBufferDmaFd = dmaBufferForGbmBuffer(m_nextGbmBo)->dmabufFd;
+ m_blendDevice->setInputBuffer(m_qtLayer, compositorBackBufferDmaFd);
+
+ if (m_currentGbmBo)
+ gbm_surface_release_buffer(m_gbmSurface, m_currentGbmBo);
+ m_currentGbmBo = m_nextGbmBo;
+ m_nextGbmBo = nullptr;
+ }
+
+ ensureModeSet();
+
+ if (!m_blendDevice)
+ initVsp2();
+
+ if (!m_blendDevice->isDirty())
+ return;
+
+ const int driFd = device()->fd();
+ drmVBlank vBlank;
+ vBlank.request.type = static_cast<drmVBlankSeqType>(DRM_VBLANK_RELATIVE | DRM_VBLANK_SECONDARY); //TODO: make secondary configurable (or automatic)
+ vBlank.request.sequence = 1;
+ vBlank.request.signal = 0;
+ drmWaitVBlank(driFd, &vBlank);
+
+ if (!m_blendDevice->blend(backBuffer.dmabufFd))
+ qWarning() << "Vsp2: blending failed";
+
+ for (auto cb : m_blendFinishedCallbacks)
+ cb();
+
+ doDrmFlip();
+}
+
+void QEglFSKmsVsp2Screen::initDumbFrameBuffer(FrameBuffer &fb)
+{
+ QKmsOutput &op(output());
+ const uint32_t width = op.modes[op.mode].hdisplay;
+ const uint32_t height = op.modes[op.mode].vdisplay;
+
+ Q_ASSERT(fb.dmabufFd == -1);
+ const uint32_t dumbBufferFlags = 0; //TODO: do we want some flags? What's possible?
+ const uint32_t bpp = 32;
+
+ drm_mode_create_dumb creq = {
+ height,
+ width,
+ bpp,
+ dumbBufferFlags,
+ 0, 0, 0 //return values
+ };
+
+ const int driFd = device()->fd();
+ if (drmIoctl(driFd, DRM_IOCTL_MODE_CREATE_DUMB, &creq) == -1)
+ qFatal("Failed to create dumb buffer: %s", strerror(errno));
+
+// uint32_t handles[4] = { gbm_bo_get_handle(bo).u32 };
+// uint32_t strides[4] = { gbm_bo_get_stride(bo) };
+// uint32_t offsets[4] = { 0 };
+// uint32_t pixelFormat = gbmFormatToDrmFormat(gbm_bo_get_format(bo));
+
+ //TODO: support additional planes
+ uint32_t gbmBoHandles[4] = { creq.handle, 0, 0, 0 };
+ uint32_t strides[4] = { creq.pitch, 0, 0, 0 };
+ uint32_t offsets[4] = { 0 };
+ uint32_t pixelFormat = DRM_FORMAT_ARGB8888; //TODO: support other formats?
+ uint32_t drmFlags = 0;
+
+ qCDebug(qLcEglfsKmsDebug) << "Adding FB" << QSize(width, height)
+ << ", DRM format" << q_fourccToString(pixelFormat);
+ int ret = drmModeAddFB2(driFd, width, height, pixelFormat,
+ gbmBoHandles, strides, offsets, &fb.drmBufferId, drmFlags);
+ if (ret)
+ qFatal("drmModeAddFB2 failed: %s", strerror(errno));
+
+ drmPrimeHandleToFD(driFd, gbmBoHandles[0], DRM_CLOEXEC, &fb.dmabufFd);
+}
+
+bool QEglFSKmsVsp2Screen::Blender::event(QEvent *event)
+{
+ switch (event->type()) {
+ case QEvent::User:
+ m_screen->blendAndFlipDrm();
+ return true;
+ default:
+ return QObject::event(event);
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2screen.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2screen.h
new file mode 100644
index 0000000000..fa03e36785
--- /dev/null
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2screen.h
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
+** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2016 Pelagicore AG
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QEGLFSKMSVSP2SCREEN_H
+#define QEGLFSKMSVSP2SCREEN_H
+
+#include "qeglfskmsscreen.h"
+#include "qvsp2blendingdevice.h"
+#include <QtCore/QMutex>
+
+#include <gbm.h>
+
+QT_BEGIN_NAMESPACE
+
+class QEglFSKmsVsp2Screen : public QEglFSKmsScreen
+{
+public:
+ QEglFSKmsVsp2Screen(QKmsDevice *device, const QKmsOutput &output);
+
+ gbm_surface *createSurface();
+ void resetSurface();
+
+ void initDumbFrameBuffers();
+ void initVsp2();
+
+ //TODO: use a fixed index API instead of auto increment?
+ int addLayer(int dmabufFd, const QSize &size, const QPoint &position, uint drmPixelFormat, uint bytesPerLine);
+ void setLayerBuffer(int id, int dmabufFd);
+ void setLayerPosition(int id, const QPoint &position);
+ void setLayerAlpha(int id, qreal alpha);
+ bool removeLayer(int id);
+ void addBlendListener(void (*callback)());
+
+ void flip();
+ void blendAndFlipDrm();
+
+private:
+ class Blender : public QObject // This is a workaround we really would want the screen to be a QObject
+ {
+ public:
+ Blender(QEglFSKmsVsp2Screen *screen) : m_screen(screen) {}
+ ~Blender() override {}
+ bool event(QEvent *event) override;
+ QEglFSKmsVsp2Screen *m_screen = nullptr;
+ };
+ QScopedArrayPointer<Blender> m_blender;
+
+ gbm_surface *m_gbmSurface = nullptr;
+ gbm_bo *m_currentGbmBo = nullptr;
+ gbm_bo *m_nextGbmBo = nullptr;
+
+ QScopedPointer<QVsp2BlendingDevice> m_blendDevice;
+
+ struct FrameBuffer { //these are for buffers that have been blended by the bru
+ uint32_t drmBufferId = 0;
+ int dmabufFd = -1;
+ };
+ std::array<FrameBuffer, 2> m_frameBuffers;
+ uint m_backFb = 0;
+ void initDumbFrameBuffer(FrameBuffer &fb);
+ QVector<void (*)()> m_blendFinishedCallbacks;
+
+ struct DmaBuffer { //these are for qt buffers before blending with additional layers (gbm buffer data)
+ int dmabufFd = -1;
+ };
+ static void dmaBufferDestroyedHandler(gbm_bo *gbmBo, void *data);
+ DmaBuffer *dmaBufferForGbmBuffer(gbm_bo *gbmBo);
+
+ void ensureModeSet();
+ void doDrmFlip();
+
+ bool m_blendScheduled = false;
+ int m_qtLayer = 0; //TODO: add API for changing this
+};
+
+QT_END_NAMESPACE
+
+#endif // QEGLFSKMSVSP2SCREEN_H
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qlinuxmediadevice.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qlinuxmediadevice.cpp
new file mode 100644
index 0000000000..25b0c39050
--- /dev/null
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qlinuxmediadevice.cpp
@@ -0,0 +1,627 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qeglfsintegration_p.h"
+#include "qlinuxmediadevice.h"
+#include <qeglfskmshelpers.h>
+
+#include <QtCore/QLoggingCategory>
+#include <QtCore/QSize>
+#include <QtCore/QRect>
+
+#include <sys/ioctl.h>
+#include <fcntl.h>
+
+#include <cstdlib> //this needs to go before mediactl/mediactl.h because it uses size_t without including it
+extern "C" {
+#include <mediactl/mediactl.h>
+#include <mediactl/v4l2subdev.h>
+}
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(qLcEglfsKmsDebug)
+
+static QString mediaBusFmtToString(uint code)
+{
+ switch (code) {
+ case MEDIA_BUS_FMT_FIXED: return "FIXED";
+ case MEDIA_BUS_FMT_RGB444_1X12: return "RGB444_1X12";
+// case MEDIA_BUS_FMT_RGB444_2X8_PADHI_B: return "RGB444_2X8_PADHI_B";
+// case MEDIA_BUS_FMT_RGB444_2X8_PADHI_L: return "RGB444_2X8_PADHI_L";
+// case MEDIA_BUS_FMT_RGB555_2X8_PADHI_B: return "RGB555_2X8_PADHI_B";
+// case MEDIA_BUS_FMT_RGB555_2X8_PADHI_L: return "RGB555_2X8_PADHI_L";
+ case MEDIA_BUS_FMT_RGB565_1X16: return "RGB565_1X16";
+ case MEDIA_BUS_FMT_BGR565_2X8_BE: return "BGR565_2X8_BE";
+ case MEDIA_BUS_FMT_BGR565_2X8_LE: return "BGR565_2X8_LE";
+ case MEDIA_BUS_FMT_RGB565_2X8_BE: return "RGB565_2X8_BE";
+ case MEDIA_BUS_FMT_RGB565_2X8_LE: return "RGB565_2X8_LE";
+ case MEDIA_BUS_FMT_RGB666_1X18: return "RGB666_1X18";
+ case MEDIA_BUS_FMT_RBG888_1X24: return "RBG888_1X24";
+// case MEDIA_BUS_FMT_RGB666_1X24_CPADH: return "RGB666_1X24_CPADH";
+ case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG: return "RGB666_1X7X3_SPWG";
+ case MEDIA_BUS_FMT_BGR888_1X24: return "BGR888_1X24";
+ case MEDIA_BUS_FMT_GBR888_1X24: return "GBR888_1X24";
+ case MEDIA_BUS_FMT_RGB888_1X24: return "RGB888_1X24";
+ case MEDIA_BUS_FMT_RGB888_2X12_BE: return "RGB888_2X12_BE";
+ case MEDIA_BUS_FMT_RGB888_2X12_LE: return "RGB888_2X12_LE";
+ case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG: return "RGB888_1X7X4_SPWG";
+// case MEDIA_BUS_FMT_RGB888_1X7X4_JEID: return "RGB888_1X7X4_JEID";
+ case MEDIA_BUS_FMT_ARGB8888_1X32: return "ARGB8888_1X32";
+ case MEDIA_BUS_FMT_RGB888_1X32_PADHI: return "RGB888_1X32_PADHI";
+// case MEDIA_BUS_FMT_RGB101010_1X30: return "RGB101010_1X30";
+// case MEDIA_BUS_FMT_RGB121212_1X36: return "RGB121212_1X36";
+// case MEDIA_BUS_FMT_RGB161616_1X48: return "RGB161616_1X48";
+ case MEDIA_BUS_FMT_Y8_1X8: return "Y8_1X8";
+ case MEDIA_BUS_FMT_UV8_1X8: return "UV8_1X8";
+ case MEDIA_BUS_FMT_UYVY8_1_5X8: return "UYVY8_1_5X8";
+ case MEDIA_BUS_FMT_VYUY8_1_5X8: return "VYUY8_1_5X8";
+ case MEDIA_BUS_FMT_YUYV8_1_5X8: return "YUYV8_1_5X8";
+ case MEDIA_BUS_FMT_YVYU8_1_5X8: return "YVYU8_1_5X8";
+ case MEDIA_BUS_FMT_UYVY8_2X8: return "UYVY8_2X8";
+ case MEDIA_BUS_FMT_VYUY8_2X8: return "VYUY8_2X8";
+ case MEDIA_BUS_FMT_YUYV8_2X8: return "YUYV8_2X8";
+ case MEDIA_BUS_FMT_YVYU8_2X8: return "YVYU8_2X8";
+ case MEDIA_BUS_FMT_Y10_1X10: return "Y10_1X10";
+ case MEDIA_BUS_FMT_UYVY10_2X10: return "UYVY10_2X10";
+ case MEDIA_BUS_FMT_VYUY10_2X10: return "VYUY10_2X10";
+ case MEDIA_BUS_FMT_YUYV10_2X10: return "YUYV10_2X10";
+ case MEDIA_BUS_FMT_YVYU10_2X10: return "YVYU10_2X10";
+ case MEDIA_BUS_FMT_Y12_1X12: return "Y12_1X12";
+ case MEDIA_BUS_FMT_UYVY12_2X12: return "UYVY12_2X12";
+ case MEDIA_BUS_FMT_VYUY12_2X12: return "VYUY12_2X12";
+ case MEDIA_BUS_FMT_YUYV12_2X12: return "YUYV12_2X12";
+ case MEDIA_BUS_FMT_YVYU12_2X12: return "YVYU12_2X12";
+ case MEDIA_BUS_FMT_UYVY8_1X16: return "UYVY8_1X16";
+ case MEDIA_BUS_FMT_VYUY8_1X16: return "VYUY8_1X16";
+ case MEDIA_BUS_FMT_YUYV8_1X16: return "YUYV8_1X16";
+ case MEDIA_BUS_FMT_YVYU8_1X16: return "YVYU8_1X16";
+ case MEDIA_BUS_FMT_YDYUYDYV8_1X16: return "YDYUYDYV8_1X16";
+ case MEDIA_BUS_FMT_UYVY10_1X20: return "UYVY10_1X20";
+ case MEDIA_BUS_FMT_VYUY10_1X20: return "VYUY10_1X20";
+ case MEDIA_BUS_FMT_YUYV10_1X20: return "YUYV10_1X20";
+ case MEDIA_BUS_FMT_YVYU10_1X20: return "YVYU10_1X20";
+ case MEDIA_BUS_FMT_VUY8_1X24: return "VUY8_1X24";
+ case MEDIA_BUS_FMT_YUV8_1X24: return "YUV8_1X24";
+// case MEDIA_BUS_FMT_UYYVYY8_0_5X24: return "UYYVYY8_0_5X24";
+ case MEDIA_BUS_FMT_UYVY12_1X24: return "UYVY12_1X24";
+ case MEDIA_BUS_FMT_VYUY12_1X24: return "VYUY12_1X24";
+ case MEDIA_BUS_FMT_YUYV12_1X24: return "YUYV12_1X24";
+ case MEDIA_BUS_FMT_YVYU12_1X24: return "YVYU12_1X24";
+ case MEDIA_BUS_FMT_YUV10_1X30: return "YUV10_1X30";
+// case MEDIA_BUS_FMT_UYYVYY10_0_5X30: return "UYYVYY10_0_5X30";
+ case MEDIA_BUS_FMT_AYUV8_1X32: return "AYUV8_1X32";
+// case MEDIA_BUS_FMT_UYYVYY12_0_5X36: return "UYYVYY12_0_5X36";
+// case MEDIA_BUS_FMT_YUV12_1X36: return "YUV12_1X36";
+// case MEDIA_BUS_FMT_YUV16_1X48: return "YUV16_1X48";
+// case MEDIA_BUS_FMT_UYYVYY16_0_5X48: return "UYYVYY16_0_5X48";
+ case MEDIA_BUS_FMT_SBGGR8_1X8: return "SBGGR8_1X8";
+ case MEDIA_BUS_FMT_SGBRG8_1X8: return "SGBRG8_1X8";
+ case MEDIA_BUS_FMT_SGRBG8_1X8: return "SGRBG8_1X8";
+ case MEDIA_BUS_FMT_SRGGB8_1X8: return "SRGGB8_1X8";
+ case MEDIA_BUS_FMT_SBGGR10_ALAW8_1X8: return "SBGGR10_ALAW8_1X8";
+ case MEDIA_BUS_FMT_SGBRG10_ALAW8_1X8: return "SGBRG10_ALAW8_1X8";
+ case MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8: return "SGRBG10_ALAW8_1X8";
+ case MEDIA_BUS_FMT_SRGGB10_ALAW8_1X8: return "SRGGB10_ALAW8_1X8";
+ case MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8: return "SBGGR10_DPCM8_1X8";
+ case MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8: return "SGBRG10_DPCM8_1X8";
+ case MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8: return "SGRBG10_DPCM8_1X8";
+ case MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8: return "SRGGB10_DPCM8_1X8";
+// case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_B: return "SBGGR10_2X8_PADHI_B";
+// case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_L: return "SBGGR10_2X8_PADHI_L";
+// case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_B: return "SBGGR10_2X8_PADLO_B";
+// case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_L: return "SBGGR10_2X8_PADLO_L";
+ case MEDIA_BUS_FMT_SBGGR10_1X10: return "SBGGR10_1X10";
+ case MEDIA_BUS_FMT_SGBRG10_1X10: return "SGBRG10_1X10";
+ case MEDIA_BUS_FMT_SGRBG10_1X10: return "SGRBG10_1X10";
+ case MEDIA_BUS_FMT_SRGGB10_1X10: return "SRGGB10_1X10";
+ case MEDIA_BUS_FMT_SBGGR12_1X12: return "SBGGR12_1X12";
+ case MEDIA_BUS_FMT_SGBRG12_1X12: return "SGBRG12_1X12";
+ case MEDIA_BUS_FMT_SGRBG12_1X12: return "SGRBG12_1X12";
+ case MEDIA_BUS_FMT_SRGGB12_1X12: return "SRGGB12_1X12";
+ case MEDIA_BUS_FMT_SBGGR14_1X14: return "SBGGR14_1X14";
+ case MEDIA_BUS_FMT_SGBRG14_1X14: return "SGBRG14_1X14";
+ case MEDIA_BUS_FMT_SGRBG14_1X14: return "SGRBG14_1X14";
+ case MEDIA_BUS_FMT_SRGGB14_1X14: return "SRGGB14_1X14";
+ case MEDIA_BUS_FMT_SBGGR16_1X16: return "SBGGR16_1X16";
+ case MEDIA_BUS_FMT_SGBRG16_1X16: return "SGBRG16_1X16";
+ case MEDIA_BUS_FMT_SGRBG16_1X16: return "SGRBG16_1X16";
+ case MEDIA_BUS_FMT_SRGGB16_1X16: return "SRGGB16_1X16";
+ case MEDIA_BUS_FMT_JPEG_1X8: return "JPEG_1X8";
+ case MEDIA_BUS_FMT_S5C_UYVY_JPEG_1X8: return "S5C_UYVY_JPEG_1X8";
+ case MEDIA_BUS_FMT_AHSV8888_1X32: return "AHSV8888_1X32";
+ default: return QString(code);
+ }
+}
+
+static QDebug operator<<(QDebug debug, const struct v4l2_mbus_framefmt &format)
+{
+ QDebugStateSaver saver(debug);
+ debug.nospace() << "v4l2_mbus_framefmt("
+ << "code: " << mediaBusFmtToString(format.code) << ", "
+ << "size: " << format.width << "x" << format.height << ")";
+ return debug;
+}
+
+static QDebug operator<<(QDebug debug, const struct v4l2_pix_format_mplane &format)
+{
+ QDebugStateSaver saver(debug);
+ debug.nospace() << "v4l2_pix_format_mplane("
+ << "pixel format: " << q_fourccToString(format.pixelformat) << ", "
+ << "size: " << format.width << "x" << format.height << ", "
+ << "planes: " << format.num_planes << ")";
+ return debug;
+}
+
+QLinuxMediaDevice::QLinuxMediaDevice(const QString &devicePath)
+ : m_mediaDevice(media_device_new(devicePath.toStdString().c_str()))
+{
+ if (!m_mediaDevice)
+ qFatal("Couldn't get media device");
+
+ if (media_device_enumerate(m_mediaDevice))
+ qFatal("Couldn't enumerate media device");
+
+ m_info = media_get_info(m_mediaDevice);
+
+ qCDebug(qLcEglfsKmsDebug) << "Opened linux media device:"
+ << "\n\t Path:" << devicePath
+ << "\n\t Model:" << model()
+ << "\n\t Device name:" << deviceName();
+
+ resetLinks();
+}
+
+QLinuxMediaDevice::~QLinuxMediaDevice()
+{
+ if (m_mediaDevice)
+ media_device_unref(m_mediaDevice);
+}
+
+QString QLinuxMediaDevice::model()
+{
+ return QString(m_info->model);
+}
+
+QString QLinuxMediaDevice::deviceName()
+{
+ return QString(m_info->bus_info).split(":").last();
+}
+
+bool QLinuxMediaDevice::resetLinks()
+{
+ if (media_reset_links(m_mediaDevice)) {
+ qWarning() << "Could not reset media controller links.";
+ return false;
+ }
+ qCDebug(qLcEglfsKmsDebug) << "Reset media links";
+ return true;
+}
+
+struct media_link *QLinuxMediaDevice::parseLink(const QString &link)
+{
+ char *endp = nullptr;;
+ struct media_link *mediaLink = media_parse_link(m_mediaDevice, link.toStdString().c_str(), &endp);
+
+ if (!mediaLink)
+ qWarning() << "Failed to parse media link:" << link;
+
+ return mediaLink;
+}
+
+struct media_pad *QLinuxMediaDevice::parsePad(const QString &pad)
+{
+ struct media_pad *mediaPad = media_parse_pad(m_mediaDevice, pad.toStdString().c_str(), nullptr);
+
+ if (!mediaPad)
+ qWarning() << "Failed to parse media pad:" << pad;
+
+ return mediaPad;
+}
+
+bool QLinuxMediaDevice::enableLink(struct media_link *link)
+{
+ if (media_setup_link(m_mediaDevice, link->source, link->sink, 1)) {
+ qWarning() << "Failed to enable media link.";
+ return false;
+ }
+ return true;
+}
+
+bool QLinuxMediaDevice::disableLink(struct media_link *link)
+{
+ if (media_setup_link(m_mediaDevice, link->source, link->sink, 0)) {
+ qWarning() << "Failed to disable media link.";
+ return false;
+ }
+ return true;
+}
+
+media_entity *QLinuxMediaDevice::getEntity(const QString &name)
+{
+ struct media_entity *entity = media_get_entity_by_name(m_mediaDevice, name.toStdString().c_str(), name.length());
+
+ if (!entity)
+ qWarning() << "Failed to get media entity:" << name;
+
+ return entity;
+}
+
+int QLinuxMediaDevice::openVideoDevice(const QString &name)
+{
+ struct media_entity *entity = getEntity(name);
+ const char *deviceName = media_entity_get_devname(entity);
+ int fd = open(deviceName, O_RDWR);
+ qCDebug(qLcEglfsKmsDebug) << "Opened video device:" << deviceName << "with fd" << fd;
+ return fd;
+}
+
+QLinuxMediaDevice::CaptureSubDevice::CaptureSubDevice(QLinuxMediaDevice *mediaDevice, const QString &name)
+ : m_subdevFd(mediaDevice->openVideoDevice(name))
+{
+}
+
+bool QLinuxMediaDevice::CaptureSubDevice::setFormat(const QSize &size, uint32_t pixelFormat)
+{
+ Q_ASSERT(size.isValid());
+ struct v4l2_format format;
+ memset(&format, 0, sizeof(struct v4l2_format));
+ format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ if (ioctl(m_subdevFd, VIDIOC_G_FMT, &format) == -1) {
+ qErrnoWarning("VIDIOC_G_FMT for capture device failed");
+ return false;
+ }
+
+ format.fmt.pix_mp.width = static_cast<uint>(size.width());
+ format.fmt.pix_mp.height = static_cast<uint>(size.height());
+ format.fmt.pix_mp.field = V4L2_FIELD_NONE;
+ format.fmt.pix_mp.pixelformat = pixelFormat;
+ format.fmt.pix_mp.num_planes = 1;
+ format.fmt.pix_mp.flags = 0;
+ format.fmt.pix_mp.plane_fmt[0].bytesperline = 0;
+ format.fmt.pix_mp.plane_fmt[0].sizeimage = 0;
+
+ if (ioctl(m_subdevFd, VIDIOC_S_FMT, &format) == -1) {
+ qWarning() << "Capture device" << m_subdevFd << "VIDIOC_S_FMT with format" << format.fmt.pix_mp
+ << "failed:" << strerror(errno);
+ return false;
+ }
+
+ qCDebug(qLcEglfsKmsDebug) << "Capture device" << m_subdevFd << "format set:" << format.fmt.pix_mp;
+ return true;
+}
+
+bool QLinuxMediaDevice::CaptureSubDevice::clearBuffers()
+{
+ struct v4l2_requestbuffers requestBuffers;
+ memset(&requestBuffers, 0, sizeof(requestBuffers));
+ requestBuffers.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ requestBuffers.memory = V4L2_MEMORY_DMABUF;
+ requestBuffers.count = 0;
+ if (ioctl(m_subdevFd, VIDIOC_REQBUFS, &requestBuffers) == -1) {
+ qWarning("Capture device %d: VIDIOC_REQBUFS clear failed: %s", m_subdevFd, strerror(errno));
+ return false;
+ }
+ qCDebug(qLcEglfsKmsDebug, "Capture device %d: Deallocced buffers with REQBUF, now has %d buffers", m_subdevFd, requestBuffers.count);
+ Q_ASSERT(requestBuffers.count == 0);
+ return true;
+}
+
+bool QLinuxMediaDevice::CaptureSubDevice::requestBuffer()
+{
+ struct v4l2_requestbuffers requestBuffers;
+ memset(&requestBuffers, 0, sizeof(requestBuffers));
+ requestBuffers.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ requestBuffers.memory = V4L2_MEMORY_DMABUF;
+ requestBuffers.count = 1;
+ if (ioctl(m_subdevFd, VIDIOC_REQBUFS, &requestBuffers) == -1) {
+ if (errno == EINVAL)
+ qWarning("Capture device %d: Multi-planar capture or dma buffers not supported", m_subdevFd);
+ qWarning("Capture device %d: VIDIOC_REQBUFS failed: %s", m_subdevFd, strerror(errno));
+ return false;
+ }
+ Q_ASSERT(requestBuffers.count == 1);
+ return true;
+}
+
+bool QLinuxMediaDevice::CaptureSubDevice::queueBuffer(int dmabufFd, const QSize &bufferSize)
+{
+ const uint numPlanes = 1;
+ struct v4l2_buffer buffer;
+ memset(&buffer, 0, sizeof(buffer));
+ buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ buffer.memory = V4L2_MEMORY_DMABUF;
+ buffer.index = 0;
+
+ struct v4l2_plane planes[VIDEO_MAX_PLANES];
+ buffer.m.planes = planes;
+ buffer.length = numPlanes;
+ memset(planes, 0, sizeof(planes));
+ for (uint i = 0; i < numPlanes; i++) {
+ buffer.m.planes[i].m.fd = dmabufFd;
+ buffer.m.planes[i].length = static_cast<uint>(bufferSize.width() * bufferSize.height() * 4); //TODO: don't harcode bpp
+ buffer.m.planes[i].bytesused = static_cast<uint>(bufferSize.width() * bufferSize.height() * 4); //TODO: don't harcode bpp
+ }
+
+ if (ioctl(m_subdevFd, VIDIOC_QBUF, &buffer) == -1) {
+ qWarning("Capture device %d: VIDIOC_QBUF failed for dma buffer with fd %d: %s",
+ m_subdevFd, dmabufFd, strerror(errno));
+ return false;
+ }
+
+ return true;
+}
+
+bool QLinuxMediaDevice::CaptureSubDevice::dequeueBuffer()
+{
+ const int numPlanes = 1;
+ struct v4l2_buffer buffer;
+ struct v4l2_plane planes[VIDEO_MAX_PLANES];
+
+ memset(&buffer, 0, sizeof(buffer));
+ buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ buffer.memory = V4L2_MEMORY_DMABUF;
+ buffer.index = 0;
+ buffer.m.planes = planes;
+ buffer.length = numPlanes;
+ memset(planes, 0, sizeof(planes));
+
+ if (ioctl(m_subdevFd, VIDIOC_DQBUF, &buffer) == -1) {
+ qWarning("Capture device %d: VIDIOC_DQBUF failed: %s", m_subdevFd, strerror(errno));
+ return false;
+ }
+
+ return true;
+}
+
+bool QLinuxMediaDevice::CaptureSubDevice::streamOn()
+{
+ return QLinuxMediaDevice::streamOn(m_subdevFd, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+}
+
+bool QLinuxMediaDevice::CaptureSubDevice::streamOff()
+{
+ return QLinuxMediaDevice::streamOff(m_subdevFd, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+}
+
+QLinuxMediaDevice::OutputSubDevice::OutputSubDevice(QLinuxMediaDevice *mediaDevice, const QString &name)
+ : m_subdevFd(mediaDevice->openVideoDevice(name))
+{
+}
+
+bool QLinuxMediaDevice::OutputSubDevice::setFormat(const QSize &size, uint32_t pixelFormat, uint32_t bytesPerLine)
+{
+ Q_ASSERT(size.isValid());
+ struct v4l2_format format;
+ memset(&format, 0, sizeof(struct v4l2_format));
+ format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ if (ioctl(m_subdevFd, VIDIOC_G_FMT, &format) == -1) {
+ qErrnoWarning("VIDIOC_G_FMT for output device failed");
+ return false;
+ }
+
+ format.fmt.pix_mp.width = static_cast<uint>(size.width());
+ format.fmt.pix_mp.height = static_cast<uint>(size.height());
+ format.fmt.pix_mp.field = V4L2_FIELD_NONE;
+ format.fmt.pix_mp.pixelformat = pixelFormat;
+ format.fmt.pix_mp.num_planes = 1;
+ format.fmt.pix_mp.flags = 0;
+ format.fmt.pix_mp.plane_fmt[0].bytesperline = bytesPerLine;
+ format.fmt.pix_mp.plane_fmt[0].sizeimage = 0;
+
+ if (ioctl(m_subdevFd, VIDIOC_S_FMT, &format) == -1) {
+ qWarning() << "Output device" << m_subdevFd << "VIDIOC_S_FMT with format" << format.fmt.pix_mp
+ << "failed:" << strerror(errno);
+ return false;
+ }
+
+ qCDebug(qLcEglfsKmsDebug) << "Output device device" << m_subdevFd << "format set:" << format.fmt.pix_mp;
+ return true;
+}
+
+bool QLinuxMediaDevice::OutputSubDevice::clearBuffers()
+{
+ struct v4l2_requestbuffers requestBuffers;
+ memset(&requestBuffers, 0, sizeof(requestBuffers));
+ requestBuffers.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ requestBuffers.memory = V4L2_MEMORY_DMABUF;
+ requestBuffers.count = 0;
+ if (ioctl(m_subdevFd, VIDIOC_REQBUFS, &requestBuffers) == -1) {
+ qWarning("Output device %d: VIDIOC_REQBUFS clear failed: %s", m_subdevFd, strerror(errno));
+ return false;
+ }
+ qCDebug(qLcEglfsKmsDebug, "Output device %d: Deallocced buffers with REQBUF, now has %d buffers", m_subdevFd, requestBuffers.count);
+ Q_ASSERT(requestBuffers.count == 0);
+ return true;
+}
+
+bool QLinuxMediaDevice::OutputSubDevice::requestBuffer()
+{
+ struct v4l2_requestbuffers requestBuffers;
+ memset(&requestBuffers, 0, sizeof(requestBuffers));
+ requestBuffers.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ requestBuffers.memory = V4L2_MEMORY_DMABUF;
+ requestBuffers.count = 1;
+ if (ioctl(m_subdevFd, VIDIOC_REQBUFS, &requestBuffers) == -1) {
+ if (errno == EINVAL)
+ qWarning("Output device %d: Multi-planar output or dma buffers not supported", m_subdevFd);
+ qWarning("Output device %d: VIDIOC_REQBUFS failed: %s", m_subdevFd, strerror(errno));
+ return false;
+ }
+ qCDebug(qLcEglfsKmsDebug) << "REQBUF returned" << requestBuffers.count << "buffers for output device" << m_subdevFd;
+ return true;
+}
+
+bool QLinuxMediaDevice::OutputSubDevice::queueBuffer(int dmabufFd, uint bytesUsed, uint length)
+{
+ const int numPlanes = 1;
+ struct v4l2_buffer buffer;
+ memset(&buffer, 0, sizeof(buffer));
+ buffer.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ buffer.memory = V4L2_MEMORY_DMABUF;
+ buffer.index = 0;
+ buffer.length = numPlanes;
+ buffer.bytesused = bytesUsed;
+ buffer.field = V4L2_FIELD_NONE; //TODO: what is this?
+
+ struct v4l2_plane planes[numPlanes];
+ memset(planes, 0, sizeof(planes));
+ buffer.m.planes = planes;
+
+ for (int i = 0; i < numPlanes; i++) {
+ buffer.m.planes[i].m.fd = dmabufFd;
+ buffer.m.planes[i].length = length;
+ buffer.m.planes[i].bytesused = bytesUsed;
+ }
+
+ if (ioctl(m_subdevFd, VIDIOC_QBUF, &buffer) == -1) {
+ qWarning("Output device %d: VIDIOC_QBUF failed for dmabuf %d: %s", m_subdevFd, dmabufFd, strerror(errno));
+ return false;
+ }
+
+ if (!(buffer.flags & V4L2_BUF_FLAG_QUEUED)) {
+ qWarning() << "Queued flag not set on buffer for output device";
+ return false;
+ }
+
+ return true;
+}
+
+bool QLinuxMediaDevice::OutputSubDevice::streamOn()
+{
+ return QLinuxMediaDevice::streamOn(m_subdevFd, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+}
+
+bool QLinuxMediaDevice::OutputSubDevice::streamOff()
+{
+ return QLinuxMediaDevice::streamOff(m_subdevFd, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+}
+
+int QLinuxMediaDevice::openVideoDevice(media_pad *pad)
+{
+ const char *deviceName = media_entity_get_devname(pad->entity);
+ int fd = open(deviceName, O_RDWR);
+ qCDebug(qLcEglfsKmsDebug) << "Opened video device:" << deviceName << "with fd" << fd;
+ return fd;
+}
+
+bool QLinuxMediaDevice::setSubdevFormat(struct media_pad *pad, const QSize &size, uint32_t mbusFormat)
+{
+ Q_ASSERT(size.isValid());
+ struct v4l2_mbus_framefmt format;
+ format.width = static_cast<uint>(size.width());
+ format.height = static_cast<uint>(size.height());
+ format.code = mbusFormat;
+
+ if (v4l2_subdev_set_format(pad->entity, &format, pad->index, V4L2_SUBDEV_FORMAT_ACTIVE)) {
+ qWarning() << "Setting v4l2_subdev_set_format failed for format" << format;
+ return false;
+ }
+
+ if (format.code != mbusFormat) {
+ qWarning() << "Got" << mediaBusFmtToString(format.code) << "instead of"
+ << mediaBusFmtToString(mbusFormat) << "when setting subdevice format";
+ return false;
+ }
+
+ qCDebug(qLcEglfsKmsDebug) << "Set format to" << format << "for entity" << pad->entity << "index" << pad->index;
+ return true;
+}
+
+bool QLinuxMediaDevice::setSubdevAlpha(int subdevFd, qreal alpha)
+{
+ struct v4l2_control control;
+ control.id = V4L2_CID_ALPHA_COMPONENT;
+ control.value = static_cast<__s32>(alpha * 0xff);
+ if (ioctl(subdevFd, VIDIOC_S_CTRL, &control) == -1) {
+ qErrnoWarning("Setting alpha (%d) failed", control.value);
+ return false;
+ }
+ return true;
+}
+
+bool QLinuxMediaDevice::setSubdevSelection(struct media_pad *pad, const QRect &geometry, uint target)
+{
+ Q_ASSERT(geometry.isValid());
+ struct v4l2_rect rect;
+ rect.left = geometry.left();
+ rect.top = geometry.top();
+ rect.width = static_cast<uint>(geometry.width());
+ rect.height = static_cast<uint>(geometry.height());
+
+ int ret = v4l2_subdev_set_selection(pad->entity, &rect, pad->index, target, V4L2_SUBDEV_FORMAT_ACTIVE);
+ if (ret) {
+ qWarning() << "Setting subdev selection failed.";
+ return false;
+ }
+
+ return true;
+}
+
+bool QLinuxMediaDevice::setSubdevCrop(struct media_pad *pad, const QRect &geometry)
+{
+ return setSubdevSelection(pad, geometry, V4L2_SEL_TGT_CROP);
+}
+
+bool QLinuxMediaDevice::setSubdevCompose(struct media_pad *pad, const QRect &geometry)
+{
+ return setSubdevSelection(pad, geometry, V4L2_SEL_TGT_COMPOSE);
+}
+
+bool QLinuxMediaDevice::streamOn(int subDeviceFd, v4l2_buf_type bufferType)
+{
+ if (ioctl(subDeviceFd, VIDIOC_STREAMON, &bufferType) == -1) {
+ qWarning("VIDIOC_STREAMON failed for subdevice %d: %s", subDeviceFd, strerror(errno));
+ return false;
+ }
+
+ return true;
+}
+
+bool QLinuxMediaDevice::streamOff(int subDeviceFd, v4l2_buf_type bufferType)
+{
+ if (ioctl(subDeviceFd, VIDIOC_STREAMOFF, &bufferType) == -1) {
+ qWarning("VIDIOC_STREAMOFF failed for subdevice %d: %s", subDeviceFd, strerror(errno));
+ return false;
+ }
+
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qlinuxmediadevice.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qlinuxmediadevice.h
new file mode 100644
index 0000000000..26f863214b
--- /dev/null
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qlinuxmediadevice.h
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QLINUXMEDIADEVICE_H
+#define QLINUXMEDIADEVICE_H
+
+#include <linux/v4l2-mediabus.h>
+
+#include <QtCore/qglobal.h>
+#include <QtCore/QString>
+
+QT_BEGIN_NAMESPACE
+
+class QSize;
+class QRect;
+
+class QLinuxMediaDevice
+{
+public:
+ QLinuxMediaDevice(const QString &devicePath);
+ ~QLinuxMediaDevice();
+
+ QString model();
+ QString deviceName();
+ bool resetLinks();
+ struct media_link *parseLink(const QString &link);
+ struct media_pad *parsePad(const QString &pad);
+ bool enableLink(struct media_link *link);
+ bool disableLink(struct media_link *link);
+ struct media_entity *getEntity(const QString &name);
+ int openVideoDevice(const QString &name);
+
+ class CaptureSubDevice
+ {
+ public:
+ CaptureSubDevice(QLinuxMediaDevice *mediaDevice, const QString &name);
+ bool setFormat(const QSize &size, uint32_t pixelFormat = V4L2_PIX_FMT_ABGR32); //TODO: fix to match output device
+ bool clearBuffers();
+ bool requestBuffer();
+ bool queueBuffer(int dmabufFd, const QSize &bufferSize);
+ bool dequeueBuffer();
+ bool streamOn();
+ bool streamOff();
+ private:
+ int m_subdevFd = -1;
+ };
+
+ class OutputSubDevice
+ {
+ public:
+ OutputSubDevice(QLinuxMediaDevice *mediaDevice, const QString &name);
+ bool setFormat(const QSize &size, uint32_t pixelFormat, uint32_t bytesPerLine);
+ bool clearBuffers();
+ bool requestBuffer();
+ bool queueBuffer(int dmabufFd, uint bytesUsed, uint length);
+ bool streamOn();
+ bool streamOff();
+ private:
+ int m_subdevFd = -1;
+ };
+
+ static int openVideoDevice(media_pad *pad);
+
+ static bool setSubdevFormat(struct media_pad *pad, const QSize &size, uint32_t mbusFormat = MEDIA_BUS_FMT_ARGB8888_1X32);
+ static bool setSubdevAlpha(int subdevFd, qreal alpha);
+
+ static bool setSubdevSelection(struct media_pad *pad, const QRect &geometry, uint target);
+ static bool setSubdevCrop(struct media_pad *pad, const QRect &geometry);
+ static bool setSubdevCompose(struct media_pad *pad, const QRect &geometry);
+
+private:
+ static bool streamOn(int subDeviceFd, v4l2_buf_type bufferType);
+ static bool streamOff(int subDeviceFd, v4l2_buf_type bufferType);
+ struct media_device *m_mediaDevice = nullptr;
+ const struct media_device_info *m_info = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QLINUXMEDIADEVICE_H
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qvsp2blendingdevice.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qvsp2blendingdevice.cpp
new file mode 100644
index 0000000000..879d312341
--- /dev/null
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qvsp2blendingdevice.cpp
@@ -0,0 +1,333 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qvsp2blendingdevice.h"
+#include <qeglfskmshelpers.h>
+
+#include <QDebug>
+#include <QtCore/QLoggingCategory>
+
+#include <drm_fourcc.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(qLcEglfsKmsDebug)
+
+//TODO: is this the right place for these conversion functions?
+static inline uint drmFormatToV4l2PixelFormat(uint drmFormat) {
+ //ARGB8888 == ABGR32 because linux media list stuff in the opposite order, but the fourcc is the same
+ Q_ASSERT(DRM_FORMAT_ARGB8888 == V4L2_PIX_FMT_ABGR32);
+ return drmFormat;
+}
+
+static uint drmFormatToMediaBusFormat(uint drmFormat)
+{
+ switch (drmFormat) {
+ case DRM_FORMAT_RGB888:
+ return MEDIA_BUS_FMT_RGB888_1X24;
+ case DRM_FORMAT_BGR888:
+ return MEDIA_BUS_FMT_BGR888_1X24;
+ case DRM_FORMAT_XRGB8888:
+ case DRM_FORMAT_XBGR8888:
+// return MEDIA_BUS_FMT_RGB888_1X32_PADHI; // doesn't work on renesas m3, just use fallthrough to argb for now
+ case DRM_FORMAT_RGBX8888:
+ case DRM_FORMAT_BGRX8888:
+ case DRM_FORMAT_ARGB8888:
+ case DRM_FORMAT_ABGR8888:
+ case DRM_FORMAT_RGBA8888:
+ case DRM_FORMAT_BGRA8888:
+ return MEDIA_BUS_FMT_ARGB8888_1X32;
+ default:
+ qWarning() << "Unknown drm format" << q_fourccToString(drmFormat) << "defaulting to argb8888";
+ return MEDIA_BUS_FMT_ARGB8888_1X32;
+ }
+}
+
+QVsp2BlendingDevice::QVsp2BlendingDevice(const QSize &screenSize)
+ : m_mediaDevice("/dev/media0")
+ , m_screenSize(screenSize)
+{
+ QLinuxMediaDevice &md = m_mediaDevice;
+ QString deviceName = md.deviceName();
+
+ if (md.model() != QString("VSP2"))
+ qWarning() << "Unsupported media device model:" << md.model();
+
+ if (deviceName != "fe960000.vsp")
+ qWarning() << "Unknown media device name:" << deviceName;
+
+ const int numInputs = 5;
+
+ for (int i = 0; i < numInputs; ++i) {
+ Input input;
+ input.linkToBru = md.parseLink(QString("'%1 rpf.%2':1 -> '%1 bru':%2").arg(deviceName).arg(i));
+ input.inputFormatPad = md.parsePad(QString("'%1 rpf.%2':0").arg(deviceName).arg(i));
+ input.outputFormatPad = md.parsePad(QString("'%1 rpf.%2':1").arg(deviceName).arg(i));
+ input.outputFormatFd = QLinuxMediaDevice::openVideoDevice(input.outputFormatPad);
+ input.bruInputFormatPad = md.parsePad(QString("'%1 bru':%2").arg(deviceName).arg(i));
+ input.rpfInput = new QLinuxMediaDevice::OutputSubDevice(&md, QString("%1 rpf.%2 input").arg(deviceName).arg(i));
+ m_inputs.append(input);
+ }
+
+ m_wpfOutput = new QLinuxMediaDevice::CaptureSubDevice(&md, QString("%1 wpf.0 output").arg(deviceName));
+
+ // Setup links for output
+ md.enableLink(md.parseLink(QString("'%1 bru':5 -> '%1 wpf.0':0").arg(deviceName)));
+ md.enableLink(md.parseLink(QString("'%1 wpf.0':1 -> '%1 wpf.0 output':0").arg(deviceName)));
+
+ // Output pads
+ auto bruOutputFormatPad = md.parsePad(QString("'%1 bru':5").arg(deviceName));
+ auto wpfInputFormatPad = md.parsePad(QString("'%1 wpf.0':0").arg(deviceName));
+ auto wpfOutputFormatPad = md.parsePad(QString("'%1 wpf.0':1").arg(deviceName));
+
+ m_wpfOutput->setFormat(screenSize);
+ QLinuxMediaDevice::setSubdevFormat(bruOutputFormatPad, screenSize);
+ QLinuxMediaDevice::setSubdevFormat(wpfInputFormatPad, screenSize);
+ QLinuxMediaDevice::setSubdevFormat(wpfOutputFormatPad, screenSize);
+
+ m_wpfOutput->requestBuffer();
+}
+
+bool QVsp2BlendingDevice::enableInput(int i, const QRect &bufferGeometry, uint drmFormat, uint bytesPerLine)
+{
+ qCDebug(qLcEglfsKmsDebug) << "Blend unit: Enabling input" << i;
+ if (m_inputs[i].enabled) {
+ qWarning("Vsp2: Input %d already enabled", i);
+ return false;
+ }
+
+ if (!bufferGeometry.isValid()) { //TODO: bounds checking as well?
+ qWarning() << "Vsp2: Invalid buffer geometry";
+ return false;
+ }
+
+ Input &input = m_inputs[i];
+ if (!m_mediaDevice.enableLink(input.linkToBru))
+ return false;
+
+ uint pixelFormat = drmFormatToV4l2PixelFormat(drmFormat);
+ if (!setInputFormat(i, bufferGeometry, pixelFormat, bytesPerLine)) {
+ disableInput(i);
+ return false;
+ }
+
+ input.rpfInput->requestBuffer();
+ return true;
+}
+
+int QVsp2BlendingDevice::enableInput(const QRect &bufferGeometry, uint drmFormat, uint bytesPerLine)
+{
+ for (int i = 0; i < m_inputs.size(); ++i) {
+ if (!m_inputs[i].enabled)
+ return enableInput(i, bufferGeometry, drmFormat, bytesPerLine) ? i : -1;
+ }
+ qWarning() << "Vsp2: No more inputs available in blend unit";
+ return -1;
+}
+
+bool QVsp2BlendingDevice::disableInput(int i)
+{
+ qCDebug(qLcEglfsKmsDebug) << "Vsp2: disabling input" << i;
+ if (!m_inputs[i].enabled) {
+ qWarning("Vsp2: Input %d already disabled", i);
+ return false;
+ }
+ m_mediaDevice.disableLink(m_inputs[i].linkToBru);
+ m_inputs[i].rpfInput->clearBuffers();
+ m_inputs[i].enabled = false;
+ return true;
+}
+
+bool QVsp2BlendingDevice::setInputBuffer(int index, int dmabufFd)
+{
+ Input &input = m_inputs[index];
+
+ if (!input.enabled) {
+ qWarning() << "Vsp2: Can't queue on disabled input" << index;
+ return false;
+ }
+
+ // Don't queue the buffer yet, store it here and wait until blending
+ if (input.dmabuf.fd != dmabufFd) {
+ m_dirty = true;
+ input.dmabuf.fd = dmabufFd;
+ }
+ return true;
+}
+
+bool QVsp2BlendingDevice::setInputPosition(int index, const QPoint &position)
+{
+ Input &input = m_inputs[index];
+
+ if (input.geometry.topLeft() == position)
+ return true;
+
+ m_dirty = true;
+ input.geometry.moveTopLeft(position);
+ return QLinuxMediaDevice::setSubdevCompose(input.bruInputFormatPad, input.geometry);
+}
+
+bool QVsp2BlendingDevice::setInputAlpha(int index, qreal alpha)
+{
+ Input &input = m_inputs[index];
+ if (input.alpha == alpha)
+ return true;
+
+ m_dirty = true;
+ input.alpha = alpha;
+ return QLinuxMediaDevice::setSubdevAlpha(input.outputFormatFd, alpha);
+}
+
+bool QVsp2BlendingDevice::blend(int outputDmabufFd)
+{
+ if (!m_dirty)
+ qWarning("Blending without being dirty, should not be necessary");
+
+ if (!m_inputs[0].enabled) {
+ qWarning("Vsp2: Can't blend with layer 0 disabled");
+ return false;
+ }
+
+ // Queue dma input buffers
+ for (int i=0; i < m_inputs.size(); ++i) {
+ auto &input = m_inputs[i];
+ if (input.enabled) {
+ if (!input.rpfInput->queueBuffer(input.dmabuf.fd, input.dmabuf.bytesUsed, input.dmabuf.length)) {
+ qWarning() << "Vsp2: Failed to queue buffer for input" << i
+ << "with dmabuf" << input.dmabuf.fd
+ << "and size" << input.geometry.size();
+
+ if (!disableInput(i))
+ qWarning() << "Vsp2: Failed to disable input" << i;
+ }
+ }
+ }
+
+ if (!m_wpfOutput->queueBuffer(outputDmabufFd, m_screenSize)) {
+ qWarning() << "Vsp2: Failed to queue blending output buffer" << outputDmabufFd << m_screenSize;
+ return false;
+ }
+
+ if (!streamOn()) {
+ qWarning() << "Vsp2: Failed to start streaming";
+ return false;
+ }
+
+ if (!m_wpfOutput->dequeueBuffer()) {
+ qWarning() << "Vsp2: Failed to dequeue blending output buffer";
+ if (!streamOff())
+ qWarning() << "Vsp2: Failed to stop streaming when recovering after a broken blend.";
+ return false;
+ }
+
+ if (!streamOff()) {
+ qWarning() << "Vsp2: Failed to stop streaming";
+ return false;
+ }
+
+ m_dirty = false;
+ return true;
+}
+
+int QVsp2BlendingDevice::numInputs() const
+{
+ return m_inputs.size();
+}
+
+bool QVsp2BlendingDevice::streamOn()
+{
+ for (auto &input : m_inputs) {
+ if (input.enabled) {
+ if (!input.rpfInput->streamOn()) {
+ //TODO: perhaps it's better to try to continue with the other inputs?
+ return false;
+ }
+ }
+ }
+
+ return m_wpfOutput->streamOn();
+}
+
+bool QVsp2BlendingDevice::streamOff()
+{
+ bool succeeded = m_wpfOutput->streamOff();
+ for (auto &input : m_inputs) {
+ if (input.enabled)
+ succeeded &= input.rpfInput->streamOff();
+ }
+ return succeeded;
+}
+
+bool QVsp2BlendingDevice::setInputFormat(int i, const QRect &bufferGeometry, uint pixelFormat, uint bytesPerLine)
+{
+ Input &input = m_inputs[i];
+
+ Q_ASSERT(bufferGeometry.isValid());
+
+ const uint bpp = 4; //TODO: don't hardcode bpp, get it from pixelFormat?
+ input.enabled = true;
+ input.geometry = bufferGeometry;
+ input.dmabuf.bytesUsed = bpp * static_cast<uint>(bufferGeometry.width()) * static_cast<uint>(bufferGeometry.height());
+ input.dmabuf.length = static_cast<uint>(bufferGeometry.height()) * bytesPerLine;
+
+ const QSize size = bufferGeometry.size();
+
+ if (!input.rpfInput->setFormat(size, pixelFormat, bytesPerLine)) // rpf.x input
+ return false;
+
+ const uint mediaBusFormat = drmFormatToMediaBusFormat(pixelFormat);
+ if (!QLinuxMediaDevice::setSubdevFormat(input.inputFormatPad, size, mediaBusFormat)) // rpf.x:0
+ return false;
+
+ if (!QLinuxMediaDevice::setSubdevFormat(input.outputFormatPad, size, mediaBusFormat)) // rpf.x:1
+ return false;
+
+ if (!QLinuxMediaDevice::setSubdevFormat(input.bruInputFormatPad, size, mediaBusFormat)) // bru:x
+ return false;
+
+ if (!QLinuxMediaDevice::setSubdevCrop(input.inputFormatPad, QRect(QPoint(0, 0), size)))
+ return false;
+
+ if (!QLinuxMediaDevice::setSubdevCompose(input.bruInputFormatPad, bufferGeometry))
+ return false;
+
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qvsp2blendingdevice.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qvsp2blendingdevice.h
new file mode 100644
index 0000000000..be48954f47
--- /dev/null
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qvsp2blendingdevice.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QVSP2BLENDINGDEVICE_H
+#define QVSP2BLENDINGDEVICE_H
+
+#include <QtCore/QRect>
+#include <QtCore/QVector>
+#include <QtCore/qglobal.h>
+
+#include "qlinuxmediadevice.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSize;
+
+class QVsp2BlendingDevice
+{
+public:
+ QVsp2BlendingDevice(const QSize& screenSize); //TODO: add support for output format as well?
+ bool enableInput(int i, const QRect &bufferGeometry, uint drmFormat, uint bytesPerLine);
+ int enableInput(const QRect &bufferGeometry, uint drmFormat, uint bytesPerLine);
+ bool disableInput(int i);
+ bool setInputBuffer(int index, int dmabufFd);
+ bool setInputPosition(int index, const QPoint &position);
+ bool setInputAlpha(int index, qreal alpha);
+ bool blend(int outputDmabufFd);
+ int numInputs() const;
+ bool isDirty() const { return m_dirty; }
+private:
+ bool streamOn();
+ bool streamOff();
+ bool setInputFormat(int i, const QRect &bufferGeometry, uint pixelFormat, uint bytesPerLine);
+ QLinuxMediaDevice m_mediaDevice;
+ QLinuxMediaDevice::CaptureSubDevice *m_wpfOutput = nullptr; // wpf output
+ struct Input {
+ bool enabled = false;
+ QRect geometry;
+ qreal alpha = 1;
+ struct {
+ int fd = -1;
+ uint bytesUsed = 0;
+ uint length = 0;
+ } dmabuf;
+ struct media_link *linkToBru = nullptr; //rpf.x:1 -> bru:x
+ struct media_pad *inputFormatPad = nullptr; // rpf.x:0
+ struct media_pad *outputFormatPad = nullptr; // rpf.x:1
+ int outputFormatFd = -1; // rpf.x:1 (again, because v4l2_subdev_* doesn't have a way to set alpha)
+ struct media_pad *bruInputFormatPad = nullptr; // bru:x
+ QLinuxMediaDevice::OutputSubDevice *rpfInput = nullptr; // rpf.x input
+ };
+ QVector<struct Input> m_inputs;
+ const QSize m_screenSize;
+ bool m_dirty = true;
+};
+
+QT_END_NAMESPACE
+
+#endif // QVSP2BLENDINGDEVICE
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11integration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11integration.cpp
index 64d0d9b515..2e84915c80 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11integration.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11integration.cpp
@@ -63,83 +63,12 @@ private:
QAtomicInt running;
-static Qt::MouseButtons translateMouseButtons(int s)
-{
- Qt::MouseButtons ret = 0;
- if (s & XCB_BUTTON_MASK_1)
- ret |= Qt::LeftButton;
- if (s & XCB_BUTTON_MASK_2)
- ret |= Qt::MidButton;
- if (s & XCB_BUTTON_MASK_3)
- ret |= Qt::RightButton;
- return ret;
-}
-
-static Qt::MouseButton translateMouseButton(xcb_button_t s)
-{
- switch (s) {
- case 1: return Qt::LeftButton;
- case 2: return Qt::MidButton;
- case 3: return Qt::RightButton;
- // Button values 4-7 were already handled as Wheel events, and won't occur here.
- case 8: return Qt::BackButton; // Also known as Qt::ExtraButton1
- case 9: return Qt::ForwardButton; // Also known as Qt::ExtraButton2
- case 10: return Qt::ExtraButton3;
- case 11: return Qt::ExtraButton4;
- case 12: return Qt::ExtraButton5;
- case 13: return Qt::ExtraButton6;
- case 14: return Qt::ExtraButton7;
- case 15: return Qt::ExtraButton8;
- case 16: return Qt::ExtraButton9;
- case 17: return Qt::ExtraButton10;
- case 18: return Qt::ExtraButton11;
- case 19: return Qt::ExtraButton12;
- case 20: return Qt::ExtraButton13;
- case 21: return Qt::ExtraButton14;
- case 22: return Qt::ExtraButton15;
- case 23: return Qt::ExtraButton16;
- case 24: return Qt::ExtraButton17;
- case 25: return Qt::ExtraButton18;
- case 26: return Qt::ExtraButton19;
- case 27: return Qt::ExtraButton20;
- case 28: return Qt::ExtraButton21;
- case 29: return Qt::ExtraButton22;
- case 30: return Qt::ExtraButton23;
- case 31: return Qt::ExtraButton24;
- default: return Qt::NoButton;
- }
-}
-
void EventReader::run()
{
- Qt::MouseButtons buttons;
-
xcb_generic_event_t *event = nullptr;
while (running.load() && (event = xcb_wait_for_event(m_integration->connection()))) {
uint response_type = event->response_type & ~0x80;
switch (response_type) {
- case XCB_BUTTON_PRESS: {
- xcb_button_press_event_t *press = (xcb_button_press_event_t *)event;
- QPoint p(press->event_x, press->event_y);
- buttons = (buttons & ~0x7) | translateMouseButtons(press->state);
- buttons |= translateMouseButton(press->detail);
- QWindowSystemInterface::handleMouseEvent(0, press->time, p, p, buttons);
- break;
- }
- case XCB_BUTTON_RELEASE: {
- xcb_button_release_event_t *release = (xcb_button_release_event_t *)event;
- QPoint p(release->event_x, release->event_y);
- buttons = (buttons & ~0x7) | translateMouseButtons(release->state);
- buttons &= ~translateMouseButton(release->detail);
- QWindowSystemInterface::handleMouseEvent(0, release->time, p, p, buttons);
- break;
- }
- case XCB_MOTION_NOTIFY: {
- xcb_motion_notify_event_t *motion = (xcb_motion_notify_event_t *)event;
- QPoint p(motion->event_x, motion->event_y);
- QWindowSystemInterface::handleMouseEvent(0, motion->time, p, p, buttons);
- break;
- }
case XCB_CLIENT_MESSAGE: {
xcb_client_message_event_t *client = (xcb_client_message_event_t *) event;
const xcb_atom_t *atoms = m_integration->atoms();
diff --git a/src/plugins/platforms/haiku/main.cpp b/src/plugins/platforms/haiku/main.cpp
index 02168d0165..841891970d 100644
--- a/src/plugins/platforms/haiku/main.cpp
+++ b/src/plugins/platforms/haiku/main.cpp
@@ -47,7 +47,7 @@ QPlatformIntegration *QHaikuIntegrationPlugin::create(const QString& system, con
if (!system.compare(QLatin1String("haiku"), Qt::CaseInsensitive))
return new QHaikuIntegration(paramList);
- return Q_NULLPTR;
+ return nullptr;
}
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/haiku/main.h b/src/plugins/platforms/haiku/main.h
index 82f3313652..e316b79d7c 100644
--- a/src/plugins/platforms/haiku/main.h
+++ b/src/plugins/platforms/haiku/main.h
@@ -47,7 +47,7 @@ class QHaikuIntegrationPlugin : public QPlatformIntegrationPlugin
Q_PLUGIN_METADATA(IID QPlatformIntegrationFactoryInterface_iid FILE "haiku.json")
public:
- QPlatformIntegration *create(const QString&, const QStringList&) Q_DECL_OVERRIDE;
+ QPlatformIntegration *create(const QString&, const QStringList&) override;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/haiku/qhaikuapplication.h b/src/plugins/platforms/haiku/qhaikuapplication.h
index 0696df8109..a9ea442691 100644
--- a/src/plugins/platforms/haiku/qhaikuapplication.h
+++ b/src/plugins/platforms/haiku/qhaikuapplication.h
@@ -49,8 +49,8 @@ class QHaikuApplication : public BApplication
public:
explicit QHaikuApplication(const char *signature);
- bool QuitRequested() Q_DECL_OVERRIDE;
- void RefsReceived(BMessage* message) Q_DECL_OVERRIDE;
+ bool QuitRequested() override;
+ void RefsReceived(BMessage* message) override;
};
#endif
diff --git a/src/plugins/platforms/haiku/qhaikubuffer.cpp b/src/plugins/platforms/haiku/qhaikubuffer.cpp
index c6f6ffe6bc..f25ddef86b 100644
--- a/src/plugins/platforms/haiku/qhaikubuffer.cpp
+++ b/src/plugins/platforms/haiku/qhaikubuffer.cpp
@@ -45,7 +45,7 @@
QT_BEGIN_NAMESPACE
QHaikuBuffer::QHaikuBuffer()
- : m_buffer(Q_NULLPTR)
+ : m_buffer(nullptr)
{
}
@@ -63,12 +63,12 @@ BBitmap* QHaikuBuffer::nativeBuffer() const
const QImage *QHaikuBuffer::image() const
{
- return (m_buffer != Q_NULLPTR) ? &m_image : Q_NULLPTR;
+ return (m_buffer != nullptr) ? &m_image : nullptr;
}
QImage *QHaikuBuffer::image()
{
- return (m_buffer != Q_NULLPTR) ? &m_image : Q_NULLPTR;
+ return (m_buffer != nullptr) ? &m_image : nullptr;
}
QRect QHaikuBuffer::rect() const
diff --git a/src/plugins/platforms/haiku/qhaikuclipboard.cpp b/src/plugins/platforms/haiku/qhaikuclipboard.cpp
index 774da4432a..20519e21d0 100644
--- a/src/plugins/platforms/haiku/qhaikuclipboard.cpp
+++ b/src/plugins/platforms/haiku/qhaikuclipboard.cpp
@@ -47,8 +47,8 @@
#include <Clipboard.h>
QHaikuClipboard::QHaikuClipboard()
- : m_systemMimeData(Q_NULLPTR)
- , m_userMimeData(Q_NULLPTR)
+ : m_systemMimeData(nullptr)
+ , m_userMimeData(nullptr)
{
if (be_clipboard)
be_clipboard->StartWatching(BMessenger(this));
@@ -81,12 +81,12 @@ QMimeData *QHaikuClipboard::mimeData(QClipboard::Mode mode)
const BMessage *clipboard = be_clipboard->Data();
if (clipboard) {
- char *name = Q_NULLPTR;
+ char *name = nullptr;
uint32 type = 0;
int32 count = 0;
for (int i = 0; clipboard->GetInfo(B_MIME_TYPE, i, &name, &type, &count) == B_OK; i++) {
- const void *data = Q_NULLPTR;
+ const void *data = nullptr;
int32 dataLen = 0;
const status_t status = clipboard->FindData(name, B_MIME_TYPE, &data, &dataLen);
@@ -162,7 +162,7 @@ void QHaikuClipboard::MessageReceived(BMessage* message)
{
if (message->what == B_CLIPBOARD_CHANGED) {
delete m_userMimeData;
- m_userMimeData = Q_NULLPTR;
+ m_userMimeData = nullptr;
emitChanged(QClipboard::Clipboard);
}
diff --git a/src/plugins/platforms/haiku/qhaikuclipboard.h b/src/plugins/platforms/haiku/qhaikuclipboard.h
index 3dd4496e8d..b6eb3f591f 100644
--- a/src/plugins/platforms/haiku/qhaikuclipboard.h
+++ b/src/plugins/platforms/haiku/qhaikuclipboard.h
@@ -54,13 +54,13 @@ public:
QHaikuClipboard();
~QHaikuClipboard();
- QMimeData *mimeData(QClipboard::Mode mode = QClipboard::Clipboard) Q_DECL_OVERRIDE;
- void setMimeData(QMimeData *data, QClipboard::Mode mode = QClipboard::Clipboard) Q_DECL_OVERRIDE;
- bool supportsMode(QClipboard::Mode mode) const Q_DECL_OVERRIDE;
- bool ownsMode(QClipboard::Mode mode) const Q_DECL_OVERRIDE;
+ QMimeData *mimeData(QClipboard::Mode mode = QClipboard::Clipboard) override;
+ void setMimeData(QMimeData *data, QClipboard::Mode mode = QClipboard::Clipboard) override;
+ bool supportsMode(QClipboard::Mode mode) const override;
+ bool ownsMode(QClipboard::Mode mode) const override;
// override from BHandler to catch change notifications from Haiku clipboard
- void MessageReceived(BMessage* message) Q_DECL_OVERRIDE;
+ void MessageReceived(BMessage* message) override;
private:
QMimeData *m_systemMimeData;
diff --git a/src/plugins/platforms/haiku/qhaikucursor.h b/src/plugins/platforms/haiku/qhaikucursor.h
index 5d70c97d9e..73a1d2c492 100644
--- a/src/plugins/platforms/haiku/qhaikucursor.h
+++ b/src/plugins/platforms/haiku/qhaikucursor.h
@@ -52,7 +52,7 @@ public:
QHaikuCursor();
#ifndef QT_NO_CURSOR
- void changeCursor(QCursor *windowCursor, QWindow *window) Q_DECL_OVERRIDE;
+ void changeCursor(QCursor *windowCursor, QWindow *window) override;
#endif
private:
diff --git a/src/plugins/platforms/haiku/qhaikuintegration.cpp b/src/plugins/platforms/haiku/qhaikuintegration.cpp
index d46d77ff18..8bd2171794 100644
--- a/src/plugins/platforms/haiku/qhaikuintegration.cpp
+++ b/src/plugins/platforms/haiku/qhaikuintegration.cpp
@@ -87,13 +87,13 @@ QHaikuIntegration::QHaikuIntegration(const QStringList &parameters)
QHaikuIntegration::~QHaikuIntegration()
{
destroyScreen(m_screen);
- m_screen = Q_NULLPTR;
+ m_screen = nullptr;
delete m_services;
- m_services = Q_NULLPTR;
+ m_services = nullptr;
delete m_clipboard;
- m_clipboard = Q_NULLPTR;
+ m_clipboard = nullptr;
be_app->LockLooper();
be_app->Quit();
diff --git a/src/plugins/platforms/haiku/qhaikuintegration.h b/src/plugins/platforms/haiku/qhaikuintegration.h
index 1b938acb82..5c7a173c91 100644
--- a/src/plugins/platforms/haiku/qhaikuintegration.h
+++ b/src/plugins/platforms/haiku/qhaikuintegration.h
@@ -54,17 +54,17 @@ public:
explicit QHaikuIntegration(const QStringList &paramList);
~QHaikuIntegration();
- bool hasCapability(QPlatformIntegration::Capability cap) const Q_DECL_OVERRIDE;
+ bool hasCapability(QPlatformIntegration::Capability cap) const override;
- QPlatformWindow *createPlatformWindow(QWindow *window) const Q_DECL_OVERRIDE;
- QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const Q_DECL_OVERRIDE;
- QAbstractEventDispatcher *createEventDispatcher() const Q_DECL_OVERRIDE;
+ QPlatformWindow *createPlatformWindow(QWindow *window) const override;
+ QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const override;
+ QAbstractEventDispatcher *createEventDispatcher() const override;
- QPlatformFontDatabase *fontDatabase() const Q_DECL_OVERRIDE;
- QPlatformServices *services() const Q_DECL_OVERRIDE;
+ QPlatformFontDatabase *fontDatabase() const override;
+ QPlatformServices *services() const override;
#ifndef QT_NO_CLIPBOARD
- QPlatformClipboard *clipboard() const Q_DECL_OVERRIDE;
+ QPlatformClipboard *clipboard() const override;
#endif
private:
diff --git a/src/plugins/platforms/haiku/qhaikurasterbackingstore.cpp b/src/plugins/platforms/haiku/qhaikurasterbackingstore.cpp
index ee53f693cd..613ae471cb 100644
--- a/src/plugins/platforms/haiku/qhaikurasterbackingstore.cpp
+++ b/src/plugins/platforms/haiku/qhaikurasterbackingstore.cpp
@@ -47,14 +47,14 @@ QT_BEGIN_NAMESPACE
QHaikuRasterBackingStore::QHaikuRasterBackingStore(QWindow *window)
: QPlatformBackingStore(window)
- , m_bitmap(Q_NULLPTR)
+ , m_bitmap(nullptr)
{
}
QHaikuRasterBackingStore::~QHaikuRasterBackingStore()
{
delete m_bitmap;
- m_bitmap = Q_NULLPTR;
+ m_bitmap = nullptr;
}
QPaintDevice *QHaikuRasterBackingStore::paintDevice()
@@ -62,7 +62,7 @@ QPaintDevice *QHaikuRasterBackingStore::paintDevice()
if (!m_bufferSize.isEmpty() && m_bitmap)
return m_buffer.image();
- return Q_NULLPTR;
+ return nullptr;
}
void QHaikuRasterBackingStore::flush(QWindow *window, const QRegion &region, const QPoint &offset)
diff --git a/src/plugins/platforms/haiku/qhaikurasterbackingstore.h b/src/plugins/platforms/haiku/qhaikurasterbackingstore.h
index 06a46e7eb3..060ab27126 100644
--- a/src/plugins/platforms/haiku/qhaikurasterbackingstore.h
+++ b/src/plugins/platforms/haiku/qhaikurasterbackingstore.h
@@ -55,9 +55,9 @@ public:
explicit QHaikuRasterBackingStore(QWindow *window);
~QHaikuRasterBackingStore();
- QPaintDevice *paintDevice() Q_DECL_OVERRIDE;
- void flush(QWindow *window, const QRegion &region, const QPoint &offset) Q_DECL_OVERRIDE;
- void resize(const QSize &size, const QRegion &staticContents) Q_DECL_OVERRIDE;
+ QPaintDevice *paintDevice() override;
+ void flush(QWindow *window, const QRegion &region, const QPoint &offset) override;
+ void resize(const QSize &size, const QRegion &staticContents) override;
private:
BBitmap *m_bitmap;
diff --git a/src/plugins/platforms/haiku/qhaikurasterwindow.cpp b/src/plugins/platforms/haiku/qhaikurasterwindow.cpp
index 9834b7cbc7..7e57e67bab 100644
--- a/src/plugins/platforms/haiku/qhaikurasterwindow.cpp
+++ b/src/plugins/platforms/haiku/qhaikurasterwindow.cpp
@@ -211,7 +211,7 @@ void HaikuViewProxy::handleKeyEvent(QEvent::Type type, BMessage *message)
{
int32 key = 0;
uint32 code = 0;
- const char *bytes = Q_NULLPTR;
+ const char *bytes = nullptr;
QString text;
if (message) {
@@ -265,7 +265,7 @@ QHaikuRasterWindow::~QHaikuRasterWindow()
m_window->UnlockLooper();
delete m_view;
- m_view = Q_NULLPTR;
+ m_view = nullptr;
}
BView* QHaikuRasterWindow::nativeViewHandle() const
diff --git a/src/plugins/platforms/haiku/qhaikurasterwindow.h b/src/plugins/platforms/haiku/qhaikurasterwindow.h
index ae57e0f0e4..24b13aa122 100644
--- a/src/plugins/platforms/haiku/qhaikurasterwindow.h
+++ b/src/plugins/platforms/haiku/qhaikurasterwindow.h
@@ -51,15 +51,15 @@ class HaikuViewProxy : public QObject, public BView
Q_OBJECT
public:
- explicit HaikuViewProxy(BWindow *window, QObject *parent = Q_NULLPTR);
+ explicit HaikuViewProxy(BWindow *window, QObject *parent = nullptr);
- void MessageReceived(BMessage *message) Q_DECL_OVERRIDE;
- void Draw(BRect updateRect) Q_DECL_OVERRIDE;
- void MouseDown(BPoint pos) Q_DECL_OVERRIDE;
- void MouseUp(BPoint pos) Q_DECL_OVERRIDE;
- void MouseMoved(BPoint pos, uint32 code, const BMessage *dragMessage) Q_DECL_OVERRIDE;
- void KeyDown(const char *bytes, int32 numBytes) Q_DECL_OVERRIDE;
- void KeyUp(const char *bytes, int32 numBytes) Q_DECL_OVERRIDE;
+ void MessageReceived(BMessage *message) override;
+ void Draw(BRect updateRect) override;
+ void MouseDown(BPoint pos) override;
+ void MouseUp(BPoint pos) override;
+ void MouseMoved(BPoint pos, uint32 code, const BMessage *dragMessage) override;
+ void KeyDown(const char *bytes, int32 numBytes) override;
+ void KeyUp(const char *bytes, int32 numBytes) override;
Q_SIGNALS:
void mouseEvent(const QPoint &localPosition, const QPoint &globalPosition, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, Qt::MouseEventSource source);
diff --git a/src/plugins/platforms/haiku/qhaikuscreen.cpp b/src/plugins/platforms/haiku/qhaikuscreen.cpp
index 54951a0af5..2c8abba8aa 100644
--- a/src/plugins/platforms/haiku/qhaikuscreen.cpp
+++ b/src/plugins/platforms/haiku/qhaikuscreen.cpp
@@ -58,10 +58,10 @@ QHaikuScreen::QHaikuScreen()
QHaikuScreen::~QHaikuScreen()
{
delete m_cursor;
- m_cursor = Q_NULLPTR;
+ m_cursor = nullptr;
delete m_screen;
- m_screen = Q_NULLPTR;
+ m_screen = nullptr;
}
QPixmap QHaikuScreen::grabWindow(WId winId, int x, int y, int width, int height) const
@@ -69,8 +69,8 @@ QPixmap QHaikuScreen::grabWindow(WId winId, int x, int y, int width, int height)
if (width == 0 || height == 0)
return QPixmap();
- BScreen screen(Q_NULLPTR);
- BBitmap *bitmap = Q_NULLPTR;
+ BScreen screen(nullptr);
+ BBitmap *bitmap = nullptr;
screen.GetBitmap(&bitmap);
const BRect frame = (winId ? ((BWindow*)winId)->Frame() : screen.Frame());
diff --git a/src/plugins/platforms/haiku/qhaikuscreen.h b/src/plugins/platforms/haiku/qhaikuscreen.h
index 49fa3f0a60..98de6fdd03 100644
--- a/src/plugins/platforms/haiku/qhaikuscreen.h
+++ b/src/plugins/platforms/haiku/qhaikuscreen.h
@@ -53,13 +53,13 @@ public:
QHaikuScreen();
~QHaikuScreen();
- QPixmap grabWindow(WId window, int x, int y, int width, int height) const Q_DECL_OVERRIDE;
+ QPixmap grabWindow(WId window, int x, int y, int width, int height) const override;
- QRect geometry() const Q_DECL_OVERRIDE;
- int depth() const Q_DECL_OVERRIDE;
- QImage::Format format() const Q_DECL_OVERRIDE;
+ QRect geometry() const override;
+ int depth() const override;
+ QImage::Format format() const override;
- QPlatformCursor *cursor() const Q_DECL_OVERRIDE;
+ QPlatformCursor *cursor() const override;
private:
BScreen *m_screen;
diff --git a/src/plugins/platforms/haiku/qhaikuservices.h b/src/plugins/platforms/haiku/qhaikuservices.h
index a210eb8b61..59ff6395fb 100644
--- a/src/plugins/platforms/haiku/qhaikuservices.h
+++ b/src/plugins/platforms/haiku/qhaikuservices.h
@@ -47,10 +47,10 @@ QT_BEGIN_NAMESPACE
class QHaikuServices : public QPlatformServices
{
public:
- bool openUrl(const QUrl &url) Q_DECL_OVERRIDE;
- bool openDocument(const QUrl &url) Q_DECL_OVERRIDE;
+ bool openUrl(const QUrl &url) override;
+ bool openDocument(const QUrl &url) override;
- QByteArray desktopEnvironment() const Q_DECL_OVERRIDE;
+ QByteArray desktopEnvironment() const override;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/haiku/qhaikuwindow.cpp b/src/plugins/platforms/haiku/qhaikuwindow.cpp
index 4634012eda..f8fdf3f92d 100644
--- a/src/plugins/platforms/haiku/qhaikuwindow.cpp
+++ b/src/plugins/platforms/haiku/qhaikuwindow.cpp
@@ -118,12 +118,12 @@ void HaikuWindowProxy::zoomByQt()
QHaikuWindow::QHaikuWindow(QWindow *window)
: QPlatformWindow(window)
- , m_window(Q_NULLPTR)
+ , m_window(nullptr)
, m_windowState(Qt::WindowNoState)
{
const QRect rect = initialGeometry(window, window->geometry(), DefaultWindowWidth, DefaultWindowHeight);
- HaikuWindowProxy *haikuWindow = new HaikuWindowProxy(window, rect, Q_NULLPTR);
+ HaikuWindowProxy *haikuWindow = new HaikuWindowProxy(window, rect, nullptr);
connect(haikuWindow, SIGNAL(moved(QPoint)), SLOT(haikuWindowMoved(QPoint)));
connect(haikuWindow, SIGNAL(resized(QSize,bool)), SLOT(haikuWindowResized(QSize,bool)));
connect(haikuWindow, SIGNAL(windowActivated(bool)), SLOT(haikuWindowActivated(bool)));
@@ -145,7 +145,7 @@ QHaikuWindow::~QHaikuWindow()
m_window->LockLooper();
m_window->Quit();
- m_window = Q_NULLPTR;
+ m_window = nullptr;
}
void QHaikuWindow::setGeometry(const QRect &rect)
@@ -205,26 +205,23 @@ void QHaikuWindow::requestActivateWindow()
m_window->Activate(true);
}
-void QHaikuWindow::setWindowState(Qt::WindowState state)
+void QHaikuWindow::setWindowState(Qt::WindowStates state)
{
if (m_windowState == state)
return;
- const Qt::WindowState oldState = m_windowState;
+ const Qt::WindowStates oldState = m_windowState;
m_windowState = state;
- if (m_windowState == Qt::WindowMaximized) {
- m_window->zoomByQt();
- } else if (m_windowState == Qt::WindowMinimized) {
+ if (m_windowState & Qt::WindowMinimized)
m_window->Minimize(true);
- } else if (m_windowState == Qt::WindowNoState) {
- if (oldState == Qt::WindowMaximized)
- m_window->zoomByQt(); // undo zoom
-
- if (oldState == Qt::WindowMinimized)
- m_window->Minimize(false); // undo minimize
- }
+ else if (m_windowState & Qt::WindowMaximized)
+ m_window->zoomByQt();
+ else if (oldState & Qt::WindowMinimized)
+ m_window->Minimize(false); // undo minimize
+ else if (oldState & Qt::WindowMaximized)
+ m_window->zoomByQt(); // undo zoom
}
void QHaikuWindow::setWindowFlags(Qt::WindowFlags flags)
@@ -312,7 +309,6 @@ void QHaikuWindow::haikuWindowMoved(const QPoint &pos)
{
const QRect newGeometry(pos, geometry().size());
- QPlatformWindow::setGeometry(newGeometry);
QWindowSystemInterface::handleGeometryChange(window(), newGeometry);
QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), newGeometry.size()));
}
@@ -321,7 +317,6 @@ void QHaikuWindow::haikuWindowResized(const QSize &size, bool zoomInProgress)
{
const QRect newGeometry(geometry().topLeft(), size);
- QPlatformWindow::setGeometry(newGeometry);
QWindowSystemInterface::handleGeometryChange(window(), newGeometry);
QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), newGeometry.size()));
@@ -335,7 +330,7 @@ void QHaikuWindow::haikuWindowResized(const QSize &size, bool zoomInProgress)
void QHaikuWindow::haikuWindowActivated(bool activated)
{
- QWindowSystemInterface::handleWindowActivated(activated ? window() : Q_NULLPTR);
+ QWindowSystemInterface::handleWindowActivated(activated ? window() : nullptr);
}
void QHaikuWindow::haikuWindowMinimized(bool minimize)
diff --git a/src/plugins/platforms/haiku/qhaikuwindow.h b/src/plugins/platforms/haiku/qhaikuwindow.h
index 75403fb421..bb57742087 100644
--- a/src/plugins/platforms/haiku/qhaikuwindow.h
+++ b/src/plugins/platforms/haiku/qhaikuwindow.h
@@ -51,14 +51,14 @@ class HaikuWindowProxy : public QObject, public BWindow
Q_OBJECT
public:
- explicit HaikuWindowProxy(QWindow *window, const QRect &rect, QObject *parent = Q_NULLPTR);
+ explicit HaikuWindowProxy(QWindow *window, const QRect &rect, QObject *parent = nullptr);
- void FrameMoved(BPoint pos) Q_DECL_OVERRIDE;
- void FrameResized(float width, float height) Q_DECL_OVERRIDE;
- void WindowActivated(bool activated) Q_DECL_OVERRIDE;
- void Minimize(bool minimize) Q_DECL_OVERRIDE;
- void Zoom(BPoint pos, float width, float height) Q_DECL_OVERRIDE;
- bool QuitRequested() Q_DECL_OVERRIDE;
+ void FrameMoved(BPoint pos) override;
+ void FrameResized(float width, float height) override;
+ void WindowActivated(bool activated) override;
+ void Minimize(bool minimize) override;
+ void Zoom(BPoint pos, float width, float height) override;
+ bool QuitRequested() override;
void zoomByQt();
@@ -83,23 +83,23 @@ public:
explicit QHaikuWindow(QWindow *window);
virtual ~QHaikuWindow();
- void setGeometry(const QRect &rect) Q_DECL_OVERRIDE;
- QMargins frameMargins() const Q_DECL_OVERRIDE;
- void setVisible(bool visible) Q_DECL_OVERRIDE;
+ void setGeometry(const QRect &rect) override;
+ QMargins frameMargins() const override;
+ void setVisible(bool visible) override;
- bool isExposed() const Q_DECL_OVERRIDE;
- bool isActive() const Q_DECL_OVERRIDE;
+ bool isExposed() const override;
+ bool isActive() const override;
- WId winId() const Q_DECL_OVERRIDE;
+ WId winId() const override;
BWindow* nativeHandle() const;
- void requestActivateWindow() Q_DECL_OVERRIDE;
- void setWindowState(Qt::WindowState state) Q_DECL_OVERRIDE;
- void setWindowFlags(Qt::WindowFlags flags) Q_DECL_OVERRIDE;
+ void requestActivateWindow() override;
+ void setWindowState(Qt::WindowStates state) override;
+ void setWindowFlags(Qt::WindowFlags flags) override;
- void setWindowTitle(const QString &title) Q_DECL_OVERRIDE;
+ void setWindowTitle(const QString &title) override;
- void propagateSizeHints() Q_DECL_OVERRIDE;
+ void propagateSizeHints() override;
protected:
HaikuWindowProxy *m_window;
@@ -120,7 +120,7 @@ private Q_SLOTS:
void haikuDrawRequest(const QRect &rect);
private:
- Qt::WindowState m_windowState;
+ Qt::WindowStates m_windowState;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/integrity/main.cpp b/src/plugins/platforms/integrity/main.cpp
index f75e227335..6313aa47e5 100644
--- a/src/plugins/platforms/integrity/main.cpp
+++ b/src/plugins/platforms/integrity/main.cpp
@@ -47,7 +47,7 @@ class QIntegrityFbIntegrationPlugin : public QPlatformIntegrationPlugin
Q_OBJECT
Q_PLUGIN_METADATA(IID QPlatformIntegrationFactoryInterface_iid FILE "integrity.json")
public:
- QPlatformIntegration *create(const QString&, const QStringList&) Q_DECL_OVERRIDE;
+ QPlatformIntegration *create(const QString&, const QStringList&) override;
};
QPlatformIntegration* QIntegrityFbIntegrationPlugin::create(const QString& system, const QStringList& paramList)
diff --git a/src/plugins/platforms/integrity/qintegrityfbintegration.h b/src/plugins/platforms/integrity/qintegrityfbintegration.h
index a954dc2356..d0cd5417ab 100644
--- a/src/plugins/platforms/integrity/qintegrityfbintegration.h
+++ b/src/plugins/platforms/integrity/qintegrityfbintegration.h
@@ -54,19 +54,19 @@ public:
QIntegrityFbIntegration(const QStringList &paramList);
~QIntegrityFbIntegration();
- void initialize() Q_DECL_OVERRIDE;
- bool hasCapability(QPlatformIntegration::Capability cap) const Q_DECL_OVERRIDE;
+ void initialize() override;
+ bool hasCapability(QPlatformIntegration::Capability cap) const override;
- QPlatformWindow *createPlatformWindow(QWindow *window) const Q_DECL_OVERRIDE;
- QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const Q_DECL_OVERRIDE;
+ QPlatformWindow *createPlatformWindow(QWindow *window) const override;
+ QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const override;
- QAbstractEventDispatcher *createEventDispatcher() const Q_DECL_OVERRIDE;
+ QAbstractEventDispatcher *createEventDispatcher() const override;
- QPlatformFontDatabase *fontDatabase() const Q_DECL_OVERRIDE;
- QPlatformServices *services() const Q_DECL_OVERRIDE;
- QPlatformInputContext *inputContext() const Q_DECL_OVERRIDE { return m_inputContext; }
+ QPlatformFontDatabase *fontDatabase() const override;
+ QPlatformServices *services() const override;
+ QPlatformInputContext *inputContext() const override { return m_inputContext; }
- QPlatformNativeInterface *nativeInterface() const Q_DECL_OVERRIDE;
+ QPlatformNativeInterface *nativeInterface() const override;
QList<QPlatformScreen *> screens() const;
diff --git a/src/plugins/platforms/integrity/qintegrityfbscreen.cpp b/src/plugins/platforms/integrity/qintegrityfbscreen.cpp
index 3979937955..d64b96ca4c 100644
--- a/src/plugins/platforms/integrity/qintegrityfbscreen.cpp
+++ b/src/plugins/platforms/integrity/qintegrityfbscreen.cpp
@@ -204,15 +204,14 @@ QRegion QIntegrityFbScreen::doRedraw()
if (!mBlitter)
mBlitter = new QPainter(&mFbScreenImage);
- QVector<QRect> rects = touched.rects();
- for (int i = 0; i < rects.size(); i++) {
+ for (QRect rect : touched) {
FBRect fbrect = {
- (uint32_t)rects[i].left(),
- (uint32_t)rects[i].top(),
- (uint32_t)rects[i].width(),
- (uint32_t)rects[i].height()
+ (uint32_t)rect.left(),
+ (uint32_t)rect.top(),
+ (uint32_t)rect.width(),
+ (uint32_t)rect.height()
};
- mBlitter->drawImage(rects[i], mScreenImage, rects[i]);
+ mBlitter->drawImage(rect, mScreenImage, rect);
gh_FB_expose(mFbh, &fbrect, NULL);
}
return touched;
diff --git a/src/plugins/platforms/integrity/qintegrityfbscreen.h b/src/plugins/platforms/integrity/qintegrityfbscreen.h
index 6bc78913c9..c38b4f073d 100644
--- a/src/plugins/platforms/integrity/qintegrityfbscreen.h
+++ b/src/plugins/platforms/integrity/qintegrityfbscreen.h
@@ -57,9 +57,9 @@ public:
bool initialize();
- QPixmap grabWindow(WId wid, int x, int y, int width, int height) const Q_DECL_OVERRIDE;
+ QPixmap grabWindow(WId wid, int x, int y, int width, int height) const override;
- QRegion doRedraw() Q_DECL_OVERRIDE;
+ QRegion doRedraw() override;
private:
QStringList mArgs;
diff --git a/src/plugins/platforms/integrity/qintegrityhidmanager.cpp b/src/plugins/platforms/integrity/qintegrityhidmanager.cpp
index 49583735f5..3570b90134 100644
--- a/src/plugins/platforms/integrity/qintegrityhidmanager.cpp
+++ b/src/plugins/platforms/integrity/qintegrityhidmanager.cpp
@@ -73,8 +73,8 @@ public:
{
CheckSuccess(gh_hid_close(handle));
};
- void process_event(void) Q_DECL_OVERRIDE;
- void async_wait(void) Q_DECL_OVERRIDE;
+ void process_event(void) override;
+ void async_wait(void) override;
HIDDriver *get_driver(void) { return driver; };
HIDHandle get_handle(void) { return handle; };
private:
@@ -92,8 +92,8 @@ public:
{
qDeleteAll(devices);
};
- void process_event(void) Q_DECL_OVERRIDE;
- void async_wait(void) Q_DECL_OVERRIDE;
+ void process_event(void) override;
+ void async_wait(void) override;
void find_devices(void);
private:
QHash<Value, HIDDeviceHandler *> devices;
diff --git a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.mm b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.mm
index bea2897240..54152aebf7 100644
--- a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.mm
+++ b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.mm
@@ -302,7 +302,7 @@ public:
g_iteratorCurrentUrl.setLocalData(QString());
}
- QString next() Q_DECL_OVERRIDE
+ QString next() override
{
// Cache the URL that we are about to return, since QDir will immediately create a
// new file engine on the file and ask if it exists. Unless we do this, we end up
@@ -314,17 +314,17 @@ public:
return url;
}
- bool hasNext() const Q_DECL_OVERRIDE
+ bool hasNext() const override
{
return m_enumerator->hasNext();
}
- QString currentFileName() const Q_DECL_OVERRIDE
+ QString currentFileName() const override
{
return g_iteratorCurrentUrl.localData();
}
- QFileInfo currentFileInfo() const Q_DECL_OVERRIDE
+ QFileInfo currentFileInfo() const override
{
return QFileInfo(currentFileName());
}
diff --git a/src/plugins/platforms/ios/qiosbackingstore.h b/src/plugins/platforms/ios/qiosbackingstore.h
index 3954347471..38006ba90b 100644
--- a/src/plugins/platforms/ios/qiosbackingstore.h
+++ b/src/plugins/platforms/ios/qiosbackingstore.h
@@ -54,10 +54,7 @@ public:
QIOSBackingStore(QWindow *window);
~QIOSBackingStore();
- void flush(QWindow *window, const QRegion &region, const QPoint &offset) Q_DECL_OVERRIDE;
-
-private:
- QOpenGLContext *m_context;
+ void flush(QWindow *window, const QRegion &region, const QPoint &offset) override;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/ios/qiosbackingstore.mm b/src/plugins/platforms/ios/qiosbackingstore.mm
index 74229684e3..db4dd81b2e 100644
--- a/src/plugins/platforms/ios/qiosbackingstore.mm
+++ b/src/plugins/platforms/ios/qiosbackingstore.mm
@@ -55,7 +55,6 @@ QT_BEGIN_NAMESPACE
*/
QIOSBackingStore::QIOSBackingStore(QWindow *window)
: QRasterBackingStore(window)
- , m_context(new QOpenGLContext)
{
// We use the surface both for raster operations and for GL drawing (when
// we blit the raster image), so the type needs to cover both use cases.
@@ -64,22 +63,10 @@ QIOSBackingStore::QIOSBackingStore(QWindow *window)
Q_ASSERT_X(window->surfaceType() != QSurface::OpenGLSurface, "QIOSBackingStore",
"QBackingStore on iOS can only be used with raster-enabled surfaces.");
-
- m_context->setFormat(window->requestedFormat());
- m_context->setScreen(window->screen());
- Q_ASSERT(QOpenGLContext::globalShareContext());
- m_context->setShareContext(QOpenGLContext::globalShareContext());
- m_context->create();
}
QIOSBackingStore::~QIOSBackingStore()
{
- // We're using composeAndFlush from QPlatformBackingStore, which
- // need to clean up any textures in its destructor, so make the
- // context current and keep it alive until QPlatformBackingStore
- // has cleaned up everything.
- m_context->makeCurrent(window());
- m_context->deleteLater();
}
void QIOSBackingStore::flush(QWindow *window, const QRegion &region, const QPoint &offset)
@@ -98,7 +85,7 @@ void QIOSBackingStore::flush(QWindow *window, const QRegion &region, const QPoin
}
static QPlatformTextureList emptyTextureList;
- composeAndFlush(window, region, offset, &emptyTextureList, m_context, false);
+ composeAndFlush(window, region, offset, &emptyTextureList, false);
}
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/ios/qiosclipboard.h b/src/plugins/platforms/ios/qiosclipboard.h
index f3ccfcace0..3fe9b29b71 100644
--- a/src/plugins/platforms/ios/qiosclipboard.h
+++ b/src/plugins/platforms/ios/qiosclipboard.h
@@ -58,10 +58,10 @@ public:
QIOSClipboard();
~QIOSClipboard();
- QMimeData *mimeData(QClipboard::Mode mode = QClipboard::Clipboard) Q_DECL_OVERRIDE;
- void setMimeData(QMimeData *mimeData, QClipboard::Mode mode = QClipboard::Clipboard) Q_DECL_OVERRIDE;
- bool supportsMode(QClipboard::Mode mode) const Q_DECL_OVERRIDE;
- bool ownsMode(QClipboard::Mode mode) const Q_DECL_OVERRIDE;
+ QMimeData *mimeData(QClipboard::Mode mode = QClipboard::Clipboard) override;
+ void setMimeData(QMimeData *mimeData, QClipboard::Mode mode = QClipboard::Clipboard) override;
+ bool supportsMode(QClipboard::Mode mode) const override;
+ bool ownsMode(QClipboard::Mode mode) const override;
private:
QUIClipboard *m_clipboard;
diff --git a/src/plugins/platforms/ios/qiosclipboard.mm b/src/plugins/platforms/ios/qiosclipboard.mm
index 6a585c9052..9a975eadc9 100644
--- a/src/plugins/platforms/ios/qiosclipboard.mm
+++ b/src/plugins/platforms/ios/qiosclipboard.mm
@@ -138,8 +138,8 @@ public:
QIOSMimeData(QClipboard::Mode mode) : QMimeData(), m_mode(mode) { }
~QIOSMimeData() { }
- QStringList formats() const Q_DECL_OVERRIDE;
- QVariant retrieveData(const QString &mimeType, QVariant::Type type) const Q_DECL_OVERRIDE;
+ QStringList formats() const override;
+ QVariant retrieveData(const QString &mimeType, QVariant::Type type) const override;
private:
const QClipboard::Mode m_mode;
@@ -232,7 +232,7 @@ void QIOSClipboard::setMimeData(QMimeData *mimeData, QClipboard::Mode mode)
mimeDataAsVariant = mimeData->imageData();
} else if (mimeData->hasUrls()) {
QVariantList urlList;
- for (const QUrl &url : mimeData->urls())
+ for (QUrl url : mimeData->urls())
urlList << url;
mimeDataAsVariant = QVariant(urlList);
} else {
diff --git a/src/plugins/platforms/ios/qioscontext.h b/src/plugins/platforms/ios/qioscontext.h
index ce50eff1d9..a2595877dc 100644
--- a/src/plugins/platforms/ios/qioscontext.h
+++ b/src/plugins/platforms/ios/qioscontext.h
@@ -57,18 +57,18 @@ public:
QIOSContext(QOpenGLContext *context);
~QIOSContext();
- QSurfaceFormat format() const Q_DECL_OVERRIDE;
+ QSurfaceFormat format() const override;
- void swapBuffers(QPlatformSurface *surface) Q_DECL_OVERRIDE;
+ void swapBuffers(QPlatformSurface *surface) override;
- bool makeCurrent(QPlatformSurface *surface) Q_DECL_OVERRIDE;
- void doneCurrent() Q_DECL_OVERRIDE;
+ bool makeCurrent(QPlatformSurface *surface) override;
+ void doneCurrent() override;
- GLuint defaultFramebufferObject(QPlatformSurface *) const Q_DECL_OVERRIDE;
- QFunctionPointer getProcAddress(const char *procName) Q_DECL_OVERRIDE;
+ GLuint defaultFramebufferObject(QPlatformSurface *) const override;
+ QFunctionPointer getProcAddress(const char *procName) override;
- bool isSharing() const Q_DECL_OVERRIDE;
- bool isValid() const Q_DECL_OVERRIDE;
+ bool isSharing() const override;
+ bool isValid() const override;
private Q_SLOTS:
void windowDestroyed(QObject *object);
diff --git a/src/plugins/platforms/ios/qioseventdispatcher.h b/src/plugins/platforms/ios/qioseventdispatcher.h
index c1442ed1e8..62133b9510 100644
--- a/src/plugins/platforms/ios/qioseventdispatcher.h
+++ b/src/plugins/platforms/ios/qioseventdispatcher.h
@@ -51,8 +51,8 @@ class QIOSEventDispatcher : public QEventDispatcherCoreFoundation
public:
explicit QIOSEventDispatcher(QObject *parent = 0);
- bool processEvents(QEventLoop::ProcessEventsFlags flags) Q_DECL_OVERRIDE;
- bool processPostedEvents() Q_DECL_OVERRIDE;
+ bool processEvents(QEventLoop::ProcessEventsFlags flags) override;
+ bool processPostedEvents() override;
void handleRunLoopExit(CFRunLoopActivity activity);
diff --git a/src/plugins/platforms/ios/qioseventdispatcher.mm b/src/plugins/platforms/ios/qioseventdispatcher.mm
index 7194d5bed1..a6f6a7aac9 100644
--- a/src/plugins/platforms/ios/qioseventdispatcher.mm
+++ b/src/plugins/platforms/ios/qioseventdispatcher.mm
@@ -228,7 +228,7 @@ extern "C" int qt_main_wrapper(int argc, char *argv[])
}
}
- qEventDispatcherDebug() << "Running UIApplicationMain"; qIndent();
+ qCDebug(lcEventDispatcher) << "Running UIApplicationMain";
return UIApplicationMain(argc, argv, nil, NSStringFromClass([QIOSApplicationDelegate class]));
}
}
@@ -263,7 +263,7 @@ static void __attribute__((noinline, noreturn)) user_main_trampoline()
int exitCode = main(argc, argv);
delete[] argv;
- qEventDispatcherDebug() << "Returned from main with exit code " << exitCode;
+ qCDebug(lcEventDispatcher) << "Returned from main with exit code " << exitCode;
if (Q_UNLIKELY(debugStackUsage))
userMainStack.printUsage();
@@ -322,7 +322,7 @@ static bool rootLevelRunLoopIntegration()
+ (void)applicationDidFinishLaunching:(NSNotification *)notification
{
- qCDebug(lcQpaApplication) << "Application launched with options" << notification.userInfo;
+ qCDebug(lcEventDispatcher) << "Application launched with options" << notification.userInfo;
if (!isQtApplication())
return;
@@ -331,7 +331,7 @@ static bool rootLevelRunLoopIntegration()
// We schedule the main-redirection for the next run-loop pass, so that we
// can return from this function and let UIApplicationMain finish its job.
// This results in running Qt's application eventloop as a nested runloop.
- qCDebug(lcQpaApplication) << "Scheduling main() on next run-loop pass";
+ qCDebug(lcEventDispatcher) << "Scheduling main() on next run-loop pass";
CFRunLoopTimerRef userMainTimer = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault,
CFAbsoluteTimeGetCurrent(), 0, 0, 0, ^(CFRunLoopTimerRef) { user_main_trampoline(); });
CFRunLoopAddTimer(CFRunLoopGetMain(), userMainTimer, kCFRunLoopCommonModes);
@@ -339,10 +339,10 @@ static bool rootLevelRunLoopIntegration()
return;
}
+
switch (setjmp(processEventEnterJumpPoint)) {
case kJumpPointSetSuccessfully:
- qCDebug(lcQpaApplication) << "Running main() on separate stack"; qIndent();
-
+ qCDebug(lcEventDispatcher) << "Running main() on separate stack";
// Redirect the stack pointer to the start of the reserved stack. This ensures
// that when we longjmp out of the event dispatcher and continue execution, the
// 'Qt main' call-stack will not be smashed, as it lives in a part of the stack
@@ -360,7 +360,7 @@ static bool rootLevelRunLoopIntegration()
case kJumpedFromEventDispatcherProcessEvents:
// We've returned from the longjmp in the event dispatcher,
// and the stack has been restored to its old self.
- qUnIndent(); qCDebug(lcQpaApplication) << "Returned from processEvents";
+ qCDebug(lcEventDispatcher) << "↳ Jumped from processEvents due to exec";
if (Q_UNLIKELY(debugStackUsage))
userMainStack.printUsage();
@@ -374,7 +374,7 @@ static bool rootLevelRunLoopIntegration()
// We treat applicationWillTerminate as SIGTERM, even if it can't be ignored,
// and follow the bash convention of encoding the signal number in the upper
// four bits of the exit code (exit(3) will only pass on the lower 8 bits).
-static const char kApplicationWillTerminateExitCode = SIGTERM | 0x80;
+static const char kApplicationWillTerminateExitCode = char(SIGTERM | 0x80);
+ (void)applicationWillTerminate
{
@@ -396,18 +396,18 @@ static const char kApplicationWillTerminateExitCode = SIGTERM | 0x80;
applicationAboutToTerminate = true;
switch (setjmp(applicationWillTerminateJumpPoint)) {
case kJumpPointSetSuccessfully:
- qEventDispatcherDebug() << "Exiting qApp with SIGTERM exit code"; qIndent();
+ qCDebug(lcEventDispatcher) << "Exiting qApp with SIGTERM exit code";
qApp->exit(kApplicationWillTerminateExitCode);
// The runloop will not exit when the application is about to terminate,
// so we'll never see the exit activity and have a chance to return from
// QEventLoop::exec(). We initiate the return manually as a workaround.
- qEventDispatcherDebug() << "Manually triggering return from event loop exec";
+ qCDebug(lcEventDispatcher) << "Manually triggering return from event loop exec";
static_cast<QIOSEventDispatcher *>(qApp->eventDispatcher())->interruptEventLoopExec();
break;
case kJumpedFromUserMainTrampoline:
// The user's main has returned, so we're ready to let iOS terminate the application
- qUnIndent(); qEventDispatcherDebug() << "kJumpedFromUserMainTrampoline, allowing iOS to terminate";
+ qCDebug(lcEventDispatcher) << "kJumpedFromUserMainTrampoline, allowing iOS to terminate";
break;
default:
qFatal("Unexpected jump result in event loop integration");
@@ -434,13 +434,15 @@ bool __attribute__((returns_twice)) QIOSEventDispatcher::processEvents(QEventLoo
return QEventDispatcherCoreFoundation::processEvents(flags);
if (applicationAboutToTerminate) {
- qEventDispatcherDebug() << "Detected QEventLoop exec after application termination";
+ qCDebug(lcEventDispatcher) << "Detected QEventLoop exec after application termination";
// Re-issue exit, and return immediately
qApp->exit(kApplicationWillTerminateExitCode);
return false;
}
if (!m_processEventLevel && (flags & QEventLoop::EventLoopExec)) {
+ qCDebug(lcEventDispatcher) << "Processing events with flags" << flags;
+
++m_processEventLevel;
m_runLoopExitObserver.addToMode(kCFRunLoopCommonModes);
@@ -449,7 +451,7 @@ bool __attribute__((returns_twice)) QIOSEventDispatcher::processEvents(QEventLoo
// is asked to exit, so that we can return from QEventLoop::exec().
switch (setjmp(processEventExitJumpPoint)) {
case kJumpPointSetSuccessfully:
- qEventDispatcherDebug() << "QEventLoop exec detected, jumping back to native runloop";
+ qCDebug(lcEventDispatcher) << "QEventLoop exec detected, jumping back to system runloop ↵";
longjmp(processEventEnterJumpPoint, kJumpedFromEventDispatcherProcessEvents);
break;
case kJumpedFromEventLoopExecInterrupt:
@@ -457,7 +459,7 @@ bool __attribute__((returns_twice)) QIOSEventDispatcher::processEvents(QEventLoo
// signal), and we jumped back though processEventExitJumpPoint. We return from processEvents,
// which will emit aboutToQuit if it's QApplication's event loop, and then return to the user's
// main, which can do whatever it wants, including calling exec() on the application again.
- qEventDispatcherDebug() << "kJumpedFromEventLoopExecInterrupt, returning with eventsProcessed = true";
+ qCDebug(lcEventDispatcher) << "⇢ System runloop exited, returning with eventsProcessed = true";
return true;
default:
qFatal("Unexpected jump result in event loop integration");
@@ -485,9 +487,8 @@ bool QIOSEventDispatcher::processPostedEvents()
if (!QEventDispatcherCoreFoundation::processPostedEvents())
return false;
- qEventDispatcherDebug() << "Sending window system events for " << m_processEvents.flags; qIndent();
+ qCDebug(lcEventDispatcher) << "Sending window system events for" << m_processEvents.flags;
QWindowSystemInterface::sendWindowSystemEvents(m_processEvents.flags);
- qUnIndent();
return true;
}
@@ -497,10 +498,8 @@ void QIOSEventDispatcher::handleRunLoopExit(CFRunLoopActivity activity)
Q_UNUSED(activity);
Q_ASSERT(activity == kCFRunLoopExit);
- if (m_processEventLevel == 1 && !currentEventLoop()->isRunning()) {
- qEventDispatcherDebug() << "Root runloop level exited";
+ if (m_processEventLevel == 1 && !currentEventLoop()->isRunning())
interruptEventLoopExec();
- }
}
void QIOSEventDispatcher::interruptEventLoopExec()
@@ -516,12 +515,12 @@ void QIOSEventDispatcher::interruptEventLoopExec()
// processEvents, instead of back in didFinishLaunchingWithOptions.
switch (setjmp(processEventEnterJumpPoint)) {
case kJumpPointSetSuccessfully:
- qEventDispatcherDebug() << "Jumping back to processEvents";
+ qCDebug(lcEventDispatcher) << "Jumping into processEvents due to system runloop exit ⇢";
longjmp(processEventExitJumpPoint, kJumpedFromEventLoopExecInterrupt);
break;
case kJumpedFromEventDispatcherProcessEvents:
// QEventLoop was re-executed
- qEventDispatcherDebug() << "kJumpedFromEventDispatcherProcessEvents";
+ qCDebug(lcEventDispatcher) << "↳ Jumped from processEvents due to re-exec";
break;
default:
qFatal("Unexpected jump result in event loop integration");
diff --git a/src/plugins/platforms/ios/qiosfiledialog.h b/src/plugins/platforms/ios/qiosfiledialog.h
index 0b56bd20bf..5cb1b45e20 100644
--- a/src/plugins/platforms/ios/qiosfiledialog.h
+++ b/src/plugins/platforms/ios/qiosfiledialog.h
@@ -53,17 +53,17 @@ public:
QIOSFileDialog();
~QIOSFileDialog();
- void exec() Q_DECL_OVERRIDE;
- bool defaultNameFilterDisables() const Q_DECL_OVERRIDE { return false; }
- bool show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent) Q_DECL_OVERRIDE;
- void hide() Q_DECL_OVERRIDE;
- void setDirectory(const QUrl &) Q_DECL_OVERRIDE {}
- QUrl directory() const Q_DECL_OVERRIDE { return QUrl(); }
- void selectFile(const QUrl &) Q_DECL_OVERRIDE {}
- QList<QUrl> selectedFiles() const Q_DECL_OVERRIDE;
- void setFilter() Q_DECL_OVERRIDE {}
- void selectNameFilter(const QString &) Q_DECL_OVERRIDE {}
- QString selectedNameFilter() const Q_DECL_OVERRIDE { return QString(); }
+ void exec() override;
+ bool defaultNameFilterDisables() const override { return false; }
+ bool show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent) override;
+ void hide() override;
+ void setDirectory(const QUrl &) override {}
+ QUrl directory() const override { return QUrl(); }
+ void selectFile(const QUrl &) override {}
+ QList<QUrl> selectedFiles() const override;
+ void setFilter() override {}
+ void selectNameFilter(const QString &) override {}
+ QString selectedNameFilter() const override { return QString(); }
void selectedFilesChanged(QList<QUrl> selection);
diff --git a/src/plugins/platforms/ios/qiosfiledialog.mm b/src/plugins/platforms/ios/qiosfiledialog.mm
index c5722d33f8..5987bc1540 100644
--- a/src/plugins/platforms/ios/qiosfiledialog.mm
+++ b/src/plugins/platforms/ios/qiosfiledialog.mm
@@ -48,7 +48,7 @@
#include "qiosoptionalplugininterface.h"
QIOSFileDialog::QIOSFileDialog()
- : m_viewController(Q_NULLPTR)
+ : m_viewController(nullptr)
{
}
@@ -112,7 +112,7 @@ void QIOSFileDialog::hide()
[m_viewController dismissViewControllerAnimated:YES completion:nil];
[m_viewController release];
- m_viewController = Q_NULLPTR;
+ m_viewController = nullptr;
m_eventLoop.exit();
}
diff --git a/src/plugins/platforms/ios/qiosinputcontext.h b/src/plugins/platforms/ios/qiosinputcontext.h
index 966d1a7e80..255cf8bca9 100644
--- a/src/plugins/platforms/ios/qiosinputcontext.h
+++ b/src/plugins/platforms/ios/qiosinputcontext.h
@@ -88,24 +88,24 @@ public:
QIOSInputContext();
~QIOSInputContext();
- bool isValid() const Q_DECL_OVERRIDE { return true; }
+ bool isValid() const override { return true; }
- void showInputPanel() Q_DECL_OVERRIDE;
- void hideInputPanel() Q_DECL_OVERRIDE;
+ void showInputPanel() override;
+ void hideInputPanel() override;
- bool isInputPanelVisible() const Q_DECL_OVERRIDE;
- bool isAnimating() const Q_DECL_OVERRIDE;
- QRectF keyboardRect() const Q_DECL_OVERRIDE;
+ bool isInputPanelVisible() const override;
+ bool isAnimating() const override;
+ QRectF keyboardRect() const override;
- void update(Qt::InputMethodQueries) Q_DECL_OVERRIDE;
- void reset() Q_DECL_OVERRIDE;
- void commit() Q_DECL_OVERRIDE;
+ void update(Qt::InputMethodQueries) override;
+ void reset() override;
+ void commit() override;
- QLocale locale() const Q_DECL_OVERRIDE;
+ QLocale locale() const override;
void clearCurrentFocusObject();
- void setFocusObject(QObject *object) Q_DECL_OVERRIDE;
+ void setFocusObject(QObject *object) override;
void focusWindowChanged(QWindow *focusWindow);
void scrollToCursor();
diff --git a/src/plugins/platforms/ios/qiosintegration.h b/src/plugins/platforms/ios/qiosintegration.h
index 1d53734096..6be8855020 100644
--- a/src/plugins/platforms/ios/qiosintegration.h
+++ b/src/plugins/platforms/ios/qiosintegration.h
@@ -58,54 +58,49 @@ class QIOSServices;
class QIOSIntegration : public QPlatformNativeInterface, public QPlatformIntegration
{
Q_OBJECT
- Q_PROPERTY(bool debugWindowManagement READ debugWindowManagement WRITE setDebugWindowManagement);
-
public:
QIOSIntegration();
~QIOSIntegration();
- bool hasCapability(Capability cap) const Q_DECL_OVERRIDE;
+ bool hasCapability(Capability cap) const override;
- QPlatformWindow *createPlatformWindow(QWindow *window) const Q_DECL_OVERRIDE;
- QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const Q_DECL_OVERRIDE;
+ QPlatformWindow *createPlatformWindow(QWindow *window) const override;
+ QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const override;
- QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const Q_DECL_OVERRIDE;
- QPlatformOffscreenSurface *createPlatformOffscreenSurface(QOffscreenSurface *surface) const Q_DECL_OVERRIDE;
+ QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const override;
+ QPlatformOffscreenSurface *createPlatformOffscreenSurface(QOffscreenSurface *surface) const override;
- QPlatformFontDatabase *fontDatabase() const Q_DECL_OVERRIDE;
+ QPlatformFontDatabase *fontDatabase() const override;
#ifndef QT_NO_CLIPBOARD
- QPlatformClipboard *clipboard() const Q_DECL_OVERRIDE;
+ QPlatformClipboard *clipboard() const override;
#endif
- QPlatformInputContext *inputContext() const Q_DECL_OVERRIDE;
- QPlatformServices *services() const Q_DECL_OVERRIDE;
+ QPlatformInputContext *inputContext() const override;
+ QPlatformServices *services() const override;
- QVariant styleHint(StyleHint hint) const Q_DECL_OVERRIDE;
+ QVariant styleHint(StyleHint hint) const override;
- QStringList themeNames() const Q_DECL_OVERRIDE;
- QPlatformTheme *createPlatformTheme(const QString &name) const Q_DECL_OVERRIDE;
+ QStringList themeNames() const override;
+ QPlatformTheme *createPlatformTheme(const QString &name) const override;
- QAbstractEventDispatcher *createEventDispatcher() const Q_DECL_OVERRIDE;
- QPlatformNativeInterface *nativeInterface() const Q_DECL_OVERRIDE;
+ QAbstractEventDispatcher *createEventDispatcher() const override;
+ QPlatformNativeInterface *nativeInterface() const override;
QTouchDevice *touchDevice();
#ifndef QT_NO_ACCESSIBILITY
- QPlatformAccessibility *accessibility() const Q_DECL_OVERRIDE;
+ QPlatformAccessibility *accessibility() const override;
#endif
// Called from Objective-C class QIOSScreenTracker, which can't be friended
void addScreen(QPlatformScreen *screen) { screenAdded(screen); }
void destroyScreen(QPlatformScreen *screen) { QPlatformIntegration::destroyScreen(screen); }
- void beep() const Q_DECL_OVERRIDE;
+ void beep() const override;
static QIOSIntegration *instance();
// -- QPlatformNativeInterface --
- void *nativeResourceForWindow(const QByteArray &resource, QWindow *window) Q_DECL_OVERRIDE;
-
- void setDebugWindowManagement(bool);
- bool debugWindowManagement() const;
+ void *nativeResourceForWindow(const QByteArray &resource, QWindow *window) override;
QFactoryLoader *optionalPlugins() { return m_optionalPlugins; }
@@ -124,8 +119,6 @@ private:
#ifndef Q_OS_TVOS
QIOSTextInputOverlay m_textInputOverlay;
#endif
-
- bool m_debugWindowManagement;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/ios/qiosintegration.mm b/src/plugins/platforms/ios/qiosintegration.mm
index 482f996943..92c1e39d72 100644
--- a/src/plugins/platforms/ios/qiosintegration.mm
+++ b/src/plugins/platforms/ios/qiosintegration.mm
@@ -85,7 +85,6 @@ QIOSIntegration::QIOSIntegration()
, m_platformServices(new QIOSServices)
, m_accessibility(0)
, m_optionalPlugins(new QFactoryLoader(QIosOptionalPluginInterface_iid, QLatin1String("/platforms/darwin")))
- , m_debugWindowManagement(false)
{
if (Q_UNLIKELY(![UIApplication sharedApplication])) {
qFatal("Error: You are creating QApplication before calling UIApplicationMain.\n" \
@@ -94,10 +93,6 @@ QIOSIntegration::QIOSIntegration()
"'applicationDidFinishLaunching' inside your UIApplication delegate.\n");
}
- // The backingstore needs a global share context in order to support composition in
- // QPlatformBackingStore.
- qApp->setAttribute(Qt::AA_ShareOpenGLContexts, true);
-
// Set current directory to app bundle folder
QDir::setCurrent(QString::fromUtf8([[[NSBundle mainBundle] bundlePath] UTF8String]));
@@ -117,12 +112,15 @@ QIOSIntegration::QIOSIntegration()
m_touchDevice = new QTouchDevice;
m_touchDevice->setType(QTouchDevice::TouchScreen);
QTouchDevice::Capabilities touchCapabilities = QTouchDevice::Position | QTouchDevice::NormalizedPosition;
- if (QOperatingSystemVersion::current() >= QOperatingSystemVersion(QOperatingSystemVersion::IOS, 9)) {
+ if (__builtin_available(iOS 9, *)) {
if (mainScreen.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable)
touchCapabilities |= QTouchDevice::Pressure;
}
m_touchDevice->setCapabilities(touchCapabilities);
QWindowSystemInterface::registerTouchDevice(m_touchDevice);
+#if QT_CONFIG(tabletevent)
+ QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(false);
+#endif
QMacInternalPasteboardMime::initializeMimeTypes();
for (int i = 0; i < m_optionalPlugins->metaData().size(); ++i)
@@ -201,12 +199,12 @@ class QIOSOffscreenSurface : public QPlatformOffscreenSurface
public:
QIOSOffscreenSurface(QOffscreenSurface *offscreenSurface) : QPlatformOffscreenSurface(offscreenSurface) {}
- QSurfaceFormat format() const Q_DECL_OVERRIDE
+ QSurfaceFormat format() const override
{
Q_ASSERT(offscreenSurface());
return offscreenSurface()->requestedFormat();
}
- bool isValid() const Q_DECL_OVERRIDE { return true; }
+ bool isValid() const override { return true; }
};
QPlatformOffscreenSurface *QIOSIntegration::createPlatformOffscreenSurface(QOffscreenSurface *surface) const
@@ -322,16 +320,6 @@ void *QIOSIntegration::nativeResourceForWindow(const QByteArray &resource, QWind
return 0;
}
-void QIOSIntegration::setDebugWindowManagement(bool enabled)
-{
- m_debugWindowManagement = enabled;
-}
-
-bool QIOSIntegration::debugWindowManagement() const
-{
- return m_debugWindowManagement;
-}
-
// ---------------------------------------------------------
#include "moc_qiosintegration.cpp"
diff --git a/src/plugins/platforms/ios/qiosmenu.h b/src/plugins/platforms/ios/qiosmenu.h
index b7371a5f49..32022a3bb8 100644
--- a/src/plugins/platforms/ios/qiosmenu.h
+++ b/src/plugins/platforms/ios/qiosmenu.h
@@ -56,25 +56,21 @@ class QIOSMenuItem : public QPlatformMenuItem
public:
QIOSMenuItem();
- void setTag(quintptr tag) Q_DECL_OVERRIDE;
- quintptr tag()const Q_DECL_OVERRIDE;
-
- void setText(const QString &text) Q_DECL_OVERRIDE;
- void setIcon(const QIcon &) Q_DECL_OVERRIDE {}
- void setMenu(QPlatformMenu *) Q_DECL_OVERRIDE;
- void setVisible(bool isVisible) Q_DECL_OVERRIDE;
- void setIsSeparator(bool) Q_DECL_OVERRIDE;
- void setFont(const QFont &) Q_DECL_OVERRIDE {}
- void setRole(MenuRole role) Q_DECL_OVERRIDE;
- void setCheckable(bool) Q_DECL_OVERRIDE {}
- void setChecked(bool) Q_DECL_OVERRIDE {}
+ void setText(const QString &text) override;
+ void setIcon(const QIcon &) override {}
+ void setMenu(QPlatformMenu *) override;
+ void setVisible(bool isVisible) override;
+ void setIsSeparator(bool) override;
+ void setFont(const QFont &) override {}
+ void setRole(MenuRole role) override;
+ void setCheckable(bool) override {}
+ void setChecked(bool) override {}
#ifndef QT_NO_SHORTCUT
- void setShortcut(const QKeySequence&) Q_DECL_OVERRIDE;
+ void setShortcut(const QKeySequence&) override;
#endif
- void setEnabled(bool enabled) Q_DECL_OVERRIDE;
- void setIconSize(int) Q_DECL_OVERRIDE {}
+ void setEnabled(bool enabled) override;
+ void setIconSize(int) override {}
- quintptr m_tag;
bool m_visible;
QString m_text;
MenuRole m_role;
@@ -92,25 +88,22 @@ public:
QIOSMenu();
~QIOSMenu();
- void insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before) Q_DECL_OVERRIDE;
- void removeMenuItem(QPlatformMenuItem *menuItem) Q_DECL_OVERRIDE;
- void syncMenuItem(QPlatformMenuItem *) Q_DECL_OVERRIDE;
- void syncSeparatorsCollapsible(bool) Q_DECL_OVERRIDE {}
+ void insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before) override;
+ void removeMenuItem(QPlatformMenuItem *menuItem) override;
+ void syncMenuItem(QPlatformMenuItem *) override;
+ void syncSeparatorsCollapsible(bool) override {}
- void setTag(quintptr tag) Q_DECL_OVERRIDE;
- quintptr tag()const Q_DECL_OVERRIDE;
+ void setText(const QString &) override;
+ void setIcon(const QIcon &) override {}
+ void setEnabled(bool enabled) override;
+ void setVisible(bool visible) override;
+ void setMenuType(MenuType type) override;
- void setText(const QString &) Q_DECL_OVERRIDE;
- void setIcon(const QIcon &) Q_DECL_OVERRIDE {}
- void setEnabled(bool enabled) Q_DECL_OVERRIDE;
- void setVisible(bool visible) Q_DECL_OVERRIDE;
- void setMenuType(MenuType type) Q_DECL_OVERRIDE;
+ void showPopup(const QWindow *parentWindow, const QRect &targetRect, const QPlatformMenuItem *item) override;
+ void dismiss() override;
- void showPopup(const QWindow *parentWindow, const QRect &targetRect, const QPlatformMenuItem *item) Q_DECL_OVERRIDE;
- void dismiss() Q_DECL_OVERRIDE;
-
- QPlatformMenuItem *menuItemAt(int position) const Q_DECL_OVERRIDE;
- QPlatformMenuItem *menuItemForTag(quintptr tag) const Q_DECL_OVERRIDE;
+ QPlatformMenuItem *menuItemAt(int position) const override;
+ QPlatformMenuItem *menuItemForTag(quintptr tag) const override;
void handleItemSelected(QIOSMenuItem *menuItem);
@@ -118,10 +111,9 @@ public:
static id menuActionTarget() { return m_currentMenu ? m_currentMenu->m_menuController : 0; }
protected:
- bool eventFilter(QObject *obj, QEvent *event) Q_DECL_OVERRIDE;
+ bool eventFilter(QObject *obj, QEvent *event) override;
private:
- quintptr m_tag;
bool m_enabled;
bool m_visible;
QString m_text;
diff --git a/src/plugins/platforms/ios/qiosmenu.mm b/src/plugins/platforms/ios/qiosmenu.mm
index 01cb3badea..6c70676a31 100644
--- a/src/plugins/platforms/ios/qiosmenu.mm
+++ b/src/plugins/platforms/ios/qiosmenu.mm
@@ -259,7 +259,6 @@ static NSString *const kSelectorPrefix = @"_qtMenuItem_";
QIOSMenuItem::QIOSMenuItem()
: QPlatformMenuItem()
- , m_tag(0)
, m_visible(true)
, m_text(QString())
, m_role(MenuRole(0))
@@ -269,16 +268,6 @@ QIOSMenuItem::QIOSMenuItem()
{
}
-void QIOSMenuItem::setTag(quintptr tag)
-{
- m_tag = tag;
-}
-
-quintptr QIOSMenuItem::tag() const
-{
- return m_tag;
-}
-
void QIOSMenuItem::setText(const QString &text)
{
m_text = QPlatformTheme::removeMnemonics(text);
@@ -319,7 +308,6 @@ void QIOSMenuItem::setEnabled(bool enabled)
QIOSMenu::QIOSMenu()
: QPlatformMenu()
- , m_tag(0)
, m_enabled(true)
, m_visible(false)
, m_text(QString())
@@ -371,16 +359,6 @@ void QIOSMenu::syncMenuItem(QPlatformMenuItem *)
}
}
-void QIOSMenu::setTag(quintptr tag)
-{
- m_tag = tag;
-}
-
-quintptr QIOSMenu::tag() const
-{
- return m_tag;
-}
-
void QIOSMenu::setText(const QString &text)
{
m_text = text;
diff --git a/src/plugins/platforms/ios/qiosmessagedialog.h b/src/plugins/platforms/ios/qiosmessagedialog.h
index e67e10a5e1..92a4db8319 100644
--- a/src/plugins/platforms/ios/qiosmessagedialog.h
+++ b/src/plugins/platforms/ios/qiosmessagedialog.h
@@ -54,9 +54,9 @@ public:
QIOSMessageDialog();
~QIOSMessageDialog();
- void exec() Q_DECL_OVERRIDE;
- bool show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent) Q_DECL_OVERRIDE;
- void hide() Q_DECL_OVERRIDE;
+ void exec() override;
+ bool show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent) override;
+ void hide() override;
private:
QEventLoop m_eventLoop;
diff --git a/src/plugins/platforms/ios/qiosmessagedialog.mm b/src/plugins/platforms/ios/qiosmessagedialog.mm
index 4f0c667861..9d05b792c2 100644
--- a/src/plugins/platforms/ios/qiosmessagedialog.mm
+++ b/src/plugins/platforms/ios/qiosmessagedialog.mm
@@ -39,7 +39,6 @@
#import <UIKit/UIKit.h>
-#include <QtCore/qoperatingsystemversion.h>
#include <QtGui/qwindow.h>
#include <QtGui/private/qguiapplication_p.h>
#include <qpa/qplatformtheme.h>
@@ -49,7 +48,7 @@
#include "qiosmessagedialog.h"
QIOSMessageDialog::QIOSMessageDialog()
- : m_alertController(Q_NULLPTR)
+ : m_alertController(nullptr)
{
}
@@ -109,8 +108,7 @@ bool QIOSMessageDialog::show(Qt::WindowFlags windowFlags, Qt::WindowModality win
Q_UNUSED(windowFlags);
if (m_alertController // Ensure that the dialog is not showing already
|| !options() // Some message dialogs don't have options (QErrorMessage)
- || windowModality == Qt::NonModal // We can only do modal dialogs
- || QOperatingSystemVersion::current() < QOperatingSystemVersion(QOperatingSystemVersion::IOS, 8)) // API limitation
+ || windowModality == Qt::NonModal) // We can only do modal dialogs
return false;
m_alertController = [[UIAlertController
@@ -138,5 +136,5 @@ void QIOSMessageDialog::hide()
m_eventLoop.exit();
[m_alertController dismissViewControllerAnimated:YES completion:nil];
[m_alertController release];
- m_alertController = Q_NULLPTR;
+ m_alertController = nullptr;
}
diff --git a/src/plugins/platforms/ios/qiosoptionalplugininterface.h b/src/plugins/platforms/ios/qiosoptionalplugininterface.h
index 3f74e41c83..660c74e856 100644
--- a/src/plugins/platforms/ios/qiosoptionalplugininterface.h
+++ b/src/plugins/platforms/ios/qiosoptionalplugininterface.h
@@ -55,7 +55,7 @@ class QIosOptionalPluginInterface
public:
virtual ~QIosOptionalPluginInterface() {}
virtual void initPlugin() const {};
- virtual UIViewController* createImagePickerController(QIOSFileDialog *) const { return Q_NULLPTR; };
+ virtual UIViewController* createImagePickerController(QIOSFileDialog *) const { return nullptr; };
};
Q_DECLARE_INTERFACE(QIosOptionalPluginInterface, QIosOptionalPluginInterface_iid)
diff --git a/src/plugins/platforms/ios/qiosscreen.h b/src/plugins/platforms/ios/qiosscreen.h
index be0f301710..d5a253461d 100644
--- a/src/plugins/platforms/ios/qiosscreen.h
+++ b/src/plugins/platforms/ios/qiosscreen.h
@@ -62,18 +62,18 @@ public:
QString name() const override;
- QRect geometry() const Q_DECL_OVERRIDE;
- QRect availableGeometry() const Q_DECL_OVERRIDE;
- int depth() const Q_DECL_OVERRIDE;
- QImage::Format format() const Q_DECL_OVERRIDE;
- QSizeF physicalSize() const Q_DECL_OVERRIDE;
- QDpi logicalDpi() const Q_DECL_OVERRIDE;
- qreal devicePixelRatio() const Q_DECL_OVERRIDE;
+ QRect geometry() const override;
+ QRect availableGeometry() const override;
+ int depth() const override;
+ QImage::Format format() const override;
+ QSizeF physicalSize() const override;
+ QDpi logicalDpi() const override;
+ qreal devicePixelRatio() const override;
qreal refreshRate() const override;
- Qt::ScreenOrientation nativeOrientation() const Q_DECL_OVERRIDE;
- Qt::ScreenOrientation orientation() const Q_DECL_OVERRIDE;
- void setOrientationUpdateMask(Qt::ScreenOrientations mask) Q_DECL_OVERRIDE;
+ Qt::ScreenOrientation nativeOrientation() const override;
+ Qt::ScreenOrientation orientation() const override;
+ void setOrientationUpdateMask(Qt::ScreenOrientations mask) override;
QPixmap grabWindow(WId window, int x, int y, int width, int height) const override;
diff --git a/src/plugins/platforms/ios/qiosscreen.mm b/src/plugins/platforms/ios/qiosscreen.mm
index 5717483a81..c394592d76 100644
--- a/src/plugins/platforms/ios/qiosscreen.mm
+++ b/src/plugins/platforms/ios/qiosscreen.mm
@@ -45,7 +45,6 @@
#include "qiosapplicationdelegate.h"
#include "qiosviewcontroller.h"
#include "quiview.h"
-#include <QtCore/qoperatingsystemversion.h>
#include <QtGui/private/qwindow_p.h>
#include <private/qcoregraphics_p.h>
@@ -330,14 +329,6 @@ void QIOSScreen::updateProperties()
if (m_uiScreen == [UIScreen mainScreen]) {
Qt::ScreenOrientation statusBarOrientation = toQtScreenOrientation(UIDeviceOrientation([UIApplication sharedApplication].statusBarOrientation));
- if (QOperatingSystemVersion::current() < QOperatingSystemVersion(QOperatingSystemVersion::IOS, 8)) {
- // On iOS < 8.0 the UIScreen geometry is always in portait, and the system applies
- // the screen rotation to the root view-controller's view instead of directly to the
- // screen, like iOS 8 and above does.
- m_geometry = mapBetween(Qt::PortraitOrientation, statusBarOrientation, m_geometry);
- m_availableGeometry = transformBetween(Qt::PortraitOrientation, statusBarOrientation, m_geometry).mapRect(m_availableGeometry);
- }
-
QIOSViewController *qtViewController = [m_uiWindow.rootViewController isKindOfClass:[QIOSViewController class]] ?
static_cast<QIOSViewController *>(m_uiWindow.rootViewController) : nil;
@@ -357,20 +348,15 @@ void QIOSScreen::updateProperties()
#endif
if (m_geometry != previousGeometry) {
- QRectF physicalGeometry;
- if (QOperatingSystemVersion::current() >= QOperatingSystemVersion(QOperatingSystemVersion::IOS, 8)) {
- // We can't use the primaryOrientation of screen(), as we haven't reported the new geometry yet
- Qt::ScreenOrientation primaryOrientation = m_geometry.width() >= m_geometry.height() ?
- Qt::LandscapeOrientation : Qt::PortraitOrientation;
-
- // On iPhone 6+ devices, or when display zoom is enabled, the render buffer is scaled
- // before being output on the physical display. We have to take this into account when
- // computing the physical size. Note that unlike the native bounds, the physical size
- // follows the primary orientation of the screen.
- physicalGeometry = mapBetween(nativeOrientation(), primaryOrientation, QRectF::fromCGRect(m_uiScreen.nativeBounds).toRect());
- } else {
- physicalGeometry = QRectF(0, 0, m_geometry.width() * devicePixelRatio(), m_geometry.height() * devicePixelRatio());
- }
+ // We can't use the primaryOrientation of screen(), as we haven't reported the new geometry yet
+ Qt::ScreenOrientation primaryOrientation = m_geometry.width() >= m_geometry.height() ?
+ Qt::LandscapeOrientation : Qt::PortraitOrientation;
+
+ // On iPhone 6+ devices, or when display zoom is enabled, the render buffer is scaled
+ // before being output on the physical display. We have to take this into account when
+ // computing the physical size. Note that unlike the native bounds, the physical size
+ // follows the primary orientation of the screen.
+ const QRectF physicalGeometry = mapBetween(nativeOrientation(), primaryOrientation, QRectF::fromCGRect(m_uiScreen.nativeBounds).toRect());
static const qreal millimetersPerInch = 25.4;
m_physicalSize = physicalGeometry.size() / m_physicalDpi * millimetersPerInch;
diff --git a/src/plugins/platforms/ios/qiosservices.mm b/src/plugins/platforms/ios/qiosservices.mm
index 0ecc8e123f..3c44e1d7d6 100644
--- a/src/plugins/platforms/ios/qiosservices.mm
+++ b/src/plugins/platforms/ios/qiosservices.mm
@@ -55,11 +55,13 @@ bool QIOSServices::openUrl(const QUrl &url)
return openDocument(url);
NSURL *nsUrl = url.toNSURL();
+ UIApplication *application = [UIApplication sharedApplication];
- if (![[UIApplication sharedApplication] canOpenURL:nsUrl])
+ if (![application canOpenURL:nsUrl])
return false;
- return [[UIApplication sharedApplication] openURL:nsUrl];
+ [application openURL:nsUrl options:@{} completionHandler:nil];
+ return true;
}
bool QIOSServices::openDocument(const QUrl &url)
diff --git a/src/plugins/platforms/ios/qiostextinputoverlay.mm b/src/plugins/platforms/ios/qiostextinputoverlay.mm
index 9b97ce17bb..fe3c29d037 100644
--- a/src/plugins/platforms/ios/qiostextinputoverlay.mm
+++ b/src/plugins/platforms/ios/qiostextinputoverlay.mm
@@ -229,12 +229,6 @@ static void executeBlockWithoutAnimation(Block block)
borderLayer.cornerRadius = cornerRadius;
borderLayer.borderColor = [[UIColor lightGrayColor] CGColor];
[self addSublayer:borderLayer];
-
- if (QOperatingSystemVersion::current() < QOperatingSystemVersion(QOperatingSystemVersion::IOS, 7)) {
- // [UIView snapshotViewAfterScreenUpdates:] is available since iOS 7.0.
- // Just silently ignore showing the loupe for older versions.
- self.hidden = YES;
- }
}
return self;
@@ -278,9 +272,6 @@ static void executeBlockWithoutAnimation(Block block)
- (void)display
{
- if (QOperatingSystemVersion::current() < QOperatingSystemVersion(QOperatingSystemVersion::IOS, 7))
- return;
-
// Take a snapshow of the target view, magnify the area around the focal
// point, and add the snapshow layer as a child of the container layer
// to make it look like a loupe. Then place this layer at the position of
@@ -617,7 +608,7 @@ static void executeBlockWithoutAnimation(Block block)
- (QIOSLoupeLayer *)createLoupeLayer
{
Q_UNREACHABLE();
- return Q_NULLPTR;
+ return nullptr;
}
- (void)updateFocalPoint:(QPointF)touchPoint
@@ -993,12 +984,12 @@ static void executeBlockWithoutAnimation(Block block)
QT_BEGIN_NAMESPACE
-QIOSEditMenu *QIOSTextInputOverlay::s_editMenu = Q_NULLPTR;
+QIOSEditMenu *QIOSTextInputOverlay::s_editMenu = nullptr;
QIOSTextInputOverlay::QIOSTextInputOverlay()
- : m_cursorRecognizer(Q_NULLPTR)
- , m_selectionRecognizer(Q_NULLPTR)
- , m_openMenuOnTapRecognizer(Q_NULLPTR)
+ : m_cursorRecognizer(nullptr)
+ , m_selectionRecognizer(nullptr)
+ , m_openMenuOnTapRecognizer(nullptr)
{
connect(qApp, &QGuiApplication::focusObjectChanged, this, &QIOSTextInputOverlay::updateFocusObject);
}
@@ -1021,10 +1012,10 @@ void QIOSTextInputOverlay::updateFocusObject()
[m_selectionRecognizer release];
[m_openMenuOnTapRecognizer release];
[s_editMenu release];
- m_cursorRecognizer = Q_NULLPTR;
- m_selectionRecognizer = Q_NULLPTR;
- m_openMenuOnTapRecognizer = Q_NULLPTR;
- s_editMenu = Q_NULLPTR;
+ m_cursorRecognizer = nullptr;
+ m_selectionRecognizer = nullptr;
+ m_openMenuOnTapRecognizer = nullptr;
+ s_editMenu = nullptr;
}
if (platformInputContext()->inputMethodAccepted()) {
diff --git a/src/plugins/platforms/ios/qiostextresponder.mm b/src/plugins/platforms/ios/qiostextresponder.mm
index 33a0b6a42e..b029c49a67 100644
--- a/src/plugins/platforms/ios/qiostextresponder.mm
+++ b/src/plugins/platforms/ios/qiostextresponder.mm
@@ -238,7 +238,7 @@
self.inputAccessoryView = [[[WrapperView alloc] initWithView:accessoryView] autorelease];
#ifndef Q_OS_TVOS
- if (QSysInfo::MacintoshVersion >= QSysInfo::MV_IOS_9_0) {
+ if (__builtin_available(iOS 9, *)) {
if (platformData.value(kImePlatformDataHideShortcutsBar).toBool()) {
// According to the docs, leadingBarButtonGroups/trailingBarButtonGroups should be set to nil to hide the shortcuts bar.
// However, starting with iOS 10, the API has been surrounded with NS_ASSUME_NONNULL, which contradicts this and causes
@@ -879,9 +879,10 @@
- (UITextPosition *)closestPositionToPoint:(CGPoint)point
{
- // No API in Qt for determining this. Use sensible default instead:
- Q_UNUSED(point);
- return [QUITextPosition positionWithIndex:[self currentImeState:Qt::ImCursorPosition].toInt()];
+ QPointF p = QPointF::fromCGPoint(point);
+ const QTransform mapToLocal = QGuiApplication::inputMethod()->inputItemTransform().inverted();
+ int textPos = QInputMethod::queryFocusObject(Qt::ImCursorPosition, p * mapToLocal).toInt();
+ return [QUITextPosition positionWithIndex:textPos];
}
- (UITextPosition *)closestPositionToPoint:(CGPoint)point withinRange:(UITextRange *)range
diff --git a/src/plugins/platforms/ios/qiostheme.h b/src/plugins/platforms/ios/qiostheme.h
index fc6b58178a..c917679a91 100644
--- a/src/plugins/platforms/ios/qiostheme.h
+++ b/src/plugins/platforms/ios/qiostheme.h
@@ -52,16 +52,16 @@ public:
QIOSTheme();
~QIOSTheme();
- const QPalette *palette(Palette type = SystemPalette) const Q_DECL_OVERRIDE;
- QVariant themeHint(ThemeHint hint) const Q_DECL_OVERRIDE;
+ const QPalette *palette(Palette type = SystemPalette) const override;
+ QVariant themeHint(ThemeHint hint) const override;
- QPlatformMenuItem* createPlatformMenuItem() const Q_DECL_OVERRIDE;
- QPlatformMenu* createPlatformMenu() const Q_DECL_OVERRIDE;
+ QPlatformMenuItem* createPlatformMenuItem() const override;
+ QPlatformMenu* createPlatformMenu() const override;
- bool usePlatformNativeDialog(DialogType type) const Q_DECL_OVERRIDE;
- QPlatformDialogHelper *createPlatformDialogHelper(DialogType type) const Q_DECL_OVERRIDE;
+ bool usePlatformNativeDialog(DialogType type) const override;
+ QPlatformDialogHelper *createPlatformDialogHelper(DialogType type) const override;
- const QFont *font(Font type = SystemFont) const Q_DECL_OVERRIDE;
+ const QFont *font(Font type = SystemFont) const override;
static const char *name;
diff --git a/src/plugins/platforms/ios/qiosviewcontroller.mm b/src/plugins/platforms/ios/qiosviewcontroller.mm
index a9fdfaf9f1..a7663b9e94 100644
--- a/src/plugins/platforms/ios/qiosviewcontroller.mm
+++ b/src/plugins/platforms/ios/qiosviewcontroller.mm
@@ -77,8 +77,7 @@
if (!(self = [super init]))
return nil;
- QIOSIntegration *iosIntegration = QIOSIntegration::instance();
- if (iosIntegration && iosIntegration->debugWindowManagement()) {
+ if (qEnvironmentVariableIntValue("QT_IOS_DEBUG_WINDOW_MANAGEMENT")) {
static UIImage *gridPattern = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
@@ -196,8 +195,8 @@
return;
// Re-apply window states to update geometry
- if (window->windowState() & (Qt::WindowFullScreen | Qt::WindowMaximized))
- window->handle()->setWindowState(window->windowState());
+ if (window->windowStates() & (Qt::WindowFullScreen | Qt::WindowMaximized))
+ window->handle()->setWindowState(window->windowStates());
}
// Even if the root view controller has both wantsFullScreenLayout and
diff --git a/src/plugins/platforms/ios/qioswindow.h b/src/plugins/platforms/ios/qioswindow.h
index 85c60f61df..d5af31044d 100644
--- a/src/plugins/platforms/ios/qioswindow.h
+++ b/src/plugins/platforms/ios/qioswindow.h
@@ -60,37 +60,37 @@ public:
explicit QIOSWindow(QWindow *window);
~QIOSWindow();
- void setGeometry(const QRect &rect) Q_DECL_OVERRIDE;
+ void setGeometry(const QRect &rect) override;
- void setWindowState(Qt::WindowState state) Q_DECL_OVERRIDE;
- void setParent(const QPlatformWindow *window) Q_DECL_OVERRIDE;
- void handleContentOrientationChange(Qt::ScreenOrientation orientation) Q_DECL_OVERRIDE;
- void setVisible(bool visible) Q_DECL_OVERRIDE;
- void setOpacity(qreal level) Q_DECL_OVERRIDE;
+ void setWindowState(Qt::WindowStates state) override;
+ void setParent(const QPlatformWindow *window) override;
+ void handleContentOrientationChange(Qt::ScreenOrientation orientation) override;
+ void setVisible(bool visible) override;
+ void setOpacity(qreal level) override;
- bool isExposed() const Q_DECL_OVERRIDE;
- void propagateSizeHints() Q_DECL_OVERRIDE {}
+ bool isExposed() const override;
+ void propagateSizeHints() override {}
QMargins safeAreaMargins() const override;
- void raise() Q_DECL_OVERRIDE{ raiseOrLower(true); }
- void lower() Q_DECL_OVERRIDE { raiseOrLower(false); }
+ void raise() override{ raiseOrLower(true); }
+ void lower() override { raiseOrLower(false); }
bool shouldAutoActivateWindow() const;
- void requestActivateWindow() Q_DECL_OVERRIDE;
+ void requestActivateWindow() override;
- qreal devicePixelRatio() const Q_DECL_OVERRIDE;
+ qreal devicePixelRatio() const override;
- bool setMouseGrabEnabled(bool grab) Q_DECL_OVERRIDE { return grab; }
- bool setKeyboardGrabEnabled(bool grab) Q_DECL_OVERRIDE { return grab; }
+ bool setMouseGrabEnabled(bool grab) override { return grab; }
+ bool setKeyboardGrabEnabled(bool grab) override { return grab; }
- WId winId() const Q_DECL_OVERRIDE { return WId(m_view); }
+ WId winId() const override { return WId(m_view); }
void clearAccessibleCache();
- QSurfaceFormat format() const Q_DECL_OVERRIDE;
+ QSurfaceFormat format() const override;
- void requestUpdate() Q_DECL_OVERRIDE;
+ void requestUpdate() override;
CAEAGLLayer *eaglLayer() const;
diff --git a/src/plugins/platforms/ios/qioswindow.mm b/src/plugins/platforms/ios/qioswindow.mm
index 4ce73d7b5d..38136c05db 100644
--- a/src/plugins/platforms/ios/qioswindow.mm
+++ b/src/plugins/platforms/ios/qioswindow.mm
@@ -73,7 +73,7 @@ QIOSWindow::QIOSWindow(QWindow *window)
m_normalGeometry = initialGeometry(window, QPlatformWindow::geometry(),
screen()->availableGeometry().width(), screen()->availableGeometry().height());
- setWindowState(window->windowState());
+ setWindowState(window->windowStates());
setOpacity(window->opacity());
Qt::ScreenOrientation initialOrientation = window->contentOrientation();
@@ -236,7 +236,7 @@ bool QIOSWindow::isExposed() const
&& window()->isVisible() && !window()->geometry().isEmpty();
}
-void QIOSWindow::setWindowState(Qt::WindowState state)
+void QIOSWindow::setWindowState(Qt::WindowStates state)
{
// Update the QWindow representation straight away, so that
// we can update the statusbar visibility based on the new
@@ -246,12 +246,9 @@ void QIOSWindow::setWindowState(Qt::WindowState state)
if (window()->isTopLevel() && window()->isVisible() && window()->isActive())
[m_view.qtViewController updateProperties];
- switch (state) {
- case Qt::WindowNoState:
- applyGeometry(m_normalGeometry);
- break;
- case Qt::WindowMaximized:
- case Qt::WindowFullScreen: {
+ if (state & Qt::WindowMinimized) {
+ applyGeometry(QRect());
+ } else if (state & (Qt::WindowFullScreen | Qt::WindowMaximized)) {
// When an application is in split-view mode, the UIScreen still has the
// same geometry, but the UIWindow is resized to the area reserved for the
// application. We use this to constrain the geometry used when applying the
@@ -268,15 +265,8 @@ void QIOSWindow::setWindowState(Qt::WindowState state)
applyGeometry(fullscreenGeometry);
else
applyGeometry(maximizedGeometry);
- break;
- }
- case Qt::WindowMinimized:
- applyGeometry(QRect());
- break;
- case Qt::WindowActive:
- Q_UNREACHABLE();
- default:
- Q_UNREACHABLE();
+ } else {
+ applyGeometry(m_normalGeometry);
}
}
diff --git a/src/plugins/platforms/ios/quiview.h b/src/plugins/platforms/ios/quiview.h
index 1ce9007a35..3e3c579075 100644
--- a/src/plugins/platforms/ios/quiview.h
+++ b/src/plugins/platforms/ios/quiview.h
@@ -58,6 +58,7 @@ QT_END_NAMESPACE
QT_PREPEND_NAMESPACE(QIOSWindow) *m_qioswindow;
@private
QHash<UITouch *, QWindowSystemInterface::TouchPoint> m_activeTouches;
+ UITouch *m_activePencilTouch;
int m_nextTouchId;
@private
diff --git a/src/plugins/platforms/ios/quiview.mm b/src/plugins/platforms/ios/quiview.mm
index 17506ff8fb..6960698bf1 100644
--- a/src/plugins/platforms/ios/quiview.mm
+++ b/src/plugins/platforms/ios/quiview.mm
@@ -49,11 +49,12 @@
#include "qiosmenu.h"
#endif
-#include <QtCore/qoperatingsystemversion.h>
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/private/qwindow_p.h>
#include <qpa/qwindowsysteminterface_p.h>
+Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet")
+
@implementation QUIView
+ (void)load
@@ -83,10 +84,11 @@
- (id)initWithQIOSWindow:(QT_PREPEND_NAMESPACE(QIOSWindow) *)window
{
- if (self = [self initWithFrame:window->geometry().toCGRect()])
+ if (self = [self initWithFrame:window->geometry().toCGRect()]) {
m_qioswindow = window;
+ m_accessibleElements = [[NSMutableArray alloc] init];
+ }
- m_accessibleElements = [[NSMutableArray alloc] init];
return self;
}
@@ -107,7 +109,7 @@
self.multipleTouchEnabled = YES;
#endif
- if (QIOSIntegration::instance()->debugWindowManagement()) {
+ if (qEnvironmentVariableIntValue("QT_IOS_DEBUG_WINDOW_MANAGEMENT")) {
static CGFloat hue = 0.0;
CGFloat lastHue = hue;
for (CGFloat diff = 0; diff < 0.1 || diff > 0.9; diff = fabs(hue - lastHue))
@@ -116,7 +118,6 @@
#define colorWithBrightness(br) \
[UIColor colorWithHue:hue saturation:0.5 brightness:br alpha:1.0].CGColor
- self.layer.backgroundColor = colorWithBrightness(0.5);
self.layer.borderColor = colorWithBrightness(1.0);
self.layer.borderWidth = 1.0;
}
@@ -141,6 +142,13 @@
return self;
}
+- (void)dealloc
+{
+ [m_accessibleElements release];
+
+ [super dealloc];
+}
+
- (void)willMoveToWindow:(UIWindow *)newWindow
{
// UIKIt will normally set the scale factor of a view to match the corresponding
@@ -188,24 +196,13 @@
qWarning() << m_qioswindow->window()
<< "is backed by a UIView that has a transform set. This is not supported.";
- // The original geometry requested by setGeometry() might be different
- // from what we end up with after applying window constraints.
- QRect requestedGeometry = m_qioswindow->geometry();
-
- QRect actualGeometry = QRectF::fromCGRect(self.frame).toRect();
-
- // Persist the actual/new geometry so that QWindow::geometry() can
- // be queried on the resize event.
- m_qioswindow->QPlatformWindow::setGeometry(actualGeometry);
-
- QRect previousGeometry = requestedGeometry != actualGeometry ?
- requestedGeometry : qt_window_private(m_qioswindow->window())->geometry;
-
QWindow *window = m_qioswindow->window();
- qCDebug(lcQpaWindow) << m_qioswindow->window() << "new geometry is" << actualGeometry;
- QWindowSystemInterface::handleGeometryChange(window, actualGeometry, previousGeometry);
+ QRect lastReportedGeometry = qt_window_private(window)->geometry;
+ QRect currentGeometry = QRectF::fromCGRect(self.frame).toRect();
+ qCDebug(lcQpaWindow) << m_qioswindow->window() << "new geometry is" << currentGeometry;
+ QWindowSystemInterface::handleGeometryChange(window, currentGeometry);
- if (actualGeometry.size() != previousGeometry.size()) {
+ if (currentGeometry.size() != lastReportedGeometry.size()) {
// Trigger expose event on resize
[self setNeedsDisplay];
@@ -341,7 +338,7 @@
QTouchDevice *touchDevice = QIOSIntegration::instance()->touchDevice();
QTouchDevice::Capabilities touchCapabilities = touchDevice->capabilities();
- if (QOperatingSystemVersion::current() >= QOperatingSystemVersion(QOperatingSystemVersion::IOS, 9)) {
+ if (__builtin_available(iOS 9, *)) {
if (self.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable)
touchCapabilities |= QTouchDevice::Pressure;
else
@@ -358,11 +355,44 @@
return [super pointInside:point withEvent:event];
}
-- (void)updateTouchList:(NSSet *)touches withState:(Qt::TouchPointState)state
+- (void)handleTouches:(NSSet *)touches withEvent:(UIEvent *)event withState:(Qt::TouchPointState)state withTimestamp:(ulong)timeStamp
{
+ QIOSIntegration *iosIntegration = QIOSIntegration::instance();
bool supportsPressure = QIOSIntegration::instance()->touchDevice()->capabilities() & QTouchDevice::Pressure;
- foreach (UITouch *uiTouch, m_activeTouches.keys()) {
+#if QT_CONFIG(tabletevent)
+ if (m_activePencilTouch && [touches containsObject:m_activePencilTouch]) {
+ NSArray<UITouch *> *cTouches = [event coalescedTouchesForTouch:m_activePencilTouch];
+ int i = 0;
+ for (UITouch *cTouch in cTouches) {
+ QPointF localViewPosition = QPointF::fromCGPoint([cTouch preciseLocationInView:self]);
+ QPoint localViewPositionI = localViewPosition.toPoint();
+ QPointF globalScreenPosition = m_qioswindow->mapToGlobal(localViewPositionI) +
+ (localViewPosition - localViewPositionI);
+ qreal pressure = cTouch.force / cTouch.maximumPossibleForce;
+ // azimuth unit vector: +x to the right, +y going downwards
+ CGVector azimuth = [cTouch azimuthUnitVectorInView: self];
+ // azimuthAngle given in radians, zero when the stylus points towards +x axis; converted to degrees with 0 pointing straight up
+ qreal azimuthAngle = [cTouch azimuthAngleInView: self] * 180 / M_PI + 90;
+ // altitudeAngle given in radians, pi / 2 is with the stylus perpendicular to the iPad, smaller values mean more tilted, but never negative.
+ // Convert to degrees with zero being perpendicular.
+ qreal altitudeAngle = 90 - cTouch.altitudeAngle * 180 / M_PI;
+ qCDebug(lcQpaTablet) << i << ":" << timeStamp << localViewPosition << pressure << state << "azimuth" << azimuth.dx << azimuth.dy
+ << "angle" << azimuthAngle << "altitude" << cTouch.altitudeAngle
+ << "xTilt" << qBound(-60.0, altitudeAngle * azimuth.dx, 60.0) << "yTilt" << qBound(-60.0, altitudeAngle * azimuth.dy, 60.0);
+ QWindowSystemInterface::handleTabletEvent(m_qioswindow->window(), timeStamp, localViewPosition, globalScreenPosition,
+ // device, pointerType, buttons
+ QTabletEvent::RotationStylus, QTabletEvent::Pen, state == Qt::TouchPointReleased ? Qt::NoButton : Qt::LeftButton,
+ // pressure, xTilt, yTilt
+ pressure, qBound(-60.0, altitudeAngle * azimuth.dx, 60.0), qBound(-60.0, altitudeAngle * azimuth.dy, 60.0),
+ // tangentialPressure, rotation, z, uid, modifiers
+ 0, azimuthAngle, 0, 0, Qt::NoModifier);
+ ++i;
+ }
+ }
+#endif
+
+ for (UITouch *uiTouch : m_activeTouches.keys()) {
QWindowSystemInterface::TouchPoint &touchPoint = m_activeTouches[uiTouch];
if (![touches containsObject:uiTouch]) {
touchPoint.state = Qt::TouchPointStationary;
@@ -391,16 +421,14 @@
touchPoint.pressure = uiTouch.force / uiTouch.maximumPossibleForce;
} else {
// We don't claim that our touch device supports QTouchDevice::Pressure,
- // but fill in a meaningfull value in case clients use it anyways.
+ // but fill in a meaningful value in case clients use it anyway.
touchPoint.pressure = (state == Qt::TouchPointReleased) ? 0.0 : 1.0;
}
}
}
-}
-
-- (void)sendTouchEventWithTimestamp:(ulong)timeStamp
-{
- QIOSIntegration *iosIntegration = QIOSIntegration::instance();
+ if (m_activeTouches.isEmpty())
+ return;
+ QWindowSystemInterface::handleTouchEvent<QWindowSystemInterface::SynchronousDelivery>(m_qioswindow->window(), timeStamp, iosIntegration->touchDevice(), m_activeTouches.values());
if (!static_cast<QUIWindow *>(self.window).sendingEvent) {
// The event is likely delivered as part of delayed touch delivery, via
// _UIGestureEnvironmentSortAndSendDelayedTouches, due to one of the two
@@ -425,8 +453,21 @@
// points to QWindowSystemInterface::TouchPoints, and assigns each TouchPoint
// an id for use by Qt.
for (UITouch *touch in touches) {
- Q_ASSERT(!m_activeTouches.contains(touch));
- m_activeTouches[touch].id = m_nextTouchId++;
+#if QT_CONFIG(tabletevent)
+ if (touch.type == UITouchTypeStylus) {
+ if (Q_UNLIKELY(m_activePencilTouch)) {
+ qWarning("ignoring additional Pencil while first is still active");
+ continue;
+ }
+ m_activePencilTouch = touch;
+ } else
+ {
+ Q_ASSERT(!m_activeTouches.contains(touch));
+#endif
+ m_activeTouches[touch].id = m_nextTouchId++;
+#if QT_CONFIG(tabletevent)
+ }
+#endif
}
if (m_qioswindow->shouldAutoActivateWindow() && m_activeTouches.size() == 1) {
@@ -437,31 +478,36 @@
topLevel->requestActivateWindow();
}
- [self updateTouchList:touches withState:Qt::TouchPointPressed];
- [self sendTouchEventWithTimestamp:ulong(event.timestamp * 1000)];
+ [self handleTouches:touches withEvent:event withState:Qt::TouchPointPressed withTimestamp:ulong(event.timestamp * 1000)];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
- [self updateTouchList:touches withState:Qt::TouchPointMoved];
- [self sendTouchEventWithTimestamp:ulong(event.timestamp * 1000)];
+ [self handleTouches:touches withEvent:event withState:Qt::TouchPointMoved withTimestamp:ulong(event.timestamp * 1000)];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
- [self updateTouchList:touches withState:Qt::TouchPointReleased];
- [self sendTouchEventWithTimestamp:ulong(event.timestamp * 1000)];
+ [self handleTouches:touches withEvent:event withState:Qt::TouchPointReleased withTimestamp:ulong(event.timestamp * 1000)];
// Remove ended touch points from the active set:
- for (UITouch *touch in touches)
- m_activeTouches.remove(touch);
- if (m_activeTouches.isEmpty())
+ for (UITouch *touch in touches) {
+#if QT_CONFIG(tabletevent)
+ if (touch.type == UITouchTypeStylus) {
+ m_activePencilTouch = nil;
+ } else
+#endif
+ {
+ m_activeTouches.remove(touch);
+ }
+ }
+ if (m_activeTouches.isEmpty() && !m_activePencilTouch)
m_nextTouchId = 0;
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
- if (m_activeTouches.isEmpty())
+ if (m_activeTouches.isEmpty() && !m_activePencilTouch)
return;
// When four-finger swiping, we get a touchesCancelled callback
@@ -485,11 +531,12 @@
// sub-set of the active touch events are intentionally cancelled.
NSInteger count = static_cast<NSInteger>([touches count]);
- if (count != 0 && count != m_activeTouches.count())
+ if (count != 0 && count != m_activeTouches.count() && !m_activePencilTouch)
qWarning("Subset of active touches cancelled by UIKit");
m_activeTouches.clear();
m_nextTouchId = 0;
+ m_activePencilTouch = nil;
NSTimeInterval timestamp = event ? event.timestamp : [[NSProcessInfo processInfo] systemUptime];
diff --git a/src/plugins/platforms/ios/quiview_accessibility.mm b/src/plugins/platforms/ios/quiview_accessibility.mm
index 8dafaea552..69a4d375bd 100644
--- a/src/plugins/platforms/ios/quiview_accessibility.mm
+++ b/src/plugins/platforms/ios/quiview_accessibility.mm
@@ -50,7 +50,7 @@
return;
QAccessible::Id accessibleId = QAccessible::uniqueId(iface);
UIAccessibilityElement *elem = [[QMacAccessibilityElement alloc] initWithId: accessibleId withAccessibilityContainer: self];
- [m_accessibleElements addObject: elem];
+ [m_accessibleElements addObject:[elem autorelease]];
}
- (void)createAccessibleContainer:(QAccessibleInterface *)iface
diff --git a/src/plugins/platforms/linuxfb/main.cpp b/src/plugins/platforms/linuxfb/main.cpp
index 82b916b9a9..24156b68e8 100644
--- a/src/plugins/platforms/linuxfb/main.cpp
+++ b/src/plugins/platforms/linuxfb/main.cpp
@@ -47,7 +47,7 @@ class QLinuxFbIntegrationPlugin : public QPlatformIntegrationPlugin
Q_OBJECT
Q_PLUGIN_METADATA(IID QPlatformIntegrationFactoryInterface_iid FILE "linuxfb.json")
public:
- QPlatformIntegration *create(const QString&, const QStringList&) Q_DECL_OVERRIDE;
+ QPlatformIntegration *create(const QString&, const QStringList&) override;
};
QPlatformIntegration* QLinuxFbIntegrationPlugin::create(const QString& system, const QStringList& paramList)
diff --git a/src/plugins/platforms/linuxfb/qlinuxfbdrmscreen.cpp b/src/plugins/platforms/linuxfb/qlinuxfbdrmscreen.cpp
index e15d6fee24..dcc1ef2790 100644
--- a/src/plugins/platforms/linuxfb/qlinuxfbdrmscreen.cpp
+++ b/src/plugins/platforms/linuxfb/qlinuxfbdrmscreen.cpp
@@ -43,7 +43,6 @@
// Multiscreen: QWindow-QScreen(-output) association. Needs some reorg (device cannot be owned by screen)
// Find card via devicediscovery like in eglfs_kms.
// Mode restore like QEglFSKmsInterruptHandler.
-// Formats other then 32 bpp?
// grabWindow
#include "qlinuxfbdrmscreen.h"
@@ -187,15 +186,67 @@ void QLinuxFbDevice::registerScreen(QPlatformScreen *screen,
Q_UNREACHABLE();
}
+static uint32_t bppForDrmFormat(uint32_t drmFormat)
+{
+ switch (drmFormat) {
+ case DRM_FORMAT_RGB565:
+ case DRM_FORMAT_BGR565:
+ return 16;
+ default:
+ return 32;
+ }
+}
+
+static int depthForDrmFormat(uint32_t drmFormat)
+{
+ switch (drmFormat) {
+ case DRM_FORMAT_RGB565:
+ case DRM_FORMAT_BGR565:
+ return 16;
+ case DRM_FORMAT_XRGB8888:
+ case DRM_FORMAT_XBGR8888:
+ return 24;
+ case DRM_FORMAT_XRGB2101010:
+ case DRM_FORMAT_XBGR2101010:
+ return 30;
+ default:
+ return 32;
+ }
+}
+
+static QImage::Format formatForDrmFormat(uint32_t drmFormat)
+{
+ switch (drmFormat) {
+ case DRM_FORMAT_XRGB8888:
+ case DRM_FORMAT_XBGR8888:
+ return QImage::Format_RGB32;
+ case DRM_FORMAT_ARGB8888:
+ case DRM_FORMAT_ABGR8888:
+ return QImage::Format_ARGB32;
+ case DRM_FORMAT_RGB565:
+ case DRM_FORMAT_BGR565:
+ return QImage::Format_RGB16;
+ case DRM_FORMAT_XRGB2101010:
+ case DRM_FORMAT_XBGR2101010:
+ return QImage::Format_RGB30;
+ case DRM_FORMAT_ARGB2101010:
+ case DRM_FORMAT_ABGR2101010:
+ return QImage::Format_A2RGB30_Premultiplied;
+ default:
+ return QImage::Format_ARGB32;
+ }
+}
+
bool QLinuxFbDevice::createFramebuffer(QLinuxFbDevice::Output *output, int bufferIdx)
{
const QSize size = output->currentRes();
const uint32_t w = size.width();
const uint32_t h = size.height();
+ const uint32_t bpp = bppForDrmFormat(output->kmsOutput.drm_format);
drm_mode_create_dumb creq = {
h,
w,
- 32,
+ bpp,
0, 0, 0, 0
};
if (drmIoctl(fd(), DRM_IOCTL_MODE_CREATE_DUMB, &creq) == -1) {
@@ -207,10 +258,15 @@ bool QLinuxFbDevice::createFramebuffer(QLinuxFbDevice::Output *output, int buffe
fb.handle = creq.handle;
fb.pitch = creq.pitch;
fb.size = creq.size;
- qCDebug(qLcFbDrm, "Got a dumb buffer for size %dx%d, handle %u, pitch %u, size %u",
- w, h, fb.handle, fb.pitch, (uint) fb.size);
+ qCDebug(qLcFbDrm, "Got a dumb buffer for size %dx%d and bpp %u: handle %u, pitch %u, size %u",
+ w, h, bpp, fb.handle, fb.pitch, (uint) fb.size);
+
+ uint32_t handles[4] = { fb.handle };
+ uint32_t strides[4] = { fb.pitch };
+ uint32_t offsets[4] = { 0 };
- if (drmModeAddFB(fd(), w, h, 24, 32, fb.pitch, fb.handle, &fb.fb) == -1) {
+ if (drmModeAddFB2(fd(), w, h, output->kmsOutput.drm_format,
+ handles, strides, offsets, &fb.fb, 0) == -1) {
qErrnoWarning(errno, "Failed to add FB");
return false;
}
@@ -229,10 +285,10 @@ bool QLinuxFbDevice::createFramebuffer(QLinuxFbDevice::Output *output, int buffe
return false;
}
- qCDebug(qLcFbDrm, "FB is %u, mapped at %p", fb.fb, fb.p);
+ qCDebug(qLcFbDrm, "FB is %u (DRM format 0x%x), mapped at %p", fb.fb, output->kmsOutput.drm_format, fb.p);
memset(fb.p, 0, fb.size);
- fb.wrapper = QImage(static_cast<uchar *>(fb.p), w, h, fb.pitch, QImage::Format_ARGB32);
+ fb.wrapper = QImage(static_cast<uchar *>(fb.p), w, h, fb.pitch, formatForDrmFormat(output->kmsOutput.drm_format));
return true;
}
@@ -357,10 +413,10 @@ bool QLinuxFbDrmScreen::initialize()
QLinuxFbDevice::Output *output(m_device->output(0));
mGeometry = QRect(QPoint(0, 0), output->currentRes());
- mDepth = 32;
- mFormat = QImage::Format_ARGB32;
+ mDepth = depthForDrmFormat(output->kmsOutput.drm_format);
+ mFormat = formatForDrmFormat(output->kmsOutput.drm_format);
mPhysicalSize = output->kmsOutput.physical_size;
- qCDebug(qLcFbDrm) << mGeometry << mPhysicalSize;
+ qCDebug(qLcFbDrm) << mGeometry << mPhysicalSize << mDepth << mFormat;
QFbScreen::initializeCompositor();
diff --git a/src/plugins/platforms/linuxfb/qlinuxfbintegration.h b/src/plugins/platforms/linuxfb/qlinuxfbintegration.h
index 9934a8cd54..22578bf980 100644
--- a/src/plugins/platforms/linuxfb/qlinuxfbintegration.h
+++ b/src/plugins/platforms/linuxfb/qlinuxfbintegration.h
@@ -55,19 +55,19 @@ public:
QLinuxFbIntegration(const QStringList &paramList);
~QLinuxFbIntegration();
- void initialize() Q_DECL_OVERRIDE;
- bool hasCapability(QPlatformIntegration::Capability cap) const Q_DECL_OVERRIDE;
+ void initialize() override;
+ bool hasCapability(QPlatformIntegration::Capability cap) const override;
- QPlatformWindow *createPlatformWindow(QWindow *window) const Q_DECL_OVERRIDE;
- QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const Q_DECL_OVERRIDE;
+ QPlatformWindow *createPlatformWindow(QWindow *window) const override;
+ QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const override;
- QAbstractEventDispatcher *createEventDispatcher() const Q_DECL_OVERRIDE;
+ QAbstractEventDispatcher *createEventDispatcher() const override;
- QPlatformFontDatabase *fontDatabase() const Q_DECL_OVERRIDE;
- QPlatformServices *services() const Q_DECL_OVERRIDE;
- QPlatformInputContext *inputContext() const Q_DECL_OVERRIDE { return m_inputContext; }
+ QPlatformFontDatabase *fontDatabase() const override;
+ QPlatformServices *services() const override;
+ QPlatformInputContext *inputContext() const override { return m_inputContext; }
- QPlatformNativeInterface *nativeInterface() const Q_DECL_OVERRIDE;
+ QPlatformNativeInterface *nativeInterface() const override;
QList<QPlatformScreen *> screens() const;
diff --git a/src/plugins/platforms/minimal/main.cpp b/src/plugins/platforms/minimal/main.cpp
index 29809c1843..f9a0c17509 100644
--- a/src/plugins/platforms/minimal/main.cpp
+++ b/src/plugins/platforms/minimal/main.cpp
@@ -48,7 +48,7 @@ class QMinimalIntegrationPlugin : public QPlatformIntegrationPlugin
Q_OBJECT
Q_PLUGIN_METADATA(IID QPlatformIntegrationFactoryInterface_iid FILE "minimal.json")
public:
- QPlatformIntegration *create(const QString&, const QStringList&) Q_DECL_OVERRIDE;
+ QPlatformIntegration *create(const QString&, const QStringList&) override;
};
QPlatformIntegration *QMinimalIntegrationPlugin::create(const QString& system, const QStringList& paramList)
diff --git a/src/plugins/platforms/minimal/minimal.pro b/src/plugins/platforms/minimal/minimal.pro
index 8cfb68824e..a1a2da547b 100644
--- a/src/plugins/platforms/minimal/minimal.pro
+++ b/src/plugins/platforms/minimal/minimal.pro
@@ -14,6 +14,8 @@ HEADERS = qminimalintegration.h \
OTHER_FILES += minimal.json
+qtConfig(freetype): QMAKE_USE_PRIVATE += freetype
+
PLUGIN_TYPE = platforms
PLUGIN_CLASS_NAME = QMinimalIntegrationPlugin
!equals(TARGET, $$QT_DEFAULT_QPA_PLUGIN): PLUGIN_EXTENDS = -
diff --git a/src/plugins/platforms/minimal/qminimalbackingstore.h b/src/plugins/platforms/minimal/qminimalbackingstore.h
index 3d7aaf2b99..2119894809 100644
--- a/src/plugins/platforms/minimal/qminimalbackingstore.h
+++ b/src/plugins/platforms/minimal/qminimalbackingstore.h
@@ -52,9 +52,9 @@ public:
QMinimalBackingStore(QWindow *window);
~QMinimalBackingStore();
- QPaintDevice *paintDevice() Q_DECL_OVERRIDE;
- void flush(QWindow *window, const QRegion &region, const QPoint &offset) Q_DECL_OVERRIDE;
- void resize(const QSize &size, const QRegion &staticContents) Q_DECL_OVERRIDE;
+ QPaintDevice *paintDevice() override;
+ void flush(QWindow *window, const QRegion &region, const QPoint &offset) override;
+ void resize(const QSize &size, const QRegion &staticContents) override;
private:
QImage mImage;
diff --git a/src/plugins/platforms/minimal/qminimalintegration.cpp b/src/plugins/platforms/minimal/qminimalintegration.cpp
index ca33689cd7..0c04608fca 100644
--- a/src/plugins/platforms/minimal/qminimalintegration.cpp
+++ b/src/plugins/platforms/minimal/qminimalintegration.cpp
@@ -54,11 +54,17 @@
# endif
#elif defined(Q_OS_DARWIN)
# include <QtFontDatabaseSupport/private/qcoretextfontdatabase_p.h>
-#elif QT_CONFIG(fontconfig)
+#endif
+
+#if QT_CONFIG(fontconfig)
# include <QtFontDatabaseSupport/private/qgenericunixfontdatabase_p.h>
# include <qpa/qplatformfontdatabase.h>
#endif
+#if QT_CONFIG(freetype)
+#include <QtFontDatabaseSupport/private/qfontengine_ft_p.h>
+#endif
+
#if !defined(Q_OS_WIN)
#include <QtEventDispatcherSupport/private/qgenericunixeventdispatcher_p.h>
#elif defined(Q_OS_WINRT)
@@ -81,6 +87,8 @@ static inline unsigned parseOptions(const QStringList &paramList)
options |= QMinimalIntegration::EnableFonts;
else if (param == QLatin1String("freetype"))
options |= QMinimalIntegration::FreeTypeFontDatabase;
+ else if (param == QLatin1String("fontconfig"))
+ options |= QMinimalIntegration::FontconfigDatabase;
}
return options;
}
@@ -123,15 +131,13 @@ bool QMinimalIntegration::hasCapability(QPlatformIntegration::Capability cap) co
class DummyFontDatabase : public QPlatformFontDatabase
{
public:
- virtual void populateFontDatabase() Q_DECL_OVERRIDE {}
+ virtual void populateFontDatabase() override {}
};
QPlatformFontDatabase *QMinimalIntegration::fontDatabase() const
{
if (!m_fontDatabase && (m_options & EnableFonts)) {
-#if QT_CONFIG(fontconfig)
- m_fontDatabase = new QGenericUnixFontDatabase;
-#elif defined(Q_OS_WINRT)
+#if defined(Q_OS_WINRT)
m_fontDatabase = new QWinRTFontDatabase;
#elif defined(Q_OS_WIN)
if (m_options & FreeTypeFontDatabase) {
@@ -142,10 +148,24 @@ QPlatformFontDatabase *QMinimalIntegration::fontDatabase() const
m_fontDatabase = new QWindowsFontDatabase;
}
#elif defined(Q_OS_DARWIN)
- m_fontDatabase = new QCoreTextFontDatabaseEngineFactory<QCoreTextFontEngine>;
+ if (!(m_options & FontconfigDatabase)) {
+ if (m_options & FreeTypeFontDatabase) {
+# if QT_CONFIG(freetype)
+ m_fontDatabase = new QCoreTextFontDatabaseEngineFactory<QFontEngineFT>;
+# endif // freetype
+ } else {
+ m_fontDatabase = new QCoreTextFontDatabaseEngineFactory<QCoreTextFontEngine>;
+ }
+ }
+#endif
+
+ if (!m_fontDatabase) {
+#if QT_CONFIG(fontconfig)
+ m_fontDatabase = new QGenericUnixFontDatabase;
#else
- m_fontDatabase = QPlatformIntegration::fontDatabase();
+ m_fontDatabase = QPlatformIntegration::fontDatabase();
#endif
+ }
}
if (!m_fontDatabase)
m_fontDatabase = new DummyFontDatabase;
diff --git a/src/plugins/platforms/minimal/qminimalintegration.h b/src/plugins/platforms/minimal/qminimalintegration.h
index eaa2f228c5..ad1bec2112 100644
--- a/src/plugins/platforms/minimal/qminimalintegration.h
+++ b/src/plugins/platforms/minimal/qminimalintegration.h
@@ -51,9 +51,9 @@ public:
QMinimalScreen()
: mDepth(32), mFormat(QImage::Format_ARGB32_Premultiplied) {}
- QRect geometry() const Q_DECL_OVERRIDE { return mGeometry; }
- int depth() const Q_DECL_OVERRIDE { return mDepth; }
- QImage::Format format() const Q_DECL_OVERRIDE { return mFormat; }
+ QRect geometry() const override { return mGeometry; }
+ int depth() const override { return mDepth; }
+ QImage::Format format() const override { return mFormat; }
public:
QRect mGeometry;
@@ -68,18 +68,19 @@ public:
enum Options { // Options to be passed on command line or determined from environment
DebugBackingStore = 0x1,
EnableFonts = 0x2,
- FreeTypeFontDatabase = 0x4
+ FreeTypeFontDatabase = 0x4,
+ FontconfigDatabase = 0x8
};
explicit QMinimalIntegration(const QStringList &parameters);
~QMinimalIntegration();
- bool hasCapability(QPlatformIntegration::Capability cap) const Q_DECL_OVERRIDE;
- QPlatformFontDatabase *fontDatabase() const Q_DECL_OVERRIDE;
+ bool hasCapability(QPlatformIntegration::Capability cap) const override;
+ QPlatformFontDatabase *fontDatabase() const override;
- QPlatformWindow *createPlatformWindow(QWindow *window) const Q_DECL_OVERRIDE;
- QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const Q_DECL_OVERRIDE;
- QAbstractEventDispatcher *createEventDispatcher() const Q_DECL_OVERRIDE;
+ QPlatformWindow *createPlatformWindow(QWindow *window) const override;
+ QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const override;
+ QAbstractEventDispatcher *createEventDispatcher() const override;
unsigned options() const { return m_options; }
diff --git a/src/plugins/platforms/minimalegl/main.cpp b/src/plugins/platforms/minimalegl/main.cpp
index a010ed76e2..5aac71e140 100644
--- a/src/plugins/platforms/minimalegl/main.cpp
+++ b/src/plugins/platforms/minimalegl/main.cpp
@@ -47,7 +47,7 @@ class QMinimalEglIntegrationPlugin : public QPlatformIntegrationPlugin
Q_OBJECT
Q_PLUGIN_METADATA(IID QPlatformIntegrationFactoryInterface_iid FILE "minimalegl.json")
public:
- QPlatformIntegration *create(const QString&, const QStringList&) Q_DECL_OVERRIDE;
+ QPlatformIntegration *create(const QString&, const QStringList&) override;
};
QPlatformIntegration* QMinimalEglIntegrationPlugin::create(const QString& system, const QStringList& paramList)
diff --git a/src/plugins/platforms/minimalegl/qminimaleglbackingstore.h b/src/plugins/platforms/minimalegl/qminimaleglbackingstore.h
index 0e22298891..382f2d1404 100644
--- a/src/plugins/platforms/minimalegl/qminimaleglbackingstore.h
+++ b/src/plugins/platforms/minimalegl/qminimaleglbackingstore.h
@@ -53,13 +53,13 @@ public:
QMinimalEglBackingStore(QWindow *window);
~QMinimalEglBackingStore();
- QPaintDevice *paintDevice() Q_DECL_OVERRIDE;
+ QPaintDevice *paintDevice() override;
- void beginPaint(const QRegion &) Q_DECL_OVERRIDE;
- void endPaint() Q_DECL_OVERRIDE;
+ void beginPaint(const QRegion &) override;
+ void endPaint() override;
- void flush(QWindow *window, const QRegion &region, const QPoint &offset) Q_DECL_OVERRIDE;
- void resize(const QSize &size, const QRegion &staticContents) Q_DECL_OVERRIDE;
+ void flush(QWindow *window, const QRegion &region, const QPoint &offset) override;
+ void resize(const QSize &size, const QRegion &staticContents) override;
private:
QOpenGLContext *m_context;
diff --git a/src/plugins/platforms/minimalegl/qminimaleglintegration.cpp b/src/plugins/platforms/minimalegl/qminimaleglintegration.cpp
index a716a6092a..5d31af53d5 100644
--- a/src/plugins/platforms/minimalegl/qminimaleglintegration.cpp
+++ b/src/plugins/platforms/minimalegl/qminimaleglintegration.cpp
@@ -71,7 +71,7 @@ public:
QWinRTEventDispatcher() {}
protected:
- bool hasPendingEvents() Q_DECL_OVERRIDE
+ bool hasPendingEvents() override
{
return QEventDispatcherWinRT::hasPendingEvents() || QWindowSystemInterface::windowSystemEventsQueued();
}
@@ -156,7 +156,7 @@ QAbstractEventDispatcher *QMinimalEglIntegration::createEventDispatcher() const
#elif defined(Q_OS_WIN)
return new QWindowsGuiEventDispatcher;
#else
- return Q_NULLPTR;
+ return nullptr;
#endif
}
diff --git a/src/plugins/platforms/minimalegl/qminimaleglintegration.h b/src/plugins/platforms/minimalegl/qminimaleglintegration.h
index d0ab75bd3c..70a51004a6 100644
--- a/src/plugins/platforms/minimalegl/qminimaleglintegration.h
+++ b/src/plugins/platforms/minimalegl/qminimaleglintegration.h
@@ -51,18 +51,18 @@ public:
QMinimalEglIntegration();
~QMinimalEglIntegration();
- bool hasCapability(QPlatformIntegration::Capability cap) const Q_DECL_OVERRIDE;
+ bool hasCapability(QPlatformIntegration::Capability cap) const override;
- QPlatformWindow *createPlatformWindow(QWindow *window) const Q_DECL_OVERRIDE;
- QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const Q_DECL_OVERRIDE;
+ QPlatformWindow *createPlatformWindow(QWindow *window) const override;
+ QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const override;
#ifndef QT_NO_OPENGL
- QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const Q_DECL_OVERRIDE;
+ QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const override;
#endif
- QPlatformFontDatabase *fontDatabase() const Q_DECL_OVERRIDE;
+ QPlatformFontDatabase *fontDatabase() const override;
- QAbstractEventDispatcher *createEventDispatcher() const Q_DECL_OVERRIDE;
+ QAbstractEventDispatcher *createEventDispatcher() const override;
- QVariant styleHint(QPlatformIntegration::StyleHint hint) const Q_DECL_OVERRIDE;
+ QVariant styleHint(QPlatformIntegration::StyleHint hint) const override;
private:
QPlatformFontDatabase *mFontDb;
diff --git a/src/plugins/platforms/minimalegl/qminimaleglscreen.cpp b/src/plugins/platforms/minimalegl/qminimaleglscreen.cpp
index 0175d2dbdd..6e122e28ce 100644
--- a/src/plugins/platforms/minimalegl/qminimaleglscreen.cpp
+++ b/src/plugins/platforms/minimalegl/qminimaleglscreen.cpp
@@ -64,7 +64,7 @@ public:
{
}
- EGLSurface eglSurfaceForPlatformSurface(QPlatformSurface *surface) Q_DECL_OVERRIDE
+ EGLSurface eglSurfaceForPlatformSurface(QPlatformSurface *surface) override
{
QMinimalEglWindow *window = static_cast<QMinimalEglWindow *>(surface);
QMinimalEglScreen *screen = static_cast<QMinimalEglScreen *>(window->screen());
diff --git a/src/plugins/platforms/minimalegl/qminimaleglscreen.h b/src/plugins/platforms/minimalegl/qminimaleglscreen.h
index 24098b8127..926936ae32 100644
--- a/src/plugins/platforms/minimalegl/qminimaleglscreen.h
+++ b/src/plugins/platforms/minimalegl/qminimaleglscreen.h
@@ -56,9 +56,9 @@ public:
QMinimalEglScreen(EGLNativeDisplayType display);
~QMinimalEglScreen();
- QRect geometry() const Q_DECL_OVERRIDE;
- int depth() const Q_DECL_OVERRIDE;
- QImage::Format format() const Q_DECL_OVERRIDE;
+ QRect geometry() const override;
+ int depth() const override;
+ QImage::Format format() const override;
#ifndef QT_NO_OPENGL
QPlatformOpenGLContext *platformContext() const;
#endif
diff --git a/src/plugins/platforms/minimalegl/qminimaleglwindow.h b/src/plugins/platforms/minimalegl/qminimaleglwindow.h
index b8bfd6c8d2..098ec05e6b 100644
--- a/src/plugins/platforms/minimalegl/qminimaleglwindow.h
+++ b/src/plugins/platforms/minimalegl/qminimaleglwindow.h
@@ -51,8 +51,8 @@ class QMinimalEglWindow : public QPlatformWindow
public:
QMinimalEglWindow(QWindow *w);
- void setGeometry(const QRect &) Q_DECL_OVERRIDE;
- WId winId() const Q_DECL_OVERRIDE;
+ void setGeometry(const QRect &) override;
+ WId winId() const override;
private:
WId m_winid;
diff --git a/src/plugins/platforms/mirclient/qmirclientinput.cpp b/src/plugins/platforms/mirclient/qmirclientinput.cpp
index ea13f3cc17..e5319b0435 100644
--- a/src/plugins/platforms/mirclient/qmirclientinput.cpp
+++ b/src/plugins/platforms/mirclient/qmirclientinput.cpp
@@ -156,6 +156,36 @@ static const uint32_t KeyTable[] = {
XKB_KEY_dead_belowdot, Qt::Key_Dead_Belowdot,
XKB_KEY_dead_hook, Qt::Key_Dead_Hook,
XKB_KEY_dead_horn, Qt::Key_Dead_Horn,
+ XKB_KEY_dead_stroke, Qt::Key_Dead_Stroke,
+ XKB_KEY_dead_abovecomma, Qt::Key_Dead_Abovecomma,
+ XKB_KEY_dead_abovereversedcomma, Qt::Key_Dead_Abovereversedcomma,
+ XKB_KEY_dead_doublegrave, Qt::Key_Dead_Doublegrave,
+ XKB_KEY_dead_belowring, Qt::Key_Dead_Belowring,
+ XKB_KEY_dead_belowmacron, Qt::Key_Dead_Belowmacron,
+ XKB_KEY_dead_belowcircumflex, Qt::Key_Dead_Belowcircumflex,
+ XKB_KEY_dead_belowtilde, Qt::Key_Dead_Belowtilde,
+ XKB_KEY_dead_belowbreve, Qt::Key_Dead_Belowbreve,
+ XKB_KEY_dead_belowdiaeresis, Qt::Key_Dead_Belowdiaeresis,
+ XKB_KEY_dead_invertedbreve, Qt::Key_Dead_Invertedbreve,
+ XKB_KEY_dead_belowcomma, Qt::Key_Dead_Belowcomma,
+ XKB_KEY_dead_currency, Qt::Key_Dead_Currency,
+ XKB_KEY_dead_a, Qt::Key_Dead_a,
+ XKB_KEY_dead_A, Qt::Key_Dead_A,
+ XKB_KEY_dead_e, Qt::Key_Dead_e,
+ XKB_KEY_dead_E, Qt::Key_Dead_E,
+ XKB_KEY_dead_i, Qt::Key_Dead_i,
+ XKB_KEY_dead_I, Qt::Key_Dead_I,
+ XKB_KEY_dead_o, Qt::Key_Dead_o,
+ XKB_KEY_dead_O, Qt::Key_Dead_O,
+ XKB_KEY_dead_u, Qt::Key_Dead_u,
+ XKB_KEY_dead_U, Qt::Key_Dead_U,
+ XKB_KEY_dead_small_schwa, Qt::Key_Dead_Small_Schwa,
+ XKB_KEY_dead_capital_schwa, Qt::Key_Dead_Capital_Schwa,
+ XKB_KEY_dead_greek, Qt::Key_Dead_Greek,
+ XKB_KEY_dead_lowline, Qt::Key_Dead_Lowline,
+ XKB_KEY_dead_aboveverticalline, Qt::Key_Dead_Aboveverticalline,
+ XKB_KEY_dead_belowverticalline, Qt::Key_Dead_Belowverticalline,
+ XKB_KEY_dead_longsolidusoverlay, Qt::Key_Dead_Longsolidusoverlay,
XKB_KEY_Mode_switch, Qt::Key_Mode_switch,
XKB_KEY_script_switch, Qt::Key_Mode_switch,
diff --git a/src/plugins/platforms/mirclient/qmirclientwindow.cpp b/src/plugins/platforms/mirclient/qmirclientwindow.cpp
index adc8ae652a..decd21516e 100644
--- a/src/plugins/platforms/mirclient/qmirclientwindow.cpp
+++ b/src/plugins/platforms/mirclient/qmirclientwindow.cpp
@@ -516,7 +516,6 @@ UbuntuSurface::UbuntuSurface(QMirClientWindow *platformWindow, EGLDisplay displa
// Assume that the buffer size matches the surface size at creation time
mBufferSize = geom.size();
- platformWindow->QPlatformWindow::setGeometry(geom);
QWindowSystemInterface::handleGeometryChange(mWindow, geom);
qCDebug(mirclient) << "Created surface with geometry:" << geom << "title:" << mWindow->title()
@@ -636,7 +635,6 @@ void UbuntuSurface::onSwapBuffersDone()
QRect newGeometry = mPlatformWindow->geometry();
newGeometry.setSize(mBufferSize);
- mPlatformWindow->QPlatformWindow::setGeometry(newGeometry);
QWindowSystemInterface::handleGeometryChange(mWindow, newGeometry);
} else {
qCDebug(mirclientBufferSwap, "onSwapBuffersDone(window=%p) [%d] - buffer size (%d,%d)",
@@ -785,8 +783,16 @@ void QMirClientWindow::handleSurfaceStateChanged(Qt::WindowState state)
QWindowSystemInterface::handleWindowStateChanged(window(), state);
}
-void QMirClientWindow::setWindowState(Qt::WindowState state)
+void QMirClientWindow::setWindowState(Qt::WindowStates states)
{
+ Qt::WindowState state = Qt::WindowNoState;
+ if (states & Qt::WindowMinimized)
+ state = Qt::WindowMinimized;
+ else if (states & Qt::WindowFullScreen)
+ state = Qt::WindowFullScreen;
+ else if (states & Qt::WindowMaximized)
+ state = Qt::WindowMaximized;
+
QMutexLocker lock(&mMutex);
qCDebug(mirclient, "setWindowState(window=%p, %s)", this, qtWindowStateToStr(state));
diff --git a/src/plugins/platforms/mirclient/qmirclientwindow.h b/src/plugins/platforms/mirclient/qmirclientwindow.h
index 324e7691ff..6c5695d62f 100644
--- a/src/plugins/platforms/mirclient/qmirclientwindow.h
+++ b/src/plugins/platforms/mirclient/qmirclientwindow.h
@@ -73,7 +73,7 @@ public:
WId winId() const override;
QRect geometry() const override;
void setGeometry(const QRect&) override;
- void setWindowState(Qt::WindowState state) override;
+ void setWindowState(Qt::WindowStates state) override;
void setWindowFlags(Qt::WindowFlags flags) override;
void setVisible(bool visible) override;
void setWindowTitle(const QString &title) override;
diff --git a/src/plugins/platforms/offscreen/main.cpp b/src/plugins/platforms/offscreen/main.cpp
index 9750c4f7ca..207db60f3a 100644
--- a/src/plugins/platforms/offscreen/main.cpp
+++ b/src/plugins/platforms/offscreen/main.cpp
@@ -48,7 +48,7 @@ class QOffscreenIntegrationPlugin : public QPlatformIntegrationPlugin
Q_OBJECT
Q_PLUGIN_METADATA(IID QPlatformIntegrationFactoryInterface_iid FILE "offscreen.json")
public:
- QPlatformIntegration *create(const QString&, const QStringList&) Q_DECL_OVERRIDE;
+ QPlatformIntegration *create(const QString&, const QStringList&) override;
};
QPlatformIntegration *QOffscreenIntegrationPlugin::create(const QString& system, const QStringList& paramList)
diff --git a/src/plugins/platforms/offscreen/qoffscreencommon.cpp b/src/plugins/platforms/offscreen/qoffscreencommon.cpp
index 85422071aa..f0eb69718a 100644
--- a/src/plugins/platforms/offscreen/qoffscreencommon.cpp
+++ b/src/plugins/platforms/offscreen/qoffscreencommon.cpp
@@ -55,8 +55,8 @@ class QOffscreenCursor : public QPlatformCursor
public:
QOffscreenCursor() : m_pos(10, 10) {}
- QPoint pos() const Q_DECL_OVERRIDE { return m_pos; }
- void setPos(const QPoint &pos) Q_DECL_OVERRIDE
+ QPoint pos() const override { return m_pos; }
+ void setPos(const QPoint &pos) override
{
m_pos = pos;
const QWindowList wl = QGuiApplication::topLevelWindows();
@@ -82,7 +82,7 @@ public:
QOffscreenScreen::windowContainingCursor = containing ? containing->handle() : 0;
}
#ifndef QT_NO_CURSOR
- void changeCursor(QCursor *windowCursor, QWindow *window) Q_DECL_OVERRIDE
+ void changeCursor(QCursor *windowCursor, QWindow *window) override
{
Q_UNUSED(windowCursor);
Q_UNUSED(window);
diff --git a/src/plugins/platforms/offscreen/qoffscreencommon.h b/src/plugins/platforms/offscreen/qoffscreencommon.h
index 72d6f16d26..541c07384c 100644
--- a/src/plugins/platforms/offscreen/qoffscreencommon.h
+++ b/src/plugins/platforms/offscreen/qoffscreencommon.h
@@ -57,12 +57,12 @@ class QOffscreenScreen : public QPlatformScreen
public:
QOffscreenScreen();
- QRect geometry() const Q_DECL_OVERRIDE { return m_geometry; }
- int depth() const Q_DECL_OVERRIDE { return 32; }
- QImage::Format format() const Q_DECL_OVERRIDE { return QImage::Format_RGB32; }
- QPlatformCursor *cursor() const Q_DECL_OVERRIDE { return m_cursor.data(); }
+ QRect geometry() const override { return m_geometry; }
+ int depth() const override { return 32; }
+ QImage::Format format() const override { return QImage::Format_RGB32; }
+ QPlatformCursor *cursor() const override { return m_cursor.data(); }
- QPixmap grabWindow(WId window, int x, int y, int width, int height) const Q_DECL_OVERRIDE;
+ QPixmap grabWindow(WId window, int x, int y, int width, int height) const override;
static QPlatformWindow *windowContainingCursor;
@@ -75,8 +75,7 @@ public:
class QOffscreenDrag : public QPlatformDrag
{
public:
- QMimeData *platformDropData() Q_DECL_OVERRIDE { return 0; }
- Qt::DropAction drag(QDrag *) Q_DECL_OVERRIDE { return Qt::IgnoreAction; }
+ Qt::DropAction drag(QDrag *) override { return Qt::IgnoreAction; }
};
#endif
@@ -86,10 +85,10 @@ public:
QOffscreenBackingStore(QWindow *window);
~QOffscreenBackingStore();
- QPaintDevice *paintDevice() Q_DECL_OVERRIDE;
- void flush(QWindow *window, const QRegion &region, const QPoint &offset) Q_DECL_OVERRIDE;
- void resize(const QSize &size, const QRegion &staticContents) Q_DECL_OVERRIDE;
- bool scroll(const QRegion &area, int dx, int dy) Q_DECL_OVERRIDE;
+ QPaintDevice *paintDevice() override;
+ void flush(QWindow *window, const QRegion &region, const QPoint &offset) override;
+ void resize(const QSize &size, const QRegion &staticContents) override;
+ bool scroll(const QRegion &area, int dx, int dy) override;
QPixmap grabWindow(WId window, const QRect &rect) const;
diff --git a/src/plugins/platforms/offscreen/qoffscreenintegration.cpp b/src/plugins/platforms/offscreen/qoffscreenintegration.cpp
index 0c39950019..75bb786b28 100644
--- a/src/plugins/platforms/offscreen/qoffscreenintegration.cpp
+++ b/src/plugins/platforms/offscreen/qoffscreenintegration.cpp
@@ -59,6 +59,9 @@
#include <QtGui/private/qpixmap_raster_p.h>
#include <QtGui/private/qguiapplication_p.h>
+#include <qpa/qplatforminputcontextfactory_p.h>
+#include <qpa/qplatforminputcontext.h>
+#include <qpa/qplatformtheme.h>
#include <qpa/qplatformservices.h>
@@ -118,6 +121,16 @@ QOffscreenIntegration::~QOffscreenIntegration()
{
}
+void QOffscreenIntegration::initialize()
+{
+ m_inputContext.reset(QPlatformInputContextFactory::create());
+}
+
+QPlatformInputContext *QOffscreenIntegration::inputContext() const
+{
+ return m_inputContext.data();
+}
+
bool QOffscreenIntegration::hasCapability(QPlatformIntegration::Capability cap) const
{
switch (cap) {
@@ -155,6 +168,37 @@ QAbstractEventDispatcher *QOffscreenIntegration::createEventDispatcher() const
#endif
}
+static QString themeName() { return QStringLiteral("offscreen"); }
+
+QStringList QOffscreenIntegration::themeNames() const
+{
+ return QStringList(themeName());
+}
+
+// Restrict the styles to "fusion" to prevent native styles requiring native
+// window handles (eg Windows Vista style) from being used.
+class OffscreenTheme : public QPlatformTheme
+{
+public:
+ OffscreenTheme() {}
+
+ QVariant themeHint(ThemeHint h) const override
+ {
+ switch (h) {
+ case StyleNames:
+ return QVariant(QStringList(QStringLiteral("fusion")));
+ default:
+ break;
+ }
+ return QPlatformTheme::themeHint(h);
+ }
+};
+
+QPlatformTheme *QOffscreenIntegration::createPlatformTheme(const QString &name) const
+{
+ return name == themeName() ? new OffscreenTheme() : nullptr;
+}
+
QPlatformFontDatabase *QOffscreenIntegration::fontDatabase() const
{
return m_fontDatabase.data();
diff --git a/src/plugins/platforms/offscreen/qoffscreenintegration.h b/src/plugins/platforms/offscreen/qoffscreenintegration.h
index df647a31ef..a1e3a9bde8 100644
--- a/src/plugins/platforms/offscreen/qoffscreenintegration.h
+++ b/src/plugins/platforms/offscreen/qoffscreenintegration.h
@@ -54,17 +54,23 @@ public:
QOffscreenIntegration();
~QOffscreenIntegration();
- bool hasCapability(QPlatformIntegration::Capability cap) const Q_DECL_OVERRIDE;
+ void initialize() override;
+ bool hasCapability(QPlatformIntegration::Capability cap) const override;
- QPlatformWindow *createPlatformWindow(QWindow *window) const Q_DECL_OVERRIDE;
- QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const Q_DECL_OVERRIDE;
+ QPlatformWindow *createPlatformWindow(QWindow *window) const override;
+ QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const override;
#ifndef QT_NO_DRAGANDDROP
- QPlatformDrag *drag() const Q_DECL_OVERRIDE;
+ QPlatformDrag *drag() const override;
#endif
- QPlatformServices *services() const Q_DECL_OVERRIDE;
- QPlatformFontDatabase *fontDatabase() const Q_DECL_OVERRIDE;
- QAbstractEventDispatcher *createEventDispatcher() const Q_DECL_OVERRIDE;
+ QPlatformInputContext *inputContext() const override;
+ QPlatformServices *services() const override;
+
+ QPlatformFontDatabase *fontDatabase() const override;
+ QAbstractEventDispatcher *createEventDispatcher() const override;
+
+ QStringList themeNames() const override;
+ QPlatformTheme *createPlatformTheme(const QString &name) const override;
static QOffscreenIntegration *createOffscreenIntegration();
@@ -73,6 +79,7 @@ private:
#ifndef QT_NO_DRAGANDDROP
QScopedPointer<QPlatformDrag> m_drag;
#endif
+ QScopedPointer<QPlatformInputContext> m_inputContext;
QScopedPointer<QPlatformServices> m_services;
};
diff --git a/src/plugins/platforms/offscreen/qoffscreenintegration_x11.h b/src/plugins/platforms/offscreen/qoffscreenintegration_x11.h
index aaca74d2fb..5e1c6b799b 100644
--- a/src/plugins/platforms/offscreen/qoffscreenintegration_x11.h
+++ b/src/plugins/platforms/offscreen/qoffscreenintegration_x11.h
@@ -55,9 +55,9 @@ class QOffscreenX11Info;
class QOffscreenX11Integration : public QOffscreenIntegration
{
public:
- bool hasCapability(QPlatformIntegration::Capability cap) const Q_DECL_OVERRIDE;
+ bool hasCapability(QPlatformIntegration::Capability cap) const override;
- QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const Q_DECL_OVERRIDE;
+ QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const override;
private:
mutable QScopedPointer<QOffscreenX11Connection> m_connection;
@@ -88,14 +88,14 @@ public:
QOffscreenX11GLXContext(QOffscreenX11Info *x11, QOpenGLContext *context);
~QOffscreenX11GLXContext();
- bool makeCurrent(QPlatformSurface *surface) Q_DECL_OVERRIDE;
- void doneCurrent() Q_DECL_OVERRIDE;
- void swapBuffers(QPlatformSurface *surface) Q_DECL_OVERRIDE;
- QFunctionPointer getProcAddress(const char *procName) Q_DECL_OVERRIDE;
+ bool makeCurrent(QPlatformSurface *surface) override;
+ void doneCurrent() override;
+ void swapBuffers(QPlatformSurface *surface) override;
+ QFunctionPointer getProcAddress(const char *procName) override;
- QSurfaceFormat format() const Q_DECL_OVERRIDE;
- bool isSharing() const Q_DECL_OVERRIDE;
- bool isValid() const Q_DECL_OVERRIDE;
+ QSurfaceFormat format() const override;
+ bool isSharing() const override;
+ bool isValid() const override;
private:
QScopedPointer<QOffscreenX11GLXContextData> d;
diff --git a/src/plugins/platforms/offscreen/qoffscreenwindow.cpp b/src/plugins/platforms/offscreen/qoffscreenwindow.cpp
index 81f262f9ed..832e94034d 100644
--- a/src/plugins/platforms/offscreen/qoffscreenwindow.cpp
+++ b/src/plugins/platforms/offscreen/qoffscreenwindow.cpp
@@ -56,7 +56,7 @@ QOffscreenWindow::QOffscreenWindow(QWindow *window)
if (window->windowState() == Qt::WindowNoState)
setGeometry(window->geometry());
else
- setWindowState(window->windowState());
+ setWindowState(window->windowStates());
QWindowSystemInterface::flushWindowSystemEvents();
@@ -166,26 +166,19 @@ void QOffscreenWindow::setFrameMarginsEnabled(bool enabled)
}
}
-void QOffscreenWindow::setWindowState(Qt::WindowState state)
+void QOffscreenWindow::setWindowState(Qt::WindowStates state)
{
- setFrameMarginsEnabled(state != Qt::WindowFullScreen);
+ setFrameMarginsEnabled(!(state & Qt::WindowFullScreen));
m_positionIncludesFrame = false;
- switch (state) {
- case Qt::WindowFullScreen:
+ if (state & Qt::WindowMinimized)
+ ; // nothing to do
+ else if (state & Qt::WindowFullScreen)
setGeometryImpl(screen()->geometry());
- break;
- case Qt::WindowMaximized:
+ else if (state & Qt::WindowMaximized)
setGeometryImpl(screen()->availableGeometry().adjusted(m_margins.left(), m_margins.top(), -m_margins.right(), -m_margins.bottom()));
- break;
- case Qt::WindowMinimized:
- break;
- case Qt::WindowNoState:
+ else
setGeometryImpl(m_normalGeometry);
- break;
- default:
- break;
- }
QWindowSystemInterface::handleWindowStateChanged(window(), state);
}
diff --git a/src/plugins/platforms/offscreen/qoffscreenwindow.h b/src/plugins/platforms/offscreen/qoffscreenwindow.h
index f75458eb8e..e1f37bb034 100644
--- a/src/plugins/platforms/offscreen/qoffscreenwindow.h
+++ b/src/plugins/platforms/offscreen/qoffscreenwindow.h
@@ -53,15 +53,15 @@ public:
QOffscreenWindow(QWindow *window);
~QOffscreenWindow();
- void setGeometry(const QRect &rect) Q_DECL_OVERRIDE;
- void setWindowState(Qt::WindowState state) Q_DECL_OVERRIDE;
+ void setGeometry(const QRect &rect) override;
+ void setWindowState(Qt::WindowStates states) override;
- QMargins frameMargins() const Q_DECL_OVERRIDE;
+ QMargins frameMargins() const override;
- void setVisible(bool visible) Q_DECL_OVERRIDE;
- void requestActivateWindow() Q_DECL_OVERRIDE;
+ void setVisible(bool visible) override;
+ void requestActivateWindow() override;
- WId winId() const Q_DECL_OVERRIDE;
+ WId winId() const override;
static QOffscreenWindow *windowForWinId(WId id);
diff --git a/src/plugins/platforms/qnx/qqnxrasterbackingstore.cpp b/src/plugins/platforms/qnx/qqnxrasterbackingstore.cpp
index a758bdf7f4..dd7f907ee0 100644
--- a/src/plugins/platforms/qnx/qqnxrasterbackingstore.cpp
+++ b/src/plugins/platforms/qnx/qqnxrasterbackingstore.cpp
@@ -139,6 +139,7 @@ void QQnxRasterBackingStore::beginPaint(const QRegion &region)
platformWindow()->adjustBufferSize();
if (window()->requestedFormat().alphaBufferSize() > 0) {
+ auto platformScreen = static_cast<QQnxScreen *>(platformWindow()->screen());
for (const QRect &r : region) {
// Clear transparent regions
const int bg[] = {
@@ -149,11 +150,11 @@ void QQnxRasterBackingStore::beginPaint(const QRegion &region)
SCREEN_BLIT_DESTINATION_HEIGHT, r.height(),
SCREEN_BLIT_END
};
- Q_SCREEN_CHECKERROR(screen_fill(platformWindow()->screen()->nativeContext(),
+ Q_SCREEN_CHECKERROR(screen_fill(platformScreen->nativeContext(),
platformWindow()->renderBuffer().nativeBuffer(), bg),
"failed to clear transparent regions");
}
- Q_SCREEN_CHECKERROR(screen_flush_blits(platformWindow()->screen()->nativeContext(),
+ Q_SCREEN_CHECKERROR(screen_flush_blits(platformScreen->nativeContext(),
SCREEN_WAIT_IDLE), "failed to flush blits");
}
}
diff --git a/src/plugins/platforms/qnx/qqnxrasterwindow.cpp b/src/plugins/platforms/qnx/qqnxrasterwindow.cpp
index 7f11de228e..dc844189d1 100644
--- a/src/plugins/platforms/qnx/qqnxrasterwindow.cpp
+++ b/src/plugins/platforms/qnx/qqnxrasterwindow.cpp
@@ -141,6 +141,7 @@ QQnxBuffer &QQnxRasterWindow::renderBuffer()
// Check if render buffer is invalid
if (m_currentBufferIndex == -1) {
+ auto platformScreen = static_cast<QQnxScreen *>(screen());
// Get all buffers available for rendering
screen_buffer_t buffers[MAX_BUFFER_COUNT];
const int result = screen_get_window_property_pv(nativeHandle(), SCREEN_PROPERTY_RENDER_BUFFERS,
@@ -153,11 +154,11 @@ QQnxBuffer &QQnxRasterWindow::renderBuffer()
// Clear Buffer
int bg[] = { SCREEN_BLIT_COLOR, 0x00000000, SCREEN_BLIT_END };
- Q_SCREEN_CHECKERROR(screen_fill(screen()->nativeContext(), buffers[i], bg),
+ Q_SCREEN_CHECKERROR(screen_fill(platformScreen->nativeContext(), buffers[i], bg),
"Failed to clear window buffer");
}
- Q_SCREEN_CHECKERROR(screen_flush_blits(screen()->nativeContext(), 0),
+ Q_SCREEN_CHECKERROR(screen_flush_blits(platformScreen->nativeContext(), 0),
"Failed to flush blits");
// Use the first available render buffer
@@ -185,7 +186,7 @@ void QQnxRasterWindow::adjustBufferSize()
int QQnxRasterWindow::pixelFormat() const
{
- return screen()->nativeFormat();
+ return static_cast<QQnxScreen *>(screen())->nativeFormat();
}
void QQnxRasterWindow::resetBuffers()
diff --git a/src/plugins/platforms/qnx/qqnxwindow.cpp b/src/plugins/platforms/qnx/qqnxwindow.cpp
index 30288ccb20..38b61fd782 100644
--- a/src/plugins/platforms/qnx/qqnxwindow.cpp
+++ b/src/plugins/platforms/qnx/qqnxwindow.cpp
@@ -479,7 +479,7 @@ void QQnxWindow::setParent(const QPlatformWindow *window)
if (newParent == m_parentWindow)
return;
- if (screen()->rootWindow() == this) {
+ if (static_cast<QQnxScreen *>(screen())->rootWindow() == this) {
qWarning("Application window cannot be reparented");
return;
}
@@ -539,7 +539,7 @@ void QQnxWindow::requestActivateWindow()
if (focusWindow == this)
return;
- if (screen()->rootWindow() == this ||
+ if (static_cast<QQnxScreen *>(screen())->rootWindow() == this ||
(focusWindow && findWindow(focusWindow->nativeHandle()))) {
// If the focus window is a child, we can just set the focus of our own window
// group to our window handle
@@ -550,6 +550,7 @@ void QQnxWindow::requestActivateWindow()
QQnxWindow *currentWindow = this;
QList<QQnxWindow*> windowList;
while (currentWindow) {
+ auto platformScreen = static_cast<QQnxScreen *>(screen());
windowList.prepend(currentWindow);
// If we find the focus window, we don't have to go further
if (currentWindow == focusWindow)
@@ -557,9 +558,9 @@ void QQnxWindow::requestActivateWindow()
if (currentWindow->parent()){
currentWindow = static_cast<QQnxWindow*>(currentWindow->parent());
- } else if (screen()->rootWindow() &&
- screen()->rootWindow()->m_windowGroupName == currentWindow->m_parentGroupName) {
- currentWindow = screen()->rootWindow();
+ } else if (platformScreen->rootWindow() &&
+ platformScreen->rootWindow()->m_windowGroupName == currentWindow->m_parentGroupName) {
+ currentWindow = platformScreen->rootWindow();
} else {
currentWindow = 0;
}
@@ -586,7 +587,7 @@ void QQnxWindow::setFocus(screen_window_t newFocusWindow)
}
}
-void QQnxWindow::setWindowState(Qt::WindowState state)
+void QQnxWindow::setWindowState(Qt::WindowStates state)
{
qWindowDebug() << "state =" << state;
@@ -622,6 +623,11 @@ void QQnxWindow::clearMMRendererWindow()
m_mmRendererWindow = 0;
}
+QPlatformScreen *QQnxWindow::screen() const
+{
+ return m_screen;
+}
+
QQnxWindow *QQnxWindow::findWindow(screen_window_t windowHandle)
{
if (m_window == windowHandle)
@@ -749,32 +755,19 @@ void QQnxWindow::updateZorder(screen_window_t window, int &topZorder)
void QQnxWindow::applyWindowState()
{
- switch (m_windowState) {
-
- // WindowActive is not an accepted parameter according to the docs
- case Qt::WindowActive:
- return;
-
- case Qt::WindowMinimized:
+ if (m_windowState & Qt::WindowMinimized) {
minimize();
if (m_unmaximizedGeometry.isValid())
setGeometry(m_unmaximizedGeometry);
else
setGeometry(m_screen->geometry());
-
- break;
-
- case Qt::WindowMaximized:
- case Qt::WindowFullScreen:
+ } else if (m_windowState & (Qt::WindowMaximized | Qt::WindowFullScreen)) {
m_unmaximizedGeometry = geometry();
- setGeometry(m_windowState == Qt::WindowMaximized ? m_screen->availableGeometry() : m_screen->geometry());
- break;
-
- case Qt::WindowNoState:
- if (m_unmaximizedGeometry.isValid())
- setGeometry(m_unmaximizedGeometry);
- break;
+ setGeometry(m_windowState & Qt::WindowFullScreen ? m_screen->geometry()
+ : m_screen->availableGeometry());
+ } else if (m_unmaximizedGeometry.isValid()) {
+ setGeometry(m_unmaximizedGeometry);
}
}
@@ -788,7 +781,8 @@ void QQnxWindow::windowPosted()
bool QQnxWindow::shouldMakeFullScreen() const
{
- return ((screen()->rootWindow() == this) && (QQnxIntegration::options() & QQnxIntegration::FullScreenApplication));
+ return ((static_cast<QQnxScreen *>(screen())->rootWindow() == this)
+ && (QQnxIntegration::options() & QQnxIntegration::FullScreenApplication));
}
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/qnx/qqnxwindow.h b/src/plugins/platforms/qnx/qqnxwindow.h
index 223ea6e349..2895a547b1 100644
--- a/src/plugins/platforms/qnx/qqnxwindow.h
+++ b/src/plugins/platforms/qnx/qqnxwindow.h
@@ -85,7 +85,7 @@ public:
void raise() override;
void lower() override;
void requestActivateWindow() override;
- void setWindowState(Qt::WindowState state) override;
+ void setWindowState(Qt::WindowStates state) override;
void setExposed(bool exposed);
void propagateSizeHints() override;
@@ -94,7 +94,7 @@ public:
void setMMRendererWindow(screen_window_t handle);
void clearMMRendererWindow();
- QQnxScreen *screen() const { return m_screen; }
+ QPlatformScreen *screen() const override;
const QList<QQnxWindow*>& children() const { return m_childWindows; }
QQnxWindow *findWindow(screen_window_t windowHandle);
@@ -142,7 +142,7 @@ private:
bool m_visible;
bool m_exposed;
QRect m_unmaximizedGeometry;
- Qt::WindowState m_windowState;
+ Qt::WindowStates m_windowState;
QString m_mmRendererWindowName;
screen_window_t m_mmRendererWindow;
diff --git a/src/plugins/platforms/vnc/qvnc.cpp b/src/plugins/platforms/vnc/qvnc.cpp
index fa65e8c9a4..44fc1c6101 100644
--- a/src/plugins/platforms/vnc/qvnc.cpp
+++ b/src/plugins/platforms/vnc/qvnc.cpp
@@ -476,7 +476,8 @@ void QRfbRawEncoder::write()
// rgn &= QRect(0, 0, server->screen()->geometry().width(),
// server->screen()->geometry().height());
// }
- const QVector<QRect> rects = rgn.rects();
+
+ const auto rectsInRegion = rgn.rectCount();
{
const char tmp[2] = { 0, 0 }; // msg type, padding
@@ -484,16 +485,16 @@ void QRfbRawEncoder::write()
}
{
- const quint16 count = htons(rects.size());
+ const quint16 count = htons(rectsInRegion);
socket->write((char *)&count, sizeof(count));
}
- if (rects.size() <= 0)
+ if (rectsInRegion <= 0)
return;
const QImage screenImage = client->server()->screenImage();
- for (const QRect &tileRect: rects) {
+ for (const QRect &tileRect: rgn) {
const QRfbRect rect(tileRect.x(), tileRect.y(),
tileRect.width(), tileRect.height());
rect.write(socket);
diff --git a/src/plugins/platforms/vnc/qvncintegration.cpp b/src/plugins/platforms/vnc/qvncintegration.cpp
index 8516e994f5..1e2cf6292c 100644
--- a/src/plugins/platforms/vnc/qvncintegration.cpp
+++ b/src/plugins/platforms/vnc/qvncintegration.cpp
@@ -52,9 +52,6 @@
#include <QtGui/private/qguiapplication_p.h>
#include <qpa/qplatforminputcontextfactory_p.h>
#include <private/qinputdevicemanager_p_p.h>
-#if QT_CONFIG(libinput)
-#include <QtInputSupport/private/qlibinputhandler_p.h>
-#endif
#include <QtCore/QRegularExpression>
diff --git a/src/plugins/platforms/windows/accessible/accessible.pri b/src/plugins/platforms/windows/accessible/accessible.pri
deleted file mode 100644
index 0e3aacc558..0000000000
--- a/src/plugins/platforms/windows/accessible/accessible.pri
+++ /dev/null
@@ -1,18 +0,0 @@
-SOURCES += \
- $$PWD/qwindowsaccessibility.cpp \
- $$PWD/comutils.cpp
-
-HEADERS += \
- $$PWD/qwindowsaccessibility.h \
- $$PWD/comutils.h
-
-SOURCES += $$PWD/qwindowsmsaaaccessible.cpp
-HEADERS += $$PWD/qwindowsmsaaaccessible.h
-
-!mingw: {
- SOURCES += $$PWD/iaccessible2.cpp
- HEADERS += $$PWD/iaccessible2.h
- include(../../../../3rdparty/iaccessible2/iaccessible2.pri)
-}
-
-mingw: LIBS *= -luuid
diff --git a/src/plugins/platforms/windows/accessible/comutils.cpp b/src/plugins/platforms/windows/accessible/comutils.cpp
deleted file mode 100644
index 1c072c5e2c..0000000000
--- a/src/plugins/platforms/windows/accessible/comutils.cpp
+++ /dev/null
@@ -1,280 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <qt_windows.h>
-
-#include <ocidl.h>
-#include <olectl.h>
-
-#include "comutils.h"
-#include <QtCore/qdatetime.h>
-#include <QtGui/qpixmap.h>
-#include <QtGui/qfont.h>
-
-
-#include <QtCore/qvariant.h>
-#include <QtCore/qbytearray.h>
-#include <QtGui/qcolor.h>
-
-QT_BEGIN_NAMESPACE
-
-static DATE QDateTimeToDATE(const QDateTime &dt)
-{
- if (!dt.isValid() || dt.isNull())
- return 949998; // Special value for no date (01/01/4501)
-
- SYSTEMTIME stime;
- memset(&stime, 0, sizeof(stime));
- QDate date = dt.date();
- QTime time = dt.time();
- if (date.isValid() && !date.isNull()) {
- stime.wDay = WORD(date.day());
- stime.wMonth = WORD(date.month());
- stime.wYear = WORD(date.year());
- }
- if (time.isValid() && !time.isNull()) {
- stime.wMilliseconds = WORD(time.msec());
- stime.wSecond = WORD(time.second());
- stime.wMinute = WORD(time.minute());
- stime.wHour = WORD(time.hour());
- }
-
- double vtime;
- SystemTimeToVariantTime(&stime, &vtime);
-
- return vtime;
-}
-
-inline uint QColorToOLEColor(const QColor &col)
-{
- return qRgba(col.blue(), col.green(), col.red(), 0x00);
-}
-
-bool QVariant2VARIANT(const QVariant &var, VARIANT &arg, const QByteArray &typeName, bool out)
-{
- QVariant qvar = var;
- // "type" is the expected type, so coerce if necessary
- QVariant::Type proptype = typeName.isEmpty() ? QVariant::Invalid : QVariant::nameToType(typeName);
- if (proptype == QVariant::UserType && !typeName.isEmpty()) {
- if (typeName == "short" || typeName == "char")
- proptype = QVariant::Int;
- else if (typeName == "float")
- proptype = QVariant::Double;
- }
- if (proptype != QVariant::Invalid && proptype != QVariant::UserType && proptype != qvar.type()) {
- if (qvar.canConvert(int(proptype)))
- qvar.convert(int(proptype));
- else
- qvar = QVariant(proptype);
- }
-
- if (out && arg.vt == (VT_VARIANT|VT_BYREF) && arg.pvarVal) {
- return QVariant2VARIANT(var, *arg.pvarVal, typeName, false);
- }
-
- if (out && proptype == QVariant::UserType && typeName == "QVariant") {
- VARIANT *pVariant = new VARIANT;
- QVariant2VARIANT(var, *pVariant, QByteArray(), false);
- arg.vt = VT_VARIANT|VT_BYREF;
- arg.pvarVal = pVariant;
- return true;
- }
-
- switch ((int)qvar.type()) {
- case QVariant::String:
- if (out && arg.vt == (VT_BSTR|VT_BYREF)) {
- if (*arg.pbstrVal)
- SysFreeString(*arg.pbstrVal);
- *arg.pbstrVal = QStringToBSTR(qvar.toString());
- arg.vt = VT_BSTR|VT_BYREF;
- } else {
- arg.vt = VT_BSTR;
- arg.bstrVal = QStringToBSTR(qvar.toString());
- if (out) {
- arg.pbstrVal = new BSTR(arg.bstrVal);
- arg.vt |= VT_BYREF;
- }
- }
- break;
-
- case QVariant::Int:
- if (out && arg.vt == (VT_I4|VT_BYREF)) {
- *arg.plVal = qvar.toInt();
- } else {
- arg.vt = VT_I4;
- arg.lVal = qvar.toInt();
- if (out) {
- if (typeName == "short") {
- arg.vt = VT_I2;
- arg.piVal = new short(arg.lVal);
- } else if (typeName == "char") {
- arg.vt = VT_I1;
- arg.pcVal= new char(arg.lVal);
- } else {
- arg.plVal = new long(arg.lVal);
- }
- arg.vt |= VT_BYREF;
- }
- }
- break;
-
- case QVariant::UInt:
- if (out && (arg.vt == (VT_UINT|VT_BYREF) || arg.vt == (VT_I4|VT_BYREF))) {
- *arg.puintVal = qvar.toUInt();
- } else {
- arg.vt = VT_UINT;
- arg.uintVal = qvar.toUInt();
- if (out) {
- arg.puintVal = new uint(arg.uintVal);
- arg.vt |= VT_BYREF;
- }
- }
- break;
-
- case QVariant::LongLong:
- if (out && arg.vt == (VT_CY|VT_BYREF)) {
- arg.pcyVal->int64 = qvar.toLongLong();
- } else if (out && arg.vt == (VT_I8|VT_BYREF)) {
- *arg.pllVal = qvar.toLongLong();
- } else {
- arg.vt = VT_I8;
- arg.llVal = qvar.toLongLong();
- if (out) {
- arg.pllVal = new LONGLONG(arg.llVal);
- arg.vt |= VT_BYREF;
- }
- }
- break;
-
- case QVariant::ULongLong:
- if (out && arg.vt == (VT_CY|VT_BYREF)) {
- arg.pcyVal->int64 = qvar.toULongLong();
- } else if (out && arg.vt == (VT_UI8|VT_BYREF)) {
- *arg.pullVal = qvar.toULongLong();
- } else {
- arg.vt = VT_UI8;
- arg.ullVal = qvar.toULongLong();
- if (out) {
- arg.pullVal = new ULONGLONG(arg.ullVal);
- arg.vt |= VT_BYREF;
- }
- }
- break;
-
- case QVariant::Bool:
- if (out && arg.vt == (VT_BOOL|VT_BYREF)) {
- *arg.pboolVal = qvar.toBool() ? VARIANT_TRUE : VARIANT_FALSE;
- } else {
- arg.vt = VT_BOOL;
- arg.boolVal = qvar.toBool() ? VARIANT_TRUE : VARIANT_FALSE;
- if (out) {
- arg.pboolVal = new short(arg.boolVal);
- arg.vt |= VT_BYREF;
- }
- }
- break;
- case QVariant::Double:
- if (out && arg.vt == (VT_R8|VT_BYREF)) {
- *arg.pdblVal = qvar.toDouble();
- } else {
- arg.vt = VT_R8;
- arg.dblVal = qvar.toDouble();
- if (out) {
- if (typeName == "float") {
- arg.vt = VT_R4;
- arg.pfltVal = new float(arg.dblVal);
- } else {
- arg.pdblVal = new double(arg.dblVal);
- }
- arg.vt |= VT_BYREF;
- }
- }
- break;
- case QVariant::Color:
- if (out && arg.vt == (VT_COLOR|VT_BYREF)) {
-
- *arg.plVal = QColorToOLEColor(qvariant_cast<QColor>(qvar));
- } else {
- arg.vt = VT_COLOR;
- arg.lVal = QColorToOLEColor(qvariant_cast<QColor>(qvar));
- if (out) {
- arg.plVal = new long(arg.lVal);
- arg.vt |= VT_BYREF;
- }
- }
- break;
-
- case QVariant::Date:
- case QVariant::Time:
- case QVariant::DateTime:
- if (out && arg.vt == (VT_DATE|VT_BYREF)) {
- *arg.pdate = QDateTimeToDATE(qvar.toDateTime());
- } else {
- arg.vt = VT_DATE;
- arg.date = QDateTimeToDATE(qvar.toDateTime());
- if (out) {
- arg.pdate = new DATE(arg.date);
- arg.vt |= VT_BYREF;
- }
- }
- break;
-
- case QVariant::Invalid: // default-parameters not set
- if (out && arg.vt == (VT_ERROR|VT_BYREF)) {
- *arg.plVal = DISP_E_PARAMNOTFOUND;
- } else {
- arg.vt = VT_ERROR;
- arg.lVal = DISP_E_PARAMNOTFOUND;
- if (out) {
- arg.plVal = new long(arg.lVal);
- arg.vt |= VT_BYREF;
- }
- }
- break;
-
- default:
- return false;
- }
-
- Q_ASSERT(!out || (arg.vt & VT_BYREF));
- return true;
-}
-
-QT_END_NAMESPACE
-
diff --git a/src/plugins/platforms/windows/accessible/iaccessible2.cpp b/src/plugins/platforms/windows/accessible/iaccessible2.cpp
deleted file mode 100644
index f9e8231843..0000000000
--- a/src/plugins/platforms/windows/accessible/iaccessible2.cpp
+++ /dev/null
@@ -1,1744 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#include <QtCore/QtConfig>
-#ifndef QT_NO_ACCESSIBILITY
-
-#include "iaccessible2.h"
-#include "qwindowsaccessibility.h"
-#include <QtAccessibilitySupport/private/qaccessiblebridgeutils_p.h>
-#include <QtGui/qaccessible.h>
-#include <QtGui/qclipboard.h>
-#include <QtGui/qguiapplication.h>
-#include <QtGui/private/qhighdpiscaling_p.h>
-#include <QtCore/qdebug.h>
-
-#include <algorithm>
-
-QT_BEGIN_NAMESPACE
-
-template <class T>
-static inline T *coTaskMemAllocArray(int size)
-{
- return static_cast<T *>(::CoTaskMemAlloc(sizeof(T) * size_t(size)));
-}
-
-/**************************************************************\
- * AccessibleApplication *
- **************************************************************/
-// IUnknown
-HRESULT STDMETHODCALLTYPE AccessibleApplication::QueryInterface(REFIID id, LPVOID *iface)
-{
- *iface = 0;
- if (id == IID_IUnknown) {
- qCDebug(lcQpaAccessibility) << "AccessibleApplication::QI(): IID_IUnknown";
- *iface = static_cast<IUnknown *>(this);
- } else if (id == IID_IAccessibleApplication) {
- qCDebug(lcQpaAccessibility) << "AccessibleApplication::QI(): IID_IAccessibleApplication";
- *iface = static_cast<IAccessibleApplication*>(this);
- }
-
- if (*iface) {
- AddRef();
- return S_OK;
- }
- return E_NOINTERFACE;
-}
-
-ULONG STDMETHODCALLTYPE AccessibleApplication::AddRef()
-{
- return ++m_ref;
-}
-
-ULONG STDMETHODCALLTYPE AccessibleApplication::Release()
-{
- if (!--m_ref) {
- delete this;
- return 0;
- }
- return m_ref;
-}
-
-/* IAccessibleApplication */
-HRESULT STDMETHODCALLTYPE AccessibleApplication::get_appName(/* [retval][out] */ BSTR *name)
-{
- const QString appName = QGuiApplication::applicationName();
- *name = QStringToBSTR(appName);
- return S_OK;
-}
-
-HRESULT STDMETHODCALLTYPE AccessibleApplication::get_appVersion(/* [retval][out] */ BSTR *version)
-{
- const QString appName = QGuiApplication::applicationVersion();
- *version = QStringToBSTR(appName);
- return S_OK;
-}
-
-HRESULT STDMETHODCALLTYPE AccessibleApplication::get_toolkitName(/* [retval][out] */ BSTR *name)
-{
- *name = ::SysAllocString(L"Qt");
- return S_OK;
-}
-
-HRESULT STDMETHODCALLTYPE AccessibleApplication::get_toolkitVersion(/* [retval][out] */ BSTR *version)
-{
- *version = ::SysAllocString(TEXT(QT_VERSION_STR));
- return S_OK;
-}
-
-
-/**************************************************************\
- * AccessibleRelation *
- **************************************************************/
-AccessibleRelation::AccessibleRelation(const QList<QAccessibleInterface *> &targets,
- QAccessible::Relation relation)
- : m_targets(targets), m_relation(relation), m_ref(1)
-{
- Q_ASSERT(m_targets.count());
-}
-
-/* IUnknown */
-HRESULT STDMETHODCALLTYPE AccessibleRelation::QueryInterface(REFIID id, LPVOID *iface)
-{
- *iface = 0;
- if (id == IID_IUnknown || id == IID_IAccessibleRelation)
- *iface = static_cast<IUnknown *>(this);
-
- if (*iface) {
- AddRef();
- return S_OK;
- }
-
- return E_NOINTERFACE;
-}
-
-ULONG STDMETHODCALLTYPE AccessibleRelation::AddRef()
-{
- return ++m_ref;
-}
-
-ULONG STDMETHODCALLTYPE AccessibleRelation::Release()
-{
- if (!--m_ref) {
- delete this;
- return 0;
- }
- return m_ref;
-}
-
-/* IAccessibleRelation */
-HRESULT STDMETHODCALLTYPE AccessibleRelation::get_relationType(
- /* [retval][out] */ BSTR *relationType)
-{
- *relationType = relationToBSTR(m_relation);
- return S_OK;
-}
-
-HRESULT STDMETHODCALLTYPE AccessibleRelation::get_localizedRelationType(
- /* [retval][out] */ BSTR *localizedRelationType)
-{
- // Who ever needs this???
- *localizedRelationType = relationToBSTR(m_relation);
- return S_OK;
-}
-
-HRESULT STDMETHODCALLTYPE AccessibleRelation::get_nTargets(
- /* [retval][out] */ long *nTargets)
-{
- // ### always one target
- *nTargets = m_targets.count();
- return S_OK;
-}
-
-/*!
- \internal
- Client allocates and deallocates array
- (see "Special Consideration when using Arrays", in Accessible2.idl)
- */
-HRESULT STDMETHODCALLTYPE AccessibleRelation::get_target(
- /* [in] */ long targetIndex,
- /* [retval][out] */ IUnknown **target)
-{
- if (targetIndex >= 0 && targetIndex < m_targets.count()) {
- QAccessibleInterface *iface = m_targets.at(targetIndex);
- *target = QWindowsAccessibility::wrap(iface);
- if (*target)
- return S_OK;
- return E_FAIL;
- }
- return E_INVALIDARG;
-}
-
-/*!
- \internal
- Client allocates and deallocates \a targets array
- (see "Special Consideration when using Arrays", in Accessible2.idl)
- */
-HRESULT STDMETHODCALLTYPE AccessibleRelation::get_targets(
- /* [in] */ long maxTargets,
- /* [length_is][size_is][out] */ IUnknown **targets,
- /* [retval][out] */ long *nTargets)
-{
-
- const int numTargets = qMin(int(maxTargets), m_targets.count());
- for (int i = 0; i < numTargets; ++i) {
- QAccessibleInterface *iface = m_targets.at(i);
- IAccessible *iacc = QWindowsAccessibility::wrap(iface);
- if (!iacc)
- return E_FAIL;
- *targets = iacc;
- ++targets;
- }
- *nTargets = numTargets;
- // \a targets array is allocated by client.
- return numTargets > 0 ? S_OK : S_FALSE;
-}
-
-
-/**************************************************************\
- * *
- * IUnknown *
- * *
- **************************************************************/
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::QueryInterface(REFIID id, LPVOID *iface)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- if (!accessible)
- return E_NOINTERFACE;
-
- HRESULT hr = QWindowsMsaaAccessible::QueryInterface(id, iface);
- if (!SUCCEEDED(hr)) {
- if (id == IID_IServiceProvider) {
- *iface = static_cast<IServiceProvider *>(this);
- } else if (id == IID_IAccessible2) {
- *iface = static_cast<IAccessible2 *>(this);
- } else if (id == IID_IAccessibleAction) {
- if (accessible->actionInterface())
- *iface = static_cast<IAccessibleAction *>(this);
- } else if (id == IID_IAccessibleComponent) {
- *iface = static_cast<IAccessibleComponent *>(this);
- } else if (id == IID_IAccessibleEditableText) {
- if (accessible->editableTextInterface() ||
- accessible->role() == QAccessible::EditableText)
- {
- *iface = static_cast<IAccessibleEditableText *>(this);
- }
- } else if (id == IID_IAccessibleHyperlink) {
- //*iface = static_cast<IAccessibleHyperlink *>(this);
- } else if (id == IID_IAccessibleHypertext) {
- //*iface = static_cast<IAccessibleHypertext *>(this);
- } else if (id == IID_IAccessibleImage) {
- //*iface = static_cast<IAccessibleImage *>(this);
- } else if (id == IID_IAccessibleTable) {
- //*iface = static_cast<IAccessibleTable *>(this); // not supported
- } else if (id == IID_IAccessibleTable2) {
- if (accessible->tableInterface())
- *iface = static_cast<IAccessibleTable2 *>(this);
- } else if (id == IID_IAccessibleTableCell) {
- if (accessible->tableCellInterface())
- *iface = static_cast<IAccessibleTableCell *>(this);
- } else if (id == IID_IAccessibleText) {
- if (accessible->textInterface())
- *iface = static_cast<IAccessibleText *>(this);
- } else if (id == IID_IAccessibleValue) {
- if (accessible->valueInterface())
- *iface = static_cast<IAccessibleValue *>(this);
- }
- if (*iface) {
- AddRef();
- hr = S_OK;
- } else {
- hr = E_NOINTERFACE;
- }
- }
- return hr;
-}
-
-
-/* Note that IUnknown is inherited from several interfaces. Therefore we must reimplement all its
- functions in the concrete class to avoid ambiguity.
-*/
-ULONG STDMETHODCALLTYPE QWindowsIA2Accessible::AddRef()
-{
- return QWindowsMsaaAccessible::AddRef();
-}
-
-ULONG STDMETHODCALLTYPE QWindowsIA2Accessible::Release()
-{
- return QWindowsMsaaAccessible::Release();
-}
-
-/**************************************************************\
- * *
- * IAccessible2 *
- * *
- **************************************************************/
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_nRelations(long *nRelations)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!nRelations)
- return E_INVALIDARG;
- if (!accessible)
- return E_FAIL;
-
- return getRelationsHelper(0, 0, 0, nRelations);
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_relation(long relationIndex, IAccessibleRelation **relation)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!relation)
- return E_INVALIDARG;
- if (!accessible)
- return E_FAIL;
-
- return getRelationsHelper(relation, relationIndex, 1);
-}
-
-/*!
- \internal
- Client allocates and deallocates array
- (see "Special Consideration when using Arrays", in Accessible2.idl)
- */
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_relations(long maxRelations,
- IAccessibleRelation **relations,
- long *nRelations)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- return getRelationsHelper(relations, 0, maxRelations, nRelations);
-}
-
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::role(long *ia2role)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- long r = accessible->role();
-
- switch (r) {
- case QAccessible::LayeredPane: r = IA2_ROLE_LAYERED_PANE; break;
- case QAccessible::Terminal: r = IA2_ROLE_TERMINAL; break;
- case QAccessible::Desktop: r = IA2_ROLE_DESKTOP_PANE; break;
- case QAccessible::Paragraph: r = IA2_ROLE_PARAGRAPH; break;
- case QAccessible::Section: r = IA2_ROLE_SECTION; break;
- default: break;
- }
-
- *ia2role = r;
- return S_OK;
-}
-
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::scrollTo(enum IA2ScrollType /*scrollType*/)
-{
- //### Ignore for now
- return E_NOTIMPL;
-}
-
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::scrollToPoint(enum IA2CoordinateType /*coordinateType*/, long /*x*/, long /*y*/)
-{
- //### Ignore for now
- return E_NOTIMPL;
-}
-
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_groupPosition(long *groupLevel,
- long *similarItemsInGroup,
- long *positionInGroup)
-{
- // ### Ignore for now. Not sure what this is used for.....
- *groupLevel = 0; // Not applicable
- *similarItemsInGroup = 0; // Not applicable
- *positionInGroup = 0; // Not applicable
- return S_FALSE;
-}
-
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_states(AccessibleStates *states)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
- if (!states)
- return E_POINTER;
- QAccessible::State st = accessible->state();
- AccessibleStates ia2states = 0;
- if (st.active)
- ia2states |= IA2_STATE_ACTIVE;
- if (st.invalid)
- ia2states |= IA2_STATE_DEFUNCT;
- if (st.editable)
- ia2states |= IA2_STATE_EDITABLE;
- if (st.multiLine)
- ia2states |= IA2_STATE_MULTI_LINE;
- if (st.selectableText)
- ia2states |= IA2_STATE_SELECTABLE_TEXT;
- if (st.supportsAutoCompletion)
- ia2states |= IA2_STATE_SUPPORTS_AUTOCOMPLETION;
-
- *states = ia2states;
- return S_OK;
-}
-
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_extendedRole(BSTR *extendedRole)
-{
- //###
- *extendedRole = 0;
- return E_NOTIMPL; // mozilla does this
- //return S_FALSE;
-}
-
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_localizedExtendedRole(BSTR *localizedExtendedRole)
-{
- //###
- *localizedExtendedRole = 0;
- return E_NOTIMPL; // mozilla does this
- //return S_FALSE;
-}
-
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_nExtendedStates(long *nExtendedStates)
-{
- // Who will ever intepret these values into something meaningful??
- *nExtendedStates = 0;
- return E_NOTIMPL; // mozilla does this
- //return S_FALSE;
-}
-
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_extendedStates(long /*maxExtendedStates*/,
- BSTR **extendedStates,
- long *nExtendedStates)
-{
- *extendedStates = 0;
- *nExtendedStates = 0;
- return E_NOTIMPL; // mozilla does this
- //return S_FALSE;
-}
-
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_localizedExtendedStates(long /*maxLocalizedExtendedStates*/,
- BSTR **localizedExtendedStates,
- long *nLocalizedExtendedStates)
-{
- *localizedExtendedStates = 0;
- *nLocalizedExtendedStates = 0;
- return E_NOTIMPL; // mozilla does this
- //return S_FALSE;
-}
-
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_uniqueID(long *outUniqueID)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- qCDebug(lcQpaAccessibility) << "uniqueID: " << showbase << hex << id;
-
- *outUniqueID = (long)id;
- return int(id) < 0 ? S_OK : S_FALSE;
-}
-
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_windowHandle(HWND *windowHandle)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
- return GetWindow(windowHandle);
-}
-
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_indexInParent(long *indexInParent)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
- if (!indexInParent)
- return E_INVALIDARG;
- QAccessibleInterface *par = accessible->parent();
- *indexInParent = par ? par->indexOfChild(accessible) : -1;
- if (*indexInParent < 0) {
- qCWarning(lcQpaAccessibility) << "index in parent invalid:" << accessible << "parent:" << par;
- return S_FALSE;
- }
- return S_OK;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_locale(IA2Locale *locale)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
- IA2Locale res;
- QLocale l;
- res.country = QStringToBSTR(QLocale::countryToString(l.country()));
- res.language = QStringToBSTR(QLocale::languageToString(l.language()));
- res.variant = QStringToBSTR(QString());
- *locale = res;
- return S_OK;
-}
-
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_attributes(BSTR *attributes)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
- *attributes = 0;//QStringToBSTR(QString());
- return S_FALSE;
-}
-
-/**************************************************************\
- * IAccessibleAction *
- **************************************************************/
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::nActions(long *nActions)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
- *nActions = QAccessibleBridgeUtils::effectiveActionNames(accessible).count();
- return S_OK;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::doAction(long actionIndex)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
- const QStringList actionNames = QAccessibleBridgeUtils::effectiveActionNames(accessible);
- if (actionIndex < 0 || actionIndex >= actionNames.count())
- return E_INVALIDARG;
- const QString actionName = actionNames.at(actionIndex);
- return QAccessibleBridgeUtils::performEffectiveAction(accessible, actionName) ? S_OK : S_FALSE;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_description(long actionIndex, BSTR *description)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
- *description = 0;
- const QStringList actionNames = QAccessibleBridgeUtils::effectiveActionNames(accessible);
- if (actionIndex < 0 || actionIndex >= actionNames.count())
- return E_INVALIDARG;
- const QString actionName = actionNames.at(actionIndex);
- if (QAccessibleActionInterface *actionIface = actionInterface())
- *description = QStringToBSTR(actionIface->localizedActionDescription(actionName));
- else
- *description = QStringToBSTR(qAccessibleLocalizedActionDescription(actionName));
-
- return *description ? S_OK : S_FALSE;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_keyBinding(long actionIndex, long nMaxBindings, BSTR **keyBindings, long *nBindings)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
- Q_UNUSED(nMaxBindings);
- BSTR *arrayOfBindingsToReturn = 0;
- int numBindings = 0;
- if (QAccessibleActionInterface *actionIface = actionInterface()) {
- const QStringList actionNames = actionIface->actionNames();
- if (actionIndex < 0 || actionIndex >= actionNames.count())
- return E_INVALIDARG;
- const QString actionName = actionNames.at(actionIndex);
- const QStringList keyBindings = actionIface->keyBindingsForAction(actionName);
- numBindings = keyBindings.count();
- if (numBindings > 0) {
- // The IDL documents that the client must free with CoTaskMemFree
- arrayOfBindingsToReturn = coTaskMemAllocArray<BSTR>(numBindings);
- std::transform(keyBindings.constBegin(), keyBindings.constEnd(),
- QT_MAKE_CHECKED_ARRAY_ITERATOR(arrayOfBindingsToReturn, numBindings),
- QStringToBSTR);
- }
- }
- *keyBindings = arrayOfBindingsToReturn;
- *nBindings = numBindings;
-
- return numBindings ? S_OK : S_FALSE;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_name(long actionIndex, BSTR *name)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
- *name = 0;
- const QStringList actionNames = QAccessibleBridgeUtils::effectiveActionNames(accessible);
- if (actionIndex < 0 || actionIndex >= actionNames.count())
- return E_INVALIDARG;
- const QString actionName = actionNames.at(actionIndex);
- *name = QStringToBSTR(actionName);
- return *name ? S_OK : S_FALSE;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_localizedName(long actionIndex, BSTR *localizedName)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
- *localizedName = 0;
- const QStringList actionNames = QAccessibleBridgeUtils::effectiveActionNames(accessible);
- if (actionIndex < 0 || actionIndex >= actionNames.count())
- return E_INVALIDARG;
-
- const QString actionName = actionNames.at(actionIndex);
- if (QAccessibleActionInterface *actionIface = actionInterface())
- *localizedName = QStringToBSTR(actionIface->localizedActionName(actionName));
- else
- *localizedName = QStringToBSTR(QAccessibleActionInterface::tr(qPrintable(actionName)));
-
- return *localizedName ? S_OK : S_FALSE;
-}
-
-/**************************************************************\
- * IAccessibleComponent *
- **************************************************************/
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_locationInParent(long *x, long *y)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- QPoint topLeft = accessible->rect().topLeft();
-
- QAccessibleInterface *parentIface = accessible->parent();
- if (parentIface && parentIface->isValid())
- topLeft -= parentIface->rect().topLeft();
- const QPoint nativeTopLeft = QHighDpi::toNativeLocalPosition(topLeft, accessible->window());
-
-
- *x = nativeTopLeft.x();
- *y = nativeTopLeft.y();
- return S_OK;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_foreground(IA2Color *foreground)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- // IA2Color is a typedef for long
- *foreground = static_cast<IA2Color>(accessible->foregroundColor().rgb());
- return S_OK;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_background(IA2Color *background)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- // IA2Color is a typedef for long
- *background = static_cast<IA2Color>(accessible->backgroundColor().rgb());
- return S_OK;
-}
-
-/**************************************************************\
- * IAccessibleEditableText *
- **************************************************************/
-#if QT_CONFIG(clipboard)
-/*!
- \internal
-
- if \a endOffset == -1 it means end of the text
-*/
-QString QWindowsIA2Accessible::textForRange(int startOffset, int endOffset) const
-{
- QAccessibleInterface *accessible = accessibleInterface();
-
- if (QAccessibleTextInterface *textIface = accessible->textInterface()) {
- if (endOffset == IA2_TEXT_OFFSET_LENGTH)
- endOffset = textIface->characterCount();
- return textIface->text(startOffset, endOffset);
- }
- QString txt = accessible->text(QAccessible::Value);
- if (endOffset == IA2_TEXT_OFFSET_LENGTH)
- endOffset = txt.length();
- return txt.mid(startOffset, endOffset - startOffset);
-}
-#endif
-
-/*!
- \internal
-*/
-void QWindowsIA2Accessible::replaceTextFallback(long startOffset, long endOffset, const QString &txt)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- QString t = textForRange(0, -1);
- if (endOffset == IA2_TEXT_OFFSET_LENGTH)
- endOffset = t.length();
- if (endOffset - startOffset == 0) {
- t.insert(startOffset, txt);
- } else {
- t.replace(startOffset, endOffset - startOffset, txt);
- }
- accessible->setText(QAccessible::Value, t);
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::copyText(long startOffset, long endOffset)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
-#if QT_CONFIG(clipboard)
- const QString t = textForRange(startOffset, endOffset);
- QGuiApplication::clipboard()->setText(t);
- return S_OK;
-#else
- return E_NOTIMPL;
-#endif
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::deleteText(long startOffset, long endOffset)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (QAccessibleEditableTextInterface *editableTextIface = accessible->editableTextInterface())
- editableTextIface->deleteText(startOffset, endOffset);
- else
- replaceTextFallback(startOffset, endOffset, QString());
- return S_OK;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::insertText(long offset, BSTR *text)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- const QString txt = QString::fromWCharArray(*text);
- if (QAccessibleEditableTextInterface *editableTextIface = accessible->editableTextInterface())
- editableTextIface->insertText(offset, txt);
- else
- replaceTextFallback(offset, offset, txt);
- return S_OK;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::cutText(long startOffset, long endOffset)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
-#if QT_CONFIG(clipboard)
- const QString t = textForRange(startOffset, endOffset);
- if (QAccessibleEditableTextInterface *editableTextIface = accessible->editableTextInterface())
- editableTextIface->deleteText(startOffset, endOffset);
- else
- replaceTextFallback(startOffset, endOffset, QString());
- QGuiApplication::clipboard()->setText(t);
- return S_OK;
-#else
- return E_NOTIMPL;
-#endif
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::pasteText(long offset)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
-#if QT_CONFIG(clipboard)
- const QString txt = QGuiApplication::clipboard()->text();
- if (QAccessibleEditableTextInterface *editableTextIface = accessible->editableTextInterface())
- editableTextIface->insertText(offset, txt);
- else
- replaceTextFallback(offset, offset, txt);
- return S_OK;
-#else
- return E_NOTIMPL;
-#endif
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::replaceText(long startOffset, long endOffset, BSTR *text)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- const QString txt = QString::fromWCharArray(*text);
- if (QAccessibleEditableTextInterface *editableTextIface = accessible->editableTextInterface())
- editableTextIface->replaceText(startOffset, endOffset, txt);
- else
- replaceTextFallback(startOffset, endOffset, txt);
- return S_OK;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::setAttributes(long /*startOffset*/, long /*endOffset*/, BSTR * /*attributes*/)
-{
- return E_NOTIMPL;
-}
-
-
-/**************************************************************\
- * IAccessibleTable2 *
- **************************************************************/
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_cellAt( long row, long column, IUnknown **cell)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- *cell = 0;
- if (QAccessibleTableInterface *tableIface = tableInterface()) {
- if (QAccessibleInterface *qtCell = tableIface->cellAt(row, column)) {
- *cell = QWindowsAccessibility::wrap(qtCell);
- }
- }
- qCDebug(lcQpaAccessibility) << "found cell? " << *cell;
- return *cell ? S_OK : S_FALSE;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_caption( IUnknown **captionInterface)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- *captionInterface = 0;
- if (QAccessibleTableInterface *tableIface = tableInterface()) {
- if (QAccessibleInterface *iface = tableIface->caption())
- *captionInterface = QWindowsAccessibility::wrap(iface);
- }
- return *captionInterface ? S_OK : S_FALSE;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_columnDescription( long column, BSTR *description)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- *description = 0;
- if (QAccessibleTableInterface *tableIface = tableInterface()) {
- const QString qtDesc = tableIface->columnDescription(column);
- if (!qtDesc.isEmpty())
- *description = QStringToBSTR(qtDesc);
- }
- return *description ? S_OK : S_FALSE;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_nColumns( long *columnCount)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- if (QAccessibleTableInterface *tableIface = tableInterface()) {
- *columnCount = tableIface->columnCount();
- return S_OK;
- }
- return E_FAIL;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_nRows(long *rowCount)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- if (QAccessibleTableInterface *tableIface = tableInterface()) {
- *rowCount = tableIface->rowCount();
- return S_OK;
- }
- return E_FAIL;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_nSelectedCells(long *cellCount)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- if (QAccessibleTableInterface *tableIface = tableInterface()) {
- *cellCount = tableIface->selectedCellCount();
- return S_OK;
- }
- return E_FAIL;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_nSelectedColumns(long *columnCount)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- if (QAccessibleTableInterface *tableIface = tableInterface()) {
- *columnCount = tableIface->selectedColumnCount();
- return S_OK;
- }
- return E_FAIL;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_nSelectedRows(long *rowCount)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- if (QAccessibleTableInterface *tableIface = tableInterface()) {
- *rowCount = tableIface->selectedRowCount();
- return S_OK;
- }
- return E_FAIL;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_rowDescription(long row, BSTR *description)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- *description = 0;
- if (QAccessibleTableInterface *tableIface = tableInterface()) {
- const QString qtDesc = tableIface->rowDescription(row);
- if (!qtDesc.isEmpty())
- *description = QStringToBSTR(qtDesc);
- }
- return *description ? S_OK : S_FALSE;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_selectedCells(IUnknown ***cells, long *nSelectedCells)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- Q_UNUSED(cells);
- Q_UNUSED(nSelectedCells);
- if (!accessible)
- return E_FAIL;
-
- QList<QAccessibleInterface*> selectedCells = tableInterface()->selectedCells();
- return wrapListOfCells(selectedCells, cells, nSelectedCells);
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_selectedColumns(long **selectedColumns, long *nColumns)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- if (QAccessibleTableInterface *tableIface = tableInterface()) {
- const QList<int> selectedIndices = tableIface->selectedColumns();
- const int count = selectedIndices.count();
- *nColumns = count;
- *selectedColumns = Q_NULLPTR;
- if (count) {
- *selectedColumns = coTaskMemAllocArray<long>(count);
- std::copy(selectedIndices.constBegin(), selectedIndices.constEnd(),
- QT_MAKE_CHECKED_ARRAY_ITERATOR(*selectedColumns, count));
- }
- return count ? S_OK : S_FALSE;
- }
- return E_FAIL;
-
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_selectedRows(long **selectedRows, long *nRows)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- if (QAccessibleTableInterface *tableIface = tableInterface()) {
- const QList<int> selectedIndices = tableIface->selectedRows();
- const int count = selectedIndices.count();
- *nRows = count;
- *selectedRows = Q_NULLPTR;
- if (count) {
- *selectedRows = coTaskMemAllocArray<long>(count);
- std::copy(selectedIndices.constBegin(), selectedIndices.constEnd(),
- QT_MAKE_CHECKED_ARRAY_ITERATOR(*selectedRows, count));
- }
- return count ? S_OK : S_FALSE;
- }
- return E_FAIL;
-
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_summary(IUnknown **summaryInterface)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- *summaryInterface = 0;
- if (QAccessibleTableInterface *tableIface = tableInterface()) {
- if (QAccessibleInterface *iface = tableIface->summary())
- *summaryInterface = QWindowsAccessibility::wrap(iface);
- }
- return *summaryInterface ? S_OK : S_FALSE;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_isColumnSelected(long column, boolean *isSelected)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- if (QAccessibleTableInterface *tableIface = tableInterface()) {
- *isSelected = tableIface->isColumnSelected(column);
- return S_OK;
- }
- return E_FAIL;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_isRowSelected(long row, boolean *isSelected)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- if (QAccessibleTableInterface *tableIface = tableInterface()) {
- *isSelected = tableIface->isRowSelected(row);
- return S_OK;
- }
- return E_FAIL;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::selectRow(long row)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- if (QAccessibleTableInterface *tableIface = tableInterface()) {
- bool ok = tableIface->selectRow(row);
- return ok ? S_OK : E_INVALIDARG; //### Not sure of the return value if it fails???
- }
- return E_FAIL;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::selectColumn(long column)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- if (QAccessibleTableInterface *tableIface = tableInterface()) {
- bool ok = tableIface->selectColumn(column);
- return ok ? S_OK : E_INVALIDARG; //### Not sure of the return value if it fails???
- }
- return E_FAIL;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::unselectRow(long row)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- if (QAccessibleTableInterface *tableIface = tableInterface()) {
- bool ok = tableIface->unselectRow(row);
- return ok ? S_OK : E_INVALIDARG; //### Not sure of the return value if it fails???
- }
- return E_FAIL;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::unselectColumn(long column)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- if (QAccessibleTableInterface *tableIface = tableInterface()) {
- bool ok = tableIface->unselectColumn(column);
- return ok ? S_OK : E_INVALIDARG; //### Not sure of the return value if it fails???
- }
- return E_FAIL;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_modelChange( IA2TableModelChange * /*modelChange*/)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
- return E_NOTIMPL;
-}
-
-/**************************************************************\
- * IAccessibleTableCell *
-\**************************************************************/
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_columnExtent(long *nColumnsSpanned)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- *nColumnsSpanned = tableCellInterface()->columnExtent();
- return S_OK;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_columnHeaderCells(IUnknown ***cellAccessibles,
- long *nColumnHeaderCells)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
- const QList<QAccessibleInterface*> headerCells = tableCellInterface()->columnHeaderCells();
- return wrapListOfCells(headerCells, cellAccessibles, nColumnHeaderCells);
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_columnIndex(long *columnIndex)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
- *columnIndex = tableCellInterface()->columnIndex();
- return S_OK;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_rowExtent(long *nRowsSpanned)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
- *nRowsSpanned = tableCellInterface()->rowExtent();
- return S_OK;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_rowHeaderCells(IUnknown ***cellAccessibles,
- long *nRowHeaderCells)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
- const QList<QAccessibleInterface*> headerCells = tableCellInterface()->rowHeaderCells();
- return wrapListOfCells(headerCells, cellAccessibles, nRowHeaderCells);
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_rowIndex(long *rowIndex)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
- *rowIndex = tableCellInterface()->rowIndex();
- return S_OK;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_isSelected( boolean *isSelected)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
- *isSelected = tableCellInterface()->isSelected();
- return S_OK;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_rowColumnExtents(long *row, long *column,
- long *rowExtents, long *columnExtents,
- boolean *isSelected)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible || !tableCellInterface())
- return E_FAIL;
-
- *row = tableCellInterface()->rowIndex();
- *column = tableCellInterface()->columnIndex();
- *rowExtents = tableCellInterface()->rowExtent();
- *columnExtents = tableCellInterface()->columnExtent();
- *isSelected = tableCellInterface()->isSelected();
- return S_OK;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_table(IUnknown **table)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- QAccessibleInterface *tableIface = tableCellInterface()->table();
-
- *table = QWindowsAccessibility::wrap(tableIface);
- return S_OK;
-}
-
-/**************************************************************\
- * IAccessibleText *
-\**************************************************************/
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::addSelection(long startOffset,
- long endOffset)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (QAccessibleTextInterface *text = textInterface()) {
- text->addSelection(startOffset, endOffset);
- return S_OK;
- }
- return E_FAIL;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_attributes(long offset,
- long *startOffset,
- long *endOffset,
- BSTR *textAttributes)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (QAccessibleTextInterface *text = textInterface()) {
- const QString attrs = text->attributes(offset, reinterpret_cast<int *>(startOffset),
- reinterpret_cast<int *>(endOffset));
- *textAttributes = QStringToBSTR(attrs);
- return S_OK;
- }
- return E_FAIL;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_caretOffset(long *offset)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (QAccessibleTextInterface *text = textInterface()) {
- *offset = text->cursorPosition();
- return S_OK;
- }
- return E_FAIL;
-}
-
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_characterExtents(long offset,
- enum IA2CoordinateType coordType,
- long *x,
- long *y,
- long *width,
- long *height)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (QAccessibleTextInterface *text = textInterface()) {
- QRect rect = text->characterRect(offset);
- mapFromScreenPos(coordType, rect.topLeft(), x, y);
- *width = rect.width();
- *height = rect.height();
- return S_OK;
- }
- return E_FAIL;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_nSelections(long *nSelections)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (QAccessibleTextInterface *text = textInterface()) {
- *nSelections = text->selectionCount();
- return S_OK;
- }
- return E_FAIL;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_offsetAtPoint(long x,
- long y,
- enum IA2CoordinateType coordType,
- long *offset)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (QAccessibleTextInterface *text = textInterface()) {
- QPoint screenPos = mapToScreenPos(coordType, x, y);
- *offset = text->offsetAtPoint(screenPos);
- return (*offset >=0 ? S_OK : S_FALSE);
- }
- return E_FAIL;
-
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_selection(long selectionIndex,
- long *startOffset,
- long *endOffset)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (QAccessibleTextInterface *text = textInterface()) {
- text->selection(selectionIndex, reinterpret_cast<int *>(startOffset),
- reinterpret_cast<int *>(endOffset));
- return S_OK;
- }
- return E_FAIL;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_text(long startOffset,
- long endOffset,
- BSTR *text)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (QAccessibleTextInterface *textif = textInterface()) {
- const QString t = textif->text(startOffset, endOffset);
- if (!t.isEmpty()) {
- *text = QStringToBSTR(t);
- return S_OK;
- }
- return E_INVALIDARG;
- }
- return E_FAIL;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_textBeforeOffset(long offset,
- enum IA2TextBoundaryType boundaryType,
- long *startOffset,
- long *endOffset,
- BSTR *text)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (QAccessibleTextInterface *textIface = textInterface()) {
- const QString txt =
- textIface->textBeforeOffset(offset, static_cast<QAccessible::TextBoundaryType>(boundaryType),
- reinterpret_cast<int *>(startOffset),
- reinterpret_cast<int *>(endOffset));
- if (!txt.isEmpty()) {
- *text = QStringToBSTR(txt);
- return S_OK;
- }
- return S_FALSE;
- }
- return E_FAIL;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_textAfterOffset(
- long offset,
- enum IA2TextBoundaryType boundaryType,
- long *startOffset,
- long *endOffset,
- BSTR *text)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (QAccessibleTextInterface *textIface = textInterface()) {
- const QString txt =
- textIface->textAfterOffset(offset, static_cast<QAccessible::TextBoundaryType>(boundaryType),
- reinterpret_cast<int *>(startOffset),
- reinterpret_cast<int *>(endOffset));
- if (!txt.isEmpty()) {
- *text = QStringToBSTR(txt);
- return S_OK;
- }
- return S_FALSE;
- }
- return E_FAIL;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_textAtOffset(long offset,
- enum IA2TextBoundaryType boundaryType,
- long *startOffset,
- long *endOffset,
- BSTR *text)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (QAccessibleTextInterface *textIface = textInterface()) {
- const QString txt =
- textIface->textAtOffset(offset, static_cast<QAccessible::TextBoundaryType>(boundaryType),
- reinterpret_cast<int *>(startOffset),
- reinterpret_cast<int *>(endOffset));
- if (!txt.isEmpty()) {
- *text = QStringToBSTR(txt);
- return S_OK;
- }
- return S_FALSE;
- }
- return E_FAIL;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::removeSelection(long selectionIndex)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (QAccessibleTextInterface *textIface = textInterface()) {
- textIface->removeSelection(selectionIndex);
- return S_OK;
- }
- return E_FAIL;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::setCaretOffset(long offset)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (QAccessibleTextInterface *textIface = textInterface()) {
- textIface->setCursorPosition(offset);
- return S_OK;
- }
- return E_FAIL;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::setSelection(long selectionIndex,
- long startOffset,
- long endOffset)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (QAccessibleTextInterface *textIface = textInterface()) {
- textIface->setSelection(selectionIndex, startOffset, endOffset);
- return S_OK;
- }
- return E_FAIL;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_nCharacters(long *nCharacters)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (QAccessibleTextInterface *textIface = textInterface()) {
- *nCharacters = textIface->characterCount();
- return S_OK;
- }
- return E_FAIL;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::scrollSubstringTo(long startIndex,
- long endIndex,
- enum IA2ScrollType scrollType)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (QAccessibleTextInterface *textIface = textInterface()) {
- Q_UNUSED(scrollType); //###
- textIface->scrollToSubstring(startIndex, endIndex);
- return S_OK;
- }
- return E_FAIL;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::scrollSubstringToPoint(long startIndex,
- long endIndex,
- enum IA2CoordinateType coordinateType,
- long x,
- long y)
-{
- Q_UNUSED(startIndex);
- Q_UNUSED(endIndex);
- Q_UNUSED(coordinateType);
- Q_UNUSED(x);
- Q_UNUSED(y);
-
- return E_NOTIMPL;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_newText(IA2TextSegment *newText)
-{
- Q_UNUSED(newText);
- return E_NOTIMPL;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_oldText(IA2TextSegment *oldText)
-{
- Q_UNUSED(oldText);
- return E_NOTIMPL;
-}
-
-/**************************************************************\
- * IAccessibleValue *
- **************************************************************/
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_currentValue(VARIANT *currentValue)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
- if (QAccessibleValueInterface *valueIface = valueInterface()) {
- const QVariant var = valueIface->currentValue();
- if (QVariant2VARIANT(var, *currentValue, QByteArray(), false))
- return S_OK;
-
- }
- currentValue->vt = VT_EMPTY;
- return S_FALSE;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::setCurrentValue(VARIANT value)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
- HRESULT hr = S_FALSE;
- if (QAccessibleValueInterface *valueIface = valueInterface()) {
- hr = VariantChangeType(&value, &value, 0, VT_R8);
- if (SUCCEEDED(hr)) {
- // ### works only for numbers (not date, strings, etc)
- valueIface->setCurrentValue(QVariant(value.dblVal));
- }
- }
- return hr;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_maximumValue(VARIANT *maximumValue)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
- if (QAccessibleValueInterface *valueIface = valueInterface()) {
- const QVariant var = valueIface->maximumValue();
- if (QVariant2VARIANT(var, *maximumValue, QByteArray(), false))
- return S_OK;
- }
- maximumValue->vt = VT_EMPTY;
- return S_FALSE;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_minimumValue(VARIANT *minimumValue)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
- if (QAccessibleValueInterface *valueIface = valueInterface()) {
- const QVariant var = valueIface->minimumValue();
- if (QVariant2VARIANT(var, *minimumValue, QByteArray(), false))
- return S_OK;
- }
- minimumValue->vt = VT_EMPTY;
- return S_FALSE;
-}
-
-
-/**************************************************************\
- * IServiceProvider *
- **************************************************************/
-/*!
- \internal
- Reimplemented from IServiceProvider
-*/
-HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::QueryService(REFGUID guidService, REFIID riid, void **iface)
-{
- if (!iface)
- return E_POINTER;
- Q_UNUSED(guidService);
- *iface = 0;
- qCDebug(lcQpaAccessibility) << "QWindowsIA2Accessible::QS(): " << IIDToString(riid);
-
-
- if (guidService == IID_IAccessible) {
- if (riid == IID_IServiceProvider) {
- // do not end up calling QueryInterface for IID_IServiceProvider
- *iface = 0;
- } else if (riid == IID_IAccessible || riid == IID_IUnknown || riid == IID_IDispatch) {
- // The above conditions works with AccProbe and NVDA.
- *iface = static_cast<IAccessible*>(this);
- } else {
- // According to _dicoveringInterfaces Discovery of Interfaces, we should really only
- // enter here if riid == IID_IAccessible2, but some screen readers does not like that,
- // and other servers seems to have realized that. (Chrome and Mozilla for instance,
- // calls QueryInterface more or less in the same way)
-
- // For instance, accProbe discovers IID_IAccessibleTable2 by a QueryService only.
- return QueryInterface(riid, iface);
- }
- }
-
- if (riid == IID_IAccessibleApplication) {
- *iface = new AccessibleApplication;
- return S_OK;
- }
- if (*iface) {
- AddRef();
- return S_OK;
- }
-
- return E_NOINTERFACE;
-}
-
-
-/*!
- \internal
- private function..
- \a maxRelations max number of relations to return in \a relations
- \a relations the array of relations matching
- \a startIndex Index to start to return from,
- it will return only that specific relation in \a relations
-
- If \a relations is null, \a startIndex and \a maxRelations are ignored, causing
- it to return the number of relations in \a nRelations
-*/
-HRESULT QWindowsIA2Accessible::getRelationsHelper(IAccessibleRelation **relations, int startIndex, long maxRelations, long *nRelations /* = 0*/)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- if (nRelations)
- *nRelations = 0;
- typedef QPair<QAccessibleInterface *, QAccessible::Relation> RelationEntry;
- QVector<RelationEntry> rels = accessible->relations();
- QMap<QAccessible::Relation, QAccessibleInterface *> relationMap;
- for (QVector<RelationEntry>::const_iterator it = rels.constBegin(); it != rels.constEnd(); ++it)
- {
- RelationEntry e = *it;
- relationMap.insertMulti(e.second, e.first);
- }
-
- QList<QAccessible::Relation> keys = relationMap.keys();
- const int numRelations = keys.count();
- if (relations) {
- for (int i = startIndex; i < qMin(startIndex + int(maxRelations), numRelations); ++i) {
- QAccessible::Relation relation = keys.at(i);
- QList<QAccessibleInterface*> targets = relationMap.values(relation);
- AccessibleRelation *rel = new AccessibleRelation(targets, relation);
- *relations = rel;
- ++relations;
- }
- }
- if (nRelations)
- *nRelations = numRelations;
-
- return numRelations > 0 ? S_OK : S_FALSE;
-}
-
-
-
-
-/*!
- \internal
- helper to wrap a QList<QAccessibleInterface*> inside an array of IAccessible*
- The IAccessible* array is returned as a IUnknown*
-*/
-HRESULT QWindowsIA2Accessible::wrapListOfCells(const QList<QAccessibleInterface*> &inputCells, IUnknown ***outputAccessibles, long *nCellCount)
-{
- const int count = inputCells.count();
- // Server allocates array
- *nCellCount = count;
- *outputAccessibles = Q_NULLPTR;
- if (count) {
- *outputAccessibles = coTaskMemAllocArray<IUnknown *>(count);
- std::transform(inputCells.constBegin(), inputCells.constEnd(),
- QT_MAKE_CHECKED_ARRAY_ITERATOR(*outputAccessibles, count),
- QWindowsAccessibility::wrap);
- }
- return count > 0 ? S_OK : S_FALSE;
-}
-
-#define IF_EQUAL_RETURN_IIDSTRING(id, iid) if (id == iid) return QByteArray(#iid)
-
-QByteArray QWindowsIA2Accessible::IIDToString(REFIID id)
-{
- QByteArray strGuid = QWindowsMsaaAccessible::IIDToString(id);
- if (!strGuid.isEmpty())
- return strGuid;
-
- IF_EQUAL_RETURN_IIDSTRING(id, IID_IUnknown);
- IF_EQUAL_RETURN_IIDSTRING(id, IID_IDispatch);
- IF_EQUAL_RETURN_IIDSTRING(id, IID_IAccessible);
- IF_EQUAL_RETURN_IIDSTRING(id, IID_IOleWindow);
- IF_EQUAL_RETURN_IIDSTRING(id, IID_IServiceProvider);
- IF_EQUAL_RETURN_IIDSTRING(id, IID_IAccessible2);
- IF_EQUAL_RETURN_IIDSTRING(id, IID_IAccessibleAction);
- IF_EQUAL_RETURN_IIDSTRING(id, IID_IAccessibleApplication);
- IF_EQUAL_RETURN_IIDSTRING(id, IID_IAccessibleComponent);
- IF_EQUAL_RETURN_IIDSTRING(id, IID_IAccessibleEditableText);
- IF_EQUAL_RETURN_IIDSTRING(id, IID_IAccessibleHyperlink);
- IF_EQUAL_RETURN_IIDSTRING(id, IID_IAccessibleHypertext);
- IF_EQUAL_RETURN_IIDSTRING(id, IID_IAccessibleImage);
- IF_EQUAL_RETURN_IIDSTRING(id, IID_IAccessibleRelation);
- IF_EQUAL_RETURN_IIDSTRING(id, IID_IAccessibleTable);
- IF_EQUAL_RETURN_IIDSTRING(id, IID_IAccessibleTable2);
- IF_EQUAL_RETURN_IIDSTRING(id, IID_IAccessibleTableCell);
- IF_EQUAL_RETURN_IIDSTRING(id, IID_IAccessibleText);
- IF_EQUAL_RETURN_IIDSTRING(id, IID_IAccessibleValue);
-
- // else...
-#if 0 // Can be useful for debugging, but normally we'd like to reduce the noise a bit...
- OLECHAR szGuid[39]={0};
- ::StringFromGUID2(id, szGuid, 39);
- strGuid.reserve(40);
- ::WideCharToMultiByte(CP_UTF8, 0, szGuid, 39, strGuid.data(), 39, NULL, NULL);
- strGuid[38] = '\0';
-#endif
- return strGuid;
-}
-
-// Q_STATIC_ASSERT(IA2_ROLE_CANVAS == QAccessible::Canvas); // ### Qt 6: make them the same
-Q_STATIC_ASSERT(IA2_ROLE_COLOR_CHOOSER == static_cast<IA2Role>(QAccessible::ColorChooser));
-Q_STATIC_ASSERT(IA2_ROLE_FOOTER == static_cast<IA2Role>(QAccessible::Footer));
-Q_STATIC_ASSERT(IA2_ROLE_FORM == static_cast<IA2Role>(QAccessible::Form));
-Q_STATIC_ASSERT(IA2_ROLE_HEADING == static_cast<IA2Role>(QAccessible::Heading));
-Q_STATIC_ASSERT(IA2_ROLE_NOTE == static_cast<IA2Role>(QAccessible::Note));
-Q_STATIC_ASSERT(IA2_ROLE_COMPLEMENTARY_CONTENT == static_cast<IA2Role>(QAccessible::ComplementaryContent));
-
-QT_END_NAMESPACE
-
-#endif //QT_NO_ACCESSIBILITY
diff --git a/src/plugins/platforms/windows/accessible/iaccessible2.h b/src/plugins/platforms/windows/accessible/iaccessible2.h
deleted file mode 100644
index bc5f5be60f..0000000000
--- a/src/plugins/platforms/windows/accessible/iaccessible2.h
+++ /dev/null
@@ -1,351 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#ifndef IACCESSIBLE2_H
-#define IACCESSIBLE2_H
-
-#include <QtCore/QtConfig>
-#ifndef QT_NO_ACCESSIBILITY
-
-#include "qwindowsmsaaaccessible.h"
-#include "comutils.h"
-
-#include "ia2_api_all.h"
-
-#include <servprov.h>
-
-QT_BEGIN_NAMESPACE
-
-class QWindowsIA2Accessible : public QWindowsMsaaAccessible,
- public IAccessibleAction,
- public IAccessibleComponent,
- public IAccessibleEditableText,
- public IAccessibleTable2,
- public IAccessibleTableCell,
- public IAccessibleText,
- public IAccessibleValue,
- public IServiceProvider
-{
-public:
- QWindowsIA2Accessible(QAccessibleInterface *a) : QWindowsMsaaAccessible(a) {}
-
- /* IUnknown */
- HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID *);
- ULONG STDMETHODCALLTYPE AddRef();
- ULONG STDMETHODCALLTYPE Release();
-
- /* IAccessible2 */
- HRESULT STDMETHODCALLTYPE get_nRelations(long *nRelations);
- HRESULT STDMETHODCALLTYPE get_relation(long relationIndex, IAccessibleRelation **relation);
- HRESULT STDMETHODCALLTYPE get_relations(long maxRelations, IAccessibleRelation **relations, long *nRelations);
- HRESULT STDMETHODCALLTYPE role(long *role);
- HRESULT STDMETHODCALLTYPE scrollTo(enum IA2ScrollType scrollType);
- HRESULT STDMETHODCALLTYPE scrollToPoint(enum IA2CoordinateType coordinateType, long x, long y);
- HRESULT STDMETHODCALLTYPE get_groupPosition(long *groupLevel, long *similarItemsInGroup, long *positionInGroup);
- HRESULT STDMETHODCALLTYPE get_states(AccessibleStates *states);
- HRESULT STDMETHODCALLTYPE get_extendedRole(BSTR *extendedRole);
- HRESULT STDMETHODCALLTYPE get_localizedExtendedRole(BSTR *localizedExtendedRole);
- HRESULT STDMETHODCALLTYPE get_nExtendedStates(long *nExtendedStates);
- HRESULT STDMETHODCALLTYPE get_extendedStates(long maxExtendedStates, BSTR **extendedStates, long *nExtendedStates);
- HRESULT STDMETHODCALLTYPE get_localizedExtendedStates(long maxLocalizedExtendedStates, BSTR **localizedExtendedStates, long *nLocalizedExtendedStates);
- HRESULT STDMETHODCALLTYPE get_uniqueID(long *uniqueID);
- HRESULT STDMETHODCALLTYPE get_windowHandle(HWND *windowHandle);
- HRESULT STDMETHODCALLTYPE get_indexInParent(long *indexInParent);
- HRESULT STDMETHODCALLTYPE get_locale(IA2Locale *locale);
- HRESULT STDMETHODCALLTYPE get_attributes(BSTR *attributes);
-
- /* IAccessibleAction */
- HRESULT STDMETHODCALLTYPE nActions(long *nActions);
- HRESULT STDMETHODCALLTYPE doAction(long actionIndex);
- HRESULT STDMETHODCALLTYPE get_description(long actionIndex, BSTR *description);
- HRESULT STDMETHODCALLTYPE get_keyBinding(long actionIndex, long nMaxBindings, BSTR **keyBindings, long *nBindings);
- HRESULT STDMETHODCALLTYPE get_name(long actionIndex, BSTR *name);
- HRESULT STDMETHODCALLTYPE get_localizedName(long actionIndex, BSTR *localizedName);
-
- /* IAccessibleComponent */
- HRESULT STDMETHODCALLTYPE get_locationInParent(long *x,long *y);
- HRESULT STDMETHODCALLTYPE get_foreground(IA2Color *foreground);
- HRESULT STDMETHODCALLTYPE get_background(IA2Color *background);
-
- /* IAccessibleEditableText */
- HRESULT STDMETHODCALLTYPE copyText(long startOffset, long endOffset);
- HRESULT STDMETHODCALLTYPE deleteText(long startOffset, long endOffset);
- HRESULT STDMETHODCALLTYPE insertText(long offset, BSTR *text);
- HRESULT STDMETHODCALLTYPE cutText(long startOffset, long endOffset);
- HRESULT STDMETHODCALLTYPE pasteText(long offset);
- HRESULT STDMETHODCALLTYPE replaceText(long startOffset, long endOffset, BSTR *text);
- HRESULT STDMETHODCALLTYPE setAttributes(long startOffset, long endOffset, BSTR *attributes);
-
- /* IAccessibleTable2 */
- HRESULT STDMETHODCALLTYPE get_cellAt( long row, long column, IUnknown **cell);
- HRESULT STDMETHODCALLTYPE get_caption( IUnknown **accessibleInterface);
- HRESULT STDMETHODCALLTYPE get_columnDescription( long column, BSTR *description);
- HRESULT STDMETHODCALLTYPE get_nColumns( long *columnCount);
- HRESULT STDMETHODCALLTYPE get_nRows( long *rowCount);
- HRESULT STDMETHODCALLTYPE get_nSelectedCells( long *cellCount);
- HRESULT STDMETHODCALLTYPE get_nSelectedColumns( long *columnCount);
- HRESULT STDMETHODCALLTYPE get_nSelectedRows( long *rowCount);
- HRESULT STDMETHODCALLTYPE get_rowDescription( long row, BSTR *description);
- HRESULT STDMETHODCALLTYPE get_selectedCells( IUnknown ***cells, long *nSelectedCells);
- HRESULT STDMETHODCALLTYPE get_selectedColumns( long **selectedColumns, long *nColumns);
- HRESULT STDMETHODCALLTYPE get_selectedRows( long **selectedRows, long *nRows);
- HRESULT STDMETHODCALLTYPE get_summary( IUnknown **accessibleInterface);
- HRESULT STDMETHODCALLTYPE get_isColumnSelected( long column, boolean *isSelected);
- HRESULT STDMETHODCALLTYPE get_isRowSelected( long row, boolean *isSelected);
- HRESULT STDMETHODCALLTYPE selectRow( long row);
- HRESULT STDMETHODCALLTYPE selectColumn( long column);
- HRESULT STDMETHODCALLTYPE unselectRow( long row);
- HRESULT STDMETHODCALLTYPE unselectColumn( long column);
- HRESULT STDMETHODCALLTYPE get_modelChange( IA2TableModelChange *modelChange);
-
- /* IAccessibleTableCell */
- HRESULT STDMETHODCALLTYPE get_columnExtent(long *nColumnsSpanned);
- HRESULT STDMETHODCALLTYPE get_columnHeaderCells(IUnknown ***cellAccessibles, long *nColumnHeaderCells);
- HRESULT STDMETHODCALLTYPE get_columnIndex(long *columnIndex);
- HRESULT STDMETHODCALLTYPE get_rowExtent(long *nRowsSpanned);
- HRESULT STDMETHODCALLTYPE get_rowHeaderCells(IUnknown ***cellAccessibles, long *nRowHeaderCells);
- HRESULT STDMETHODCALLTYPE get_rowIndex(long *rowIndex);
- HRESULT STDMETHODCALLTYPE get_isSelected( boolean *isSelected);
- HRESULT STDMETHODCALLTYPE get_rowColumnExtents(long *row, long *column,
- long *rowExtents, long *columnExtents,
- boolean *isSelected);
- HRESULT STDMETHODCALLTYPE get_table(IUnknown **table);
-
-
- /* IAccessibleText */
- HRESULT STDMETHODCALLTYPE addSelection(long startOffset, long endOffset);
- HRESULT STDMETHODCALLTYPE get_attributes(long offset, long *startOffset,
- long *endOffset, BSTR *textAttributes);
- HRESULT STDMETHODCALLTYPE get_caretOffset(long *offset);
- HRESULT STDMETHODCALLTYPE get_characterExtents(long offset, enum IA2CoordinateType coordType,
- long *x, long *y,
- long *width, long *height);
- HRESULT STDMETHODCALLTYPE get_nSelections(long *nSelections);
- HRESULT STDMETHODCALLTYPE get_offsetAtPoint(long x, long y, enum IA2CoordinateType coordType, long *offset);
- HRESULT STDMETHODCALLTYPE get_selection(long selectionIndex, long *startOffset, long *endOffset);
- HRESULT STDMETHODCALLTYPE get_text(long startOffset, long endOffset, BSTR *text);
- HRESULT STDMETHODCALLTYPE get_textBeforeOffset(long offset, enum IA2TextBoundaryType boundaryType,
- long *startOffset, long *endOffset, BSTR *text);
- HRESULT STDMETHODCALLTYPE get_textAfterOffset(long offset, enum IA2TextBoundaryType boundaryType,
- long *startOffset, long *endOffset, BSTR *text);
- HRESULT STDMETHODCALLTYPE get_textAtOffset(long offset, enum IA2TextBoundaryType boundaryType,
- long *startOffset, long *endOffset, BSTR *text);
- HRESULT STDMETHODCALLTYPE removeSelection(long selectionIndex);
- HRESULT STDMETHODCALLTYPE setCaretOffset(long offset);
- HRESULT STDMETHODCALLTYPE setSelection(long selectionIndex, long startOffset, long endOffset);
- HRESULT STDMETHODCALLTYPE get_nCharacters(long *nCharacters);
- HRESULT STDMETHODCALLTYPE scrollSubstringTo(long startIndex, long endIndex, enum IA2ScrollType scrollType);
- HRESULT STDMETHODCALLTYPE scrollSubstringToPoint(long startIndex, long endIndex,
- enum IA2CoordinateType coordinateType, long x, long y);
- HRESULT STDMETHODCALLTYPE get_newText(IA2TextSegment *newText);
- HRESULT STDMETHODCALLTYPE get_oldText(IA2TextSegment *oldText);
-
- /* IAccessibleValue */
- HRESULT STDMETHODCALLTYPE get_currentValue(VARIANT *currentValue);
- HRESULT STDMETHODCALLTYPE setCurrentValue(VARIANT value);
- HRESULT STDMETHODCALLTYPE get_maximumValue(VARIANT *maximumValue);
- HRESULT STDMETHODCALLTYPE get_minimumValue(VARIANT *minimumValue);
-
- /* IServiceProvider */
- HRESULT STDMETHODCALLTYPE QueryService(REFGUID guidService, REFIID riid, void **ppv);
-
- /* private helper functions */
-private:
- inline QAccessibleTextInterface *textInterface() const {
- QAccessibleInterface *accessible = accessibleInterface();
- return accessible ? accessible->textInterface() : static_cast<QAccessibleTextInterface *>(0);
- }
-
- inline QAccessibleActionInterface *actionInterface() const {
- QAccessibleInterface *accessible = accessibleInterface();
- return accessible->actionInterface();
- }
-
- inline QAccessibleValueInterface *valueInterface() const {
- QAccessibleInterface *accessible = accessibleInterface();
- return accessible->valueInterface();
- }
-
- inline QAccessibleTableInterface *tableInterface() const {
- QAccessibleInterface *accessible = accessibleInterface();
- return accessible->tableInterface();
- }
-
- inline QAccessibleTableCellInterface *tableCellInterface() const {
- QAccessibleInterface *accessible = accessibleInterface();
- return accessible->tableCellInterface();
- }
-
- /*!
- \internal
- \a screenPos is in screen relative position
- \a x and \y (out) is in parent relative position if coordType == IA2_COORDTYPE_PARENT_RELATIVE
- */
- void mapFromScreenPos(enum IA2CoordinateType coordType, const QPoint &screenPos, long *x, long *y) const {
- QAccessibleInterface *accessible = accessibleInterface();
- if (coordType == IA2_COORDTYPE_PARENT_RELATIVE) {
- // caller wants relative to parent
- if (QAccessibleInterface *parent = accessible->parent()) {
- const QRect parentScreenRect = parent->rect();
- *x = parentScreenRect.x() - screenPos.x();
- *y = parentScreenRect.y() - screenPos.y();
- return;
- }
- }
- *x = screenPos.x();
- *y = screenPos.y();
- }
-
- /*!
- \internal
- \a x and \y is in parent relative position if coordType == IA2_COORDTYPE_PARENT_RELATIVE
- \return a screen relative position
- */
- QPoint mapToScreenPos(enum IA2CoordinateType coordType, long x, long y) const {
- QAccessibleInterface *accessible = accessibleInterface();
- if (coordType == IA2_COORDTYPE_PARENT_RELATIVE) {
- if (QAccessibleInterface *parent = accessible->parent()) {
- const QRect parentScreenRect = parent->rect();
- return QPoint(parentScreenRect.x() + x, parentScreenRect.y() + y);
- }
- }
- return QPoint(x,y);
- }
-
- HRESULT getRelationsHelper(IAccessibleRelation **relations, int startIndex, long maxRelations, long *nRelations = 0);
- HRESULT wrapListOfCells(const QList<QAccessibleInterface*> &inputCells, IUnknown ***outputAccessibles, long *nCellCount);
- QByteArray IIDToString(REFIID id);
- QString textForRange(int startOffset, int endOffset) const;
- void replaceTextFallback(long startOffset, long endOffset, const QString &txt);
-
-};
-
-/**************************************************************\
- * AccessibleApplication *
- **************************************************************/
-class AccessibleApplication : public IAccessibleApplication
-{
-public:
- AccessibleApplication() : m_ref(1)
- {
-
- }
-
- virtual ~AccessibleApplication() {}
-
- /* IUnknown */
- HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID *);
- ULONG STDMETHODCALLTYPE AddRef();
- ULONG STDMETHODCALLTYPE Release();
-
- /* IAccessibleApplication */
- HRESULT STDMETHODCALLTYPE get_appName(/* [retval][out] */ BSTR *name);
- HRESULT STDMETHODCALLTYPE get_appVersion(/* [retval][out] */ BSTR *version);
- HRESULT STDMETHODCALLTYPE get_toolkitName(/* [retval][out] */ BSTR *name);
- HRESULT STDMETHODCALLTYPE get_toolkitVersion(/* [retval][out] */ BSTR *version);
-private:
- ULONG m_ref;
-};
-
-
-
-/**************************************************************\
- * AccessibleRelation *
- **************************************************************/
-class AccessibleRelation : public IAccessibleRelation
-{
-public:
- AccessibleRelation(const QList<QAccessibleInterface *> &targets,
- QAccessible::Relation relation);
-
- virtual ~AccessibleRelation() {}
-
- /* IUnknown */
- HRESULT STDMETHODCALLTYPE QueryInterface(REFIID id, LPVOID *iface);
- ULONG STDMETHODCALLTYPE AddRef();
- ULONG STDMETHODCALLTYPE Release();
-
- /* IAccessibleRelation */
- HRESULT STDMETHODCALLTYPE get_relationType(BSTR *relationType);
- HRESULT STDMETHODCALLTYPE get_localizedRelationType(BSTR *localizedRelationType);
- HRESULT STDMETHODCALLTYPE get_nTargets(long *nTargets);
- HRESULT STDMETHODCALLTYPE get_target(long targetIndex, IUnknown **target);
- HRESULT STDMETHODCALLTYPE get_targets(long maxTargets, IUnknown **targets, long *nTargets);
-
-private:
- static BSTR relationToBSTR(QAccessible::Relation relation)
- {
- const wchar_t *constRelationString = 0;
- switch (relation) {
- case QAccessible::Label:
- constRelationString = IA2_RELATION_LABEL_FOR;
- break;
- case QAccessible::Labelled:
- constRelationString = IA2_RELATION_LABELLED_BY;
- break;
- case QAccessible::Controller:
- constRelationString = IA2_RELATION_CONTROLLER_FOR;
- break;
- case QAccessible::Controlled:
- constRelationString = IA2_RELATION_CONTROLLED_BY;
- break;
- case QAccessible::AllRelations:
- constRelationString = ( L"AllRelations" );
- break;
- }
-
- if (constRelationString) {
- BSTR bstrVal;
- const UINT wlen = (UINT)wcslen(constRelationString);
- bstrVal = ::SysAllocStringLen(constRelationString, wlen);
- return bstrVal;
- }
- return 0;
- }
-
-
- QList<QAccessibleInterface *> m_targets;
- QAccessible::Relation m_relation;
- ULONG m_ref;
-};
-
-QT_END_NAMESPACE
-
-#endif //QT_NO_ACCESSIBILITY
-
-#endif // IACCESSIBLE2_H
diff --git a/src/plugins/platforms/windows/accessible/qwindowsaccessibility.cpp b/src/plugins/platforms/windows/accessible/qwindowsaccessibility.cpp
deleted file mode 100644
index aed9c94003..0000000000
--- a/src/plugins/platforms/windows/accessible/qwindowsaccessibility.cpp
+++ /dev/null
@@ -1,253 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtCore/QtConfig>
-#ifndef QT_NO_ACCESSIBILITY
-
-
-#include <private/qsystemlibrary_p.h>
-
-#include <QtCore/qlocale.h>
-#include <QtCore/qmap.h>
-#include <QtCore/qpair.h>
-#include <QtCore/qpointer.h>
-#include <QtGui/qaccessible.h>
-#include <QtGui/private/qguiapplication_p.h>
-#include <qpa/qplatformnativeinterface.h>
-#include <qpa/qplatformintegration.h>
-#include <QtGui/qwindow.h>
-#include <QtGui/qguiapplication.h>
-#include <QtFontDatabaseSupport/private/qwindowsfontdatabase_p.h> // registry helper
-
-#include "qwindowsaccessibility.h"
-#ifdef Q_CC_MINGW
-# include "qwindowsmsaaaccessible.h"
-#else
-# include "iaccessible2.h"
-#endif
-#include "comutils.h"
-
-#include <oleacc.h>
-
-//#include <uiautomationcoreapi.h>
-#ifndef UiaRootObjectId
-#define UiaRootObjectId -25
-#endif
-
-#include <winuser.h>
-#if !defined(WINABLEAPI)
-# include <winable.h>
-#endif
-
-#include <servprov.h>
-#if !defined(Q_CC_BOR) && !defined (Q_CC_GNU)
-#include <comdef.h>
-#endif
-
-#include <QtCore/qt_windows.h>
-
-QT_BEGIN_NAMESPACE
-
-/*!
- \!internal
- \class QWindowsAccessibility
-
- Implements QPlatformAccessibility
-
-*/
-QWindowsAccessibility::QWindowsAccessibility()
-{
-}
-
-// Retrieve sound name by checking the icon property of a message box
-static inline QString messageBoxAlertSound(const QObject *messageBox)
-{
- enum MessageBoxIcon { // Keep in sync with QMessageBox::Icon
- Information = 1,
- Warning = 2,
- Critical = 3
- };
- switch (messageBox->property("icon").toInt()) {
- case Information:
- return QStringLiteral("SystemAsterisk");
- case Warning:
- return QStringLiteral("SystemExclamation");
- case Critical:
- return QStringLiteral("SystemHand");
- }
- return QString();
-}
-
-static QString soundFileName(const QString &soundName)
-{
- const QString key = QStringLiteral("AppEvents\\Schemes\\Apps\\.Default\\")
- + soundName + QStringLiteral("\\.Current");
- return QWindowsFontDatabase::readRegistryString(HKEY_CURRENT_USER,
- reinterpret_cast<const wchar_t *>(key.utf16()), L"");
-}
-
-void QWindowsAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event)
-{
- QString soundName;
- switch (event->type()) {
- case QAccessible::PopupMenuStart:
- soundName = QLatin1String("MenuPopup");
- break;
-
- case QAccessible::MenuCommand:
- soundName = QLatin1String("MenuCommand");
- break;
-
- case QAccessible::Alert:
- soundName = event->object()->inherits("QMessageBox") ?
- messageBoxAlertSound(event->object()) : QStringLiteral("SystemAsterisk");
- break;
- default:
- break;
- }
-
- if (!soundName.isEmpty() && !soundFileName(soundName).isEmpty()) {
- PlaySound(reinterpret_cast<const wchar_t *>(soundName.utf16()), 0,
- SND_ALIAS | SND_ASYNC | SND_NODEFAULT | SND_NOWAIT);
- }
-
- // An event has to be associated with a window,
- // so find the first parent that is a widget and that has a WId
- QAccessibleInterface *iface = event->accessibleInterface();
- if (!isActive() || !iface || !iface->isValid())
- return;
- QWindow *window = QWindowsAccessibility::windowHelper(iface);
-
- if (!window) {
- window = QGuiApplication::focusWindow();
- if (!window)
- return;
- }
-
- QPlatformNativeInterface *platform = QGuiApplication::platformNativeInterface();
- if (!window->handle()) // Called before show(), no native window yet.
- return;
- const HWND hWnd = reinterpret_cast<HWND>(platform->nativeResourceForWindow("handle", window));
-
- if (event->type() != QAccessible::MenuCommand && // MenuCommand is faked
- event->type() != QAccessible::ObjectDestroyed) {
- ::NotifyWinEvent(event->type(), hWnd, OBJID_CLIENT, QAccessible::uniqueId(iface));
- }
-}
-
-QWindow *QWindowsAccessibility::windowHelper(const QAccessibleInterface *iface)
-{
- QWindow *window = iface->window();
- if (!window) {
- QAccessibleInterface *acc = iface->parent();
- while (acc && acc->isValid() && !window) {
- window = acc->window();
- QAccessibleInterface *par = acc->parent();
- acc = par;
- }
- }
- return window;
-}
-
-/*!
- \internal
- helper to wrap a QAccessibleInterface inside a IAccessible*
-*/
-IAccessible *QWindowsAccessibility::wrap(QAccessibleInterface *acc)
-{
- if (!acc)
- return 0;
-
- // ### FIXME: maybe we should accept double insertions into the cache
- if (!QAccessible::uniqueId(acc))
- QAccessible::registerAccessibleInterface(acc);
-
-# ifdef Q_CC_MINGW
- QWindowsMsaaAccessible *wacc = new QWindowsMsaaAccessible(acc);
-# else
- QWindowsIA2Accessible *wacc = new QWindowsIA2Accessible(acc);
-# endif
- IAccessible *iacc = 0;
- wacc->QueryInterface(IID_IAccessible, reinterpret_cast<void **>(&iacc));
- return iacc;
-}
-
-bool QWindowsAccessibility::handleAccessibleObjectFromWindowRequest(HWND hwnd, WPARAM wParam, LPARAM lParam, LRESULT *lResult)
-{
- if (static_cast<long>(lParam) == static_cast<long>(UiaRootObjectId)) {
- /* For UI Automation */
- } else if (DWORD(lParam) == DWORD(OBJID_CLIENT)) {
- // Start handling accessibility internally
- QGuiApplicationPrivate::platformIntegration()->accessibility()->setActive(true);
- // Ignoring all requests while starting up
- // ### Maybe QPA takes care of this???
- if (QCoreApplication::startingUp() || QCoreApplication::closingDown())
- return false;
-
- typedef LRESULT (WINAPI *PtrLresultFromObject)(REFIID, WPARAM, LPUNKNOWN);
- static PtrLresultFromObject ptrLresultFromObject = 0;
- static bool oleaccChecked = false;
-
- if (!oleaccChecked) {
- oleaccChecked = true;
- ptrLresultFromObject = reinterpret_cast<PtrLresultFromObject>(QSystemLibrary::resolve(QLatin1String("oleacc"), "LresultFromObject"));
- }
-
- if (ptrLresultFromObject) {
- QWindow *window = QWindowsContext::instance()->findWindow(hwnd);
- if (window) {
- QAccessibleInterface *acc = window->accessibleRoot();
- if (acc) {
- if (IAccessible *iface = wrap(acc)) {
- *lResult = ptrLresultFromObject(IID_IAccessible, wParam, iface); // ref == 2
- if (*lResult) {
- iface->Release(); // the client will release the object again, and then it will destroy itself
- }
- return true;
- }
- }
- }
- }
- }
- return false;
-}
-
-QT_END_NAMESPACE
-
-#endif //QT_NO_ACCESSIBILITY
diff --git a/src/plugins/platforms/windows/accessible/qwindowsmsaaaccessible.cpp b/src/plugins/platforms/windows/accessible/qwindowsmsaaaccessible.cpp
deleted file mode 100644
index 25b1577772..0000000000
--- a/src/plugins/platforms/windows/accessible/qwindowsmsaaaccessible.cpp
+++ /dev/null
@@ -1,1225 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtCore/QtConfig>
-#ifndef QT_NO_ACCESSIBILITY
-
-#include "qwindowsmsaaaccessible.h"
-#include "qwindowsaccessibility.h"
-#include <oleacc.h>
-#include <servprov.h>
-#include <winuser.h>
-#include "comutils.h"
-
-#include <QtCore/qdebug.h>
-#include <QtCore/qmap.h>
-#include <QtCore/qpair.h>
-#include <QtGui/qaccessible.h>
-#include <QtGui/qguiapplication.h>
-#include <qpa/qplatformnativeinterface.h>
-#include <QtGui/qwindow.h>
-#include <QtGui/private/qhighdpiscaling_p.h>
-
-//#include <uiautomationcoreapi.h>
-#ifndef UiaRootObjectId
-#define UiaRootObjectId -25
-#endif
-
-#if !defined(Q_CC_BOR) && !defined (Q_CC_GNU)
-#include <comdef.h>
-#endif
-
-
-#include <QtCore/qt_windows.h>
-
-
-QT_BEGIN_NAMESPACE
-
-class QWindowsEnumerate : public IEnumVARIANT
-{
-public:
- QWindowsEnumerate(const QVector<int> &a)
- : ref(0), current(0),array(a)
- {
- }
-
- virtual ~QWindowsEnumerate() {}
-
- HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID *);
- ULONG STDMETHODCALLTYPE AddRef();
- ULONG STDMETHODCALLTYPE Release();
-
- HRESULT STDMETHODCALLTYPE Clone(IEnumVARIANT **ppEnum);
- HRESULT STDMETHODCALLTYPE Next(unsigned long celt, VARIANT FAR* rgVar, unsigned long FAR* pCeltFetched);
- HRESULT STDMETHODCALLTYPE Reset();
- HRESULT STDMETHODCALLTYPE Skip(unsigned long celt);
-
-private:
- ULONG ref;
- ULONG current;
- QVector<int> array;
-};
-
-HRESULT STDMETHODCALLTYPE QWindowsEnumerate::QueryInterface(REFIID id, LPVOID *iface)
-{
- *iface = 0;
- if (id == IID_IUnknown)
- *iface = static_cast<IUnknown *>(this);
- else if (id == IID_IEnumVARIANT)
- *iface = static_cast<IEnumVARIANT *>(this);
-
- if (*iface) {
- AddRef();
- return S_OK;
- }
-
- return E_NOINTERFACE;
-}
-
-ULONG STDMETHODCALLTYPE QWindowsEnumerate::AddRef()
-{
- return ++ref;
-}
-
-ULONG STDMETHODCALLTYPE QWindowsEnumerate::Release()
-{
- if (!--ref) {
- delete this;
- return 0;
- }
- return ref;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsEnumerate::Clone(IEnumVARIANT **ppEnum)
-{
- QWindowsEnumerate *penum = 0;
- *ppEnum = 0;
-
- penum = new QWindowsEnumerate(array);
- if (!penum)
- return E_OUTOFMEMORY;
- penum->current = current;
- penum->array = array;
- penum->AddRef();
- *ppEnum = penum;
-
- return S_OK;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsEnumerate::Next(unsigned long celt, VARIANT FAR* rgVar, unsigned long FAR* pCeltFetched)
-{
- if (pCeltFetched)
- *pCeltFetched = 0;
-
- ULONG l;
- for (l = 0; l < celt; l++) {
- VariantInit(&rgVar[l]);
- if (current + 1 > ULONG(array.size())) {
- *pCeltFetched = l;
- return S_FALSE;
- }
-
- rgVar[l].vt = VT_I4;
- rgVar[l].lVal = array[int(current)];
- ++current;
- }
- *pCeltFetched = l;
- return S_OK;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsEnumerate::Reset()
-{
- current = 0;
- return S_OK;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsEnumerate::Skip(unsigned long celt)
-{
- current += celt;
- if (current > ULONG(array.size())) {
- current = ULONG(array.size());
- return S_FALSE;
- }
- return S_OK;
-}
-
-#if defined(DEBUG_SHOW_ATCLIENT_COMMANDS)
-void accessibleDebugClientCalls_helper(const char* funcName, const QAccessibleInterface *iface)
-{
- qCDebug(lcQpaAccessibility) << iface << funcName;
-}
-#endif
-
-/**************************************************************\
- * *
- * IUnknown *
- * *
- **************************************************************/
-HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::QueryInterface(REFIID id, LPVOID *iface)
-{
- *iface = 0;
-
- QByteArray strIID = IIDToString(id);
- if (!strIID.isEmpty()) {
- qCDebug(lcQpaAccessibility) << "QWindowsIA2Accessible::QI() - IID:"
- << strIID << ", iface:" << accessibleInterface();
- }
- if (id == IID_IUnknown) {
- *iface = static_cast<IUnknown *>(static_cast<IDispatch *>(this));
- } else if (id == IID_IDispatch) {
- *iface = static_cast<IDispatch *>(this);
- } else if (id == IID_IAccessible) {
- *iface = static_cast<IAccessible *>(this);
- } else if (id == IID_IOleWindow) {
- *iface = static_cast<IOleWindow *>(this);
- }
-
- if (*iface) {
- AddRef();
- return S_OK;
- }
-
- return E_NOINTERFACE;
-}
-
-ULONG STDMETHODCALLTYPE QWindowsMsaaAccessible::AddRef()
-{
- return ++ref;
-}
-
-ULONG STDMETHODCALLTYPE QWindowsMsaaAccessible::Release()
-{
- if (!--ref) {
- delete this;
- return 0;
- }
- return ref;
-}
-
-
-/*
- IDispatch
-*/
-
-HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::GetTypeInfoCount(unsigned int * pctinfo)
-{
- // We don't use a type library
- *pctinfo = 0;
- return S_OK;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::GetTypeInfo(unsigned int, unsigned long, ITypeInfo **pptinfo)
-{
- // We don't use a type library
- *pptinfo = 0;
- return S_OK;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::GetIDsOfNames(const _GUID &, wchar_t **rgszNames, unsigned int, unsigned long, long *rgdispid)
-{
-#if !defined(Q_CC_BOR) && !defined(Q_CC_GNU)
- // PROPERTIES: Hierarchical
- if (_bstr_t(rgszNames[0]) == _bstr_t(L"accParent"))
- rgdispid[0] = DISPID_ACC_PARENT;
- else if (_bstr_t(rgszNames[0]) == _bstr_t(L"accChildCount"))
- rgdispid[0] = DISPID_ACC_CHILDCOUNT;
- else if (_bstr_t(rgszNames[0]) == _bstr_t(L"accChild"))
- rgdispid[0] = DISPID_ACC_CHILD;
-
- // PROPERTIES: Descriptional
- else if (_bstr_t(rgszNames[0]) == _bstr_t(L"accName("))
- rgdispid[0] = DISPID_ACC_NAME;
- else if (_bstr_t(rgszNames[0]) == _bstr_t(L"accValue"))
- rgdispid[0] = DISPID_ACC_VALUE;
- else if (_bstr_t(rgszNames[0]) == _bstr_t(L"accDescription"))
- rgdispid[0] = DISPID_ACC_DESCRIPTION;
- else if (_bstr_t(rgszNames[0]) == _bstr_t(L"accRole"))
- rgdispid[0] = DISPID_ACC_ROLE;
- else if (_bstr_t(rgszNames[0]) == _bstr_t(L"accState"))
- rgdispid[0] = DISPID_ACC_STATE;
- else if (_bstr_t(rgszNames[0]) == _bstr_t(L"accHelp"))
- rgdispid[0] = DISPID_ACC_HELP;
- else if (_bstr_t(rgszNames[0]) == _bstr_t(L"accHelpTopic"))
- rgdispid[0] = DISPID_ACC_HELPTOPIC;
- else if (_bstr_t(rgszNames[0]) == _bstr_t(L"accKeyboardShortcut"))
- rgdispid[0] = DISPID_ACC_KEYBOARDSHORTCUT;
- else if (_bstr_t(rgszNames[0]) == _bstr_t(L"accFocus"))
- rgdispid[0] = DISPID_ACC_FOCUS;
- else if (_bstr_t(rgszNames[0]) == _bstr_t(L"accSelection"))
- rgdispid[0] = DISPID_ACC_SELECTION;
- else if (_bstr_t(rgszNames[0]) == _bstr_t(L"accDefaultAction"))
- rgdispid[0] = DISPID_ACC_DEFAULTACTION;
-
- // METHODS
- else if (_bstr_t(rgszNames[0]) == _bstr_t(L"accSelect"))
- rgdispid[0] = DISPID_ACC_SELECT;
- else if (_bstr_t(rgszNames[0]) == _bstr_t(L"accLocation"))
- rgdispid[0] = DISPID_ACC_LOCATION;
- else if (_bstr_t(rgszNames[0]) == _bstr_t(L"accNavigate"))
- rgdispid[0] = DISPID_ACC_NAVIGATE;
- else if (_bstr_t(rgszNames[0]) == _bstr_t(L"accHitTest"))
- rgdispid[0] = DISPID_ACC_HITTEST;
- else if (_bstr_t(rgszNames[0]) == _bstr_t(L"accDoDefaultAction"))
- rgdispid[0] = DISPID_ACC_DODEFAULTACTION;
- else
- return DISP_E_UNKNOWNINTERFACE;
-
- return S_OK;
-#else
- Q_UNUSED(rgszNames);
- Q_UNUSED(rgdispid);
-
- return DISP_E_MEMBERNOTFOUND;
-#endif
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::Invoke(long dispIdMember,
- const _GUID &,
- unsigned long,
- unsigned short wFlags,
- tagDISPPARAMS *pDispParams,
- tagVARIANT *pVarResult,
- tagEXCEPINFO *, unsigned int *)
-{
- HRESULT hr = DISP_E_MEMBERNOTFOUND;
-
- switch (dispIdMember)
- {
- case DISPID_ACC_PARENT:
- if (wFlags == DISPATCH_PROPERTYGET) {
- if (!pVarResult)
- return E_INVALIDARG;
- hr = get_accParent(&pVarResult->pdispVal);
- } else {
- hr = DISP_E_MEMBERNOTFOUND;
- }
- break;
-
- case DISPID_ACC_CHILDCOUNT:
- if (wFlags == DISPATCH_PROPERTYGET) {
- if (!pVarResult)
- return E_INVALIDARG;
- hr = get_accChildCount(&pVarResult->lVal);
- } else {
- hr = DISP_E_MEMBERNOTFOUND;
- }
- break;
-
- case DISPID_ACC_CHILD:
- if (wFlags == DISPATCH_PROPERTYGET)
- hr = get_accChild(pDispParams->rgvarg[0], &pVarResult->pdispVal);
- else
- hr = DISP_E_MEMBERNOTFOUND;
- break;
-
- case DISPID_ACC_NAME:
- if (wFlags == DISPATCH_PROPERTYGET)
- hr = get_accName(pDispParams->rgvarg[0], &pVarResult->bstrVal);
- else if (wFlags == DISPATCH_PROPERTYPUT)
- hr = put_accName(pDispParams->rgvarg[0], pVarResult->bstrVal);
- else
- hr = DISP_E_MEMBERNOTFOUND;
- break;
-
- case DISPID_ACC_VALUE:
- if (wFlags == DISPATCH_PROPERTYGET)
- hr = get_accValue(pDispParams->rgvarg[0], &pVarResult->bstrVal);
- else if (wFlags == DISPATCH_PROPERTYPUT)
- hr = put_accValue(pDispParams->rgvarg[0], pVarResult->bstrVal);
- else
- hr = DISP_E_MEMBERNOTFOUND;
- break;
-
- case DISPID_ACC_DESCRIPTION:
- if (wFlags == DISPATCH_PROPERTYGET)
- hr = get_accDescription(pDispParams->rgvarg[0], &pVarResult->bstrVal);
- else
- hr = DISP_E_MEMBERNOTFOUND;
- break;
-
- case DISPID_ACC_ROLE:
- if (wFlags == DISPATCH_PROPERTYGET)
- hr = get_accRole(pDispParams->rgvarg[0], pVarResult);
- else
- hr = DISP_E_MEMBERNOTFOUND;
- break;
-
- case DISPID_ACC_STATE:
- if (wFlags == DISPATCH_PROPERTYGET)
- hr = get_accState(pDispParams->rgvarg[0], pVarResult);
- else
- hr = DISP_E_MEMBERNOTFOUND;
- break;
-
- case DISPID_ACC_HELP:
- if (wFlags == DISPATCH_PROPERTYGET)
- hr = get_accHelp(pDispParams->rgvarg[0], &pVarResult->bstrVal);
- else
- hr = DISP_E_MEMBERNOTFOUND;
- break;
-
- case DISPID_ACC_HELPTOPIC:
- if (wFlags == DISPATCH_PROPERTYGET)
- hr = get_accHelpTopic(&pDispParams->rgvarg[2].bstrVal, pDispParams->rgvarg[1], &pDispParams->rgvarg[0].lVal);
- else
- hr = DISP_E_MEMBERNOTFOUND;
- break;
-
- case DISPID_ACC_KEYBOARDSHORTCUT:
- if (wFlags == DISPATCH_PROPERTYGET)
- hr = get_accKeyboardShortcut(pDispParams->rgvarg[0], &pVarResult->bstrVal);
- else
- hr = DISP_E_MEMBERNOTFOUND;
- break;
-
- case DISPID_ACC_FOCUS:
- if (wFlags == DISPATCH_PROPERTYGET)
- hr = get_accFocus(pVarResult);
- else
- hr = DISP_E_MEMBERNOTFOUND;
- break;
-
- case DISPID_ACC_SELECTION:
- if (wFlags == DISPATCH_PROPERTYGET)
- hr = get_accSelection(pVarResult);
- else
- hr = DISP_E_MEMBERNOTFOUND;
- break;
-
- case DISPID_ACC_DEFAULTACTION:
- if (wFlags == DISPATCH_PROPERTYGET)
- hr = get_accDefaultAction(pDispParams->rgvarg[0], &pVarResult->bstrVal);
- else
- hr = DISP_E_MEMBERNOTFOUND;
- break;
-
- case DISPID_ACC_SELECT:
- if (wFlags == DISPATCH_METHOD)
- hr = accSelect(pDispParams->rgvarg[1].lVal, pDispParams->rgvarg[0]);
- else
- hr = DISP_E_MEMBERNOTFOUND;
- break;
-
- case DISPID_ACC_LOCATION:
- if (wFlags == DISPATCH_METHOD)
- hr = accLocation(&pDispParams->rgvarg[4].lVal, &pDispParams->rgvarg[3].lVal, &pDispParams->rgvarg[2].lVal, &pDispParams->rgvarg[1].lVal, pDispParams->rgvarg[0]);
- else
- hr = DISP_E_MEMBERNOTFOUND;
- break;
-
- case DISPID_ACC_NAVIGATE:
- if (wFlags == DISPATCH_METHOD)
- hr = accNavigate(pDispParams->rgvarg[1].lVal, pDispParams->rgvarg[0], pVarResult);
- else
- hr = DISP_E_MEMBERNOTFOUND;
- break;
-
- case DISPID_ACC_HITTEST:
- if (wFlags == DISPATCH_METHOD)
- hr = accHitTest(pDispParams->rgvarg[1].lVal, pDispParams->rgvarg[0].lVal, pVarResult);
- else
- hr = DISP_E_MEMBERNOTFOUND;
- break;
-
- case DISPID_ACC_DODEFAULTACTION:
- if (wFlags == DISPATCH_METHOD)
- hr = accDoDefaultAction(pDispParams->rgvarg[0]);
- else
- hr = DISP_E_MEMBERNOTFOUND;
- break;
-
- default:
- hr = DISP_E_MEMBERNOTFOUND;
- break;
- }
-
- if (!SUCCEEDED(hr)) {
- return hr;
- }
- return hr;
-}
-
-/*
- IAccessible
-
-IAccessible::accHitTest documents the value returned in pvarID like this:
-
-| *Point location* | *vt member* | *Value member* |
-+========================================================+=============+=========================+
-| Outside of the object's boundaries, and either inside | VT_EMPTY | None. |
-| or outside of the object's bounding rectangle. | | |
-+--------------------------------------------------------+-------------+-------------------------+
-| Within the object but not within a child element or a | VT_I4 | lVal is CHILDID_SELF |
-| child object. | | |
-+--------------------------------------------------------+-------------+-------------------------+
-| Within a child element. | VT_I4 | lVal contains |
-| | | the child ID. |
-+--------------------------------------------------------+-------------+-------------------------+
-| Within a child object. | VT_DISPATCH | pdispVal is set to the |
-| | | child object's IDispatch|
-| | | interface pointer |
-+--------------------------------------------------------+-------------+-------------------------+
-*/
-HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::accHitTest(long xLeft, long yTop, VARIANT *pvarID)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- const QPoint pos = QHighDpi::fromNativeLocalPosition(QPoint(xLeft, yTop),
- QWindowsAccessibility::windowHelper(accessible));
- QAccessibleInterface *child = accessible->childAt(pos.x(), pos.y());
- if (child == 0) {
- // no child found, return this item if it contains the coordinates
- if (accessible->rect().contains(xLeft, yTop)) {
- (*pvarID).vt = VT_I4;
- (*pvarID).lVal = CHILDID_SELF;
- return S_OK;
- }
- } else {
- IAccessible *iface = QWindowsAccessibility::wrap(child);
- if (iface) {
- (*pvarID).vt = VT_DISPATCH;
- (*pvarID).pdispVal = iface;
- return S_OK;
- }
- }
-
- // Did not find anything
- (*pvarID).vt = VT_EMPTY;
- return S_FALSE;
-}
-
-/*
- It is recommended to read
- "Implementing a Microsoft Active Accessibility (MSAA) Server.
- Practical Tips for Developers and How Mozilla Does It"
- (https://developer.mozilla.org/En/Accessibility/Implementing_an_MSAA_Server)
-
- to get an overview of what's important to implement and what parts of MSAA
- can be ignored. All stuff prefixed with "moz" are information from that page.
-*/
-// moz: [important]
-HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::accLocation(long *pxLeft, long *pyTop, long *pcxWidth, long *pcyHeight, VARIANT varID)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- QAccessibleInterface *acc = childPointer(accessible, varID);
- if (!acc || !acc->isValid())
- return E_FAIL;
- const QRect rect = QHighDpi::toNativePixels(acc->rect(),
- QWindowsAccessibility::windowHelper(accessible));
-
- *pxLeft = rect.x();
- *pyTop = rect.y();
- *pcxWidth = rect.width();
- *pcyHeight = rect.height();
-
- return S_OK;
-}
-
-// moz: [important, but no need to implement up/down/left/right]
-HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::accNavigate(long navDir, VARIANT varStart, VARIANT *pvarEnd)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- QAccessibleInterface *acc = 0;
- switch (navDir) {
- case NAVDIR_FIRSTCHILD:
- acc = accessible->child(0);
- break;
- case NAVDIR_LASTCHILD:
- acc = accessible->child(accessible->childCount() - 1);
- break;
- case NAVDIR_NEXT:
- case NAVDIR_PREVIOUS:
- if (!varStart.lVal){
- QAccessibleInterface *parent = accessible->parent();
- if (parent && parent->isValid()) {
- int index = parent->indexOfChild(accessible);
- index += (navDir == NAVDIR_NEXT) ? 1 : -1;
- if (index >= 0 && index < parent->childCount())
- acc = parent->child(index);
- }
- } else {
- int index = varStart.lVal;
- index += (navDir == NAVDIR_NEXT) ? 1 : -1;
- if (index > 0 && index <= accessible->childCount())
- acc = accessible->child(index - 1);
- }
- break;
-
- // Geometrical
- case NAVDIR_UP:
- case NAVDIR_DOWN:
- case NAVDIR_LEFT:
- case NAVDIR_RIGHT: {
- QAccessibleInterface *pIface = accessible->parent();
- if (pIface && pIface->isValid()) {
- const int indexOfOurself = pIface->indexOfChild(accessible);
- QRect startg = accessible->rect();
- QPoint startc = startg.center();
- QAccessibleInterface *candidate = 0;
- unsigned mindist = UINT_MAX; // will work on screen sizes at least up to 46340x46340
- const int sibCount = pIface->childCount();
- for (int i = 0; i < sibCount; ++i) {
- QAccessibleInterface *sibling = 0;
- sibling = pIface->child(i);
- Q_ASSERT(sibling);
- if (i == indexOfOurself || sibling->state().invisible) {
- //ignore ourself and invisible siblings
- continue;
- }
-
- QRect sibg = sibling->rect();
- QPoint sibc = sibg.center();
- QPoint sibp;
- QPoint startp;
- QPoint distp;
- switch (navDir) {
- case NAVDIR_LEFT:
- startp = QPoint(startg.left(), startg.top() + startg.height() / 2);
- sibp = QPoint(sibg.right(), sibg.top() + sibg.height() / 2);
- if (QPoint(sibc - startc).x() >= 0) {
- continue;
- }
- distp = sibp - startp;
- break;
- case NAVDIR_RIGHT:
- startp = QPoint(startg.right(), startg.top() + startg.height() / 2);
- sibp = QPoint(sibg.left(), sibg.top() + sibg.height() / 2);
- if (QPoint(sibc - startc).x() <= 0) {
- continue;
- }
- distp = sibp - startp;
- break;
- case NAVDIR_UP:
- startp = QPoint(startg.left() + startg.width() / 2, startg.top());
- sibp = QPoint(sibg.left() + sibg.width() / 2, sibg.bottom());
- if (QPoint(sibc - startc).y() >= 0) {
- continue;
- }
- distp = sibp - startp;
- break;
- case NAVDIR_DOWN:
- startp = QPoint(startg.left() + startg.width() / 2, startg.bottom());
- sibp = QPoint(sibg.left() + sibg.width() / 2, sibg.top());
- if (QPoint(sibc - startc).y() <= 0) {
- continue;
- }
- distp = sibp - startp;
- break;
- default:
- break;
- }
-
- // Since we're *comparing* (and not measuring) distances, we can compare the
- // squared distance, (thus, no need to take the sqrt()).
- unsigned dist = distp.x() * distp.x() + distp.y() * distp.y();
- if (dist < mindist) {
- candidate = sibling;
- mindist = dist;
- }
- }
- acc = candidate;
- }
- }
- break;
- default:
- break;
- }
- if (!acc) {
- (*pvarEnd).vt = VT_EMPTY;
- return S_FALSE;
- }
-
- if (IAccessible *iface = QWindowsAccessibility::wrap(acc)) {
- (*pvarEnd).vt = VT_DISPATCH;
- (*pvarEnd).pdispVal = iface;
- return S_OK;
- }
-
- (*pvarEnd).vt = VT_EMPTY;
- return S_FALSE;
-}
-
-// moz: [important]
-HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accChild(VARIANT varChildID, IDispatch** ppdispChild)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- if (varChildID.vt != VT_I4)
- return E_INVALIDARG;
-
- QAccessibleInterface *acc = childPointer(accessible, varChildID);
- if (acc && acc->isValid()) {
- *ppdispChild = QWindowsAccessibility::wrap(acc);
- return S_OK;
- }
-
- return E_FAIL;
-}
-
-// moz: [important]
-HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accChildCount(long* pcountChildren)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- *pcountChildren = accessible->childCount();
- return S_OK;
-}
-
-// moz: [important]
-HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accParent(IDispatch** ppdispParent)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- QAccessibleInterface *acc = accessible->parent();
- if (acc) {
- if (IAccessible *iface = QWindowsAccessibility::wrap(acc)) {
- *ppdispParent = iface;
- return S_OK;
- }
- }
-
- *ppdispParent = 0;
- return S_FALSE;
-}
-
-/*
- Properties and methods
-*/
-HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::accDoDefaultAction(VARIANT varID)
-{
- Q_UNUSED(varID);
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- if (QAccessibleActionInterface *actionIface = accessible->actionInterface()) {
- const QString def = actionIface->actionNames().value(0);
- if (!def.isEmpty()) {
- actionIface->doAction(def);
- return S_OK;
- }
- }
- return S_FALSE;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accDefaultAction(VARIANT varID, BSTR* pszDefaultAction)
-{
- Q_UNUSED(varID);
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- *pszDefaultAction = 0;
- if (QAccessibleActionInterface *actionIface = accessible->actionInterface()) {
- const QString def = actionIface->actionNames().value(0);
- if (!def.isEmpty())
- *pszDefaultAction = QStringToBSTR(def);
- }
- return *pszDefaultAction ? S_OK : S_FALSE;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accDescription(VARIANT varID, BSTR* pszDescription)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
-
- QString descr;
- if (varID.lVal) {
- QAccessibleInterface *child = childPointer(accessible, varID);
- if (!child || !child->isValid())
- return E_FAIL;
- descr = child->text(QAccessible::Description);
- } else {
- descr = accessible->text(QAccessible::Description);
- }
- if (descr.size()) {
- *pszDescription = QStringToBSTR(descr);
- return S_OK;
- }
-
- *pszDescription = 0;
- return S_FALSE;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accHelp(VARIANT varID, BSTR *pszHelp)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- QString help;
- if (varID.lVal) {
- QAccessibleInterface *child = childPointer(accessible, varID);
- if (!child || !child->isValid())
- return E_FAIL;
- help = child->text(QAccessible::Help);
- } else {
- help = accessible->text(QAccessible::Help);
- }
- if (help.size()) {
- *pszHelp = QStringToBSTR(help);
- return S_OK;
- }
-
- *pszHelp = 0;
- return S_FALSE;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accHelpTopic(BSTR *, VARIANT, long *)
-{
- return DISP_E_MEMBERNOTFOUND;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accKeyboardShortcut(VARIANT varID, BSTR *pszKeyboardShortcut)
-{
- Q_UNUSED(varID);
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- *pszKeyboardShortcut = 0;
- if (QAccessibleActionInterface *actionIface = accessible->actionInterface()) {
- const QString def = actionIface->actionNames().value(0);
- if (!def.isEmpty()) {
- const QString keyBoardShortCut = actionIface->keyBindingsForAction(def).value(0);
- if (!keyBoardShortCut.isEmpty())
- *pszKeyboardShortcut = QStringToBSTR(keyBoardShortCut);
- }
- }
- return *pszKeyboardShortcut ? S_OK : S_FALSE;
-}
-
-static QAccessibleInterface *relatedInterface(QAccessibleInterface *iface, QAccessible::RelationFlag flag)
-{
- typedef QPair<QAccessibleInterface *, QAccessible::Relation> RelationPair;
- QVector<RelationPair> rels = iface->relations(flag);
-
- return rels.value(0).first;
-}
-
-// moz: [important]
-HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accName(VARIANT varID, BSTR* pszName)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- QString name;
- if (varID.lVal) {
- QAccessibleInterface *child = childPointer(accessible, varID);
- if (!child || !child->isValid())
- return E_FAIL;
- name = child->text(QAccessible::Name);
- if (name.isEmpty()) {
- if (QAccessibleInterface *labelInterface = relatedInterface(child, QAccessible::Label)) {
- name = labelInterface->text(QAccessible::Name);
- }
- }
- } else {
- name = accessible->text(QAccessible::Name);
- if (name.isEmpty()) {
- if (QAccessibleInterface *labelInterface = relatedInterface(accessible, QAccessible::Label)) {
- name = labelInterface->text(QAccessible::Name);
- }
- }
- }
-
- QString shortcut = accessible->text(QAccessible::Accelerator);
- if (!shortcut.isEmpty())
- name += QLatin1Char(' ') + shortcut;
-
- if (name.size()) {
- *pszName = QStringToBSTR(name);
- return S_OK;
- }
-
- *pszName = 0;
- return S_FALSE;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::put_accName(VARIANT, BSTR)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- return DISP_E_MEMBERNOTFOUND;
-}
-
-// moz: [important]
-HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accRole(VARIANT varID, VARIANT *pvarRole)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- QAccessible::Role role;
- if (varID.lVal) {
- QAccessibleInterface *child = childPointer(accessible, varID);
- if (!child || !child->isValid())
- return E_FAIL;
- role = child->role();
- } else {
- role = accessible->role();
- }
-
- if (role != QAccessible::NoRole) {
- if (role >= QAccessible::LayeredPane) {
- // This block should hopefully only be entered if the AT client
- // does not support IAccessible2, since it should prefer IA2::role() then.
- if (role == QAccessible::LayeredPane)
- role = QAccessible::Pane;
- else if (role == QAccessible::WebDocument)
- role = QAccessible::Document;
- else
- role = QAccessible::Client;
- }
- (*pvarRole).vt = VT_I4;
- (*pvarRole).lVal = role;
- } else {
- (*pvarRole).vt = VT_EMPTY;
- }
- return S_OK;
-}
-
-// moz: [important]
-HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accState(VARIANT varID, VARIANT *pvarState)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- QAccessible::State state;
- if (varID.lVal) {
- QAccessibleInterface *child = childPointer(accessible, varID);
- if (!child || !child->isValid())
- return E_FAIL;
- state = child->state();
- } else {
- state = accessible->state();
- }
-
- LONG st = 0;
- if (state.animated)
- st |= STATE_SYSTEM_ANIMATED;
- if (state.busy)
- st |= STATE_SYSTEM_BUSY;
- if (state.checked)
- st |= STATE_SYSTEM_CHECKED;
- if (state.collapsed)
- st |= STATE_SYSTEM_COLLAPSED;
- if (state.defaultButton)
- st |= STATE_SYSTEM_DEFAULT;
- if (state.expanded)
- st |= STATE_SYSTEM_EXPANDED;
- if (state.extSelectable)
- st |= STATE_SYSTEM_EXTSELECTABLE;
- if (state.focusable)
- st |= STATE_SYSTEM_FOCUSABLE;
- if (state.focused)
- st |= STATE_SYSTEM_FOCUSED;
- if (state.hasPopup)
- st |= STATE_SYSTEM_HASPOPUP;
- if (state.hotTracked)
- st |= STATE_SYSTEM_HOTTRACKED;
- if (state.invisible)
- st |= STATE_SYSTEM_INVISIBLE;
- if (state.linked)
- st |= STATE_SYSTEM_LINKED;
- if (state.marqueed)
- st |= STATE_SYSTEM_MARQUEED;
- if (state.checkStateMixed)
- st |= STATE_SYSTEM_MIXED;
- if (state.movable)
- st |= STATE_SYSTEM_MOVEABLE;
- if (state.multiSelectable)
- st |= STATE_SYSTEM_MULTISELECTABLE;
- if (state.offscreen)
- st |= STATE_SYSTEM_OFFSCREEN;
- if (state.pressed)
- st |= STATE_SYSTEM_PRESSED;
- if (state.passwordEdit)
- st |= STATE_SYSTEM_PROTECTED;
- if (state.readOnly)
- st |= STATE_SYSTEM_READONLY;
- if (state.selectable)
- st |= STATE_SYSTEM_SELECTABLE;
- if (state.selected)
- st |= STATE_SYSTEM_SELECTED;
- if (state.selfVoicing)
- st |= STATE_SYSTEM_SELFVOICING;
- if (state.sizeable)
- st |= STATE_SYSTEM_SIZEABLE;
- if (state.traversed)
- st |= STATE_SYSTEM_TRAVERSED;
- if (state.disabled)
- st |= STATE_SYSTEM_UNAVAILABLE;
-
- (*pvarState).vt = VT_I4;
- (*pvarState).lVal = st;
- return S_OK;
-}
-
-// moz: [important]
-HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accValue(VARIANT varID, BSTR* pszValue)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (varID.vt != VT_I4)
- return E_INVALIDARG;
-
- if (!accessible || !accessible->isValid() || varID.lVal) {
- return E_FAIL;
- }
-
- QString value;
- if (accessible->valueInterface()) {
- value = accessible->valueInterface()->currentValue().toString();
- } else {
- value = accessible->text(QAccessible::Value);
- }
- if (!value.isNull()) {
- *pszValue = QStringToBSTR(value);
- return S_OK;
- }
-
- *pszValue = 0;
- qCDebug(lcQpaAccessibility) << "return S_FALSE";
- return S_FALSE;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::put_accValue(VARIANT, BSTR value)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
-
- if (!accessible || !accessible->isValid()) {
- return E_FAIL;
- }
-
- QString qstrValue = QString::fromWCharArray(value);
-
- if (accessible->valueInterface()) {
- accessible->valueInterface()->setCurrentValue(qstrValue);
- } else {
- accessible->setText(QAccessible::Value, qstrValue);
- }
-
- return S_OK;
-}
-
-// moz: [important]
-HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::accSelect(long flagsSelect, VARIANT varID)
-{
- Q_UNUSED(flagsSelect);
- Q_UNUSED(varID);
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- bool res = false;
-
-/*
- ### Check for accessibleTableInterface() or accessibleTextInterface()
-
- ### and if there are no ia2 interfaces we should do nothing??
- if (flagsSelect & SELFLAG_TAKEFOCUS)
- res = accessible()->doAction(SetFocus, varID.lVal, QVariantList());
- if (flagsSelect & SELFLAG_TAKESELECTION) {
- accessible()->doAction(ClearSelection, 0, QVariantList());
- res = accessible()->doAction(AddToSelection, varID.lVal, QVariantList());
- }
- if (flagsSelect & SELFLAG_EXTENDSELECTION)
- res = accessible()->doAction(ExtendSelection, varID.lVal, QVariantList());
- if (flagsSelect & SELFLAG_ADDSELECTION)
- res = accessible()->doAction(AddToSelection, varID.lVal, QVariantList());
- if (flagsSelect & SELFLAG_REMOVESELECTION)
- res = accessible()->doAction(RemoveSelection, varID.lVal, QVariantList());
-*/
- return res ? S_OK : S_FALSE;
-}
-
-/*!
- \internal
- Can return:
-
- +-------------+------------------------------------------------------------------------------+
- | VT_EMPTY | None. Neither this object nor any of its children has the keyboard focus. |
- +-------------+------------------------------------------------------------------------------+
- | VT_I4 | lVal is CHILDID_SELF. The object itself has the keyboard focus. |
- +-------------+------------------------------------------------------------------------------+
- | VT_I4 | lVal contains the child ID of the child element that has the keyboard focus. |
- +-------------+------------------------------------------------------------------------------+
- | VT_DISPATCH | pdispVal member is the address of the IDispatch interface for the child |
- | | object that has the keyboard focus. |
- +-------------+------------------------------------------------------------------------------+
- moz: [important]
-*/
-HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accFocus(VARIANT *pvarID)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- if (QAccessibleInterface *acc = accessible->focusChild()) {
- if (acc == accessible) {
- (*pvarID).vt = VT_I4;
- (*pvarID).lVal = CHILDID_SELF;
- return S_OK;
- } else {
- if (IAccessible *iface = QWindowsAccessibility::wrap(acc)) {
- (*pvarID).vt = VT_DISPATCH;
- (*pvarID).pdispVal = iface;
- return S_OK;
- }
- }
- }
- (*pvarID).vt = VT_EMPTY;
- return S_FALSE;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accSelection(VARIANT *pvarChildren)
-{
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- int cc = accessible->childCount();
- QVector<int> sel(cc);
- int selIndex = 0;
- for (int i = 0; i < cc; ++i) {
- bool isSelected = false;
- QAccessibleInterface *child = accessible->child(i);
- if (child) {
- isSelected = child->state().selected;
- }
- if (isSelected)
- sel[selIndex++] = i+1;
- }
- sel.resize(selIndex);
- if (sel.isEmpty()) {
- (*pvarChildren).vt = VT_EMPTY;
- return S_FALSE;
- }
- if (sel.size() == 1) {
- (*pvarChildren).vt = VT_I4;
- (*pvarChildren).lVal = sel[0];
- return S_OK;
- }
- IEnumVARIANT *iface = new QWindowsEnumerate(sel);
- IUnknown *uiface;
- iface->QueryInterface(IID_IUnknown, (void**)&uiface);
- (*pvarChildren).vt = VT_UNKNOWN;
- (*pvarChildren).punkVal = uiface;
-
- return S_OK;
-}
-
-/**************************************************************\
- * IOleWindow *
- **************************************************************/
-HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::GetWindow(HWND *phwnd)
-{
- *phwnd = 0;
- QAccessibleInterface *accessible = accessibleInterface();
- accessibleDebugClientCalls(accessible);
- if (!accessible)
- return E_FAIL;
-
- QWindow *window = QWindowsAccessibility::windowHelper(accessible);
- if (!window)
- return E_FAIL;
-
- QPlatformNativeInterface *platform = QGuiApplication::platformNativeInterface();
- Q_ASSERT(platform);
- *phwnd = (HWND)platform->nativeResourceForWindow("handle", window);
- qCDebug(lcQpaAccessibility) << "QWindowsAccessible::GetWindow(): " << *phwnd;
- return S_OK;
-}
-
-HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::ContextSensitiveHelp(BOOL)
-{
- return S_OK;
-}
-
-#define IF_EQUAL_RETURN_IIDSTRING(id, iid) if (id == iid) return QByteArray(#iid)
-QByteArray QWindowsMsaaAccessible::IIDToString(REFIID id)
-{
- IF_EQUAL_RETURN_IIDSTRING(id, IID_IUnknown);
- IF_EQUAL_RETURN_IIDSTRING(id, IID_IDispatch);
- IF_EQUAL_RETURN_IIDSTRING(id, IID_IAccessible);
- IF_EQUAL_RETURN_IIDSTRING(id, IID_IOleWindow);
-
- return QByteArray();
-}
-
-QT_END_NAMESPACE
-
-#endif //QT_NO_ACCESSIBILITY
diff --git a/src/plugins/platforms/windows/accessible/qwindowsmsaaaccessible.h b/src/plugins/platforms/windows/accessible/qwindowsmsaaaccessible.h
deleted file mode 100644
index fd00f8ac8b..0000000000
--- a/src/plugins/platforms/windows/accessible/qwindowsmsaaaccessible.h
+++ /dev/null
@@ -1,175 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#ifndef QWINDOWSMSAAACCESSIBLE_H
-#define QWINDOWSMSAAACCESSIBLE_H
-
-#include <QtCore/QtConfig>
-#ifndef QT_NO_ACCESSIBILITY
-#include <QtCore/qglobal.h>
-
-#include <QtCore/qt_windows.h>
-#include <QtCore/qsharedpointer.h>
-#include <QtGui/qaccessible.h>
-#ifndef Q_CC_MINGW
-# include <oleacc.h>
-# include "ia2_api_all.h" // IAccessible2 inherits from IAccessible
-#else
- // MinGW
-# include <basetyps.h>
-# include <oleacc.h>
-#endif
-
-QT_BEGIN_NAMESPACE
-
-#ifndef QT_NO_DEBUG_OUTPUT
-#define DEBUG_SHOW_ATCLIENT_COMMANDS
-#endif
-#if defined(DEBUG_SHOW_ATCLIENT_COMMANDS)
-void accessibleDebugClientCalls_helper(const char* funcName, const QAccessibleInterface *iface);
-# define accessibleDebugClientCalls(iface) accessibleDebugClientCalls_helper(Q_FUNC_INFO, iface)
-#else
-# define accessibleDebugClientCalls(iface)
-#endif
-
-QWindow *window_helper(const QAccessibleInterface *iface);
-
-/**************************************************************\
- * QWindowsAccessible *
- **************************************************************/
-class QWindowsMsaaAccessible : public
-#ifdef Q_CC_MINGW
- IAccessible
-#else
- IAccessible2
-#endif
- , public IOleWindow
-{
-public:
- QWindowsMsaaAccessible(QAccessibleInterface *a)
- : ref(0)
- {
- id = QAccessible::uniqueId(a);
- }
-
- virtual ~QWindowsMsaaAccessible()
- {
- }
-
- /* IUnknown */
- HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID *);
- ULONG STDMETHODCALLTYPE AddRef();
- ULONG STDMETHODCALLTYPE Release();
-
- /* IDispatch */
- HRESULT STDMETHODCALLTYPE GetTypeInfoCount(unsigned int *);
- HRESULT STDMETHODCALLTYPE GetTypeInfo(unsigned int, unsigned long, ITypeInfo **);
- HRESULT STDMETHODCALLTYPE GetIDsOfNames(const _GUID &, wchar_t **, unsigned int, unsigned long, long *);
- HRESULT STDMETHODCALLTYPE Invoke(long, const _GUID &, unsigned long, unsigned short, tagDISPPARAMS *, tagVARIANT *, tagEXCEPINFO *, unsigned int *);
-
- /* IAccessible */
- HRESULT STDMETHODCALLTYPE accHitTest(long xLeft, long yTop, VARIANT *pvarID);
- HRESULT STDMETHODCALLTYPE accLocation(long *pxLeft, long *pyTop, long *pcxWidth, long *pcyHeight, VARIANT varID);
- HRESULT STDMETHODCALLTYPE accNavigate(long navDir, VARIANT varStart, VARIANT *pvarEnd);
- HRESULT STDMETHODCALLTYPE get_accChild(VARIANT varChildID, IDispatch** ppdispChild);
- HRESULT STDMETHODCALLTYPE get_accChildCount(long* pcountChildren);
- HRESULT STDMETHODCALLTYPE get_accParent(IDispatch** ppdispParent);
-
- HRESULT STDMETHODCALLTYPE accDoDefaultAction(VARIANT varID);
- HRESULT STDMETHODCALLTYPE get_accDefaultAction(VARIANT varID, BSTR* pszDefaultAction);
- HRESULT STDMETHODCALLTYPE get_accDescription(VARIANT varID, BSTR* pszDescription);
- HRESULT STDMETHODCALLTYPE get_accHelp(VARIANT varID, BSTR *pszHelp);
- HRESULT STDMETHODCALLTYPE get_accHelpTopic(BSTR *pszHelpFile, VARIANT varChild, long *pidTopic);
- HRESULT STDMETHODCALLTYPE get_accKeyboardShortcut(VARIANT varID, BSTR *pszKeyboardShortcut);
- HRESULT STDMETHODCALLTYPE get_accName(VARIANT varID, BSTR* pszName);
- HRESULT STDMETHODCALLTYPE put_accName(VARIANT varChild, BSTR szName);
- HRESULT STDMETHODCALLTYPE get_accRole(VARIANT varID, VARIANT *pvarRole);
- HRESULT STDMETHODCALLTYPE get_accState(VARIANT varID, VARIANT *pvarState);
- HRESULT STDMETHODCALLTYPE get_accValue(VARIANT varID, BSTR* pszValue);
- HRESULT STDMETHODCALLTYPE put_accValue(VARIANT varChild, BSTR szValue);
-
- HRESULT STDMETHODCALLTYPE accSelect(long flagsSelect, VARIANT varID);
- HRESULT STDMETHODCALLTYPE get_accFocus(VARIANT *pvarID);
- HRESULT STDMETHODCALLTYPE get_accSelection(VARIANT *pvarChildren);
-
- /* IOleWindow */
- HRESULT STDMETHODCALLTYPE GetWindow(HWND *phwnd);
- HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(BOOL fEnterMode);
-
-protected:
- virtual QByteArray IIDToString(REFIID id);
-
- QAccessible::Id id;
-
- QAccessibleInterface *accessibleInterface() const
- {
- QAccessibleInterface *iface = QAccessible::accessibleInterface(id);
- if (iface && iface->isValid())
- return iface;
- return 0;
- }
-
- static QAccessibleInterface *childPointer(QAccessibleInterface *parent, VARIANT varID)
- {
- // -1 since windows API always uses 1 for the first child
- Q_ASSERT(parent);
-
- QAccessibleInterface *acc = 0;
- int childIndex = varID.lVal;
- if (childIndex == 0) {
- // Yes, some AT clients (Active Accessibility Object Inspector)
- // actually ask for the same object. As a consequence, we need to clone ourselves:
- acc = parent;
- } else if (childIndex < 0) {
- acc = QAccessible::accessibleInterface((QAccessible::Id)childIndex);
- } else {
- acc = parent->child(childIndex - 1);
- }
- return acc;
- }
-
-private:
- ULONG ref;
-
-};
-
-QT_END_NAMESPACE
-
-#endif //QT_NO_ACCESSIBILITY
-
-#endif // QWINDOWSMSAAACCESSIBLE_H
diff --git a/src/plugins/platforms/windows/qtwindowsglobal.h b/src/plugins/platforms/windows/qtwindowsglobal.h
index d2e1309280..c8bdc1c93e 100644
--- a/src/plugins/platforms/windows/qtwindowsglobal.h
+++ b/src/plugins/platforms/windows/qtwindowsglobal.h
@@ -123,6 +123,9 @@ enum WindowsEventType // Simplify event types
EndSessionApplicationEvent = ApplicationEventFlag + 5,
AppCommandEvent = ApplicationEventFlag + 6,
DeviceChangeEvent = ApplicationEventFlag + 7,
+ MenuAboutToShowEvent = ApplicationEventFlag + 8,
+ AcceleratorCommandEvent = ApplicationEventFlag + 9,
+ MenuCommandEvent = ApplicationEventFlag + 10,
InputMethodStartCompositionEvent = InputMethodEventFlag + 1,
InputMethodCompositionEvent = InputMethodEventFlag + 2,
InputMethodEndCompositionEvent = InputMethodEventFlag + 3,
@@ -274,6 +277,11 @@ inline QtWindows::WindowsEventType windowsEventType(UINT message, WPARAM wParamI
return QtWindows::GestureEvent;
case WM_DEVICECHANGE:
return QtWindows::DeviceChangeEvent;
+ case WM_INITMENU:
+ case WM_INITMENUPOPUP:
+ return QtWindows::MenuAboutToShowEvent;
+ case WM_COMMAND:
+ return HIWORD(wParamIn) ? QtWindows::AcceleratorCommandEvent : QtWindows::MenuCommandEvent;
case WM_DPICHANGED:
return QtWindows::DpiChangedEvent;
case WM_ENTERSIZEMOVE:
diff --git a/src/plugins/platforms/windows/qwin10helpers.cpp b/src/plugins/platforms/windows/qwin10helpers.cpp
index 12cccd124b..ac6a34d7c2 100644
--- a/src/plugins/platforms/windows/qwin10helpers.cpp
+++ b/src/plugins/platforms/windows/qwin10helpers.cpp
@@ -40,6 +40,7 @@
#include "qwin10helpers.h"
#include <QtCore/QDebug>
+#include <QtCore/QOperatingSystemVersion>
#include <QtCore/private/qsystemlibrary_p.h>
#if defined(Q_CC_MINGW)
@@ -115,7 +116,7 @@ static QWindowsComBaseDLL baseComDll;
bool QWindowsComBaseDLL::init()
{
- if (QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS10 && !isValid()) {
+ if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10 && !isValid()) {
QSystemLibrary library(QStringLiteral("combase"));
roGetActivationFactory =
reinterpret_cast<RoGetActivationFactory>(library.resolve("RoGetActivationFactory"));
diff --git a/src/plugins/platforms/windows/qwindowsbackingstore.cpp b/src/plugins/platforms/windows/qwindowsbackingstore.cpp
index 49c7144221..80872c3ea3 100644
--- a/src/plugins/platforms/windows/qwindowsbackingstore.cpp
+++ b/src/plugins/platforms/windows/qwindowsbackingstore.cpp
@@ -93,7 +93,7 @@ void QWindowsBackingStore::flush(QWindow *window, const QRegion &region,
// Windows with alpha: Use blend function to update.
QRect r = QHighDpi::toNativePixels(window->frameGeometry(), window);
QPoint frameOffset(QHighDpi::toNativePixels(QPoint(window->frameMargins().left(), window->frameMargins().top()),
- static_cast<const QWindow *>(Q_NULLPTR)));
+ static_cast<const QWindow *>(nullptr)));
QRect dirtyRect = br.translated(offset + frameOffset);
SIZE size = {r.width(), r.height()};
diff --git a/src/plugins/platforms/windows/qwindowscombase.h b/src/plugins/platforms/windows/qwindowscombase.h
new file mode 100644
index 0000000000..5e51b6b7b7
--- /dev/null
+++ b/src/plugins/platforms/windows/qwindowscombase.h
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSCOMBASE_H
+#define QWINDOWSCOMBASE_H
+
+#include <QtCore/QtGlobal>
+
+#include <unknwn.h>
+
+QT_BEGIN_NAMESPACE
+
+// The __uuidof operator of MinGW does not work for all interfaces (for example,
+// IAccessible2). Specializations of this function can be provides to work
+// around this.
+template <class DesiredInterface>
+static IID qUuidOf() { return __uuidof(DesiredInterface); }
+
+// Helper for implementing IUnknown::QueryInterface.
+template <class DesiredInterface, class Derived>
+bool qWindowsComQueryInterface(Derived *d, REFIID id, LPVOID *iface)
+{
+ if (id == qUuidOf<DesiredInterface>()) {
+ *iface = static_cast<DesiredInterface *>(d);
+ d->AddRef();
+ return true;
+ }
+ return false;
+}
+
+// Helper for implementing IUnknown::QueryInterface for IUnknown
+// in the case of multiple inheritance via the first inherited class.
+template <class FirstInheritedInterface, class Derived>
+bool qWindowsComQueryUnknownInterfaceMulti(Derived *d, REFIID id, LPVOID *iface)
+{
+ if (id == __uuidof(IUnknown)) {
+ *iface = static_cast<FirstInheritedInterface *>(d);
+ d->AddRef();
+ return true;
+ }
+ return false;
+}
+
+// Helper base class to provide IUnknown methods for COM classes (single inheritance)
+template <class ComInterface> class QWindowsComBase : public ComInterface
+{
+ Q_DISABLE_COPY(QWindowsComBase)
+public:
+ explicit QWindowsComBase(ULONG initialRefCount = 1) : m_ref(initialRefCount) {}
+ virtual ~QWindowsComBase() {}
+
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID id, LPVOID *iface)
+ {
+ *iface = nullptr;
+ return qWindowsComQueryInterface<IUnknown>(this, id, iface) || qWindowsComQueryInterface<ComInterface>(this, id, iface)
+ ? S_OK : E_NOINTERFACE;
+ }
+
+ ULONG STDMETHODCALLTYPE AddRef() { return ++m_ref; }
+
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ if (!--m_ref) {
+ delete this;
+ return 0;
+ }
+ return m_ref;
+ }
+
+private:
+ ULONG m_ref;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWSCOMBASE_H
diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp
index cda6c99ad0..c146f8ec25 100644
--- a/src/plugins/platforms/windows/qwindowscontext.cpp
+++ b/src/plugins/platforms/windows/qwindowscontext.cpp
@@ -44,6 +44,7 @@
#include "qwindowskeymapper.h"
#include "qwindowsmousehandler.h"
#include "qtwindowsglobal.h"
+#include "qwindowsmenu.h"
#include "qwindowsmime.h"
#include "qwindowsinputcontext.h"
#if QT_CONFIG(tabletevent)
@@ -52,7 +53,7 @@
#include "qwindowstheme.h"
#include <private/qguiapplication_p.h>
#ifndef QT_NO_ACCESSIBILITY
-# include "accessible/qwindowsaccessibility.h"
+# include "uiautomation/qwindowsuiaaccessibility.h"
#endif
#if QT_CONFIG(sessionmanager)
# include <private/qsessionmanager_p.h>
@@ -94,8 +95,11 @@ Q_LOGGING_CATEGORY(lcQpaGl, "qt.qpa.gl")
Q_LOGGING_CATEGORY(lcQpaMime, "qt.qpa.mime")
Q_LOGGING_CATEGORY(lcQpaInputMethods, "qt.qpa.input.methods")
Q_LOGGING_CATEGORY(lcQpaDialogs, "qt.qpa.dialogs")
+Q_LOGGING_CATEGORY(lcQpaMenus, "qt.qpa.menus")
Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet")
Q_LOGGING_CATEGORY(lcQpaAccessibility, "qt.qpa.accessibility")
+Q_LOGGING_CATEGORY(lcQpaUiAutomation, "qt.qpa.uiautomation")
+Q_LOGGING_CATEGORY(lcQpaTrayIcon, "qt.qpa.trayicon")
int QWindowsContext::verbose = 0;
@@ -126,11 +130,19 @@ static inline bool useRTL_Extensions()
}
#if QT_CONFIG(sessionmanager)
-static inline QWindowsSessionManager *platformSessionManager() {
+static inline QWindowsSessionManager *platformSessionManager()
+{
QGuiApplicationPrivate *guiPrivate = static_cast<QGuiApplicationPrivate*>(QObjectPrivate::get(qApp));
QSessionManagerPrivate *managerPrivate = static_cast<QSessionManagerPrivate*>(QObjectPrivate::get(guiPrivate->session_manager));
return static_cast<QWindowsSessionManager *>(managerPrivate->platformSessionManager);
}
+
+static inline bool sessionManagerInteractionBlocked()
+{
+ return platformSessionManager()->isInteractionBlocked();
+}
+#else // QT_CONFIG(sessionmanager)
+static inline bool sessionManagerInteractionBlocked() { return false; }
#endif
static inline int windowDpiAwareness(HWND hwnd)
@@ -182,26 +194,14 @@ void QWindowsUser32DLL::init()
getDisplayAutoRotationPreferences = (GetDisplayAutoRotationPreferences)library.resolve("GetDisplayAutoRotationPreferences");
setDisplayAutoRotationPreferences = (SetDisplayAutoRotationPreferences)library.resolve("SetDisplayAutoRotationPreferences");
- if (QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS10) { // Appears in 10.0.14393, October 2016
+ if (QOperatingSystemVersion::current()
+ >= QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0, 14393)) {
enableNonClientDpiScaling = (EnableNonClientDpiScaling)library.resolve("EnableNonClientDpiScaling");
getWindowDpiAwarenessContext = (GetWindowDpiAwarenessContext)library.resolve("GetWindowDpiAwarenessContext");
getAwarenessFromDpiAwarenessContext = (GetAwarenessFromDpiAwarenessContext)library.resolve("GetAwarenessFromDpiAwarenessContext");
}
}
-bool QWindowsUser32DLL::initTouch()
-{
- if (!isTouchWindow && QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS7) {
- QSystemLibrary library(QStringLiteral("user32"));
- isTouchWindow = (IsTouchWindow)(library.resolve("IsTouchWindow"));
- registerTouchWindow = (RegisterTouchWindow)(library.resolve("RegisterTouchWindow"));
- unregisterTouchWindow = (UnregisterTouchWindow)(library.resolve("UnregisterTouchWindow"));
- getTouchInputInfo = (GetTouchInputInfo)(library.resolve("GetTouchInputInfo"));
- closeTouchInputHandle = (CloseTouchInputHandle)(library.resolve("CloseTouchInputHandle"));
- }
- return isTouchWindow && registerTouchWindow && unregisterTouchWindow && getTouchInputInfo && closeTouchInputHandle;
-}
-
void QWindowsShcoreDLL::init()
{
if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8_1)
@@ -258,7 +258,7 @@ QWindowsContextPrivate::QWindowsContextPrivate()
QWindowsContext::user32dll.init();
QWindowsContext::shcoredll.init();
- if (m_mouseHandler.touchDevice() && QWindowsContext::user32dll.initTouch())
+ if (m_mouseHandler.touchDevice())
m_systemInfo |= QWindowsContext::SI_SupportsTouch;
m_displayContext = GetDC(0);
m_defaultDPI = GetDeviceCaps(m_displayContext, LOGPIXELSY);
@@ -316,11 +316,6 @@ bool QWindowsContext::initTouch(unsigned integrationOptions)
if (!touchDevice)
return false;
- if (!QWindowsContext::user32dll.initTouch()) {
- delete touchDevice;
- return false;
- }
-
if (!(integrationOptions & QWindowsIntegration::DontPassOsMouseEventsSynthesizedFromTouch))
touchDevice->setCapabilities(touchDevice->capabilities() | QTouchDevice::MouseEmulation);
@@ -397,9 +392,11 @@ QList<int> QWindowsContext::possibleKeys(const QKeyEvent *e) const
return d->m_keyMapper.possibleKeys(e);
}
-void QWindowsContext::setWindowCreationContext(const QSharedPointer<QWindowCreationContext> &ctx)
+QSharedPointer<QWindowCreationContext> QWindowsContext::setWindowCreationContext(const QSharedPointer<QWindowCreationContext> &ctx)
{
+ const QSharedPointer<QWindowCreationContext> old = d->m_creationContext;
d->m_creationContext = ctx;
+ return old;
}
QSharedPointer<QWindowCreationContext> QWindowsContext::windowCreationContext() const
@@ -441,7 +438,7 @@ QString QWindowsContext::registerWindowClass(const QWindow *w)
// QOpenGLWidget or QQuickWidget later on. That cannot be detected at this stage.
if (w->surfaceType() == QSurface::OpenGLSurface || (flags & Qt::MSWindowsOwnDC))
style |= CS_OWNDC;
- if (!(flags & Qt::NoDropShadowWindowHint) && (QSysInfo::WindowsVersion & QSysInfo::WV_NT_based)
+ if (!(flags & Qt::NoDropShadowWindowHint)
&& (type == Qt::Popup || w->property("_q_windowsDropShadow").toBool())) {
style |= CS_DROPSHADOW;
}
@@ -597,6 +594,15 @@ void QWindowsContext::removeWindow(HWND hwnd)
}
}
+QWindowsWindow *QWindowsContext::findPlatformWindow(const QWindowsMenuBar *mb) const
+{
+ for (auto it = d->m_windows.cbegin(), end = d->m_windows.cend(); it != end; ++it) {
+ if ((*it)->menuBar() == mb)
+ return *it;
+ }
+ return nullptr;
+}
+
QWindowsWindow *QWindowsContext::findPlatformWindow(HWND hwnd) const
{
return d->m_windows.value(hwnd);
@@ -735,7 +741,7 @@ HWND QWindowsContext::createDummyWindow(const QString &classNameIn,
// present in the MSVCRT.DLL found on Windows XP (QTBUG-35617).
static inline QString errorMessageFromComError(const _com_error &comError)
{
- TCHAR *message = Q_NULLPTR;
+ TCHAR *message = nullptr;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL, DWORD(comError.Error()), MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
message, 0, NULL);
@@ -850,7 +856,7 @@ static inline bool resizeOnDpiChanged(const QWindow *w)
static bool shouldHaveNonClientDpiScaling(const QWindow *window)
{
- return QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS10
+ return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10
&& window->isTopLevel()
&& !window->property(QWindowsWindow::embeddedNativeParentHandleProperty).isValid()
#if QT_CONFIG(opengl) // /QTBUG-62901, EnableNonClientDpiScaling has problems with GL
@@ -934,11 +940,7 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
switch (et) {
case QtWindows::GestureEvent:
-#if QT_CONFIG(sessionmanager)
- return platformSessionManager()->isInteractionBlocked() ? true : d->m_mouseHandler.translateGestureEvent(platformWindow->window(), hwnd, et, msg, result);
-#else
- return d->m_mouseHandler.translateGestureEvent(platformWindow->window(), hwnd, et, msg, result);
-#endif
+ return sessionManagerInteractionBlocked() || d->m_mouseHandler.translateGestureEvent(platformWindow->window(), hwnd, et, msg, result);
case QtWindows::InputMethodOpenCandidateWindowEvent:
case QtWindows::InputMethodCloseCandidateWindowEvent:
// TODO: Release/regrab mouse if a popup has mouse grab.
@@ -956,7 +958,7 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
return false;
case QtWindows::AccessibleObjectFromWindowRequest:
#ifndef QT_NO_ACCESSIBILITY
- return QWindowsAccessibility::handleAccessibleObjectFromWindowRequest(hwnd, wParam, lParam, result);
+ return QWindowsUiaAccessibility::handleWmGetObject(hwnd, wParam, lParam, result);
#else
return false;
#endif
@@ -1027,11 +1029,23 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
case QtWindows::InputMethodKeyEvent:
case QtWindows::InputMethodKeyDownEvent:
case QtWindows::AppCommandEvent:
-#if QT_CONFIG(sessionmanager)
- return platformSessionManager()->isInteractionBlocked() ? true : d->m_keyMapper.translateKeyEvent(platformWindow->window(), hwnd, msg, result);
-#else
- return d->m_keyMapper.translateKeyEvent(platformWindow->window(), hwnd, msg, result);
-#endif
+ return sessionManagerInteractionBlocked() || d->m_keyMapper.translateKeyEvent(platformWindow->window(), hwnd, msg, result);
+ case QtWindows::MenuAboutToShowEvent:
+ if (sessionManagerInteractionBlocked())
+ return true;
+ if (QWindowsPopupMenu::notifyAboutToShow(reinterpret_cast<HMENU>(wParam)))
+ return true;
+ if (platformWindow == nullptr || platformWindow->menuBar() == nullptr)
+ return false;
+ return platformWindow->menuBar()->notifyAboutToShow(reinterpret_cast<HMENU>(wParam));
+ case QtWindows::MenuCommandEvent:
+ if (sessionManagerInteractionBlocked())
+ return true;
+ if (QWindowsPopupMenu::notifyTriggered(LOWORD(wParam)))
+ return true;
+ if (platformWindow == nullptr || platformWindow->menuBar() == nullptr)
+ return false;
+ return platformWindow->menuBar()->notifyTriggered(LOWORD(wParam));
case QtWindows::MoveEvent:
platformWindow->handleMoved();
return true;
@@ -1051,11 +1065,7 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
return platformWindow->handleWmPaint(hwnd, message, wParam, lParam);
case QtWindows::NonClientMouseEvent:
if (platformWindow->frameStrutEventsEnabled())
-#if QT_CONFIG(sessionmanager)
- return platformSessionManager()->isInteractionBlocked() ? true : d->m_mouseHandler.translateMouseEvent(platformWindow->window(), hwnd, et, msg, result);
-#else
- return d->m_mouseHandler.translateMouseEvent(platformWindow->window(), hwnd, et, msg, result);
-#endif
+ return sessionManagerInteractionBlocked() || d->m_mouseHandler.translateMouseEvent(platformWindow->window(), hwnd, et, msg, result);
break;
case QtWindows::EnterSizeMoveEvent:
platformWindow->setFlag(QWindowsWindow::ResizeMoveActive);
@@ -1065,11 +1075,7 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
platformWindow->checkForScreenChanged();
return true;
case QtWindows::ScrollEvent:
-#if QT_CONFIG(sessionmanager)
- return platformSessionManager()->isInteractionBlocked() ? true : d->m_mouseHandler.translateScrollEvent(platformWindow->window(), hwnd, msg, result);
-#else
- return d->m_mouseHandler.translateScrollEvent(platformWindow->window(), hwnd, msg, result);
-#endif
+ return sessionManagerInteractionBlocked() || d->m_mouseHandler.translateScrollEvent(platformWindow->window(), hwnd, msg, result);
case QtWindows::MouseWheelEvent:
case QtWindows::MouseEvent:
case QtWindows::LeaveEvent:
@@ -1079,18 +1085,10 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
window = window->parent();
if (!window)
return false;
-#if QT_CONFIG(sessionmanager)
- return platformSessionManager()->isInteractionBlocked() ? true : d->m_mouseHandler.translateMouseEvent(window, hwnd, et, msg, result);
-#else
- return d->m_mouseHandler.translateMouseEvent(window, hwnd, et, msg, result);
-#endif
+ return sessionManagerInteractionBlocked() || d->m_mouseHandler.translateMouseEvent(window, hwnd, et, msg, result);
}
case QtWindows::TouchEvent:
-#if QT_CONFIG(sessionmanager)
- return platformSessionManager()->isInteractionBlocked() ? true : d->m_mouseHandler.translateTouchEvent(platformWindow->window(), hwnd, et, msg, result);
-#else
- return d->m_mouseHandler.translateTouchEvent(platformWindow->window(), hwnd, et, msg, result);
-#endif
+ return sessionManagerInteractionBlocked() || d->m_mouseHandler.translateTouchEvent(platformWindow->window(), hwnd, et, msg, result);
case QtWindows::FocusInEvent: // see QWindowsWindow::requestActivateWindow().
case QtWindows::FocusOutEvent:
handleFocusEvent(et, platformWindow);
@@ -1292,6 +1290,29 @@ QTouchDevice *QWindowsContext::touchDevice() const
return d->m_mouseHandler.touchDevice();
}
+static DWORD readDwordRegistrySetting(const wchar_t *regKey, const wchar_t *subKey, DWORD defaultValue)
+{
+ DWORD result = defaultValue;
+ HKEY handle;
+ if (RegOpenKeyEx(HKEY_CURRENT_USER, regKey, 0, KEY_READ, &handle) == ERROR_SUCCESS) {
+ DWORD type;
+ if (RegQueryValueEx(handle, subKey, 0, &type, 0, 0) == ERROR_SUCCESS && type == REG_DWORD) {
+ DWORD value;
+ DWORD size = sizeof(result);
+ if (RegQueryValueEx(handle, subKey, 0, 0, reinterpret_cast<unsigned char *>(&value), &size) == ERROR_SUCCESS)
+ result = value;
+ }
+ RegCloseKey(handle);
+ }
+ return result;
+}
+
+DWORD QWindowsContext::readAdvancedExplorerSettings(const wchar_t *subKey, DWORD defaultValue)
+{
+ return readDwordRegistrySetting(L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced",
+ subKey, defaultValue);
+}
+
static inline bool isEmptyRect(const RECT &rect)
{
return rect.right - rect.left == 0 && rect.bottom - rect.top == 0;
diff --git a/src/plugins/platforms/windows/qwindowscontext.h b/src/plugins/platforms/windows/qwindowscontext.h
index b50010321b..f2ec307be2 100644
--- a/src/plugins/platforms/windows/qwindowscontext.h
+++ b/src/plugins/platforms/windows/qwindowscontext.h
@@ -63,11 +63,15 @@ Q_DECLARE_LOGGING_CATEGORY(lcQpaGl)
Q_DECLARE_LOGGING_CATEGORY(lcQpaMime)
Q_DECLARE_LOGGING_CATEGORY(lcQpaInputMethods)
Q_DECLARE_LOGGING_CATEGORY(lcQpaDialogs)
+Q_DECLARE_LOGGING_CATEGORY(lcQpaMenus)
Q_DECLARE_LOGGING_CATEGORY(lcQpaTablet)
Q_DECLARE_LOGGING_CATEGORY(lcQpaAccessibility)
+Q_DECLARE_LOGGING_CATEGORY(lcQpaUiAutomation)
+Q_DECLARE_LOGGING_CATEGORY(lcQpaTrayIcon)
class QWindow;
class QPlatformScreen;
+class QWindowsMenuBar;
class QWindowsScreenManager;
class QWindowsTabletSupport;
class QWindowsWindow;
@@ -81,13 +85,7 @@ class QTouchDevice;
struct QWindowsUser32DLL
{
inline void init();
- inline bool initTouch();
- typedef BOOL (WINAPI *IsTouchWindow)(HWND, PULONG); // Windows 7
- typedef BOOL (WINAPI *RegisterTouchWindow)(HWND, ULONG);
- typedef BOOL (WINAPI *UnregisterTouchWindow)(HWND);
- typedef BOOL (WINAPI *GetTouchInputInfo)(HANDLE, UINT, PVOID, int);
- typedef BOOL (WINAPI *CloseTouchInputHandle)(HANDLE);
typedef BOOL (WINAPI *SetProcessDPIAware)();
typedef BOOL (WINAPI *AddClipboardFormatListener)(HWND);
typedef BOOL (WINAPI *RemoveClipboardFormatListener)(HWND);
@@ -97,13 +95,6 @@ struct QWindowsUser32DLL
typedef int (WINAPI *GetWindowDpiAwarenessContext)(HWND);
typedef int (WINAPI *GetAwarenessFromDpiAwarenessContext)(int);
- // Touch functions from Windows 7 onwards (also for use with Q_CC_MSVC).
- IsTouchWindow isTouchWindow = nullptr;
- RegisterTouchWindow registerTouchWindow = nullptr;
- UnregisterTouchWindow unregisterTouchWindow = nullptr;
- GetTouchInputInfo getTouchInputInfo = nullptr;
- CloseTouchInputHandle closeTouchInputHandle = nullptr;
-
// Windows Vista onwards
SetProcessDPIAware setProcessDPIAware = nullptr;
@@ -177,6 +168,7 @@ public:
QWindowsWindow *findClosestPlatformWindow(HWND) const;
QWindowsWindow *findPlatformWindow(HWND) const;
+ QWindowsWindow *findPlatformWindow(const QWindowsMenuBar *mb) const;
QWindow *findWindow(HWND) const;
QWindowsWindow *findPlatformWindowAt(HWND parent, const QPoint &screenPoint,
unsigned cwex_flags) const;
@@ -192,7 +184,7 @@ public:
QWindow *keyGrabber() const;
void setKeyGrabber(QWindow *hwnd);
- void setWindowCreationContext(const QSharedPointer<QWindowCreationContext> &ctx);
+ QSharedPointer<QWindowCreationContext> setWindowCreationContext(const QSharedPointer<QWindowCreationContext> &ctx);
QSharedPointer<QWindowCreationContext> windowCreationContext() const;
void setTabletAbsoluteRange(int a);
@@ -216,6 +208,8 @@ public:
bool asyncExpose() const;
void setAsyncExpose(bool value);
+ static DWORD readAdvancedExplorerSettings(const wchar_t *subKey, DWORD defaultValue);
+
QTouchDevice *touchDevice() const;
private:
diff --git a/src/plugins/platforms/windows/qwindowscursor.cpp b/src/plugins/platforms/windows/qwindowscursor.cpp
index 0a09b87ba3..e1a5837201 100644
--- a/src/plugins/platforms/windows/qwindowscursor.cpp
+++ b/src/plugins/platforms/windows/qwindowscursor.cpp
@@ -184,7 +184,7 @@ static HCURSOR createBitmapCursor(const QCursor &cursor, qreal scaleFactor = 1)
return createBitmapCursor(bbits, mbits, cursor.hotSpot(), invb, invm);
}
-static QSize systemCursorSize(const QPlatformScreen *screen = Q_NULLPTR)
+static QSize systemCursorSize(const QPlatformScreen *screen = nullptr)
{
const QSize primaryScreenCursorSize(GetSystemMetrics(SM_CXCURSOR), GetSystemMetrics(SM_CYCURSOR));
if (screen) {
@@ -548,6 +548,8 @@ CursorHandlePtr QWindowsCursor::standardWindowCursor(Qt::CursorShape shape)
return it != m_standardCursorCache.end() ? it.value() : CursorHandlePtr(new CursorHandle);
}
+HCURSOR QWindowsCursor::m_overriddenCursor = nullptr;
+
/*!
\brief Return cached pixmap cursor or create new one.
*/
@@ -586,6 +588,13 @@ QWindowsCursor::QWindowsCursor(const QPlatformScreen *screen)
Q_UNUSED(dummy)
}
+inline CursorHandlePtr QWindowsCursor::cursorHandle(const QCursor &cursor)
+{
+ return cursor.shape() == Qt::BitmapCursor
+ ? pixmapWindowCursor(cursor)
+ : standardWindowCursor(cursor.shape());
+}
+
/*!
\brief Set a cursor on a window.
@@ -603,9 +612,7 @@ void QWindowsCursor::changeCursor(QCursor *cursorIn, QWindow *window)
platformWindow->setCursor(CursorHandlePtr(new CursorHandle));
return;
}
- const CursorHandlePtr wcursor =
- cursorIn->shape() == Qt::BitmapCursor ?
- pixmapWindowCursor(*cursorIn) : standardWindowCursor(cursorIn->shape());
+ const CursorHandlePtr wcursor = cursorHandle(*cursorIn);
if (wcursor->handle()) {
platformWindow->setCursor(wcursor);
} else {
@@ -614,6 +621,27 @@ void QWindowsCursor::changeCursor(QCursor *cursorIn, QWindow *window)
}
}
+void QWindowsCursor::setOverrideCursor(const QCursor &cursor)
+{
+ const CursorHandlePtr wcursor = cursorHandle(cursor);
+ if (wcursor->handle()) {
+ const HCURSOR previousCursor = SetCursor(wcursor->handle());
+ if (m_overriddenCursor == nullptr)
+ m_overriddenCursor = previousCursor;
+ } else {
+ qWarning("%s: Unable to obtain system cursor for %d",
+ __FUNCTION__, cursor.shape());
+ }
+}
+
+void QWindowsCursor::clearOverrideCursor()
+{
+ if (m_overriddenCursor) {
+ SetCursor(m_overriddenCursor);
+ m_overriddenCursor = nullptr;
+ }
+}
+
QPoint QWindowsCursor::mousePosition()
{
POINT p;
diff --git a/src/plugins/platforms/windows/qwindowscursor.h b/src/plugins/platforms/windows/qwindowscursor.h
index df2e22733b..4772f3fce5 100644
--- a/src/plugins/platforms/windows/qwindowscursor.h
+++ b/src/plugins/platforms/windows/qwindowscursor.h
@@ -70,7 +70,7 @@ class CursorHandle
{
Q_DISABLE_COPY(CursorHandle)
public:
- explicit CursorHandle(HCURSOR hcursor = Q_NULLPTR) : m_hcursor(hcursor) {}
+ explicit CursorHandle(HCURSOR hcursor = nullptr) : m_hcursor(hcursor) {}
~CursorHandle()
{
if (m_hcursor)
@@ -105,14 +105,17 @@ public:
explicit QWindowsCursor(const QPlatformScreen *screen);
void changeCursor(QCursor * widgetCursor, QWindow * widget) override;
+ void setOverrideCursor(const QCursor &cursor) override;
+ void clearOverrideCursor() override;
+
QPoint pos() const override;
void setPos(const QPoint &pos) override;
static HCURSOR createPixmapCursor(QPixmap pixmap, const QPoint &hotSpot, qreal scaleFactor = 1);
static HCURSOR createPixmapCursor(const PixmapCursor &pc, qreal scaleFactor = 1) { return createPixmapCursor(pc.pixmap, pc.hotSpot, scaleFactor); }
- static PixmapCursor customCursor(Qt::CursorShape cursorShape, const QPlatformScreen *screen = Q_NULLPTR);
+ static PixmapCursor customCursor(Qt::CursorShape cursorShape, const QPlatformScreen *screen = nullptr);
- static HCURSOR createCursorFromShape(Qt::CursorShape cursorShape, const QPlatformScreen *screen = Q_NULLPTR);
+ static HCURSOR createCursorFromShape(Qt::CursorShape cursorShape, const QPlatformScreen *screen = nullptr);
static QPoint mousePosition();
static CursorState cursorState();
@@ -127,6 +130,8 @@ private:
typedef QHash<Qt::CursorShape, CursorHandlePtr> StandardCursorCache;
typedef QHash<QWindowsPixmapCursorCacheKey, CursorHandlePtr> PixmapCursorCache;
+ CursorHandlePtr cursorHandle(const QCursor &c);
+
const QPlatformScreen *const m_screen;
StandardCursorCache m_standardCursorCache;
PixmapCursorCache m_pixmapCursorCache;
@@ -135,6 +140,8 @@ private:
mutable QPixmap m_moveDragCursor;
mutable QPixmap m_linkDragCursor;
mutable QPixmap m_ignoreDragCursor;
+
+ static HCURSOR m_overriddenCursor;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp
index bdae764025..80ee7b2287 100644
--- a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp
+++ b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp
@@ -39,8 +39,11 @@
#define QT_NO_URL_CAST_FROM_STRING 1
-#define _WIN32_WINNT 0x0600
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0601
+#endif
+#include "qwindowscombase.h"
#include "qwindowsdialoghelpers.h"
#include "qwindowscontext.h"
@@ -52,7 +55,7 @@
#include <QtGui/QColor>
#include <QtCore/QDebug>
-#include <QtCore/QRegExp>
+#include <QtCore/QRegularExpression>
#include <QtCore/QTimer>
#include <QtCore/QDir>
#include <QtCore/QScopedArrayPointer>
@@ -503,33 +506,11 @@ inline void QWindowsFileDialogSharedData::fromOptions(const QSharedPointer<QFile
class QWindowsNativeFileDialogBase;
-class QWindowsNativeFileDialogEventHandler : public IFileDialogEvents
+class QWindowsNativeFileDialogEventHandler : public QWindowsComBase<IFileDialogEvents>
{
public:
static IFileDialogEvents *create(QWindowsNativeFileDialogBase *nativeFileDialog);
- // IUnknown methods
- IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv)
- {
- if (riid != IID_IUnknown && riid != IID_IFileDialogEvents) {
- *ppv = NULL;
- return ResultFromScode(E_NOINTERFACE);
- }
- *ppv = this;
- AddRef();
- return NOERROR;
- }
-
- IFACEMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&m_ref); }
-
- IFACEMETHODIMP_(ULONG) Release()
- {
- const long ref = InterlockedDecrement(&m_ref);
- if (!ref)
- delete this;
- return ref;
- }
-
// IFileDialogEvents methods
IFACEMETHODIMP OnFileOk(IFileDialog *);
IFACEMETHODIMP OnFolderChange(IFileDialog *) { return S_OK; }
@@ -545,7 +526,6 @@ public:
virtual ~QWindowsNativeFileDialogEventHandler() {}
private:
- long m_ref = 1;
QWindowsNativeFileDialogBase *m_nativeFileDialog;
};
@@ -620,8 +600,8 @@ QString QWindowsShellItem::path() const
{
if (isFileSystem())
return QDir::cleanPath(QWindowsShellItem::displayName(m_item, SIGDN_FILESYSPATH));
- // Check for a "Library" item (Windows 7)
- if (QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS7 && isDir())
+ // Check for a "Library" item
+ if (isDir())
return QWindowsShellItem::libraryItemDefaultSaveFolder(m_item);
return QString();
}
@@ -732,7 +712,7 @@ QString QWindowsShellItem::libraryItemDefaultSaveFolder(IShellItem *item)
{
QString result;
if (IShellLibrary *library = sHLoadLibraryFromItem(item, STGM_READ | STGM_SHARE_DENY_WRITE)) {
- IShellItem *item = Q_NULLPTR;
+ IShellItem *item = nullptr;
if (SUCCEEDED(library->GetDefaultSaveFolder(DSFT_DETECT, IID_IShellItem, reinterpret_cast<void **>(&item)))) {
result = QDir::cleanPath(QWindowsShellItem::displayName(item, SIGDN_FILESYSPATH));
item->Release();
@@ -914,7 +894,7 @@ void QWindowsNativeFileDialogBase::setWindowTitle(const QString &title)
IShellItem *QWindowsNativeFileDialogBase::shellItem(const QUrl &url)
{
if (url.isLocalFile()) {
- IShellItem *result = Q_NULLPTR;
+ IShellItem *result = nullptr;
const QString native = QDir::toNativeSeparators(url.toLocalFile());
const HRESULT hr =
SHCreateItemFromParsingName(reinterpret_cast<const wchar_t *>(native.utf16()),
@@ -922,30 +902,30 @@ IShellItem *QWindowsNativeFileDialogBase::shellItem(const QUrl &url)
reinterpret_cast<void **>(&result));
if (FAILED(hr)) {
qErrnoWarning("%s: SHCreateItemFromParsingName(%s)) failed", __FUNCTION__, qPrintable(url.toString()));
- return Q_NULLPTR;
+ return nullptr;
}
return result;
} else if (url.scheme() == QLatin1String("clsid")) {
// Support for virtual folders via GUID
// (see https://msdn.microsoft.com/en-us/library/windows/desktop/dd378457(v=vs.85).aspx)
// specified as "clsid:<GUID>" (without '{', '}').
- IShellItem *result = Q_NULLPTR;
- const QUuid uuid(url.path());
+ IShellItem *result = nullptr;
+ const auto uuid = QUuid::fromString(url.path());
if (uuid.isNull()) {
qWarning() << __FUNCTION__ << ": Invalid CLSID: " << url.path();
- return Q_NULLPTR;
+ return nullptr;
}
PIDLIST_ABSOLUTE idList;
HRESULT hr = SHGetKnownFolderIDList(uuid, 0, 0, &idList);
if (FAILED(hr)) {
qErrnoWarning("%s: SHGetKnownFolderIDList(%s)) failed", __FUNCTION__, qPrintable(url.toString()));
- return Q_NULLPTR;
+ return nullptr;
}
hr = SHCreateItemFromIDList(idList, IID_IShellItem, reinterpret_cast<void **>(&result));
CoTaskMemFree(idList);
if (FAILED(hr)) {
qErrnoWarning("%s: SHCreateItemFromIDList(%s)) failed", __FUNCTION__, qPrintable(url.toString()));
- return Q_NULLPTR;
+ return nullptr;
}
return result;
} else {
@@ -994,7 +974,9 @@ void QWindowsNativeFileDialogBase::setMode(QFileDialogOptions::FileMode mode,
QFileDialogOptions::AcceptMode acceptMode,
QFileDialogOptions::FileDialogOptions options)
{
- DWORD flags = FOS_PATHMUSTEXIST | FOS_FORCESHOWHIDDEN;
+ DWORD flags = FOS_PATHMUSTEXIST;
+ if (QWindowsContext::readAdvancedExplorerSettings(L"Hidden", 1) == 1) // 1:show, 2:hidden
+ flags |= FOS_FORCESHOWHIDDEN;
if (options & QFileDialogOptions::DontResolveSymlinks)
flags |= FOS_NODEREFERENCELINKS;
switch (mode) {
@@ -1040,7 +1022,7 @@ static QList<FilterSpec> filterSpecs(const QStringList &filters,
result.reserve(filters.size());
*totalStringLength = 0;
- const QRegExp filterSeparatorRE(QStringLiteral("[;\\s]+"));
+ const QRegularExpression filterSeparatorRE(QStringLiteral("[;\\s]+"));
const QString separator = QStringLiteral(";");
Q_ASSERT(filterSeparatorRE.isValid());
// Split filter specification as 'Texts (*.txt[;] *.doc)', '*.txt[;] *.doc'
@@ -2084,7 +2066,7 @@ bool useHelper(QPlatformTheme::DialogType type)
return false;
switch (type) {
case QPlatformTheme::FileDialog:
- return QSysInfo::windowsVersion() >= QSysInfo::WV_XP;
+ return true;
case QPlatformTheme::ColorDialog:
#ifdef USE_NATIVE_COLOR_DIALOG
return true;
@@ -2105,13 +2087,10 @@ QPlatformDialogHelper *createHelper(QPlatformTheme::DialogType type)
if (QWindowsIntegration::instance()->options() & QWindowsIntegration::NoNativeDialogs)
return 0;
switch (type) {
- case QPlatformTheme::FileDialog: // Note: "Windows XP Professional x64 Edition has version number WV_5_2 (WV_2003).
- if (QWindowsIntegration::instance()->options() & QWindowsIntegration::XpNativeDialogs
- || QSysInfo::windowsVersion() <= QSysInfo::WV_2003) {
+ case QPlatformTheme::FileDialog:
+ if (QWindowsIntegration::instance()->options() & QWindowsIntegration::XpNativeDialogs)
return new QWindowsXpFileDialogHelper();
- }
- if (QSysInfo::windowsVersion() > QSysInfo::WV_2003)
- return new QWindowsFileDialogHelper();
+ return new QWindowsFileDialogHelper;
case QPlatformTheme::ColorDialog:
#ifdef USE_NATIVE_COLOR_DIALOG
return new QWindowsColorDialogHelper();
diff --git a/src/plugins/platforms/windows/qwindowsdrag.cpp b/src/plugins/platforms/windows/qwindowsdrag.cpp
index 26403b68e5..777c45ae86 100644
--- a/src/plugins/platforms/windows/qwindowsdrag.cpp
+++ b/src/plugins/platforms/windows/qwindowsdrag.cpp
@@ -44,7 +44,7 @@
# include "qwindowsclipboard.h"
#endif
#include "qwindowsintegration.h"
-#include "qwindowsole.h"
+#include "qwindowsdropdataobject.h"
#include <QtCore/qt_windows.h>
#include "qwindowswindow.h"
#include "qwindowsmousehandler.h"
@@ -215,7 +215,7 @@ static inline Qt::MouseButtons toQtMouseButtons(DWORD keyState)
\ingroup qt-lighthouse-win
*/
-class QWindowsOleDropSource : public IDropSource
+class QWindowsOleDropSource : public QWindowsComBase<IDropSource>
{
public:
enum Mode {
@@ -228,11 +228,6 @@ public:
void createCursors();
- // IUnknown methods
- STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObj);
- STDMETHOD_(ULONG,AddRef)(void);
- STDMETHOD_(ULONG,Release)(void);
-
// IDropSource methods
STDMETHOD(QueryContinueDrag)(BOOL fEscapePressed, DWORD grfKeyState);
STDMETHOD(GiveFeedback)(DWORD dwEffect);
@@ -251,13 +246,12 @@ private:
typedef QMap<Qt::DropAction, CursorEntry> ActionCursorMap;
- const Mode m_mode;
+ Mode m_mode;
QWindowsDrag *m_drag;
Qt::MouseButtons m_currentButtons;
ActionCursorMap m_cursors;
QWindowsDragCursorWindow *m_touchDragWindow;
- ULONG m_refs;
#ifndef QT_NO_DEBUG_STREAM
friend QDebug operator<<(QDebug, const QWindowsOleDropSource::CursorEntry &);
#endif
@@ -268,7 +262,6 @@ QWindowsOleDropSource::QWindowsOleDropSource(QWindowsDrag *drag)
, m_drag(drag)
, m_currentButtons(Qt::NoButton)
, m_touchDragWindow(0)
- , m_refs(1)
{
qCDebug(lcQpaMime) << __FUNCTION__ << m_mode;
}
@@ -308,6 +301,15 @@ void QWindowsOleDropSource::createCursors()
Q_ASSERT(platformScreen);
QPlatformCursor *platformCursor = platformScreen->cursor();
+ if (GetSystemMetrics (SM_REMOTESESSION) != 0) {
+ /* Workaround for RDP issues with large cursors.
+ * Touch drag window seems to work just fine...
+ * 96 pixel is a 'large' mouse cursor, according to RDP spec */
+ const int rdpLargeCursor = qRound(qreal(96) / QHighDpiScaling::factor(platformScreen));
+ if (pixmap.width() > rdpLargeCursor || pixmap.height() > rdpLargeCursor)
+ m_mode = TouchDrag;
+ }
+
qreal pixmapScaleFactor = 1;
qreal hotSpotScaleFactor = 1;
if (m_mode != TouchDrag) { // Touch drag: pixmap is shown in a separate QWindow, which will be scaled.)
@@ -373,38 +375,6 @@ void QWindowsOleDropSource::createCursors()
#endif // !QT_NO_DEBUG_OUTPUT
}
-//---------------------------------------------------------------------
-// IUnknown Methods
-//---------------------------------------------------------------------
-
-STDMETHODIMP
-QWindowsOleDropSource::QueryInterface(REFIID iid, void FAR* FAR* ppv)
-{
- if (iid == IID_IUnknown || iid == IID_IDropSource) {
- *ppv = this;
- ++m_refs;
- return NOERROR;
- }
- *ppv = NULL;
- return ResultFromScode(E_NOINTERFACE);
-}
-
-STDMETHODIMP_(ULONG)
-QWindowsOleDropSource::AddRef(void)
-{
- return ++m_refs;
-}
-
-STDMETHODIMP_(ULONG)
-QWindowsOleDropSource::Release(void)
-{
- if (--m_refs == 0) {
- delete this;
- return 0;
- }
- return m_refs;
-}
-
/*!
\brief Check for cancel.
*/
@@ -472,6 +442,9 @@ QWindowsOleDropSource::GiveFeedback(DWORD dwEffect)
SetCursor(e.cursor->handle());
break;
case TouchDrag:
+ // "Touch drag" with an unsuppressed cursor may happen with RDP (see createCursors())
+ if (QWindowsCursor::cursorState() != QWindowsCursor::CursorSuppressed)
+ SetCursor(nullptr);
if (!m_touchDragWindow)
m_touchDragWindow = new QWindowsDragCursorWindow;
m_touchDragWindow->setPixmap(e.pixmap);
@@ -509,34 +482,6 @@ QWindowsOleDropTarget::~QWindowsOleDropTarget()
qCDebug(lcQpaMime) << __FUNCTION__ << this;
}
-STDMETHODIMP
-QWindowsOleDropTarget::QueryInterface(REFIID iid, void FAR* FAR* ppv)
-{
- if (iid == IID_IUnknown || iid == IID_IDropTarget) {
- *ppv = this;
- AddRef();
- return NOERROR;
- }
- *ppv = NULL;
- return ResultFromScode(E_NOINTERFACE);
-}
-
-STDMETHODIMP_(ULONG)
-QWindowsOleDropTarget::AddRef(void)
-{
- return ++m_refs;
-}
-
-STDMETHODIMP_(ULONG)
-QWindowsOleDropTarget::Release(void)
-{
- if (--m_refs == 0) {
- delete this;
- return 0;
- }
- return m_refs;
-}
-
void QWindowsOleDropTarget::handleDrag(QWindow *window, DWORD grfKeyState,
const QPoint &point, LPDWORD pdwEffect)
{
@@ -740,7 +685,7 @@ Qt::DropAction QWindowsDrag::drag(QDrag *drag)
QWindowsDrag::m_canceled = false;
QWindowsOleDropSource *windowDropSource = new QWindowsOleDropSource(this);
windowDropSource->createCursors();
- QWindowsOleDataObject *dropDataObject = new QWindowsOleDataObject(dropData);
+ QWindowsDropDataObject *dropDataObject = new QWindowsDropDataObject(dropData);
const Qt::DropActions possibleActions = drag->supportedActions();
const DWORD allowedEffects = translateToWinDragEffects(possibleActions);
qCDebug(lcQpaMime) << '>' << __FUNCTION__ << "possible Actions=0x"
diff --git a/src/plugins/platforms/windows/qwindowsdrag.h b/src/plugins/platforms/windows/qwindowsdrag.h
index 983f3a67b4..2b4ca2dce1 100644
--- a/src/plugins/platforms/windows/qwindowsdrag.h
+++ b/src/plugins/platforms/windows/qwindowsdrag.h
@@ -40,6 +40,7 @@
#ifndef QWINDOWSDRAG_H
#define QWINDOWSDRAG_H
+#include "qwindowscombase.h"
#include "qwindowsinternalmimedata.h"
#include <qpa/qplatformdrag.h>
@@ -57,17 +58,12 @@ public:
IDataObject *retrieveDataObject() const override;
};
-class QWindowsOleDropTarget : public IDropTarget
+class QWindowsOleDropTarget : public QWindowsComBase<IDropTarget>
{
public:
explicit QWindowsOleDropTarget(QWindow *w);
virtual ~QWindowsOleDropTarget();
- // IUnknown methods
- STDMETHOD(QueryInterface)(REFIID riid, void FAR* FAR* ppvObj);
- STDMETHOD_(ULONG, AddRef)(void);
- STDMETHOD_(ULONG, Release)(void);
-
// IDropTarget methods
STDMETHOD(DragEnter)(LPDATAOBJECT pDataObj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect);
STDMETHOD(DragOver)(DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect);
@@ -77,7 +73,6 @@ public:
private:
void handleDrag(QWindow *window, DWORD grfKeyState, const QPoint &, LPDWORD pdwEffect);
- ULONG m_refs = 1;
QWindow *const m_window;
QRect m_answerRect;
QPoint m_lastPoint;
@@ -91,8 +86,6 @@ public:
QWindowsDrag();
virtual ~QWindowsDrag();
- QMimeData *platformDropData() override { return &m_dropData; }
-
Qt::DropAction drag(QDrag *drag) override;
static QWindowsDrag *instance();
diff --git a/src/plugins/platforms/windows/qwindowsdropdataobject.cpp b/src/plugins/platforms/windows/qwindowsdropdataobject.cpp
new file mode 100644
index 0000000000..bd532ab70e
--- /dev/null
+++ b/src/plugins/platforms/windows/qwindowsdropdataobject.cpp
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwindowsdropdataobject.h"
+
+#include <QtCore/QUrl>
+#include <QtCore/QMimeData>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QWindowsDropDataObject
+ \brief QWindowsOleDataObject subclass specialized for handling Drag&Drop.
+
+ Only allows "text/uri-list" data to be exported as CF_HDROP, to allow dropped
+ files to be attached to Office applications (instead of adding an URL link).
+
+ \internal
+ \ingroup qt-lighthouse-win
+*/
+
+QWindowsDropDataObject::QWindowsDropDataObject(QMimeData *mimeData) :
+ QWindowsOleDataObject(mimeData)
+{
+}
+
+QWindowsDropDataObject::~QWindowsDropDataObject()
+{
+}
+
+STDMETHODIMP
+QWindowsDropDataObject::GetData(LPFORMATETC pformatetc, LPSTGMEDIUM pmedium)
+{
+ if (shouldIgnore(pformatetc))
+ return ResultFromScode(DATA_E_FORMATETC);
+
+ return QWindowsOleDataObject::GetData(pformatetc, pmedium);
+}
+
+STDMETHODIMP
+QWindowsDropDataObject::QueryGetData(LPFORMATETC pformatetc)
+{
+ if (shouldIgnore(pformatetc))
+ return ResultFromScode(DATA_E_FORMATETC);
+
+ return QWindowsOleDataObject::QueryGetData(pformatetc);
+}
+
+// If the data is text/uri-list for local files, tell we can only export it as CF_HDROP.
+bool QWindowsDropDataObject::shouldIgnore(LPFORMATETC pformatetc) const
+{
+ QMimeData *dropData = mimeData();
+
+ if (dropData && dropData->hasFormat(QStringLiteral("text/uri-list")) && (pformatetc->cfFormat != CF_HDROP)) {
+ QList<QUrl> urls = dropData->urls();
+ return std::any_of(urls.cbegin(), urls.cend(), [] (const QUrl &u) { return u.isLocalFile(); });
+ }
+
+ return false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/windows/qwindowsdropdataobject.h b/src/plugins/platforms/windows/qwindowsdropdataobject.h
new file mode 100644
index 0000000000..5ef72c9336
--- /dev/null
+++ b/src/plugins/platforms/windows/qwindowsdropdataobject.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSDROPDATAOBJECT_H
+#define QWINDOWSDROPDATAOBJECT_H
+
+#include "qwindowsole.h"
+
+QT_BEGIN_NAMESPACE
+
+class QWindowsDropDataObject : public QWindowsOleDataObject
+{
+public:
+ explicit QWindowsDropDataObject(QMimeData *mimeData);
+ virtual ~QWindowsDropDataObject();
+
+ // overridden IDataObject methods
+ STDMETHOD(GetData)(LPFORMATETC pformatetcIn, LPSTGMEDIUM pmedium);
+ STDMETHOD(QueryGetData)(LPFORMATETC pformatetc);
+
+private:
+ bool shouldIgnore(LPFORMATETC pformatetc) const;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWSDROPDATAOBJECT_H
diff --git a/src/plugins/platforms/windows/qwindowseglcontext.h b/src/plugins/platforms/windows/qwindowseglcontext.h
index 47878a7169..3e5f2c81d5 100644
--- a/src/plugins/platforms/windows/qwindowseglcontext.h
+++ b/src/plugins/platforms/windows/qwindowseglcontext.h
@@ -95,7 +95,7 @@ struct QWindowsLibGLESv2
#if !defined(QT_STATIC) || defined(QT_OPENGL_DYNAMIC)
void *moduleHandle() const { return m_lib; }
#else
- void *moduleHandle() const { return Q_NULLPTR; }
+ void *moduleHandle() const { return nullptr; }
#endif
const GLubyte * (APIENTRY * glGetString)(GLenum name);
diff --git a/src/plugins/platforms/windows/qwindowsglcontext.cpp b/src/plugins/platforms/windows/qwindowsglcontext.cpp
index 751807e897..4bdf3167e4 100644
--- a/src/plugins/platforms/windows/qwindowsglcontext.cpp
+++ b/src/plugins/platforms/windows/qwindowsglcontext.cpp
@@ -145,6 +145,10 @@
#define RESET_NOTIFICATION_STRATEGY_ARB 0x8256
#define LOSE_CONTEXT_ON_RESET_ARB 0x8252
+#ifndef WGL_FRAMEBUFFER_SRGB_CAPABLE_EXT
+#define WGL_FRAMEBUFFER_SRGB_CAPABLE_EXT 0x20A9
+#endif
+
QT_BEGIN_NAMESPACE
QWindowsOpengl32DLL QOpenGLStaticContext::opengl32;
@@ -375,9 +379,7 @@ static PIXELFORMATDESCRIPTOR
initPixelFormatDescriptor(&pfd);
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.iLayerType = PFD_MAIN_PLANE;
- pfd.dwFlags = PFD_SUPPORT_OPENGL;
- if (QSysInfo::windowsVersion() >= QSysInfo::WV_VISTA)
- pfd.dwFlags = PFD_SUPPORT_COMPOSITION;
+ pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_SUPPORT_COMPOSITION;
const bool isPixmap = (additional.formatFlags & QWindowsGLRenderToPixmap) != 0;
pfd.dwFlags |= isPixmap ? PFD_DRAW_TO_BITMAP : PFD_DRAW_TO_WINDOW;
if (!(additional.formatFlags & QWindowsGLDirectRendering))
@@ -489,7 +491,7 @@ static int choosePixelFormat(HDC hdc,
const QWindowsOpenGLAdditionalFormat &additional,
PIXELFORMATDESCRIPTOR *obtainedPfd)
{
- enum { attribSize =40 };
+ enum { attribSize = 42 };
if ((additional.formatFlags & QWindowsGLRenderToPixmap) || !staticContext.hasExtensions())
return 0;
@@ -570,7 +572,15 @@ static int choosePixelFormat(HDC hdc,
iAttributes[i++] = WGL_SAMPLE_BUFFERS_ARB;
iAttributes[i++] = FALSE;
}
- // If sample buffer request cannot be satisfied, reduce request.
+ // must be the last
+ bool srgbRequested = format.colorSpace() == QSurfaceFormat::sRGBColorSpace;
+ int srgbValuePosition = 0;
+ if (srgbRequested) {
+ srgbValuePosition = i;
+ iAttributes[i++] = WGL_FRAMEBUFFER_SRGB_CAPABLE_EXT;
+ iAttributes[i++] = TRUE;
+ }
+ // If sample or sRGB request cannot be satisfied, reduce request.
int pixelFormat = 0;
uint numFormats = 0;
while (true) {
@@ -578,20 +588,25 @@ static int choosePixelFormat(HDC hdc,
staticContext.wglChoosePixelFormatARB(hdc, iAttributes, 0, 1,
&pixelFormat, &numFormats)
&& numFormats >= 1;
- if (valid || !sampleBuffersRequested)
- break;
- if (iAttributes[samplesValuePosition] > 1) {
- iAttributes[samplesValuePosition] /= 2;
- } else if (iAttributes[samplesValuePosition] == 1) {
- // Fallback in case it is unable to initialize with any
- // samples to avoid falling back to the GDI path
- // NB: The sample attributes needs to be at the end for this
- // to work correctly
- iAttributes[samplesValuePosition - 1] = FALSE;
- iAttributes[samplesValuePosition] = 0;
- iAttributes[samplesValuePosition + 1] = 0;
- } else {
+ if (valid || (!sampleBuffersRequested && !srgbRequested))
break;
+ if (srgbRequested) {
+ iAttributes[srgbValuePosition] = 0;
+ srgbRequested = false;
+ } else if (sampleBuffersRequested) {
+ if (iAttributes[samplesValuePosition] > 1) {
+ iAttributes[samplesValuePosition] /= 2;
+ } else if (iAttributes[samplesValuePosition] == 1) {
+ // Fallback in case it is unable to initialize with any
+ // samples to avoid falling back to the GDI path
+ // NB: The sample attributes needs to be at the end for this
+ // to work correctly
+ iAttributes[samplesValuePosition - 1] = FALSE;
+ iAttributes[samplesValuePosition] = 0;
+ iAttributes[samplesValuePosition + 1] = 0;
+ } else {
+ break;
+ }
}
}
// Verify if format is acceptable. Note that the returned
@@ -628,7 +643,7 @@ static QSurfaceFormat
HDC hdc, int pixelFormat,
QWindowsOpenGLAdditionalFormat *additionalIn = 0)
{
- enum { attribSize =40 };
+ enum { attribSize = 42 };
QSurfaceFormat result;
result.setRenderableType(QSurfaceFormat::OpenGL);
@@ -641,6 +656,7 @@ static QSurfaceFormat
int i = 0;
const bool hasSampleBuffers = testFlag(staticContext.extensions, QOpenGLStaticContext::SampleBuffers);
+ const bool hasSrgbSupport = testFlag(staticContext.extensions, QOpenGLStaticContext::sRGBCapableFramebuffer);
iAttributes[i++] = WGL_DOUBLE_BUFFER_ARB; // 0
iAttributes[i++] = WGL_DEPTH_BITS_ARB; // 1
@@ -658,6 +674,9 @@ static QSurfaceFormat
iAttributes[i++] = WGL_SAMPLE_BUFFERS_ARB; // 12
iAttributes[i++] = WGL_SAMPLES_ARB; // 13
}
+ if (hasSrgbSupport)
+ iAttributes[i++] = WGL_FRAMEBUFFER_SRGB_CAPABLE_EXT; // 12 or 14
+
if (!staticContext.wglGetPixelFormatAttribIVARB(hdc, pixelFormat, 0, i,
iAttributes, iValues)) {
qErrnoWarning("%s: wglGetPixelFormatAttribIVARB() failed for basic parameters.", __FUNCTION__);
@@ -673,8 +692,14 @@ static QSurfaceFormat
if (iValues[9])
result.setOption(QSurfaceFormat::StereoBuffers);
- if (hasSampleBuffers)
+ if (hasSampleBuffers) {
result.setSamples(iValues[13]);
+ if (hasSrgbSupport && iValues[14])
+ result.setColorSpace(QSurfaceFormat::sRGBColorSpace);
+ } else {
+ if (hasSrgbSupport && iValues[12])
+ result.setColorSpace(QSurfaceFormat::sRGBColorSpace);
+ }
if (additionalIn) {
if (iValues[7])
additionalIn->formatFlags |= QWindowsGLAccumBuffer;
@@ -947,7 +972,8 @@ QOpenGLStaticContext::QOpenGLStaticContext() :
wglChoosePixelFormatARB((WglChoosePixelFormatARB)QOpenGLStaticContext::opengl32.wglGetProcAddress("wglChoosePixelFormatARB")),
wglCreateContextAttribsARB((WglCreateContextAttribsARB)QOpenGLStaticContext::opengl32.wglGetProcAddress("wglCreateContextAttribsARB")),
wglSwapInternalExt((WglSwapInternalExt)QOpenGLStaticContext::opengl32.wglGetProcAddress("wglSwapIntervalEXT")),
- wglGetSwapInternalExt((WglGetSwapInternalExt)QOpenGLStaticContext::opengl32.wglGetProcAddress("wglGetSwapIntervalEXT"))
+ wglGetSwapInternalExt((WglGetSwapInternalExt)QOpenGLStaticContext::opengl32.wglGetProcAddress("wglGetSwapIntervalEXT")),
+ wglGetExtensionsStringARB((WglGetExtensionsStringARB)QOpenGLStaticContext::opengl32.wglGetProcAddress("wglGetExtensionsStringARB"))
{
if (extensionNames.startsWith(SAMPLE_BUFFER_EXTENSION " ")
|| extensionNames.indexOf(" " SAMPLE_BUFFER_EXTENSION " ") != -1)
@@ -1091,6 +1117,14 @@ QWindowsGLContext::QWindowsGLContext(QOpenGLStaticContext *staticContext,
&& !(QWindowsIntegration::instance()->options() & QWindowsIntegration::DisableArb);
QWindowsOpenGLAdditionalFormat obtainedAdditional;
if (tryExtensions) {
+ if (m_staticContext->wglGetExtensionsStringARB) {
+ const char *exts = m_staticContext->wglGetExtensionsStringARB(hdc);
+ if (exts) {
+ qCDebug(lcQpaGl) << __FUNCTION__ << "WGL extensions:" << exts;
+ if (strstr(exts, "WGL_EXT_framebuffer_sRGB"))
+ m_staticContext->extensions |= QOpenGLStaticContext::sRGBCapableFramebuffer;
+ }
+ }
m_pixelFormat =
ARB::choosePixelFormat(hdc, *m_staticContext, format,
requestedAdditional, &m_obtainedPixelFormatDescriptor);
diff --git a/src/plugins/platforms/windows/qwindowsglcontext.h b/src/plugins/platforms/windows/qwindowsglcontext.h
index dfaa428520..2d5b94af0e 100644
--- a/src/plugins/platforms/windows/qwindowsglcontext.h
+++ b/src/plugins/platforms/windows/qwindowsglcontext.h
@@ -139,7 +139,8 @@ class QOpenGLStaticContext : public QWindowsStaticOpenGLContext
public:
enum Extensions
{
- SampleBuffers = 0x1
+ SampleBuffers = 0x1,
+ sRGBCapableFramebuffer = 0x2
};
typedef bool
@@ -160,6 +161,9 @@ public:
typedef int
(APIENTRY *WglGetSwapInternalExt)(void);
+ typedef const char *
+ (APIENTRY *WglGetExtensionsStringARB)(HDC);
+
bool hasExtensions() const
{ return wglGetPixelFormatAttribIVARB && wglChoosePixelFormatARB && wglCreateContextAttribsARB; }
@@ -185,6 +189,7 @@ public:
WglCreateContextAttribsARB wglCreateContextAttribsARB;
WglSwapInternalExt wglSwapInternalExt;
WglGetSwapInternalExt wglGetSwapInternalExt;
+ WglGetExtensionsStringARB wglGetExtensionsStringARB;
static QWindowsOpengl32DLL opengl32;
};
diff --git a/src/plugins/platforms/windows/qwindowsinputcontext.cpp b/src/plugins/platforms/windows/qwindowsinputcontext.cpp
index 8c228f588e..b9dd2c557e 100644
--- a/src/plugins/platforms/windows/qwindowsinputcontext.cpp
+++ b/src/plugins/platforms/windows/qwindowsinputcontext.cpp
@@ -221,6 +221,28 @@ void QWindowsInputContext::setFocusObject(QObject *)
updateEnabled();
}
+HWND QWindowsInputContext::getVirtualKeyboardWindowHandle() const
+{
+ return ::FindWindowA("IPTip_Main_Window", nullptr);
+}
+
+QRectF QWindowsInputContext::keyboardRect() const
+{
+ if (HWND hwnd = getVirtualKeyboardWindowHandle()) {
+ RECT rect;
+ if (::GetWindowRect(hwnd, &rect)) {
+ return QRectF(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
+ }
+ }
+ return QRectF();
+}
+
+bool QWindowsInputContext::isInputPanelVisible() const
+{
+ HWND hwnd = getVirtualKeyboardWindowHandle();
+ return hwnd && ::IsWindowEnabled(hwnd) && ::IsWindowVisible(hwnd);
+}
+
void QWindowsInputContext::updateEnabled()
{
if (!QGuiApplication::focusObject())
diff --git a/src/plugins/platforms/windows/qwindowsinputcontext.h b/src/plugins/platforms/windows/qwindowsinputcontext.h
index 617ef30cef..ada1fc0d29 100644
--- a/src/plugins/platforms/windows/qwindowsinputcontext.h
+++ b/src/plugins/platforms/windows/qwindowsinputcontext.h
@@ -79,6 +79,9 @@ public:
void invokeAction(QInputMethod::Action, int cursorPosition) override;
void setFocusObject(QObject *object) override;
+ QRectF keyboardRect() const override;
+ bool isInputPanelVisible() const override;
+
bool startComposition(HWND hwnd);
bool composition(HWND hwnd, LPARAM lParam);
bool endComposition(HWND hwnd);
@@ -98,6 +101,7 @@ private:
void startContextComposition();
void endContextComposition();
void updateEnabled();
+ HWND getVirtualKeyboardWindowHandle() const;
const DWORD m_WM_MSIME_MOUSE;
static HIMC m_defaultContext;
diff --git a/src/plugins/platforms/windows/qwindowsintegration.cpp b/src/plugins/platforms/windows/qwindowsintegration.cpp
index 17cab69891..287b65cd5d 100644
--- a/src/plugins/platforms/windows/qwindowsintegration.cpp
+++ b/src/plugins/platforms/windows/qwindowsintegration.cpp
@@ -42,6 +42,7 @@
#include "qwindowswindow.h"
#include "qwindowscontext.h"
#include "qwin10helpers.h"
+#include "qwindowsmenu.h"
#include "qwindowsopenglcontext.h"
#include "qwindowsscreen.h"
@@ -59,7 +60,7 @@
#include "qwindowsinputcontext.h"
#include "qwindowskeymapper.h"
#ifndef QT_NO_ACCESSIBILITY
-# include "accessible/qwindowsaccessibility.h"
+# include "uiautomation/qwindowsuiaaccessibility.h"
#endif
#include <qpa/qplatformnativeinterface.h>
@@ -71,6 +72,7 @@
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/private/qhighdpiscaling_p.h>
#include <QtGui/qpa/qplatforminputcontextfactory_p.h>
+#include <QtGui/qpa/qplatformcursor.h>
#include <QtEventDispatcherSupport/private/qwindowsguieventdispatcher_p.h>
@@ -149,7 +151,7 @@ struct QWindowsIntegrationPrivate
#endif // QT_NO_OPENGL
QScopedPointer<QPlatformInputContext> m_inputContext;
#ifndef QT_NO_ACCESSIBILITY
- QWindowsAccessibility m_accessibility;
+ QWindowsUiaAccessibility m_accessibility;
#endif
QWindowsServices m_services;
};
@@ -206,6 +208,10 @@ static inline unsigned parseOptions(const QStringList &paramList,
} else if (parseIntOption(param, QLatin1String("verbose"), 0, INT_MAX, &QWindowsContext::verbose)
|| parseIntOption(param, QLatin1String("tabletabsoluterange"), 0, INT_MAX, tabletAbsoluteRange)
|| parseIntOption(param, QLatin1String("dpiawareness"), QtWindows::ProcessDpiUnaware, QtWindows::ProcessPerMonitorDpiAware, dpiAwareness)) {
+ } else if (param == QLatin1String("menus=native")) {
+ options |= QWindowsIntegration::AlwaysUseNativeMenus;
+ } else if (param == QLatin1String("menus=none")) {
+ options |= QWindowsIntegration::NoNativeMenus;
} else {
qWarning() << "Unknown option" << param;
}
@@ -237,6 +243,7 @@ QWindowsIntegrationPrivate::QWindowsIntegrationPrivate(const QStringList &paramL
}
m_context.initTouch(m_options);
+ QPlatformCursor::setCapability(QPlatformCursor::OverrideCursor);
}
QWindowsIntegrationPrivate::~QWindowsIntegrationPrivate()
@@ -245,7 +252,7 @@ QWindowsIntegrationPrivate::~QWindowsIntegrationPrivate()
delete m_fontDatabase;
}
-QWindowsIntegration *QWindowsIntegration::m_instance = Q_NULLPTR;
+QWindowsIntegration *QWindowsIntegration::m_instance = nullptr;
QWindowsIntegration::QWindowsIntegration(const QStringList &paramList) :
d(new QWindowsIntegrationPrivate(paramList))
@@ -259,7 +266,7 @@ QWindowsIntegration::QWindowsIntegration(const QStringList &paramList) :
QWindowsIntegration::~QWindowsIntegration()
{
- m_instance = Q_NULLPTR;
+ m_instance = nullptr;
}
void QWindowsIntegration::initialize()
@@ -329,25 +336,13 @@ QPlatformWindow *QWindowsIntegration::createPlatformWindow(QWindow *window) cons
<< " handle=" << obtained.hwnd << ' ' << obtained.flags << '\n';
if (Q_UNLIKELY(!obtained.hwnd))
- return Q_NULLPTR;
+ return nullptr;
QWindowsWindow *result = createPlatformWindowHelper(window, obtained);
Q_ASSERT(result);
- if (requested.flags != obtained.flags)
- window->setFlags(obtained.flags);
- // Trigger geometry change (unless it has a special state in which case setWindowState()
- // will send the message) and screen change signals of QWindow.
- if ((obtained.flags & Qt::Desktop) != Qt::Desktop) {
- const Qt::WindowState state = window->windowState();
- if (state != Qt::WindowMaximized && state != Qt::WindowFullScreen
- && requested.geometry != obtained.geometry) {
- QWindowSystemInterface::handleGeometryChange(window, obtained.geometry);
- }
- QPlatformScreen *screen = result->screenForGeometry(obtained.geometry);
- if (screen && result->screen() != screen)
- QWindowSystemInterface::handleWindowScreenChanged(window, screen->screen());
- }
+ if (QWindowsMenuBar *menuBarToBeInstalled = QWindowsMenuBar::menuBarOf(window))
+ menuBarToBeInstalled->install(result);
return result;
}
@@ -361,7 +356,7 @@ QPlatformWindow *QWindowsIntegration::createForeignWindow(QWindow *window, WId n
}
QWindowsForeignWindow *result = new QWindowsForeignWindow(window, hwnd);
const QRect obtainedGeometry = result->geometry();
- QScreen *screen = Q_NULLPTR;
+ QScreen *screen = nullptr;
if (const QPlatformScreen *pScreen = result->screenForGeometry(obtainedGeometry))
screen = pScreen->screen();
if (screen && screen != window->screen())
@@ -407,7 +402,7 @@ QWindowsStaticOpenGLContext *QWindowsStaticOpenGLContext::doCreate()
qCWarning(lcQpaGl, "Software OpenGL failed. Falling back to system OpenGL.");
if (QWindowsOpenGLTester::supportedRenderers() & QWindowsOpenGLTester::DesktopGl)
return QOpenGLStaticContext::create();
- return Q_NULLPTR;
+ return nullptr;
default:
break;
}
@@ -611,4 +606,11 @@ void QWindowsIntegration::beep() const
MessageBeep(MB_OK); // For QApplication
}
+#if QT_CONFIG(vulkan)
+QPlatformVulkanInstance *QWindowsIntegration::createPlatformVulkanInstance(QVulkanInstance *instance) const
+{
+ return new QWindowsVulkanInstance(instance);
+}
+#endif
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/windows/qwindowsintegration.h b/src/plugins/platforms/windows/qwindowsintegration.h
index 28d4fd3026..23f3d9ef4e 100644
--- a/src/plugins/platforms/windows/qwindowsintegration.h
+++ b/src/plugins/platforms/windows/qwindowsintegration.h
@@ -64,7 +64,9 @@ public:
DontPassOsMouseEventsSynthesizedFromTouch = 0x20, // Do not pass OS-generated mouse events from touch.
// Keep in sync with QWindowsFontDatabase::FontOptions
DontUseDirectWriteFonts = QWindowsFontDatabase::DontUseDirectWriteFonts,
- DontUseColorFonts = QWindowsFontDatabase::DontUseColorFonts
+ DontUseColorFonts = QWindowsFontDatabase::DontUseColorFonts,
+ AlwaysUseNativeMenus = 0x100,
+ NoNativeMenus = 0x200
};
explicit QWindowsIntegration(const QStringList &paramList);
@@ -113,6 +115,10 @@ public:
QPlatformSessionManager *createPlatformSessionManager(const QString &id, const QString &key) const override;
#endif
+#if QT_CONFIG(vulkan)
+ QPlatformVulkanInstance *createPlatformVulkanInstance(QVulkanInstance *instance) const override;
+#endif
+
protected:
virtual QWindowsWindow *createPlatformWindowHelper(QWindow *window, const QWindowsWindowData &) const;
diff --git a/src/plugins/platforms/windows/qwindowskeymapper.cpp b/src/plugins/platforms/windows/qwindowskeymapper.cpp
index 3987d8ca29..af62936a18 100644
--- a/src/plugins/platforms/windows/qwindowskeymapper.cpp
+++ b/src/plugins/platforms/windows/qwindowskeymapper.cpp
@@ -903,6 +903,12 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, const MSG &ms
return true;
}
+ // Enable Alt accelerators ("&File") on menus
+ if (msgType == WM_SYSKEYDOWN && (nModifiers & AltAny) != 0 && GetMenu(msg.hwnd) != nullptr)
+ return false;
+ if (msgType == WM_SYSKEYUP && nModifiers == 0 && GetMenu(msg.hwnd) != nullptr)
+ return false;
+
bool result = false;
// handle Directionality changes (BiDi) with RTL extensions
if (m_useRTLExtensions) {
diff --git a/src/plugins/platforms/windows/qwindowsmenu.cpp b/src/plugins/platforms/windows/qwindowsmenu.cpp
new file mode 100644
index 0000000000..72f11d54b4
--- /dev/null
+++ b/src/plugins/platforms/windows/qwindowsmenu.cpp
@@ -0,0 +1,969 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwindowsmenu.h"
+#include "qwindowscontext.h"
+#include "qwindowswindow.h"
+
+#include <QtGui/qwindow.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qvariant.h>
+#include <QtCore/qmetaobject.h>
+#include <QtCore/qpointer.h>
+
+#include <algorithm>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QWindowsMenuBar
+ \brief Windows native menu bar
+
+ \list
+ \li \l{https://msdn.microsoft.com/de-de/library/windows/desktop/ms647553(v=vs.85).aspx#_win32_Menu_Creation_Functions},
+ \e{About Menus}
+ \endlist
+
+ \note The destruction order of the QWindowsMenu/Item/Bar instances is
+ arbitrary depending on whether the application is Qt Quick or
+ Qt Widgets, either the containers or the items might be deleted first.
+
+ \internal
+ \ingroup qt-lighthouse-win
+*/
+
+static uint nextId = 1;
+
+// Find a QPlatformMenu[Item]* in a vector of QWindowsMenu[Item], where
+// QVector::indexOf() cannot be used since it wants a QWindowsMenu[Item]*
+template <class Derived, class Needle>
+static int indexOf(const QVector<Derived *> &v, const Needle *needle)
+{
+ for (int i = 0, size = v.size(); i < size; ++i) {
+ if (v.at(i) == needle)
+ return i;
+ }
+ return -1;
+}
+
+// Helper for inserting a QPlatformMenu[Item]* into a vector of QWindowsMenu[Item].
+template <class Derived, class Base>
+static int insertBefore(QVector<Derived *> *v, Base *newItemIn, const Base *before = nullptr)
+{
+ int index = before ? indexOf(*v, before) : -1;
+ if (index != -1) {
+ v->insert(index, static_cast<Derived *>(newItemIn));
+ } else {
+ index = v->size();
+ v->append(static_cast<Derived *>(newItemIn));
+ }
+ return index;
+}
+
+static inline const wchar_t *qStringToWChar(const QString &s)
+{
+ return reinterpret_cast<const wchar_t *>(s.utf16());
+}
+
+// Traverse menu and return the item for which predicate
+// "bool Function(QWindowsMenuItem *)" returns true
+template <class Predicate>
+static QWindowsMenuItem *traverseMenuItems(const QWindowsMenu *menu, Predicate p)
+{
+ const QWindowsMenu::MenuItems &items = menu->menuItems();
+ for (QWindowsMenuItem *item : items) {
+ if (p(item))
+ return item;
+ if (item->subMenu()) {
+ if (QWindowsMenuItem *subMenuItem = traverseMenuItems(item->subMenu(), p))
+ return subMenuItem;
+ }
+ }
+ return nullptr;
+}
+
+// Traverse menu bar return the item for which predicate
+// "bool Function(QWindowsMenuItem *)" returns true
+template <class Predicate>
+static QWindowsMenuItem *traverseMenuItems(const QWindowsMenuBar *menuBar, Predicate p)
+{
+ const QWindowsMenuBar::Menus &menus = menuBar->menus();
+ for (QWindowsMenu *menu : menus) {
+ if (QWindowsMenuItem *item = traverseMenuItems(menu, p))
+ return item;
+ }
+ return nullptr;
+}
+
+template <class Menu /* Menu[Bar] */>
+static QWindowsMenuItem *findMenuItemById(const Menu *menu, uint id)
+{
+ return traverseMenuItems(menu, [id] (const QWindowsMenuItem *i) { return i->id() == id; });
+}
+
+// Traverse menu and return the menu for which predicate
+// "bool Function(QWindowsMenu *)" returns true
+template <class Predicate>
+static QWindowsMenu *traverseMenus(const QWindowsMenu *menu, Predicate p)
+{
+ const QWindowsMenu::MenuItems &items = menu->menuItems();
+ for (QWindowsMenuItem *item : items) {
+ if (QWindowsMenu *subMenu = item->subMenu()) {
+ if (p(subMenu))
+ return subMenu;
+ if (QWindowsMenu *menu = traverseMenus(subMenu, p))
+ return menu;
+ }
+ }
+ return nullptr;
+}
+
+// Traverse menu bar return the item for which
+// function "bool Function(QWindowsMenu *)" returns true
+template <class Predicate>
+static QWindowsMenu *traverseMenus(const QWindowsMenuBar *menuBar, Predicate p)
+{
+ const QWindowsMenuBar::Menus &menus = menuBar->menus();
+ for (QWindowsMenu *menu : menus) {
+ if (p(menu))
+ return menu;
+ if (QWindowsMenu *subMenu = traverseMenus(menu, p))
+ return subMenu;
+ }
+ return nullptr;
+}
+
+template <class Menu /* Menu[Bar] */>
+static QWindowsMenu *findMenuByHandle(const Menu *menu, HMENU hMenu)
+{
+ return traverseMenus(menu, [hMenu] (const QWindowsMenu *i) { return i->menuHandle() == hMenu; });
+}
+
+template <class MenuType>
+static int findNextVisibleEntry(const QVector<MenuType *> &entries, int pos)
+{
+ for (int i = pos, size = entries.size(); i < size; ++i) {
+ if (entries.at(i)->isVisible())
+ return i;
+ }
+ return -1;
+}
+
+static inline void menuItemInfoInit(MENUITEMINFO &menuItemInfo)
+{
+ memset(&menuItemInfo, 0, sizeof(MENUITEMINFO));
+ menuItemInfo.cbSize = sizeof(MENUITEMINFO);
+}
+
+static inline void menuItemInfoSetText(MENUITEMINFO &menuItemInfo, const QString &text)
+{
+ menuItemInfoInit(menuItemInfo);
+ menuItemInfo.fMask = MIIM_STRING;
+ menuItemInfo.dwTypeData = const_cast<wchar_t *>(qStringToWChar(text));
+ menuItemInfo.cch = UINT(text.size());
+}
+
+static UINT menuItemState(HMENU hMenu, UINT uItem, BOOL fByPosition)
+{
+ MENUITEMINFO menuItemInfo;
+ menuItemInfoInit(menuItemInfo);
+ menuItemInfo.fMask = MIIM_STATE;
+ return GetMenuItemInfo(hMenu, uItem, fByPosition, &menuItemInfo) == TRUE ? menuItemInfo.fState : 0;
+}
+
+static void menuItemSetState(HMENU hMenu, UINT uItem, BOOL fByPosition, UINT flags)
+{
+ MENUITEMINFO menuItemInfo;
+ menuItemInfoInit(menuItemInfo);
+ menuItemInfo.fMask = MIIM_STATE;
+ menuItemInfo.fState = flags;
+ SetMenuItemInfo(hMenu, uItem, fByPosition, &menuItemInfo);
+}
+
+static void menuItemSetChangeState(HMENU hMenu, UINT uItem, BOOL fByPosition,
+ bool value, UINT trueState, UINT falseState)
+{
+ const UINT oldState = menuItemState(hMenu, uItem, fByPosition);
+ UINT newState = oldState;
+ if (value) {
+ newState |= trueState;
+ newState &= ~falseState;
+ } else {
+ newState &= ~trueState;
+ newState |= falseState;
+ }
+ if (oldState != newState)
+ menuItemSetState(hMenu, uItem, fByPosition, newState);
+}
+
+// ------------ QWindowsMenuItem
+QWindowsMenuItem::QWindowsMenuItem(QWindowsMenu *parentMenu)
+ : m_parentMenu(parentMenu)
+ , m_id(0)
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << static_cast<const void *>(this)
+ << "parentMenu=" << parentMenu;
+}
+
+QWindowsMenuItem::~QWindowsMenuItem()
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << static_cast<const void *>(this);
+ removeFromMenu();
+ freeBitmap();
+}
+
+void QWindowsMenuItem::freeBitmap()
+{
+ if (m_hbitmap) {
+ DeleteObject(m_hbitmap);
+ m_hbitmap = nullptr;
+ }
+}
+
+void QWindowsMenuItem::setIcon(const QIcon &icon)
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << icon << ')' << this;
+ if (m_icon.cacheKey() == icon.cacheKey())
+ return;
+ m_icon = icon;
+ if (m_parentMenu != nullptr)
+ updateBitmap();
+}
+
+Q_GUI_EXPORT HBITMAP qt_pixmapToWinHBITMAP(const QPixmap &p, int hbitmapFormat = 0);
+
+void QWindowsMenuItem::updateBitmap()
+{
+ freeBitmap();
+ if (!m_icon.isNull()) {
+ const int size = m_iconSize ? m_iconSize : GetSystemMetrics(SM_CYMENUCHECK);
+ m_hbitmap = qt_pixmapToWinHBITMAP(m_icon.pixmap(QSize(size, size)), 1);
+ }
+ MENUITEMINFO itemInfo;
+ menuItemInfoInit(itemInfo);
+ itemInfo.fMask = MIIM_BITMAP;
+ itemInfo.hbmpItem = m_hbitmap;
+ SetMenuItemInfo(parentMenuHandle(), m_id, FALSE, &itemInfo);
+}
+
+void QWindowsMenuItem::setText(const QString &text)
+{
+ qCDebug(lcQpaMenus).nospace().noquote()
+ << __FUNCTION__ << "(\"" << text << "\") " << this;
+ if (m_text == text)
+ return;
+ m_text = text;
+ if (m_parentMenu != nullptr)
+ updateText();
+}
+
+void QWindowsMenuItem::updateText()
+{
+ MENUITEMINFO menuItemInfo;
+ const QString &text = nativeText();
+ menuItemInfoSetText(menuItemInfo, text);
+ SetMenuItemInfo(parentMenuHandle(), m_id, FALSE, &menuItemInfo);
+}
+
+void QWindowsMenuItem::setMenu(QPlatformMenu *menuIn)
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << menuIn << ')' << this;
+ if (menuIn == m_subMenu)
+ return;
+ const uint oldId = m_id;
+ if (menuIn != nullptr) { // Set submenu
+ m_subMenu = static_cast<QWindowsMenu *>(menuIn);
+ m_subMenu->setAsItemSubMenu(this);
+ m_id = m_subMenu->id();
+ if (m_parentMenu != nullptr) {
+ ModifyMenu(m_parentMenu->menuHandle(), oldId, MF_BYCOMMAND | MF_POPUP,
+ m_id, qStringToWChar(m_text));
+ }
+ return;
+ }
+ // Clear submenu
+ m_subMenu = nullptr;
+ if (m_parentMenu != nullptr) {
+ m_id = nextId++;
+ ModifyMenu(m_parentMenu->menuHandle(), oldId, MF_BYCOMMAND,
+ m_id, qStringToWChar(m_text));
+ } else {
+ m_id = 0;
+ }
+}
+
+void QWindowsMenuItem::setVisible(bool isVisible)
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << isVisible << ')' << this;
+ if (m_visible == isVisible)
+ return;
+ m_visible = isVisible;
+ if (m_parentMenu == nullptr)
+ return;
+ // Windows menu items do not implement settable visibility, we need to work
+ // around by removing the item from the menu. It will be kept in the list.
+ if (isVisible)
+ insertIntoMenuHelper(m_parentMenu, false, m_parentMenu->menuItems().indexOf(this));
+ else
+ RemoveMenu(parentMenuHandle(), m_id, MF_BYCOMMAND);
+}
+
+void QWindowsMenuItem::setIsSeparator(bool isSeparator)
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << isSeparator << ')' << this;
+ if (m_separator == isSeparator)
+ return;
+ m_separator = isSeparator;
+}
+
+void QWindowsMenuItem::setCheckable(bool checkable)
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << checkable << ')' << this;
+ if (m_checkable == checkable)
+ return;
+ m_checkable = checkable;
+ if (m_parentMenu == nullptr)
+ return;
+ UINT state = menuItemState(parentMenuHandle(), m_id, FALSE);
+ if (m_checkable)
+ state |= m_checked ? MF_CHECKED : MF_UNCHECKED;
+ else
+ state &= ~(MF_CHECKED | MF_UNCHECKED);
+ menuItemSetState(parentMenuHandle(), m_id, FALSE, state);
+}
+
+void QWindowsMenuItem::setChecked(bool isChecked)
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << isChecked << ')' << this;
+ if (m_checked == isChecked)
+ return;
+ m_checked = isChecked;
+ // Convenience: Allow to set checkable by calling setChecked(true) for
+ // Quick Controls 1
+ if (isChecked)
+ m_checkable = true;
+ if (m_parentMenu == nullptr || !m_checkable)
+ return;
+ menuItemSetChangeState(parentMenuHandle(), m_id, FALSE, m_checked, MF_CHECKED, MF_UNCHECKED);
+}
+
+void QWindowsMenuItem::setShortcut(const QKeySequence &shortcut)
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << shortcut << ')' << this;
+ if (m_shortcut == shortcut)
+ return;
+ m_shortcut = shortcut;
+ if (m_parentMenu != nullptr)
+ updateText();
+}
+
+void QWindowsMenuItem::setEnabled(bool enabled)
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << enabled << ')' << this;
+ if (m_enabled == enabled)
+ return;
+ m_enabled = enabled;
+ if (m_parentMenu != nullptr)
+ menuItemSetChangeState(parentMenuHandle(), m_id, FALSE, m_enabled, MF_ENABLED, MF_GRAYED);
+}
+
+void QWindowsMenuItem::setIconSize(int size)
+{
+ if (m_iconSize == size)
+ return;
+ m_iconSize = size;
+ if (m_parentMenu != nullptr)
+ updateBitmap();
+}
+
+HMENU QWindowsMenuItem::parentMenuHandle() const
+{
+ return m_parentMenu ? m_parentMenu->menuHandle() : nullptr;
+}
+
+UINT QWindowsMenuItem::state() const
+{
+ if (m_separator)
+ return MF_SEPARATOR;
+ UINT result = MF_STRING | (m_enabled ? MF_ENABLED : MF_GRAYED);
+ if (m_subMenu != nullptr)
+ result |= MF_POPUP;
+ if (m_checkable)
+ result |= m_checked ? MF_CHECKED : MF_UNCHECKED;
+ if (QGuiApplication::layoutDirection() == Qt::RightToLeft)
+ result |= MFT_RIGHTORDER;
+ return result;
+}
+
+QString QWindowsMenuItem::nativeText() const
+{
+ QString result = m_text;
+ if (!m_shortcut.isEmpty()) {
+ result += QLatin1Char('\t');
+ result += m_shortcut.toString(QKeySequence::NativeText);
+ }
+ return result;
+}
+
+void QWindowsMenuItem::insertIntoMenu(QWindowsMenu *menu, bool append, int index)
+{
+ if (m_id == 0 && m_subMenu == nullptr)
+ m_id = nextId++;
+ insertIntoMenuHelper(menu, append, index);
+ m_parentMenu = menu;
+}
+
+void QWindowsMenuItem::insertIntoMenuHelper(QWindowsMenu *menu, bool append, int index)
+{
+ const QString &text = nativeText();
+
+ UINT_PTR idBefore = 0;
+ if (!append) {
+ // Skip over self (either newly inserted or when called from setVisible()
+ const int nextIndex = findNextVisibleEntry(menu->menuItems(), index + 1);
+ if (nextIndex != -1)
+ idBefore = menu->menuItems().at(nextIndex)->id();
+ }
+
+ if (idBefore)
+ InsertMenu(menu->menuHandle(), idBefore, state(), m_id, qStringToWChar(text));
+ else
+ AppendMenu(menu->menuHandle(), state(), m_id, qStringToWChar(text));
+
+ updateBitmap();
+}
+
+bool QWindowsMenuItem::removeFromMenu()
+{
+ if (QWindowsMenu *parentMenu = m_parentMenu) {
+ m_parentMenu = nullptr;
+ RemoveMenu(parentMenu->menuHandle(), m_id, MF_BYCOMMAND);
+ parentMenu->notifyRemoved(this);
+ return true;
+ }
+ return false;
+}
+
+// ------------ QWindowsMenu
+
+QWindowsMenu::QWindowsMenu() : QWindowsMenu(nullptr, CreateMenu())
+{
+}
+
+QWindowsMenu::QWindowsMenu(QWindowsMenu *parentMenu, HMENU menu)
+ : m_parentMenu(parentMenu)
+ , m_hMenu(menu)
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << static_cast<const void *>(this)
+ << "parentMenu=" << parentMenu << "HMENU=" << m_hMenu;
+}
+
+QWindowsMenu::~QWindowsMenu()
+{
+ qCDebug(lcQpaMenus).noquote().nospace() << __FUNCTION__
+ << " \"" <<m_text << "\", " << static_cast<const void *>(this);
+ for (int i = m_menuItems.size() - 1; i>= 0; --i)
+ m_menuItems.at(i)->removeFromMenu();
+ removeFromParent();
+ DestroyMenu(m_hMenu);
+}
+
+void QWindowsMenu::insertMenuItem(QPlatformMenuItem *menuItemIn, QPlatformMenuItem *before)
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << menuItemIn << ", before=" << before << ')' << this;
+ QWindowsMenuItem *menuItem = static_cast<QWindowsMenuItem *>(menuItemIn);
+ const int index = insertBefore(&m_menuItems, menuItemIn, before);
+ const bool append = index == m_menuItems.size() - 1;
+ menuItem->insertIntoMenu(this, append, index);
+}
+
+void QWindowsMenu::removeMenuItem(QPlatformMenuItem *menuItemIn)
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << menuItemIn << ')' << this;
+ static_cast<QWindowsMenuItem *>(menuItemIn)->removeFromMenu();
+}
+
+void QWindowsMenu::setText(const QString &text)
+{
+ qCDebug(lcQpaMenus).nospace().noquote()
+ << __FUNCTION__ << "(\"" << text << "\") " << this;
+ if (m_text == text)
+ return;
+ m_text = text;
+ if (!m_visible)
+ return;
+ const HMENU ph = parentHandle();
+ if (ph == nullptr)
+ return;
+ MENUITEMINFO menuItemInfo;
+ menuItemInfoSetText(menuItemInfo, m_text);
+ SetMenuItemInfo(ph, id(), FALSE, &menuItemInfo);
+}
+
+void QWindowsMenu::setIcon(const QIcon &icon)
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << icon << ')' << this;
+ m_icon = icon;
+}
+
+void QWindowsMenu::setEnabled(bool enabled)
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << enabled << ')' << this;
+ if (m_enabled == enabled)
+ return;
+ m_enabled = enabled;
+ if (!m_visible)
+ return;
+ if (const HMENU ph = parentHandle())
+ menuItemSetChangeState(ph, id(), FALSE, m_enabled, MF_ENABLED, MF_GRAYED);
+}
+
+QWindowsMenuItem *QWindowsMenu::itemForSubMenu(const QWindowsMenu *subMenu) const
+{
+ const auto it = std::find_if(m_menuItems.cbegin(), m_menuItems.cend(),
+ [subMenu] (const QWindowsMenuItem *i) { return i->subMenu() == subMenu; });
+ return it != m_menuItems.cend() ? *it : nullptr;
+}
+
+void QWindowsMenu::insertIntoMenuBar(QWindowsMenuBar *bar, bool append, int index)
+{
+ UINT_PTR idBefore = 0;
+ if (!append) {
+ // Skip over self (either newly inserted or when called from setVisible()
+ const int nextIndex = findNextVisibleEntry(bar->menus(), index + 1);
+ if (nextIndex != -1)
+ idBefore = bar->menus().at(nextIndex)->id();
+ }
+ m_parentMenuBar = bar;
+ m_parentMenu = nullptr;
+ if (idBefore)
+ InsertMenu(bar->menuBarHandle(), idBefore, MF_POPUP | MF_BYCOMMAND, id(), qStringToWChar(m_text));
+ else
+ AppendMenu(bar->menuBarHandle(), MF_POPUP, id(), qStringToWChar(m_text));
+}
+
+bool QWindowsMenu::removeFromParent()
+{
+ if (QWindowsMenuBar *bar = m_parentMenuBar) {
+ m_parentMenuBar = nullptr;
+ bar->notifyRemoved(this);
+ return RemoveMenu(bar->menuBarHandle(), id(), MF_BYCOMMAND) == TRUE;
+ }
+ if (QWindowsMenu *menu = m_parentMenu) {
+ m_parentMenu = nullptr;
+ QWindowsMenuItem *item = menu->itemForSubMenu(this);
+ if (item)
+ item->setMenu(nullptr);
+ return item != nullptr;
+ }
+ return false;
+}
+
+void QWindowsMenu::setVisible(bool visible)
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << visible << ')' << this;
+ if (m_visible == visible)
+ return;
+ m_visible = visible;
+ const HMENU ph = parentHandle();
+ if (ph == nullptr)
+ return;
+ // Windows menus do not implement settable visibility, we need to work
+ // around by removing the menu from the parent. It will be kept in the list.
+ if (visible) {
+ if (m_parentMenuBar)
+ insertIntoMenuBar(m_parentMenuBar, false, m_parentMenuBar->menus().indexOf(this));
+ } else {
+ RemoveMenu(ph, id(), MF_BYCOMMAND);
+ }
+ if (m_parentMenuBar)
+ m_parentMenuBar->redraw();
+}
+
+QPlatformMenuItem *QWindowsMenu::menuItemAt(int position) const
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << position;
+ return position >= 0 && position < m_menuItems.size()
+ ? m_menuItems.at(position) : nullptr;
+}
+
+QPlatformMenuItem *QWindowsMenu::menuItemForTag(quintptr tag) const
+{
+ return traverseMenuItems(this, [tag] (const QPlatformMenuItem *i) { return i->tag() == tag; });
+}
+
+QPlatformMenuItem *QWindowsMenu::createMenuItem() const
+{
+ QPlatformMenuItem *result = new QWindowsMenuItem;
+ qCDebug(lcQpaMenus) << __FUNCTION__ << this << "returns" << result;
+ return result;
+}
+
+QPlatformMenu *QWindowsMenu::createSubMenu() const
+{
+ QPlatformMenu *result = new QWindowsMenu;
+ qCDebug(lcQpaMenus) << __FUNCTION__ << this << "returns" << result;
+ return result;
+}
+
+void QWindowsMenu::setAsItemSubMenu(QWindowsMenuItem *item)
+{
+ m_parentMenu = item->parentMenu();
+}
+
+HMENU QWindowsMenu::parentMenuHandle() const
+{
+ return m_parentMenu ? m_parentMenu->menuHandle() : nullptr;
+}
+
+HMENU QWindowsMenu::parentMenuBarHandle() const
+{
+ return m_parentMenuBar ? m_parentMenuBar->menuBarHandle() : nullptr;
+}
+
+HMENU QWindowsMenu::parentHandle() const
+{
+ if (m_parentMenuBar)
+ return m_parentMenuBar->menuBarHandle();
+ if (m_parentMenu)
+ return m_parentMenu->menuHandle();
+ return nullptr;
+}
+
+// --------------- QWindowsPopupMenu
+
+static QPointer<QWindowsPopupMenu> lastShownPopupMenu;
+
+QWindowsPopupMenu::QWindowsPopupMenu() : QWindowsMenu(nullptr, CreatePopupMenu())
+{
+}
+
+void QWindowsPopupMenu::showPopup(const QWindow *parentWindow, const QRect &targetRect,
+ const QPlatformMenuItem *item)
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << '>' << this << parentWindow << targetRect << item;
+ const QWindowsBaseWindow *window = static_cast<const QWindowsBaseWindow *>(parentWindow->handle());
+ const QPoint globalPos = window->mapToGlobal(targetRect.topLeft());
+ trackPopupMenu(window->handle(), globalPos.x(), globalPos.y());
+}
+
+bool QWindowsPopupMenu::trackPopupMenu(HWND windowHandle, int x, int y)
+{
+ lastShownPopupMenu = this;
+ // Emulate Show()/Hide() signals. Could be implemented by catching the
+ // WM_EXITMENULOOP, WM_ENTERMENULOOP messages; but they do not carry
+ // information telling which menu was opened.
+ emit aboutToShow();
+ const bool result =
+ TrackPopupMenu(menuHandle(),
+ QGuiApplication::layoutDirection() == Qt::RightToLeft ? UINT(TPM_RIGHTALIGN) : UINT(0),
+ x, y, 0, windowHandle, nullptr) == TRUE;
+ emit aboutToHide();
+ return result;
+}
+
+bool QWindowsPopupMenu::notifyTriggered(uint id)
+{
+ QPlatformMenuItem *result = lastShownPopupMenu.isNull()
+ ? nullptr
+ : findMenuItemById(lastShownPopupMenu.data(), id);
+ if (result != nullptr) {
+ qCDebug(lcQpaMenus) << __FUNCTION__ << "id=" << id;
+ emit result->activated();
+ }
+ lastShownPopupMenu = nullptr;
+ return result != nullptr;
+}
+
+bool QWindowsPopupMenu::notifyAboutToShow(HMENU hmenu)
+{
+ if (lastShownPopupMenu.isNull())
+ return false;
+ if (lastShownPopupMenu->menuHandle() == hmenu) {
+ emit lastShownPopupMenu->aboutToShow();
+ return true;
+ }
+ if (QWindowsMenu *menu = findMenuByHandle(lastShownPopupMenu.data(), hmenu)) {
+ emit menu->aboutToShow();
+ return true;
+ }
+ return false;
+}
+
+// --------------- QWindowsMenuBar
+
+QWindowsMenuBar::QWindowsMenuBar() : m_hMenuBar(CreateMenu())
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << static_cast<const void *>(this);
+}
+
+QWindowsMenuBar::~QWindowsMenuBar()
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << static_cast<const void *>(this);
+ for (int m = m_menus.size() - 1; m >= 0; --m)
+ m_menus.at(m)->removeFromParent();
+ removeFromWindow();
+ DestroyMenu(m_hMenuBar);
+}
+
+void QWindowsMenuBar::insertMenu(QPlatformMenu *menuIn, QPlatformMenu *before)
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << menuIn << "before=" << before;
+ QWindowsMenu *menu = static_cast<QWindowsMenu *>(menuIn);
+ const int index = insertBefore(&m_menus, menuIn, before);
+ menu->insertIntoMenuBar(this, index == m_menus.size() - 1, index);
+}
+
+void QWindowsMenuBar::removeMenu(QPlatformMenu *menu)
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << menu << ')' << this;
+ const int index = indexOf(m_menus, menu);
+ if (index != -1)
+ m_menus[index]->removeFromParent();
+}
+
+// When calling handleReparent() for a QWindow instances that does not have
+// a platform window yet, set the menubar as dynamic property to be installed
+// on platform window creation.
+static const char menuBarPropertyName[] = "_q_windowsNativeMenuBar";
+
+void QWindowsMenuBar::handleReparent(QWindow *newParentWindow)
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << newParentWindow << ')' << this;
+ if (newParentWindow == nullptr) {
+ removeFromWindow();
+ return; // Happens during Quick Controls 1 property setup
+ }
+ if (QPlatformWindow *platWin = newParentWindow->handle())
+ install(static_cast<QWindowsWindow *>(platWin));
+ else // Store for later creation, see menuBarOf()
+ newParentWindow->setProperty(menuBarPropertyName, qVariantFromValue<QObject *>(this));
+}
+
+QWindowsMenuBar *QWindowsMenuBar::menuBarOf(const QWindow *notYetCreatedWindow)
+{
+ const QVariant menuBarV = notYetCreatedWindow->property(menuBarPropertyName);
+ return menuBarV.canConvert<QObject *>()
+ ? qobject_cast<QWindowsMenuBar *>(menuBarV.value<QObject *>()) : nullptr;
+}
+
+static inline void forceNcCalcSize(HWND hwnd)
+{
+ // Force WM_NCCALCSIZE to adjust margin: Does not appear to work?
+ SetWindowPos(hwnd, 0, 0, 0, 0, 0,
+ SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
+}
+
+void QWindowsMenuBar::install(QWindowsWindow *window)
+{
+ const HWND hwnd = window->handle();
+ const BOOL result = SetMenu(hwnd, m_hMenuBar);
+ if (result) {
+ window->setMenuBar(this);
+ forceNcCalcSize(hwnd);
+ }
+}
+
+void QWindowsMenuBar::removeFromWindow()
+{
+ if (QWindowsWindow *window = platformWindow()) {
+ const HWND hwnd = window->handle();
+ SetMenu(hwnd, nullptr);
+ window->setMenuBar(nullptr);
+ forceNcCalcSize(hwnd);
+ }
+}
+
+QPlatformMenu *QWindowsMenuBar::menuForTag(quintptr tag) const
+{
+ return traverseMenus(this, [tag] (const QWindowsMenu *m) { return m->tag() == tag; });
+}
+
+QPlatformMenu *QWindowsMenuBar::createMenu() const
+{
+ QPlatformMenu *result = new QWindowsMenu;
+ qCDebug(lcQpaMenus) << __FUNCTION__ << this << "returns" << result;
+ return result;
+}
+
+bool QWindowsMenuBar::notifyTriggered(uint id)
+{
+ QPlatformMenuItem *result = findMenuItemById(this, id);
+ if (result != nullptr) {
+ qCDebug(lcQpaMenus) << __FUNCTION__ << "id=" << id;
+ emit result->activated();
+ }
+ return result != nullptr;
+}
+
+bool QWindowsMenuBar::notifyAboutToShow(HMENU hmenu)
+{
+ if (QWindowsMenu *menu = findMenuByHandle(this, hmenu)) {
+ emit menu->aboutToShow();
+ return true;
+ }
+ return false;
+}
+
+QWindowsWindow *QWindowsMenuBar::platformWindow() const
+{
+ if (const QWindowsContext *ctx = QWindowsContext::instance()) {
+ if (QWindowsWindow *w = ctx->findPlatformWindow(this))
+ return w;
+ }
+ return nullptr;
+}
+
+void QWindowsMenuBar::redraw() const
+{
+ if (const QWindowsWindow *window = platformWindow())
+ DrawMenuBar(window->handle());
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+
+template <class M> /* Menu[Item] */
+static void formatTextSequence(QDebug &d, const QVector<M *> &v)
+{
+ if (const int size = v.size()) {
+ d << '[' << size << "](";
+ for (int i = 0; i < size; ++i) {
+ if (i)
+ d << ", ";
+ if (!v.at(i)->isVisible())
+ d << "[hidden] ";
+ d << '"' << v.at(i)->text() << '"';
+ }
+ d << ')';
+ }
+}
+
+void QWindowsMenuItem::formatDebug(QDebug &d) const
+{
+ if (m_separator)
+ d << "separator, ";
+ else
+ d << '"' << m_text << "\", ";
+ d << static_cast<const void *>(this);
+ if (m_parentMenu)
+ d << ", parentMenu=" << static_cast<const void *>(m_parentMenu);
+ if (m_subMenu)
+ d << ", subMenu=" << static_cast<const void *>(m_subMenu);
+ d << ", tag=" << showbase << hex
+ << tag() << noshowbase << dec << ", id=" << m_id;
+ if (!m_shortcut.isEmpty())
+ d << ", shortcut=" << m_shortcut;
+ if (m_visible)
+ d << " [visible]";
+ if (m_enabled)
+ d << " [enabled]";
+ if (m_checkable)
+ d << ", checked=" << m_checked;
+}
+
+QDebug operator<<(QDebug d, const QPlatformMenuItem *i)
+{
+ QDebugStateSaver saver(d);
+ d.nospace();
+ d.noquote();
+ d << "QPlatformMenuItem(";
+ if (i)
+ static_cast<const QWindowsMenuItem *>(i)->formatDebug(d);
+ else
+ d << '0';
+ d << ')';
+ return d;
+}
+
+void QWindowsMenu::formatDebug(QDebug &d) const
+{
+ d << '"' << m_text << "\", " << static_cast<const void *>(this)
+ << ", handle=" << m_hMenu;
+ if (m_parentMenuBar != nullptr)
+ d << " [on menubar]";
+ if (m_parentMenu != nullptr)
+ d << " [on menu]";
+ if (tag())
+ d << ", tag=" << showbase << hex << tag() << noshowbase << dec;
+ if (m_visible)
+ d << " [visible]";
+ if (m_enabled)
+ d << " [enabled]";
+ d <<' ';
+ formatTextSequence(d, m_menuItems);
+}
+
+void QWindowsMenuBar::formatDebug(QDebug &d) const
+{
+ d << static_cast<const void *>(this) << ' ';
+ formatTextSequence(d, m_menus);
+}
+
+QDebug operator<<(QDebug d, const QPlatformMenu *m)
+{
+ QDebugStateSaver saver(d);
+ d.nospace();
+ d.noquote();
+ if (m) {
+ d << m->metaObject()->className() << '(';
+ static_cast<const QWindowsMenu *>(m)->formatDebug(d);
+ d << ')';
+ } else {
+ d << "QPlatformMenu(0)";
+ }
+ return d;
+}
+
+QDebug operator<<(QDebug d, const QPlatformMenuBar *mb)
+{
+ QDebugStateSaver saver(d);
+ d.nospace();
+ d.noquote();
+ d << "QPlatformMenuBar(";
+ if (mb)
+ static_cast<const QWindowsMenuBar *>(mb)->formatDebug(d);
+ else
+ d << '0';
+ d << ')';
+ return d;
+}
+
+#endif // !QT_NO_DEBUG_STREAM
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/windows/qwindowsmenu.h b/src/plugins/platforms/windows/qwindowsmenu.h
new file mode 100644
index 0000000000..d51a29676e
--- /dev/null
+++ b/src/plugins/platforms/windows/qwindowsmenu.h
@@ -0,0 +1,243 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSMENU_H
+#define QWINDOWSMENU_H
+
+#include "qtwindowsglobal.h"
+
+#include <qpa/qplatformmenu.h>
+
+#include <QtCore/QVector>
+#include <QtCore/QPair>
+
+QT_BEGIN_NAMESPACE
+
+class QDebug;
+
+class QWindowsMenu;
+class QWindowsMenuBar;
+class QWindowsWindow;
+
+class QWindowsMenuItem : public QPlatformMenuItem
+{
+ Q_OBJECT
+public:
+ explicit QWindowsMenuItem(QWindowsMenu *parentMenu = nullptr);
+ ~QWindowsMenuItem();
+
+ void setText(const QString &text) override;
+ void setIcon(const QIcon &icon) override;
+ void setMenu(QPlatformMenu *menu) override;
+ void setVisible(bool isVisible) override;
+ void setIsSeparator(bool isSeparator) override;
+ void setFont(const QFont &) override {}
+ void setRole(MenuRole) override {}
+ void setCheckable(bool checkable) override;
+ void setChecked(bool isChecked) override;
+#ifndef QT_NO_SHORTCUT
+ void setShortcut(const QKeySequence& shortcut) override;
+#endif
+ void setEnabled(bool enabled) override;
+ void setIconSize(int size) override;
+
+ const QWindowsMenu *parentMenu() const { return m_parentMenu; }
+ QWindowsMenu *parentMenu() { return m_parentMenu; }
+ HMENU parentMenuHandle() const;
+ QWindowsMenu *subMenu() const { return m_subMenu; }
+ UINT_PTR id() const { return m_id; }
+ void setId(uint id) { m_id = id; }
+ UINT state() const;
+ QString text() const { return m_text; }
+ QString nativeText() const;
+ bool isVisible() const { return m_visible; }
+
+ void insertIntoMenu(QWindowsMenu *menuItem, bool append, int index);
+ bool removeFromMenu();
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &d) const;
+#endif
+
+private:
+ void updateBitmap();
+ void freeBitmap();
+ void updateText();
+ void insertIntoMenuHelper(QWindowsMenu *menu, bool append, int index);
+
+ QWindowsMenu *m_parentMenu = nullptr;
+ QWindowsMenu *m_subMenu = nullptr;
+ UINT_PTR m_id; // Windows Id sent as wParam with WM_COMMAND or submenu handle.
+ QString m_text;
+ QIcon m_icon;
+ HBITMAP m_hbitmap = nullptr;
+ int m_iconSize = 0;
+ bool m_separator = false;
+ bool m_visible = true;
+ bool m_checkable = false;
+ bool m_checked = false;
+ bool m_enabled = true;
+ QKeySequence m_shortcut;
+};
+
+class QWindowsMenu : public QPlatformMenu
+{
+ Q_OBJECT
+public:
+ typedef QVector<QWindowsMenuItem *> MenuItems;
+
+ QWindowsMenu();
+ ~QWindowsMenu();
+
+ void insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before) override;
+ void removeMenuItem(QPlatformMenuItem *menuItem) override;
+ void syncMenuItem(QPlatformMenuItem *) override {}
+ void syncSeparatorsCollapsible(bool) override {}
+
+ void setText(const QString &text) override;
+ void setIcon(const QIcon &icon) override;
+ void setEnabled(bool enabled) override;
+ bool isEnabled() const override { return m_enabled; }
+ void setVisible(bool visible) override;
+
+ QPlatformMenuItem *menuItemAt(int position) const override;
+ QPlatformMenuItem *menuItemForTag(quintptr tag) const override;
+
+ QPlatformMenuItem *createMenuItem() const override;
+ QPlatformMenu *createSubMenu() const override;
+
+ HMENU menuHandle() const { return m_hMenu; }
+ UINT_PTR id() const { return reinterpret_cast<UINT_PTR>(m_hMenu); }
+ QString text() const { return m_text; }
+ const MenuItems &menuItems() const { return m_menuItems; }
+ QWindowsMenuItem *itemForSubMenu(const QWindowsMenu *subMenu) const;
+
+ const QWindowsMenuBar *parentMenuBar() const { return m_parentMenuBar; }
+ HMENU parentMenuBarHandle() const;
+ const QWindowsMenu *parentMenu() const { return m_parentMenu; }
+ void setAsItemSubMenu(QWindowsMenuItem *item);
+ void notifyRemoved(QWindowsMenuItem *item) { m_menuItems.removeOne(item); }
+ HMENU parentMenuHandle() const;
+ HMENU parentHandle() const;
+ bool isVisible() const { return m_visible; }
+ void insertIntoMenuBar(QWindowsMenuBar *bar, bool append, int index);
+ bool removeFromParent();
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &d) const;
+#endif
+
+protected:
+ explicit QWindowsMenu(QWindowsMenu *parentMenu, HMENU menu);
+
+private:
+ QWindowsMenuBar *m_parentMenuBar = nullptr;
+ QWindowsMenu *m_parentMenu = nullptr;
+ MenuItems m_menuItems;
+ HMENU m_hMenu = nullptr;
+ QString m_text;
+ QIcon m_icon;
+ bool m_visible = true;
+ bool m_enabled = true;
+};
+
+class QWindowsPopupMenu : public QWindowsMenu
+{
+ Q_OBJECT
+public:
+ QWindowsPopupMenu();
+
+ static bool notifyTriggered(uint id);
+ static bool notifyAboutToShow(HMENU hmenu);
+
+ void showPopup(const QWindow *parentWindow, const QRect &targetRect, const QPlatformMenuItem *item) override;
+ void dismiss() override {}
+
+ bool trackPopupMenu(HWND windowHandle, int x, int y);
+};
+
+class QWindowsMenuBar : public QPlatformMenuBar
+{
+ Q_OBJECT
+public:
+ typedef QVector<QWindowsMenu *> Menus;
+
+ QWindowsMenuBar();
+ ~QWindowsMenuBar();
+
+ void insertMenu(QPlatformMenu *menu, QPlatformMenu *before) override;
+ void removeMenu(QPlatformMenu *menu) override;
+ void syncMenu(QPlatformMenu *) override {}
+ void handleReparent(QWindow *newParentWindow) override;
+
+ QPlatformMenu *menuForTag(quintptr tag) const override;
+ QPlatformMenu *createMenu() const override;
+
+ HMENU menuBarHandle() const { return m_hMenuBar; }
+ const Menus &menus() const { return m_menus; }
+ bool notifyTriggered(uint id);
+ bool notifyAboutToShow(HMENU hmenu);
+ void notifyRemoved(QWindowsMenu *menu) { m_menus.removeOne(menu); }
+ void redraw() const;
+
+ void install(QWindowsWindow *window);
+
+ static QWindowsMenuBar *menuBarOf(const QWindow *notYetCreatedWindow);
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &d) const;
+#endif
+
+private:
+ QWindowsWindow *platformWindow() const;
+ void removeFromWindow();
+
+ Menus m_menus;
+ HMENU m_hMenuBar = nullptr;
+};
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug d, const QPlatformMenuItem *);
+QDebug operator<<(QDebug d, const QPlatformMenu *);
+QDebug operator<<(QDebug d, const QPlatformMenuBar *);
+#endif // !QT_NO_DEBUG_STREAM
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWSMENU_H
diff --git a/src/plugins/platforms/windows/qwindowsmime.cpp b/src/plugins/platforms/windows/qwindowsmime.cpp
index 9c9382c44c..34e6041687 100644
--- a/src/plugins/platforms/windows/qwindowsmime.cpp
+++ b/src/plugins/platforms/windows/qwindowsmime.cpp
@@ -41,6 +41,7 @@
#include "qwindowscontext.h"
#include <QtGui/private/qdnd_p.h>
+#include <QtCore/QByteArrayMatcher>
#include <QtCore/QTextCodec>
#include <QtCore/QMap>
#include <QtCore/QUrl>
@@ -51,6 +52,7 @@
#include <QtGui/QImageWriter>
#include <shlobj.h>
+#include <algorithm>
QT_BEGIN_NAMESPACE
@@ -955,9 +957,11 @@ QVariant QWindowsMimeHtml::convertToMime(const QString &mime, IDataObject *pData
QVariant result;
if (canConvertToMime(mime, pDataObj)) {
QByteArray html = getData(CF_HTML, pDataObj);
+ static Q_RELAXED_CONSTEXPR auto startMatcher = qMakeStaticByteArrayMatcher("StartHTML:");
+ static Q_RELAXED_CONSTEXPR auto endMatcher = qMakeStaticByteArrayMatcher("EndHTML:");
qCDebug(lcQpaMime) << __FUNCTION__ << "raw:" << html;
- int start = html.indexOf("StartHTML:");
- int end = html.indexOf("EndHTML:");
+ int start = startMatcher.indexIn(html);
+ int end = endMatcher.indexIn(html);
if (start != -1) {
int startOffset = start + 10;
@@ -997,10 +1001,13 @@ bool QWindowsMimeHtml::convertFromMime(const FORMATETC &formatetc, const QMimeDa
"StartFragment:0000000000\r\n" // 56-81
"EndFragment:0000000000\r\n\r\n"; // 82-107
- if (data.indexOf("<!--StartFragment-->") == -1)
+ static Q_RELAXED_CONSTEXPR auto startFragmentMatcher = qMakeStaticByteArrayMatcher("<!--StartFragment-->");
+ static Q_RELAXED_CONSTEXPR auto endFragmentMatcher = qMakeStaticByteArrayMatcher("<!--EndFragment-->");
+
+ if (startFragmentMatcher.indexIn(data) == -1)
result += "<!--StartFragment-->";
result += data;
- if (data.indexOf("<!--EndFragment-->") == -1)
+ if (endFragmentMatcher.indexIn(data) == -1)
result += "<!--EndFragment-->";
// set the correct number for EndHTML
@@ -1008,9 +1015,9 @@ bool QWindowsMimeHtml::convertFromMime(const FORMATETC &formatetc, const QMimeDa
memcpy(reinterpret_cast<char *>(result.data() + 53 - pos.length()), pos.constData(), size_t(pos.length()));
// set correct numbers for StartFragment and EndFragment
- pos = QByteArray::number(result.indexOf("<!--StartFragment-->") + 20);
+ pos = QByteArray::number(startFragmentMatcher.indexIn(result) + 20);
memcpy(reinterpret_cast<char *>(result.data() + 79 - pos.length()), pos.constData(), size_t(pos.length()));
- pos = QByteArray::number(result.indexOf("<!--EndFragment-->"));
+ pos = QByteArray::number(endFragmentMatcher.indexIn(result));
memcpy(reinterpret_cast<char *>(result.data() + 103 - pos.length()), pos.constData(), size_t(pos.length()));
return setData(result, pmedium);
@@ -1258,15 +1265,16 @@ bool QBuiltInMimes::convertFromMime(const FORMATETC &formatetc, const QMimeData
QVector<FORMATETC> QBuiltInMimes::formatsForMime(const QString &mimeType, const QMimeData *mimeData) const
{
QVector<FORMATETC> formatetcs;
- if (!outFormats.keys(mimeType).isEmpty() && mimeData->formats().contains(mimeType))
- formatetcs += setCf(outFormats.key(mimeType));
+ const auto mit = std::find(outFormats.cbegin(), outFormats.cend(), mimeType);
+ if (mit != outFormats.cend() && mimeData->formats().contains(mimeType))
+ formatetcs += setCf(mit.key());
return formatetcs;
}
bool QBuiltInMimes::canConvertToMime(const QString &mimeType, IDataObject *pDataObj) const
{
- return (!inFormats.keys(mimeType).isEmpty())
- && canGetData(inFormats.key(mimeType), pDataObj);
+ const auto mit = std::find(inFormats.cbegin(), inFormats.cend(), mimeType);
+ return mit != inFormats.cend() && canGetData(mit.key(), pDataObj);
}
QVariant QBuiltInMimes::convertToMime(const QString &mimeType, IDataObject *pDataObj, QVariant::Type preferredType) const
@@ -1309,7 +1317,7 @@ public:
QString mimeForFormat(const FORMATETC &formatetc) const override;
private:
- QMap<int, QString> formats;
+ mutable QMap<int, QString> formats;
static QStringList ianaTypes;
static QStringList excludeList;
};
@@ -1374,15 +1382,13 @@ bool QLastResortMimes::convertFromMime(const FORMATETC &formatetc, const QMimeDa
QVector<FORMATETC> QLastResortMimes::formatsForMime(const QString &mimeType, const QMimeData * /*mimeData*/) const
{
QVector<FORMATETC> formatetcs;
- if (!formats.keys(mimeType).isEmpty()) {
- formatetcs += setCf(formats.key(mimeType));
- } else if (!excludeList.contains(mimeType, Qt::CaseInsensitive)){
- // register any other available formats
- int cf = QWindowsMime::registerMimeType(mimeType);
- QLastResortMimes *that = const_cast<QLastResortMimes *>(this);
- that->formats.insert(cf, mimeType);
- formatetcs += setCf(cf);
- }
+ auto mit = std::find(formats.begin(), formats.end(), mimeType);
+ // register any other available formats
+ if (mit == formats.end() && !excludeList.contains(mimeType, Qt::CaseInsensitive))
+ mit = formats.insert(QWindowsMime::registerMimeType(mimeType), mimeType);
+ if (mit != formats.end())
+ formatetcs += setCf(mit.key());
+
if (!formatetcs.isEmpty())
qCDebug(lcQpaMime) << __FUNCTION__ << mimeType << formatetcs;
return formatetcs;
@@ -1420,14 +1426,11 @@ bool QLastResortMimes::canConvertToMime(const QString &mimeType, IDataObject *pD
QString clipFormat = customMimeType(mimeType);
const UINT cf = RegisterClipboardFormat(reinterpret_cast<const wchar_t *> (clipFormat.utf16()));
return canGetData(int(cf), pDataObj);
- } else if (formats.keys(mimeType).isEmpty()) {
- // if it is not in there then register it and see if we can get it
- int cf = QWindowsMime::registerMimeType(mimeType);
- return canGetData(cf, pDataObj);
- } else {
- return canGetData(formats.key(mimeType), pDataObj);
}
- return false;
+ // if it is not in there then register it and see if we can get it
+ const auto mit = std::find(formats.cbegin(), formats.cend(), mimeType);
+ const int cf = mit != formats.cend() ? mit.key() : QWindowsMime::registerMimeType(mimeType);
+ return canGetData(cf, pDataObj);
}
QVariant QLastResortMimes::convertToMime(const QString &mimeType, IDataObject *pDataObj, QVariant::Type preferredType) const
@@ -1441,11 +1444,10 @@ QVariant QLastResortMimes::convertToMime(const QString &mimeType, IDataObject *p
QString clipFormat = customMimeType(mimeType, &lindex);
const UINT cf = RegisterClipboardFormat(reinterpret_cast<const wchar_t *> (clipFormat.utf16()));
data = getData(int(cf), pDataObj, lindex);
- } else if (formats.keys(mimeType).isEmpty()) {
- int cf = QWindowsMime::registerMimeType(mimeType);
- data = getData(cf, pDataObj);
} else {
- data = getData(formats.key(mimeType), pDataObj);
+ const auto mit = std::find(formats.cbegin(), formats.cend(), mimeType);
+ const int cf = mit != formats.cend() ? mit.key() : QWindowsMime::registerMimeType(mimeType);
+ data = getData(cf, pDataObj);
}
if (!data.isEmpty())
val = data; // it should be enough to return the data and let QMimeData do the rest.
diff --git a/src/plugins/platforms/windows/qwindowsmousehandler.cpp b/src/plugins/platforms/windows/qwindowsmousehandler.cpp
index 0cabb66bca..814291c54a 100644
--- a/src/plugins/platforms/windows/qwindowsmousehandler.cpp
+++ b/src/plugins/platforms/windows/qwindowsmousehandler.cpp
@@ -56,37 +56,6 @@
#include <windowsx.h>
-/* Touch is supported from Windows 7 onwards and data structures
- * are present in the Windows SDK's, but not in older MSVC Express
- * versions. */
-
-#if defined(Q_CC_MINGW) || !defined(TOUCHEVENTF_MOVE)
-
-typedef struct tagTOUCHINPUT {
- LONG x;
- LONG y;
- HANDLE hSource;
- DWORD dwID;
- DWORD dwFlags;
- DWORD dwMask;
- DWORD dwTime;
- ULONG_PTR dwExtraInfo;
- DWORD cxContact;
- DWORD cyContact;
-} TOUCHINPUT, *PTOUCHINPUT;
-typedef TOUCHINPUT const * PCTOUCHINPUT;
-
-# define TOUCHEVENTF_MOVE 0x0001
-# define TOUCHEVENTF_DOWN 0x0002
-# define TOUCHEVENTF_UP 0x0004
-# define TOUCHEVENTF_INRANGE 0x0008
-# define TOUCHEVENTF_PRIMARY 0x0010
-# define TOUCHEVENTF_NOCOALESCE 0x0020
-# define TOUCHEVENTF_PALM 0x0080
-# define TOUCHINPUTMASKF_CONTACTAREA 0x0004
-# define TOUCHINPUTMASKF_EXTRAINFO 0x0002
-#endif // if defined(Q_CC_MINGW) || !defined(TOUCHEVENTF_MOVE)
-
QT_BEGIN_NAMESPACE
static inline void compressMouseMove(MSG *msg)
@@ -154,8 +123,6 @@ static inline QTouchDevice *createTouchDevice()
QT_NID_INTEGRATED_TOUCH = 0x1, QT_NID_EXTERNAL_TOUCH = 0x02,
QT_NID_MULTI_INPUT = 0x40, QT_NID_READY = 0x80 };
- if (QSysInfo::windowsVersion() < QSysInfo::WV_WINDOWS7)
- return 0;
const int digitizers = GetSystemMetrics(QT_SM_DIGITIZER);
if (!(digitizers & (QT_NID_INTEGRATED_TOUCH | QT_NID_EXTERNAL_TOUCH)))
return 0;
@@ -523,9 +490,8 @@ bool QWindowsMouseHandler::translateTouchEvent(QWindow *window, HWND,
touchPoints.reserve(winTouchPointCount);
Qt::TouchPointStates allStates = 0;
- QWindowsContext::user32dll.getTouchInputInfo(reinterpret_cast<HANDLE>(msg.lParam),
- UINT(msg.wParam),
- winTouchInputs.data(), sizeof(TOUCHINPUT));
+ GetTouchInputInfo(reinterpret_cast<HTOUCHINPUT>(msg.lParam),
+ UINT(msg.wParam), winTouchInputs.data(), sizeof(TOUCHINPUT));
for (int i = 0; i < winTouchPointCount; ++i) {
const TOUCHINPUT &winTouchInput = winTouchInputs[i];
int id = m_touchInputIDToTouchPointID.value(winTouchInput.dwID, -1);
@@ -566,7 +532,7 @@ bool QWindowsMouseHandler::translateTouchEvent(QWindow *window, HWND,
touchPoints.append(touchPoint);
}
- QWindowsContext::user32dll.closeTouchInputHandle(reinterpret_cast<HANDLE>(msg.lParam));
+ CloseTouchInputHandle(reinterpret_cast<HTOUCHINPUT>(msg.lParam));
// all touch points released, forget the ids we've seen, they may not be reused
if (allStates == Qt::TouchPointReleased)
@@ -574,7 +540,8 @@ bool QWindowsMouseHandler::translateTouchEvent(QWindow *window, HWND,
QWindowSystemInterface::handleTouchEvent(window,
m_touchDevice,
- touchPoints);
+ touchPoints,
+ QWindowsKeyMapper::queryKeyboardModifiers());
return true;
}
diff --git a/src/plugins/platforms/windows/qwindowsnativeinterface.cpp b/src/plugins/platforms/windows/qwindowsnativeinterface.cpp
index d750eef19d..324b00144e 100644
--- a/src/plugins/platforms/windows/qwindowsnativeinterface.cpp
+++ b/src/plugins/platforms/windows/qwindowsnativeinterface.cpp
@@ -64,7 +64,8 @@ enum ResourceType {
HandleType,
GlHandleType,
GetDCType,
- ReleaseDCType
+ ReleaseDCType,
+ VkSurface
};
static int resourceType(const QByteArray &key)
@@ -77,7 +78,8 @@ static int resourceType(const QByteArray &key)
"handle",
"glhandle",
"getdc",
- "releasedc"
+ "releasedc",
+ "vkSurface"
};
const char ** const end = names + sizeof(names) / sizeof(names[0]);
const char **result = std::find(names, end, key);
@@ -112,6 +114,12 @@ void *QWindowsNativeInterface::nativeResourceForWindow(const QByteArray &resourc
case QWindow::OpenGLSurface:
case QWindow::OpenVGSurface:
break;
+ case QWindow::VulkanSurface:
+#if QT_CONFIG(vulkan)
+ if (type == VkSurface)
+ return bw->surface(nullptr, nullptr); // returns the address of the VkSurfaceKHR, not the value, as expected
+#endif
+ break;
}
qWarning("%s: Invalid key '%s' requested.", __FUNCTION__, resource.constData());
return 0;
@@ -126,7 +134,7 @@ void *QWindowsNativeInterface::nativeResourceForCursor(const QByteArray &resourc
return static_cast<const QWindowsCursor *>(pCursor)->hCursor(cursor);
}
}
- return Q_NULLPTR;
+ return nullptr;
}
#endif // !QT_NO_CURSOR
@@ -272,7 +280,7 @@ QFunctionPointer QWindowsNativeInterface::platformFunction(const QByteArray &fun
return QFunctionPointer(QWindowsWindow::setHasBorderInFullScreenStatic);
else if (function == QWindowsWindowFunctions::isTabletModeIdentifier())
return QFunctionPointer(QWindowsNativeInterface::isTabletMode);
- return Q_NULLPTR;
+ return nullptr;
}
QVariant QWindowsNativeInterface::gpu() const
diff --git a/src/plugins/platforms/windows/qwindowsole.cpp b/src/plugins/platforms/windows/qwindowsole.cpp
index 9b71061aa5..0ceb0d82fa 100644
--- a/src/plugins/platforms/windows/qwindowsole.cpp
+++ b/src/plugins/platforms/windows/qwindowsole.cpp
@@ -99,39 +99,6 @@ DWORD QWindowsOleDataObject::reportedPerformedEffect() const
return performedEffect;
}
-//---------------------------------------------------------------------
-// IUnknown Methods
-//---------------------------------------------------------------------
-
-STDMETHODIMP
-QWindowsOleDataObject::QueryInterface(REFIID iid, void FAR* FAR* ppv)
-{
- if (iid == IID_IUnknown || iid == IID_IDataObject) {
- *ppv = this;
- AddRef();
- return NOERROR;
- }
- *ppv = NULL;
- return ResultFromScode(E_NOINTERFACE);
-}
-
-STDMETHODIMP_(ULONG)
-QWindowsOleDataObject::AddRef(void)
-{
- return ++m_refs;
-}
-
-STDMETHODIMP_(ULONG)
-QWindowsOleDataObject::Release(void)
-{
- if (--m_refs == 0) {
- releaseQt();
- delete this;
- return 0;
- }
- return m_refs;
-}
-
STDMETHODIMP
QWindowsOleDataObject::GetData(LPFORMATETC pformatetc, LPSTGMEDIUM pmedium)
{
@@ -323,35 +290,6 @@ bool QWindowsOleEnumFmtEtc::isNull() const
return m_isNull;
}
-// IUnknown methods
-STDMETHODIMP
-QWindowsOleEnumFmtEtc::QueryInterface(REFIID riid, void FAR* FAR* ppvObj)
-{
- if (riid == IID_IUnknown || riid == IID_IEnumFORMATETC) {
- *ppvObj = this;
- AddRef();
- return NOERROR;
- }
- *ppvObj = NULL;
- return ResultFromScode(E_NOINTERFACE);
-}
-
-STDMETHODIMP_(ULONG)
-QWindowsOleEnumFmtEtc::AddRef(void)
-{
- return ++m_dwRefs;
-}
-
-STDMETHODIMP_(ULONG)
-QWindowsOleEnumFmtEtc::Release(void)
-{
- if (--m_dwRefs == 0) {
- delete this;
- return 0;
- }
- return m_dwRefs;
-}
-
// IEnumFORMATETC methods
STDMETHODIMP
QWindowsOleEnumFmtEtc::Next(ULONG celt, LPFORMATETC rgelt, ULONG FAR* pceltFetched)
diff --git a/src/plugins/platforms/windows/qwindowsole.h b/src/plugins/platforms/windows/qwindowsole.h
index 643011272b..fc58858f2c 100644
--- a/src/plugins/platforms/windows/qwindowsole.h
+++ b/src/plugins/platforms/windows/qwindowsole.h
@@ -40,6 +40,7 @@
#ifndef QWINDOWSOLE_H
#define QWINDOWSOLE_H
+#include "qwindowscombase.h"
#include <QtCore/qt_windows.h>
#include <QtCore/QMap>
@@ -53,7 +54,7 @@ QT_BEGIN_NAMESPACE
class QMimeData;
class QWindow;
-class QWindowsOleDataObject : public IDataObject
+class QWindowsOleDataObject : public QWindowsComBase<IDataObject>
{
public:
explicit QWindowsOleDataObject(QMimeData *mimeData);
@@ -63,11 +64,6 @@ public:
QMimeData *mimeData() const;
DWORD reportedPerformedEffect() const;
- // IUnknown methods
- STDMETHOD(QueryInterface)(REFIID riid, void FAR* FAR* ppvObj);
- STDMETHOD_(ULONG,AddRef)(void);
- STDMETHOD_(ULONG,Release)(void);
-
// IDataObject methods
STDMETHOD(GetData)(LPFORMATETC pformatetcIn, LPSTGMEDIUM pmedium);
STDMETHOD(GetDataHere)(LPFORMATETC pformatetc, LPSTGMEDIUM pmedium);
@@ -82,13 +78,12 @@ public:
STDMETHOD(EnumDAdvise)(LPENUMSTATDATA FAR* ppenumAdvise);
private:
- ULONG m_refs = 1;
QPointer<QMimeData> data;
const int CF_PERFORMEDDROPEFFECT;
DWORD performedEffect = DROPEFFECT_NONE;
};
-class QWindowsOleEnumFmtEtc : public IEnumFORMATETC
+class QWindowsOleEnumFmtEtc : public QWindowsComBase<IEnumFORMATETC>
{
public:
explicit QWindowsOleEnumFmtEtc(const QVector<FORMATETC> &fmtetcs);
@@ -97,11 +92,6 @@ public:
bool isNull() const;
- // IUnknown methods
- STDMETHOD(QueryInterface)(REFIID riid, void FAR* FAR* ppvObj);
- STDMETHOD_(ULONG,AddRef)(void);
- STDMETHOD_(ULONG,Release)(void);
-
// IEnumFORMATETC methods
STDMETHOD(Next)(ULONG celt, LPFORMATETC rgelt, ULONG FAR* pceltFetched);
STDMETHOD(Skip)(ULONG celt);
@@ -111,7 +101,6 @@ public:
private:
bool copyFormatEtc(LPFORMATETC dest, const FORMATETC *src) const;
- ULONG m_dwRefs = 1;
ULONG m_nIndex = 0;
QVector<LPFORMATETC> m_lpfmtetcs;
bool m_isNull = false;
diff --git a/src/plugins/platforms/windows/qwindowsscreen.cpp b/src/plugins/platforms/windows/qwindowsscreen.cpp
index cfddb3cc71..c0781df973 100644
--- a/src/plugins/platforms/windows/qwindowsscreen.cpp
+++ b/src/plugins/platforms/windows/qwindowsscreen.cpp
@@ -563,7 +563,7 @@ const QWindowsScreen *QWindowsScreenManager::screenAtDp(const QPoint &p) const
if (scr->geometry().contains(p))
return scr;
}
- return Q_NULLPTR;
+ return nullptr;
}
const QWindowsScreen *QWindowsScreenManager::screenForHwnd(HWND hwnd) const
diff --git a/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp b/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp
new file mode 100644
index 0000000000..901d132ea5
--- /dev/null
+++ b/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp
@@ -0,0 +1,443 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#if defined(WINVER) && WINVER < 0x0601
+# undef WINVER
+#endif
+#if !defined(WINVER)
+# define WINVER 0x0601 // required for NOTIFYICONDATA_V2_SIZE, ChangeWindowMessageFilterEx() (MinGW 5.3)
+#endif
+
+#if defined(NTDDI_VERSION) && NTDDI_VERSION < 0x06010000
+# undef NTDDI_VERSION
+#endif
+#if !defined(NTDDI_VERSION)
+# define NTDDI_VERSION 0x06010000 // required for Shell_NotifyIconGetRect (MinGW 5.3)
+#endif
+
+#include "qwindowssystemtrayicon.h"
+#include "qwindowscontext.h"
+#include "qwindowstheme.h"
+#include "qwindowsmenu.h"
+#include "qwindowsscreen.h"
+
+#include <QtGui/qpixmap.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qrect.h>
+#include <QtCore/qvector.h>
+#include <QtCore/qsettings.h>
+
+#include <qt_windows.h>
+#include <commctrl.h>
+#include <shellapi.h>
+#include <shlobj.h>
+#include <windowsx.h>
+
+QT_BEGIN_NAMESPACE
+
+static const UINT q_uNOTIFYICONID = 0;
+
+static uint MYWM_TASKBARCREATED = 0;
+#define MYWM_NOTIFYICON (WM_APP+101)
+
+Q_GUI_EXPORT HICON qt_pixmapToWinHICON(const QPixmap &);
+
+// Copy QString data to a limited wchar_t array including \0.
+static inline void qStringToLimitedWCharArray(QString in, wchar_t *target, int maxLength)
+{
+ const int length = qMin(maxLength - 1, in.size());
+ if (length < in.size())
+ in.truncate(length);
+ in.toWCharArray(target);
+ target[length] = wchar_t(0);
+}
+
+static inline void initNotifyIconData(NOTIFYICONDATA &tnd)
+{
+ memset(&tnd, 0, sizeof(NOTIFYICONDATA));
+ tnd.cbSize = sizeof(NOTIFYICONDATA);
+ tnd.uVersion = NOTIFYICON_VERSION_4;
+}
+
+static void setIconContents(NOTIFYICONDATA &tnd, const QString &tip, HICON hIcon)
+{
+ tnd.uFlags |= NIF_MESSAGE | NIF_ICON | NIF_TIP;
+ tnd.uCallbackMessage = MYWM_NOTIFYICON;
+ tnd.hIcon = hIcon;
+ qStringToLimitedWCharArray(tip, tnd.szTip, sizeof(tnd.szTip) / sizeof(wchar_t));
+}
+
+// Match the HWND of the dummy window to the instances
+struct QWindowsHwndSystemTrayIconEntry
+{
+ HWND hwnd;
+ QWindowsSystemTrayIcon *trayIcon;
+};
+
+typedef QVector<QWindowsHwndSystemTrayIconEntry> HwndTrayIconEntries;
+
+Q_GLOBAL_STATIC(HwndTrayIconEntries, hwndTrayIconEntries)
+
+static int indexOfHwnd(HWND hwnd)
+{
+ const HwndTrayIconEntries *entries = hwndTrayIconEntries();
+ for (int i = 0, size = entries->size(); i < size; ++i) {
+ if (entries->at(i).hwnd == hwnd)
+ return i;
+ }
+ return -1;
+}
+
+extern "C" LRESULT QT_WIN_CALLBACK qWindowsTrayIconWndProc(HWND hwnd, UINT message,
+ WPARAM wParam, LPARAM lParam)
+{
+ if (message == MYWM_TASKBARCREATED || message == MYWM_NOTIFYICON
+ || message == WM_INITMENU || message == WM_INITMENUPOPUP
+ || message == WM_COMMAND) {
+ const int index = indexOfHwnd(hwnd);
+ if (index >= 0) {
+ MSG msg;
+ msg.hwnd = hwnd; // re-create MSG structure
+ msg.message = message; // time and pt fields ignored
+ msg.wParam = wParam;
+ msg.lParam = lParam;
+ msg.pt.x = GET_X_LPARAM(lParam);
+ msg.pt.y = GET_Y_LPARAM(lParam);
+ long result = 0;
+ if (hwndTrayIconEntries()->at(index).trayIcon->winEvent(msg, &result))
+ return result;
+ }
+ }
+ return DefWindowProc(hwnd, message, wParam, lParam);
+}
+
+// Note: Message windows (HWND_MESSAGE) are not sufficient, they
+// will not receive the "TaskbarCreated" message.
+static inline HWND createTrayIconMessageWindow()
+{
+ QWindowsContext *ctx = QWindowsContext::instance();
+ if (!ctx)
+ return 0;
+ // Register window class in the platform plugin.
+ const QString className =
+ ctx->registerWindowClass(QStringLiteral("QTrayIconMessageWindowClass"),
+ qWindowsTrayIconWndProc);
+ const wchar_t windowName[] = L"QTrayIconMessageWindow";
+ return CreateWindowEx(0, reinterpret_cast<const wchar_t *>(className.utf16()),
+ windowName, WS_OVERLAPPED,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ NULL, NULL, (HINSTANCE)GetModuleHandle(0), NULL);
+}
+
+/*!
+ \class QWindowsSystemTrayIcon
+ \brief Windows native system tray icon
+
+ \internal
+ \ingroup qt-lighthouse-win
+*/
+
+QWindowsSystemTrayIcon::QWindowsSystemTrayIcon()
+{
+ // For restoring the tray icon after explorer crashes
+ if (!MYWM_TASKBARCREATED)
+ MYWM_TASKBARCREATED = RegisterWindowMessage(L"TaskbarCreated");
+ // Allow the WM_TASKBARCREATED message through the UIPI filter
+ ChangeWindowMessageFilterEx(m_hwnd, MYWM_TASKBARCREATED, MSGFLT_ALLOW, 0);
+ qCDebug(lcQpaTrayIcon) << __FUNCTION__ << this << "MYWM_TASKBARCREATED=" << MYWM_TASKBARCREATED;
+}
+
+QWindowsSystemTrayIcon::~QWindowsSystemTrayIcon()
+{
+ qCDebug(lcQpaTrayIcon) << __FUNCTION__ << this;
+ ensureCleanup();
+}
+
+void QWindowsSystemTrayIcon::init()
+{
+ qCDebug(lcQpaTrayIcon) << __FUNCTION__ << this;
+ ensureInstalled();
+}
+
+void QWindowsSystemTrayIcon::cleanup()
+{
+ qCDebug(lcQpaTrayIcon) << __FUNCTION__ << this;
+ ensureCleanup();
+}
+
+void QWindowsSystemTrayIcon::updateIcon(const QIcon &icon)
+{
+ qCDebug(lcQpaTrayIcon) << __FUNCTION__ << '(' << icon << ')' << this;
+ if (icon.cacheKey() == m_icon.cacheKey())
+ return;
+ const HICON hIconToDestroy = createIcon(icon);
+ if (ensureInstalled())
+ sendTrayMessage(NIM_MODIFY);
+ if (hIconToDestroy)
+ DestroyIcon(hIconToDestroy);
+}
+
+void QWindowsSystemTrayIcon::updateToolTip(const QString &tooltip)
+{
+ qCDebug(lcQpaTrayIcon) << __FUNCTION__ << '(' << tooltip << ')' << this;
+ if (m_toolTip == tooltip)
+ return;
+ m_toolTip = tooltip;
+ if (isInstalled())
+ sendTrayMessage(NIM_MODIFY);
+}
+
+QRect QWindowsSystemTrayIcon::geometry() const
+{
+ NOTIFYICONIDENTIFIER nid;
+ memset(&nid, 0, sizeof(nid));
+ nid.cbSize = sizeof(nid);
+ nid.hWnd = m_hwnd;
+ nid.uID = q_uNOTIFYICONID;
+ RECT rect;
+ const QRect result = SUCCEEDED(Shell_NotifyIconGetRect(&nid, &rect))
+ ? QRect(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top)
+ : QRect();
+ qCDebug(lcQpaTrayIcon) << __FUNCTION__ << this << "returns" << result;
+ return result;
+}
+
+void QWindowsSystemTrayIcon::showMessage(const QString &title, const QString &messageIn,
+ const QIcon &icon,
+ QPlatformSystemTrayIcon::MessageIcon iconType,
+ int msecsIn)
+{
+ qCDebug(lcQpaTrayIcon) << __FUNCTION__ << '(' << title << messageIn << icon
+ << iconType << msecsIn << ')' << this;
+ if (!supportsMessages())
+ return;
+ // For empty messages, ensures that they show when only title is set
+ QString message = messageIn;
+ if (message.isEmpty() && !title.isEmpty())
+ message.append(QLatin1Char(' '));
+
+ NOTIFYICONDATA tnd;
+ initNotifyIconData(tnd);
+ qStringToLimitedWCharArray(message, tnd.szInfo, 256);
+ qStringToLimitedWCharArray(title, tnd.szInfoTitle, 64);
+
+ tnd.uID = q_uNOTIFYICONID;
+ tnd.dwInfoFlags = NIIF_USER;
+
+ QSize size(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
+ const QSize largeIcon(GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON));
+ const QSize more = icon.actualSize(largeIcon);
+ if (more.height() > (largeIcon.height() * 3/4) || more.width() > (largeIcon.width() * 3/4)) {
+ tnd.dwInfoFlags |= NIIF_LARGE_ICON;
+ size = largeIcon;
+ }
+ QPixmap pm = icon.pixmap(size);
+ if (pm.isNull()) {
+ tnd.dwInfoFlags = NIIF_INFO;
+ } else {
+ if (pm.size() != size) {
+ qWarning("QSystemTrayIcon::showMessage: Wrong icon size (%dx%d), please add standard one: %dx%d",
+ pm.size().width(), pm.size().height(), size.width(), size.height());
+ pm = pm.scaled(size, Qt::IgnoreAspectRatio);
+ }
+ tnd.hBalloonIcon = qt_pixmapToWinHICON(pm);
+ }
+ tnd.hWnd = m_hwnd;
+ tnd.uTimeout = msecsIn <= 0 ? UINT(10000) : UINT(msecsIn); // 10s default
+ tnd.uFlags = NIF_INFO | NIF_SHOWTIP;
+
+ Shell_NotifyIcon(NIM_MODIFY, &tnd);
+}
+
+bool QWindowsSystemTrayIcon::supportsMessages() const
+{
+ // The key does typically not exist on Windows 10, default to true.
+ return QWindowsContext::readAdvancedExplorerSettings(L"EnableBalloonTips", 1) != 0;
+}
+
+QPlatformMenu *QWindowsSystemTrayIcon::createMenu() const
+{
+ if (QWindowsTheme::useNativeMenus() && m_menu.isNull())
+ m_menu = new QWindowsPopupMenu;
+ qCDebug(lcQpaTrayIcon) << __FUNCTION__ << this << "returns" << m_menu.data();
+ return m_menu.data();
+}
+
+// Delay-install until an Icon exists
+bool QWindowsSystemTrayIcon::ensureInstalled()
+{
+ if (isInstalled())
+ return true;
+ if (m_hIcon == nullptr)
+ return false;
+ m_hwnd = createTrayIconMessageWindow();
+ if (Q_UNLIKELY(m_hwnd == nullptr))
+ return false;
+ QWindowsHwndSystemTrayIconEntry entry{m_hwnd, this};
+ hwndTrayIconEntries()->append(entry);
+ sendTrayMessage(NIM_ADD);
+ return true;
+}
+
+void QWindowsSystemTrayIcon::ensureCleanup()
+{
+ if (isInstalled()) {
+ const int index = indexOfHwnd(m_hwnd);
+ if (index >= 0)
+ hwndTrayIconEntries()->removeAt(index);
+ sendTrayMessage(NIM_DELETE);
+ DestroyWindow(m_hwnd);
+ m_hwnd = nullptr;
+ }
+ if (m_hIcon != nullptr)
+ DestroyIcon(m_hIcon);
+ m_hIcon = nullptr;
+ m_menu = nullptr; // externally owned
+ m_toolTip.clear();
+}
+
+bool QWindowsSystemTrayIcon::sendTrayMessage(DWORD msg)
+{
+ NOTIFYICONDATA tnd;
+ initNotifyIconData(tnd);
+ tnd.uID = q_uNOTIFYICONID;
+ tnd.hWnd = m_hwnd;
+ tnd.uFlags = NIF_SHOWTIP;
+ if (msg == NIM_ADD || msg == NIM_MODIFY)
+ setIconContents(tnd, m_toolTip, m_hIcon);
+ if (!Shell_NotifyIcon(msg, &tnd))
+ return false;
+ return msg != NIM_ADD || Shell_NotifyIcon(NIM_SETVERSION, &tnd);
+}
+
+// Return the old icon to be freed after modifying the tray icon.
+HICON QWindowsSystemTrayIcon::createIcon(const QIcon &icon)
+{
+ const HICON oldIcon = m_hIcon;
+ m_hIcon = nullptr;
+ if (icon.isNull())
+ return oldIcon;
+ const QSize requestedSize = QSize(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
+ const QSize size = icon.actualSize(requestedSize);
+ const QPixmap pm = icon.pixmap(size);
+ if (!pm.isNull())
+ m_hIcon = qt_pixmapToWinHICON(pm);
+ return oldIcon;
+}
+
+bool QWindowsSystemTrayIcon::winEvent(const MSG &message, long *result)
+{
+ *result = 0;
+ switch (message.message) {
+ case MYWM_NOTIFYICON: {
+ Q_ASSERT(q_uNOTIFYICONID == HIWORD(message.lParam));
+ const int trayMessage = LOWORD(message.lParam);
+ switch (trayMessage) {
+ case NIN_SELECT:
+ case NIN_KEYSELECT:
+ if (m_ignoreNextMouseRelease)
+ m_ignoreNextMouseRelease = false;
+ else
+ emit activated(Trigger);
+ break;
+ case WM_LBUTTONDBLCLK:
+ m_ignoreNextMouseRelease = true; // Since DBLCLICK Generates a second mouse
+ emit activated(DoubleClick); // release we must ignore it
+ break;
+ case WM_CONTEXTMENU: {
+ const QPoint globalPos = QPoint(GET_X_LPARAM(message.wParam), GET_Y_LPARAM(message.wParam));
+ const QPlatformScreen *screen = QWindowsContext::instance()->screenManager().screenAtDp(globalPos);
+ emit contextMenuRequested(globalPos, screen);
+ emit activated(Context);
+ if (m_menu)
+ m_menu->trackPopupMenu(message.hwnd, globalPos.x(), globalPos.y());
+ }
+ break;
+ case NIN_BALLOONUSERCLICK:
+ emit messageClicked();
+ break;
+ case WM_MBUTTONUP:
+ emit activated(MiddleClick);
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ case WM_INITMENU:
+ case WM_INITMENUPOPUP:
+ QWindowsPopupMenu::notifyAboutToShow(reinterpret_cast<HMENU>(message.wParam));
+ break;
+ case WM_COMMAND:
+ QWindowsPopupMenu::notifyTriggered(LOWORD(message.wParam));
+ break;
+ default:
+ if (message.message == MYWM_TASKBARCREATED) // self-registered message id (tray crashed)
+ sendTrayMessage(NIM_ADD);
+ break;
+ }
+ return false;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+
+void QWindowsSystemTrayIcon::formatDebug(QDebug &d) const
+{
+ d << static_cast<const void *>(this) << ", \"" << m_toolTip
+ << "\", hwnd=" << m_hwnd << ", m_hIcon=" << m_hIcon << ", menu="
+ << m_menu.data();
+}
+
+QDebug operator<<(QDebug d, const QWindowsSystemTrayIcon *t)
+{
+ QDebugStateSaver saver(d);
+ d.nospace();
+ d.noquote();
+ d << "QWindowsSystemTrayIcon(";
+ if (t)
+ t->formatDebug(d);
+ else
+ d << '0';
+ d << ')';
+ return d;
+}
+#endif // !QT_NO_DEBUG_STREAM
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/windows/qwindowssystemtrayicon.h b/src/plugins/platforms/windows/qwindowssystemtrayicon.h
new file mode 100644
index 0000000000..1f696180cd
--- /dev/null
+++ b/src/plugins/platforms/windows/qwindowssystemtrayicon.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSSYSTEMTRAYICON_H
+#define QWINDOWSSYSTEMTRAYICON_H
+
+#include <QtGui/qicon.h>
+#include <QtGui/qpa/qplatformsystemtrayicon.h>
+
+#include <QtCore/qpointer.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qt_windows.h>
+
+QT_BEGIN_NAMESPACE
+
+class QDebug;
+
+class QWindowsPopupMenu;
+
+class QWindowsSystemTrayIcon : public QPlatformSystemTrayIcon
+{
+public:
+ QWindowsSystemTrayIcon();
+ ~QWindowsSystemTrayIcon();
+
+ void init() override;
+ void cleanup() override;
+ void updateIcon(const QIcon &icon) override;
+ void updateToolTip(const QString &tooltip) override;
+ void updateMenu(QPlatformMenu *) override {}
+ QRect geometry() const override;
+ void showMessage(const QString &title, const QString &msg,
+ const QIcon &icon, MessageIcon iconType, int msecs) override;
+
+ bool isSystemTrayAvailable() const override { return true; }
+ bool supportsMessages() const override;
+
+ QPlatformMenu *createMenu() const override;
+
+ bool winEvent(const MSG &message, long *result);
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &d) const;
+#endif
+
+private:
+ bool isInstalled() const { return m_hwnd != nullptr; }
+ bool ensureInstalled();
+ void ensureCleanup();
+ bool sendTrayMessage(DWORD msg);
+ HICON createIcon(const QIcon &icon);
+
+ QIcon m_icon;
+ QString m_toolTip;
+ HWND m_hwnd = nullptr;
+ HICON m_hIcon = nullptr;
+ mutable QPointer<QWindowsPopupMenu> m_menu;
+ bool m_ignoreNextMouseRelease = false;
+};
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug d, const QWindowsSystemTrayIcon *);
+#endif // !QT_NO_DEBUG_STREAM
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWSSYSTEMTRAYICON_H
diff --git a/src/plugins/platforms/windows/qwindowstabletsupport.cpp b/src/plugins/platforms/windows/qwindowstabletsupport.cpp
index 5fe58fbfa5..5fdc664603 100644
--- a/src/plugins/platforms/windows/qwindowstabletsupport.cpp
+++ b/src/plugins/platforms/windows/qwindowstabletsupport.cpp
@@ -355,6 +355,8 @@ bool QWindowsTabletSupport::translateTabletProximityEvent(WPARAM /* wParam */, L
if (!LOWORD(lParam)) {
qCDebug(lcQpaTablet) << "leave proximity for device #" << m_currentDevice;
+ if (m_currentDevice < 0 || m_currentDevice >= m_devices.size()) // QTBUG-65120, spurious leave observed
+ return false;
if (totalPacks > 0) {
QWindowSystemInterface::handleTabletLeaveProximityEvent(proximityBuffer[0].pkTime,
m_devices.at(m_currentDevice).currentDevice,
@@ -473,13 +475,13 @@ bool QWindowsTabletSupport::translateTabletPacketEvent()
// Z = sin(altitude)
// X Tilt = arctan(X / Z)
// Y Tilt = arctan(Y / Z)
- const double radAzim = (packet.pkOrientation.orAzimuth / 10.0) * (M_PI / 180);
- const double tanAlt = std::tan((std::abs(packet.pkOrientation.orAltitude / 10.0)) * (M_PI / 180));
+ const double radAzim = qDegreesToRadians(packet.pkOrientation.orAzimuth / 10.0);
+ const double tanAlt = std::tan(qDegreesToRadians(std::abs(packet.pkOrientation.orAltitude / 10.0)));
- const double degX = std::atan(std::sin(radAzim) / tanAlt);
- const double degY = std::atan(std::cos(radAzim) / tanAlt);
- tiltX = int(degX * (180 / M_PI));
- tiltY = int(-degY * (180 / M_PI));
+ const double radX = std::atan(std::sin(radAzim) / tanAlt);
+ const double radY = std::atan(std::cos(radAzim) / tanAlt);
+ tiltX = int(qRadiansToDegrees(radX));
+ tiltY = int(qRadiansToDegrees(-radY));
rotation = 360.0 - (packet.pkOrientation.orTwist / 10.0);
if (rotation > 180.0)
rotation -= 360.0;
diff --git a/src/plugins/platforms/windows/qwindowstheme.cpp b/src/plugins/platforms/windows/qwindowstheme.cpp
index 7916211219..651c661d6b 100644
--- a/src/plugins/platforms/windows/qwindowstheme.cpp
+++ b/src/plugins/platforms/windows/qwindowstheme.cpp
@@ -38,15 +38,19 @@
****************************************************************************/
// SHSTOCKICONINFO is only available since Vista
-#if _WIN32_WINNT < 0x0600
+#if _WIN32_WINNT < 0x0601
# undef _WIN32_WINNT
-# define _WIN32_WINNT 0x0600
+# define _WIN32_WINNT 0x0601
#endif
#include "qwindowstheme.h"
+#include "qwindowsmenu.h"
#include "qwindowsdialoghelpers.h"
#include "qwindowscontext.h"
#include "qwindowsintegration.h"
+#if QT_CONFIG(systemtrayicon)
+# include "qwindowssystemtrayicon.h"
+#endif
#include "qt_windows.h"
#include <commctrl.h>
#include <objbase.h>
@@ -415,13 +419,7 @@ static inline QStringList iconThemeSearchPaths()
static inline QStringList styleNames()
{
- QStringList result;
- if (QSysInfo::WindowsVersion >= QSysInfo::WV_VISTA)
- result.append(QStringLiteral("WindowsVista"));
- if (QSysInfo::WindowsVersion >= QSysInfo::WV_XP)
- result.append(QStringLiteral("WindowsXP"));
- result.append(QStringLiteral("Windows"));
- return result;
+ return { QStringLiteral("WindowsVista"), QStringLiteral("Windows") };
}
static inline int uiEffects()
@@ -554,6 +552,13 @@ QPlatformDialogHelper *QWindowsTheme::createPlatformDialogHelper(DialogType type
return QWindowsDialogs::createHelper(type);
}
+#if QT_CONFIG(systemtrayicon)
+QPlatformSystemTrayIcon *QWindowsTheme::createPlatformSystemTrayIcon() const
+{
+ return new QWindowsSystemTrayIcon;
+}
+#endif
+
void QWindowsTheme::windowsThemeChanged(QWindow * window)
{
refresh();
@@ -922,4 +927,55 @@ QIcon QWindowsTheme::fileIcon(const QFileInfo &fileInfo, QPlatformTheme::IconOpt
return QIcon(new QWindowsFileIconEngine(fileInfo, iconOptions));
}
+static inline bool doUseNativeMenus()
+{
+ const unsigned options = QWindowsIntegration::instance()->options();
+ if ((options & QWindowsIntegration::NoNativeMenus) != 0)
+ return false;
+ if ((options & QWindowsIntegration::AlwaysUseNativeMenus) != 0)
+ return true;
+ // "Auto" mode: For non-widget or Quick Controls 2 applications
+ if (!QCoreApplication::instance()->inherits("QApplication"))
+ return true;
+ const QWindowList &topLevels = QGuiApplication::topLevelWindows();
+ for (const QWindow *t : topLevels) {
+ if (t->inherits("QQuickApplicationWindow"))
+ return true;
+ }
+ return false;
+}
+
+bool QWindowsTheme::useNativeMenus()
+{
+ static const bool result = doUseNativeMenus();
+ return result;
+}
+
+QPlatformMenuItem *QWindowsTheme::createPlatformMenuItem() const
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__;
+ return QWindowsTheme::useNativeMenus() ? new QWindowsMenuItem : nullptr;
+}
+
+QPlatformMenu *QWindowsTheme::createPlatformMenu() const
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__;
+ // We create a popup menu here, since it will likely be used as context
+ // menu. Submenus should be created the factory functions of
+ // QPlatformMenu/Bar. Note though that Quick Controls 1 will use this
+ // function for submenus as well, but this has been found to work.
+ return QWindowsTheme::useNativeMenus() ? new QWindowsPopupMenu : nullptr;
+}
+
+QPlatformMenuBar *QWindowsTheme::createPlatformMenuBar() const
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__;
+ return QWindowsTheme::useNativeMenus() ? new QWindowsMenuBar : nullptr;
+}
+
+void QWindowsTheme::showPlatformMenuBar()
+{
+ qCDebug(lcQpaMenus) << __FUNCTION__;
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/windows/qwindowstheme.h b/src/plugins/platforms/windows/qwindowstheme.h
index a3019ff6eb..237e8158fa 100644
--- a/src/plugins/platforms/windows/qwindowstheme.h
+++ b/src/plugins/platforms/windows/qwindowstheme.h
@@ -59,6 +59,9 @@ public:
bool usePlatformNativeDialog(DialogType type) const override;
QPlatformDialogHelper *createPlatformDialogHelper(DialogType type) const override;
+#if QT_CONFIG(systemtrayicon)
+ QPlatformSystemTrayIcon *createPlatformSystemTrayIcon() const override;
+#endif
QVariant themeHint(ThemeHint) const override;
const QPalette *palette(Palette type = SystemPalette) const override
{ return m_palettes[type]; }
@@ -74,6 +77,13 @@ public:
QList<QSize> availableFileIconSizes() const { return m_fileIconSizes; }
+ QPlatformMenuItem *createPlatformMenuItem() const override;
+ QPlatformMenu *createPlatformMenu() const override;
+ QPlatformMenuBar *createPlatformMenuBar() const override;
+ void showPlatformMenuBar() override;
+
+ static bool useNativeMenus();
+
static const char *name;
private:
diff --git a/src/plugins/platforms/windows/qwindowsvulkaninstance.cpp b/src/plugins/platforms/windows/qwindowsvulkaninstance.cpp
new file mode 100644
index 0000000000..d81ee8ba29
--- /dev/null
+++ b/src/plugins/platforms/windows/qwindowsvulkaninstance.cpp
@@ -0,0 +1,136 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwindowsvulkaninstance.h"
+
+QT_BEGIN_NAMESPACE
+
+QWindowsVulkanInstance::QWindowsVulkanInstance(QVulkanInstance *instance)
+ : m_instance(instance),
+ m_getPhysDevPresSupport(nullptr),
+ m_createSurface(nullptr),
+ m_destroySurface(nullptr)
+{
+ if (qEnvironmentVariableIsSet("QT_VULKAN_LIB"))
+ m_lib.setFileName(QString::fromUtf8(qgetenv("QT_VULKAN_LIB")));
+ else
+ m_lib.setFileName(QStringLiteral("vulkan-1"));
+
+ if (!m_lib.load()) {
+ qWarning("Failed to load %s: %s", qPrintable(m_lib.fileName()), qPrintable(m_lib.errorString()));
+ return;
+ }
+
+ init(&m_lib);
+}
+
+void QWindowsVulkanInstance::createOrAdoptInstance()
+{
+ initInstance(m_instance, QByteArrayList() << QByteArrayLiteral("VK_KHR_win32_surface"));
+
+ if (!m_vkInst)
+ return;
+
+ m_getPhysDevPresSupport = reinterpret_cast<PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR>(
+ m_vkGetInstanceProcAddr(m_vkInst, "vkGetPhysicalDeviceWin32PresentationSupportKHR"));
+ if (!m_getPhysDevPresSupport)
+ qWarning("Failed to find vkGetPhysicalDeviceWin32PresentationSupportKHR");
+}
+
+QWindowsVulkanInstance::~QWindowsVulkanInstance()
+{
+}
+
+bool QWindowsVulkanInstance::supportsPresent(VkPhysicalDevice physicalDevice,
+ uint32_t queueFamilyIndex,
+ QWindow *window)
+{
+ if (!m_getPhysDevPresSupport || !m_getPhysDevSurfaceSupport)
+ return true;
+
+ bool ok = m_getPhysDevPresSupport(physicalDevice, queueFamilyIndex);
+
+ VkSurfaceKHR surface = QVulkanInstance::surfaceForWindow(window);
+ VkBool32 supported = false;
+ m_getPhysDevSurfaceSupport(physicalDevice, queueFamilyIndex, surface, &supported);
+ ok &= bool(supported);
+
+ return ok;
+}
+
+VkSurfaceKHR QWindowsVulkanInstance::createSurface(HWND win)
+{
+ VkSurfaceKHR surface = 0;
+
+ if (!m_createSurface) {
+ m_createSurface = reinterpret_cast<PFN_vkCreateWin32SurfaceKHR>(
+ m_vkGetInstanceProcAddr(m_vkInst, "vkCreateWin32SurfaceKHR"));
+ }
+ if (!m_createSurface) {
+ qWarning("Failed to find vkCreateWin32SurfaceKHR");
+ return surface;
+ }
+ if (!m_destroySurface) {
+ m_destroySurface = reinterpret_cast<PFN_vkDestroySurfaceKHR>(
+ m_vkGetInstanceProcAddr(m_vkInst, "vkDestroySurfaceKHR"));
+ }
+ if (!m_destroySurface) {
+ qWarning("Failed to find vkDestroySurfaceKHR");
+ return surface;
+ }
+
+ VkWin32SurfaceCreateInfoKHR surfaceInfo;
+ memset(&surfaceInfo, 0, sizeof(surfaceInfo));
+ surfaceInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
+ surfaceInfo.hinstance = GetModuleHandle(nullptr);
+ surfaceInfo.hwnd = win;
+ VkResult err = m_createSurface(m_vkInst, &surfaceInfo, nullptr, &surface);
+ if (err != VK_SUCCESS)
+ qWarning("Failed to create Vulkan surface: %d", err);
+
+ return surface;
+}
+
+void QWindowsVulkanInstance::destroySurface(VkSurfaceKHR surface)
+{
+ if (m_destroySurface && surface)
+ m_destroySurface(m_vkInst, surface, nullptr);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/windows/qwindowsvulkaninstance.h b/src/plugins/platforms/windows/qwindowsvulkaninstance.h
new file mode 100644
index 0000000000..ca60ab7627
--- /dev/null
+++ b/src/plugins/platforms/windows/qwindowsvulkaninstance.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSVULKANINSTANCE_H
+#define QWINDOWSVULKANINSTANCE_H
+
+#if defined(VULKAN_H_) && !defined(VK_USE_PLATFORM_WIN32_KHR)
+#error "vulkan.h included without Win32 WSI"
+#endif
+
+#define VK_USE_PLATFORM_WIN32_KHR
+
+#include <QtVulkanSupport/private/qbasicvulkanplatforminstance_p.h>
+#include <QLibrary>
+
+QT_BEGIN_NAMESPACE
+
+class QWindowsVulkanInstance : public QBasicPlatformVulkanInstance
+{
+public:
+ QWindowsVulkanInstance(QVulkanInstance *instance);
+ ~QWindowsVulkanInstance();
+
+ void createOrAdoptInstance() override;
+ bool supportsPresent(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, QWindow *window) override;
+
+ VkSurfaceKHR createSurface(HWND win);
+ void destroySurface(VkSurfaceKHR surface);
+
+private:
+ QVulkanInstance *m_instance;
+ QLibrary m_lib;
+ PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR m_getPhysDevPresSupport;
+ PFN_vkCreateWin32SurfaceKHR m_createSurface;
+ PFN_vkDestroySurfaceKHR m_destroySurface;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWSVULKANINSTANCE_H
diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp
index c1aeecf0ab..038ee5cd62 100644
--- a/src/plugins/platforms/windows/qwindowswindow.cpp
+++ b/src/plugins/platforms/windows/qwindowswindow.cpp
@@ -37,6 +37,13 @@
**
****************************************************************************/
+#if defined(WINVER) && WINVER < 0x0601
+# undef WINVER
+#endif
+#if !defined(WINVER)
+# define WINVER 0x0601 // Enable touch functions for MinGW
+#endif
+
#include "qwindowswindow.h"
#include "qwindowscontext.h"
#if QT_CONFIG(draganddrop)
@@ -44,6 +51,7 @@
#endif
#include "qwindowsscreen.h"
#include "qwindowsintegration.h"
+#include "qwindowsmenu.h"
#include "qwindowsnativeinterface.h"
#if QT_CONFIG(dynamicgl)
# include "qwindowsglcontext.h"
@@ -70,8 +78,14 @@
#include <dwmapi.h>
+#if QT_CONFIG(vulkan)
+#include "qwindowsvulkaninstance.h"
+#endif
+
QT_BEGIN_NAMESPACE
+typedef QSharedPointer<QWindowCreationContext> QWindowCreationContextPtr;
+
enum {
defaultWindowWidth = 160,
defaultWindowHeight = 160
@@ -227,6 +241,23 @@ QDebug operator<<(QDebug d, const WINDOWPLACEMENT &wp)
<< ", rcNormalPosition=" << wp.rcNormalPosition;
return d;
}
+
+QDebug operator<<(QDebug d, const GUID &guid)
+{
+ QDebugStateSaver saver(d);
+ d.nospace();
+ d << '{' << hex << uppercasedigits << qSetPadChar(QLatin1Char('0'))
+ << qSetFieldWidth(8) << guid.Data1
+ << qSetFieldWidth(0) << '-' << qSetFieldWidth(4)
+ << guid.Data2 << qSetFieldWidth(0) << '-' << qSetFieldWidth(4)
+ << guid.Data3 << qSetFieldWidth(0) << '-' << qSetFieldWidth(4)
+ << qSetFieldWidth(2) << guid.Data4[0] << guid.Data4[1]
+ << qSetFieldWidth(0) << '-' << qSetFieldWidth(2);
+ for (int i = 2; i < 8; ++i)
+ d << guid.Data4[i];
+ d << qSetFieldWidth(0) << '}';
+ return d;
+}
#endif // !QT_NO_DEBUG_STREAM
// QTBUG-43872, for windows that do not have WS_EX_TOOLWINDOW set, WINDOWPLACEMENT
@@ -294,13 +325,15 @@ static QWindow::Visibility windowVisibility_sys(HWND hwnd)
return QWindow::Windowed;
}
-static inline bool windowIsOpenGL(const QWindow *w)
+static inline bool windowIsAccelerated(const QWindow *w)
{
switch (w->surfaceType()) {
case QSurface::OpenGLSurface:
return true;
case QSurface::RasterGLSurface:
return qt_window_private(const_cast<QWindow *>(w))->compositing;
+ case QSurface::VulkanSurface:
+ return true;
default:
return false;
}
@@ -361,11 +394,11 @@ bool QWindowsWindow::setWindowLayered(HWND hwnd, Qt::WindowFlags flags, bool has
return needsLayered;
}
-static void setWindowOpacity(HWND hwnd, Qt::WindowFlags flags, bool hasAlpha, bool openGL, qreal level)
+static void setWindowOpacity(HWND hwnd, Qt::WindowFlags flags, bool hasAlpha, bool accelerated, qreal level)
{
if (QWindowsWindow::setWindowLayered(hwnd, flags, hasAlpha, level)) {
const BYTE alpha = BYTE(qRound(255.0 * level));
- if (hasAlpha && !openGL && (flags & Qt::FramelessWindowHint)) {
+ if (hasAlpha && !accelerated && (flags & Qt::FramelessWindowHint)) {
// Non-GL windows with alpha: Use blend function to update.
BLENDFUNCTION blend = {AC_SRC_OVER, 0, alpha, AC_SRC_ALPHA};
UpdateLayeredWindow(hwnd, NULL, NULL, NULL, NULL, NULL, 0, &blend, ULW_ALPHA);
@@ -379,13 +412,13 @@ static void setWindowOpacity(HWND hwnd, Qt::WindowFlags flags, bool hasAlpha, bo
static inline void updateGLWindowSettings(const QWindow *w, HWND hwnd, Qt::WindowFlags flags, qreal opacity)
{
- const bool isGL = windowIsOpenGL(w);
+ const bool isAccelerated = windowIsAccelerated(w);
const bool hasAlpha = w->format().hasAlpha();
- if (isGL && hasAlpha)
+ if (isAccelerated && hasAlpha)
applyBlurBehindWindow(hwnd);
- setWindowOpacity(hwnd, flags, hasAlpha, isGL, opacity);
+ setWindowOpacity(hwnd, flags, hasAlpha, isAccelerated, opacity);
}
/*!
@@ -598,8 +631,6 @@ void WindowCreationData::fromWindow(const QWindow *w, const Qt::WindowFlags flag
QWindowsWindowData
WindowCreationData::create(const QWindow *w, const WindowData &data, QString title) const
{
- typedef QSharedPointer<QWindowCreationContext> QWindowCreationContextPtr;
-
WindowData result;
result.flags = flags;
@@ -617,7 +648,7 @@ QWindowsWindowData
// Capture events before CreateWindowEx() returns. The context is cleared in
// the QWindowsWindow constructor.
- const QWindowCreationContextPtr context(new QWindowCreationContext(w, rect, data.customMargins, style, exStyle));
+ const QWindowCreationContextPtr context(new QWindowCreationContext(w, data.geometry, rect, data.customMargins, style, exStyle));
QWindowsContext::instance()->setWindowCreationContext(context);
qCDebug(lcQpaWindows).nospace()
@@ -672,7 +703,7 @@ void WindowCreationData::initialize(const QWindow *w, HWND hwnd, bool frameChang
{
if (!hwnd)
return;
- UINT swpFlags = SWP_NOMOVE | SWP_NOSIZE;
+ UINT swpFlags = SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER;
if (frameChange)
swpFlags |= SWP_FRAMECHANGED;
if (topLevel) {
@@ -834,7 +865,7 @@ QWindowsBaseWindow *QWindowsBaseWindow::baseWindowOf(const QWindow *w)
if (QPlatformWindow *pw = w->handle())
return static_cast<QWindowsBaseWindow *>(pw);
}
- return Q_NULLPTR;
+ return nullptr;
}
HWND QWindowsBaseWindow::handleOf(const QWindow *w)
@@ -985,10 +1016,11 @@ void QWindowsForeignWindow::setVisible(bool visible)
*/
QWindowCreationContext::QWindowCreationContext(const QWindow *w,
- const QRect &geometry,
+ const QRect &geometryIn, const QRect &geometry,
const QMargins &cm,
DWORD style_, DWORD exStyle_) :
geometryHint(w, cm), window(w), style(style_), exStyle(exStyle_),
+ requestedGeometryIn(geometryIn),
requestedGeometry(geometry), obtainedGeometry(geometry),
margins(QWindowsGeometryHint::frame(style, exStyle)), customMargins(cm)
{
@@ -1002,6 +1034,8 @@ QWindowCreationContext::QWindowCreationContext(const QWindow *w,
const QMargins effectiveMargins = margins + customMargins;
frameWidth = effectiveMargins.left() + geometry.width() + effectiveMargins.right();
frameHeight = effectiveMargins.top() + geometry.height() + effectiveMargins.bottom();
+ if (QWindowsMenuBar::menuBarOf(w) != nullptr)
+ frameHeight += GetSystemMetrics(SM_CYMENU);
const bool isDefaultPosition = !frameX && !frameY && w->isTopLevel();
if (!QWindowsGeometryHint::positionIncludesFrame(w) && !isDefaultPosition) {
frameX -= effectiveMargins.left();
@@ -1047,9 +1081,10 @@ QWindowsWindow::QWindowsWindow(QWindow *aWindow, const QWindowsWindowData &data)
m_data(data),
m_cursor(new CursorHandle),
m_format(aWindow->requestedFormat())
+#if QT_CONFIG(vulkan)
+ , m_vkSurface(0)
+#endif
{
- // Clear the creation context as the window can be found in QWindowsContext's map.
- QWindowsContext::instance()->setWindowCreationContext(QSharedPointer<QWindowCreationContext>());
QWindowsContext::instance()->addWindow(m_data.hwnd, this);
const Qt::WindowType type = aWindow->type();
if (type == Qt::Desktop)
@@ -1062,13 +1097,20 @@ QWindowsWindow::QWindowsWindow(QWindow *aWindow, const QWindowsWindowData &data)
setFlag(OpenGL_ES2);
}
#endif // QT_NO_OPENGL
+#if QT_CONFIG(vulkan)
+ if (aWindow->surfaceType() == QSurface::VulkanSurface)
+ setFlag(VulkanSurface);
+#endif
updateDropSite(window()->isTopLevel());
registerTouchWindow();
- setWindowState(aWindow->windowState());
+ setWindowState(aWindow->windowStates());
const qreal opacity = qt_window_private(aWindow)->opacity;
if (!qFuzzyCompare(opacity, qreal(1.0)))
setOpacity(opacity);
+
+ setMask(QHighDpi::toNativeLocalRegion(window()->mask(), window()));
+
if (aWindow->isTopLevel())
setWindowIcon(aWindow->icon());
clearFlag(WithinCreate);
@@ -1078,11 +1120,32 @@ QWindowsWindow::~QWindowsWindow()
{
setFlag(WithinDestroy);
if (testFlag(TouchRegistered))
- QWindowsContext::user32dll.unregisterTouchWindow(m_data.hwnd);
+ UnregisterTouchWindow(m_data.hwnd);
destroyWindow();
destroyIcon();
}
+void QWindowsWindow::initialize()
+{
+ // Clear the creation context as the window can be found in QWindowsContext's map.
+ QWindowCreationContextPtr creationContext =
+ QWindowsContext::instance()->setWindowCreationContext(QWindowCreationContextPtr());
+
+ // Trigger geometry change (unless it has a special state in which case setWindowState()
+ // will send the message) and screen change signals of QWindow.
+ QWindow *w = window();
+ if (w->type() != Qt::Desktop) {
+ const Qt::WindowState state = w->windowState();
+ if (state != Qt::WindowMaximized && state != Qt::WindowFullScreen
+ && creationContext->requestedGeometryIn != creationContext->obtainedGeometry) {
+ QWindowSystemInterface::handleGeometryChange(w, creationContext->obtainedGeometry);
+ }
+ QPlatformScreen *obtainedScreen = screenForGeometry(creationContext->obtainedGeometry);
+ if (obtainedScreen && screen() != obtainedScreen)
+ QWindowSystemInterface::handleWindowScreenChanged(w, obtainedScreen->screen());
+ }
+}
+
void QWindowsWindow::fireExpose(const QRegion &region, bool force)
{
if (region.isEmpty() && !force)
@@ -1115,6 +1178,14 @@ void QWindowsWindow::destroyWindow()
if (hasMouseCapture())
setMouseGrabEnabled(false);
setDropSiteEnabled(false);
+#if QT_CONFIG(vulkan)
+ if (m_vkSurface) {
+ QVulkanInstance *inst = window()->vulkanInstance();
+ if (inst)
+ static_cast<QWindowsVulkanInstance *>(inst->handle())->destroySurface(m_vkSurface);
+ m_vkSurface = 0;
+ }
+#endif
#ifndef QT_NO_OPENGL
if (m_surface) {
if (QWindowsStaticOpenGLContext *staticOpenGLContext = QWindowsIntegration::staticOpenGLContext())
@@ -1332,20 +1403,48 @@ static inline bool testShowWithoutActivating(const QWindow *window)
return showWithoutActivating.isValid() && showWithoutActivating.toBool();
}
+static void setMinimizedGeometry(HWND hwnd, const QRect &r)
+{
+ WINDOWPLACEMENT windowPlacement;
+ windowPlacement.length = sizeof(WINDOWPLACEMENT);
+ if (GetWindowPlacement(hwnd, &windowPlacement)) {
+ windowPlacement.showCmd = SW_SHOWMINIMIZED;
+ windowPlacement.rcNormalPosition = RECTfromQRect(r);
+ SetWindowPlacement(hwnd, &windowPlacement);
+ }
+}
+
+static void setRestoreMaximizedFlag(HWND hwnd, bool set = true)
+{
+ // Let Windows know that we need to restore as maximized
+ WINDOWPLACEMENT windowPlacement;
+ windowPlacement.length = sizeof(WINDOWPLACEMENT);
+ if (GetWindowPlacement(hwnd, &windowPlacement)) {
+ if (set)
+ windowPlacement.flags |= WPF_RESTORETOMAXIMIZED;
+ else
+ windowPlacement.flags &= ~WPF_RESTORETOMAXIMIZED;
+ SetWindowPlacement(hwnd, &windowPlacement);
+ }
+}
+
// partially from QWidgetPrivate::show_sys()
void QWindowsWindow::show_sys() const
{
int sm = SW_SHOWNORMAL;
bool fakedMaximize = false;
+ bool restoreMaximize = false;
const QWindow *w = window();
const Qt::WindowFlags flags = w->flags();
const Qt::WindowType type = w->type();
if (w->isTopLevel()) {
- const Qt::WindowState state = w->windowState();
+ const Qt::WindowStates state = w->windowStates();
if (state & Qt::WindowMinimized) {
sm = SW_SHOWMINIMIZED;
if (!isVisible())
sm = SW_SHOWMINNOACTIVE;
+ if (state & Qt::WindowMaximized)
+ restoreMaximize = true;
} else {
updateTransientParent();
if (state & Qt::WindowMaximized) {
@@ -1366,7 +1465,7 @@ void QWindowsWindow::show_sys() const
if (type == Qt::Popup || type == Qt::ToolTip || type == Qt::Tool || testShowWithoutActivating(w))
sm = SW_SHOWNOACTIVATE;
- if (w->windowState() & Qt::WindowMaximized)
+ if (w->windowStates() & Qt::WindowMaximized)
setFlag(WithinMaximize); // QTBUG-8361
ShowWindow(m_data.hwnd, sm);
@@ -1379,6 +1478,8 @@ void QWindowsWindow::show_sys() const
SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER
| SWP_FRAMECHANGED);
}
+ if (restoreMaximize)
+ setRestoreMaximizedFlag(m_data.hwnd);
}
void QWindowsWindow::setParent(const QPlatformWindow *newParent)
@@ -1434,8 +1535,10 @@ void QWindowsWindow::handleHidden()
void QWindowsWindow::handleCompositionSettingsChanged()
{
const QWindow *w = window();
- if (w->surfaceType() == QWindow::OpenGLSurface && w->format().hasAlpha())
+ if ((w->surfaceType() == QWindow::OpenGLSurface || w->surfaceType() == QWindow::VulkanSurface)
+ && w->format().hasAlpha()) {
applyBlurBehindWindow(handle());
+ }
}
static QRect normalFrameGeometry(HWND hwnd)
@@ -1452,7 +1555,8 @@ static QRect normalFrameGeometry(HWND hwnd)
QRect QWindowsWindow::normalGeometry() const
{
// Check for fake 'fullscreen' mode.
- const bool fakeFullScreen = m_savedFrameGeometry.isValid() && window()->windowState() == Qt::WindowFullScreen;
+ const bool fakeFullScreen =
+ m_savedFrameGeometry.isValid() && (window()->windowStates() & Qt::WindowFullScreen);
const QRect frame = fakeFullScreen ? m_savedFrameGeometry : normalFrameGeometry(m_data.hwnd);
const QMargins margins = fakeFullScreen ? QWindowsGeometryHint::frame(m_savedStyle, 0) : frameMargins();
return frame.isValid() ? frame.marginsRemoved(margins) : frame;
@@ -1467,13 +1571,15 @@ void QWindowsWindow::setGeometry(const QRect &rectIn)
const QMargins margins = frameMargins();
rect.moveTopLeft(rect.topLeft() + QPoint(margins.left(), margins.top()));
}
- if (m_windowState == Qt::WindowMinimized)
+ if (m_windowState & Qt::WindowMinimized)
m_data.geometry = rect; // Otherwise set by handleGeometryChange() triggered by event.
if (m_data.hwnd) {
// A ResizeEvent with resulting geometry will be sent. If we cannot
// achieve that size (for example, window title minimal constraint),
// notify and warn.
+ setFlag(WithinSetGeometry);
setGeometry_sys(rect);
+ clearFlag(WithinSetGeometry);
if (m_data.geometry != rect && (isVisible() || QLibraryInfo::isDebugBuild())) {
qWarning("%s: Unable to set geometry %dx%d+%d+%d on %s/'%s'."
" Resulting geometry: %dx%d+%d+%d "
@@ -1510,18 +1616,21 @@ void QWindowsWindow::handleResized(int wParam)
case SIZE_MAXSHOW:
return;
case SIZE_MINIMIZED: // QTBUG-53577, prevent state change events during programmatic state change
- if (!testFlag(WithinSetStyle))
- handleWindowStateChange(Qt::WindowMinimized);
+ if (!testFlag(WithinSetStyle) && !testFlag(WithinSetGeometry))
+ handleWindowStateChange(m_windowState | Qt::WindowMinimized);
return;
case SIZE_MAXIMIZED:
- if (!testFlag(WithinSetStyle))
- handleWindowStateChange(Qt::WindowMaximized);
+ if (!testFlag(WithinSetStyle) && !testFlag(WithinSetGeometry))
+ handleWindowStateChange(Qt::WindowMaximized | (isFullScreen_sys() ? Qt::WindowFullScreen
+ : Qt::WindowNoState));
handleGeometryChange();
break;
case SIZE_RESTORED:
- if (!testFlag(WithinSetStyle)) {
+ if (!testFlag(WithinSetStyle) && !testFlag(WithinSetGeometry)) {
if (isFullScreen_sys())
- handleWindowStateChange(Qt::WindowFullScreen);
+ handleWindowStateChange(
+ Qt::WindowFullScreen
+ | (testFlag(MaximizeToFullScreen) ? Qt::WindowMaximized : Qt::WindowNoState));
else if (m_windowState != Qt::WindowNoState && !testFlag(MaximizeToFullScreen))
handleWindowStateChange(Qt::WindowNoState);
}
@@ -1554,7 +1663,6 @@ void QWindowsWindow::handleGeometryChange()
{
const QRect previousGeometry = m_data.geometry;
m_data.geometry = geometry_sys();
- QPlatformWindow::setGeometry(m_data.geometry);
QWindowSystemInterface::handleGeometryChange(window(), m_data.geometry);
// QTBUG-32121: OpenGL/normal windows (with exception of ANGLE) do not receive
// expose events when shrinking, synthesize.
@@ -1668,8 +1776,11 @@ bool QWindowsWindow::handleWmPaint(HWND hwnd, UINT message,
BeginPaint(hwnd, &ps);
// Observed painting problems with Aero style disabled (QTBUG-7865).
- if (Q_UNLIKELY(testFlag(OpenGLSurface) && testFlag(OpenGLDoubleBuffered) && !dwmIsCompositionEnabled()))
+ if (Q_UNLIKELY(!dwmIsCompositionEnabled())
+ && ((testFlag(OpenGLSurface) && testFlag(OpenGLDoubleBuffered)) || testFlag(VulkanSurface)))
+ {
SelectClipRgn(ps.hdc, NULL);
+ }
// If the a window is obscured by another window (such as a child window)
// we still need to send isExposed=true, for compatibility.
@@ -1725,20 +1836,16 @@ QWindowsWindowData QWindowsWindow::setWindowFlags_sys(Qt::WindowFlags wt,
return result;
}
-void QWindowsWindow::handleWindowStateChange(Qt::WindowState state)
+void QWindowsWindow::handleWindowStateChange(Qt::WindowStates state)
{
qCDebug(lcQpaWindows) << __FUNCTION__ << this << window()
<< "\n from " << m_windowState << " to " << state;
m_windowState = state;
QWindowSystemInterface::handleWindowStateChanged(window(), state);
- switch (state) {
- case Qt::WindowMinimized:
+ if (state & Qt::WindowMinimized) {
handleHidden();
QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents); // Tell QQuickWindow to stop rendering now.
- break;
- case Qt::WindowMaximized:
- case Qt::WindowFullScreen:
- case Qt::WindowNoState: {
+ } else {
// QTBUG-17548: We send expose events when receiving WM_Paint, but for
// layered windows and transient children, we won't receive any WM_Paint.
QWindow *w = window();
@@ -1759,13 +1866,9 @@ void QWindowsWindow::handleWindowStateChange(Qt::WindowState state)
if (exposeEventsSent && !QWindowsContext::instance()->asyncExpose())
QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);
}
- break;
- default:
- break;
- }
}
-void QWindowsWindow::setWindowState(Qt::WindowState state)
+void QWindowsWindow::setWindowState(Qt::WindowStates state)
{
if (m_data.hwnd) {
setWindowState_sys(state);
@@ -1796,18 +1899,19 @@ bool QWindowsWindow::isFullScreen_sys() const
to ShowWindow.
*/
-void QWindowsWindow::setWindowState_sys(Qt::WindowState newState)
+void QWindowsWindow::setWindowState_sys(Qt::WindowStates newState)
{
- const Qt::WindowState oldState = m_windowState;
+ const Qt::WindowStates oldState = m_windowState;
if (oldState == newState)
return;
qCDebug(lcQpaWindows) << '>' << __FUNCTION__ << this << window()
<< " from " << oldState << " to " << newState;
const bool visible = isVisible();
+ auto stateChange = oldState ^ newState;
- if ((oldState == Qt::WindowFullScreen) != (newState == Qt::WindowFullScreen)) {
- if (newState == Qt::WindowFullScreen) {
+ if (stateChange & Qt::WindowFullScreen) {
+ if (newState & Qt::WindowFullScreen) {
#ifndef Q_FLATTEN_EXPOSE
UINT newStyle = WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_POPUP;
#else
@@ -1818,7 +1922,7 @@ void QWindowsWindow::setWindowState_sys(Qt::WindowState newState)
// Window state but emulated by changing geometry and style.
if (!m_savedStyle) {
m_savedStyle = style();
- if (oldState == Qt::WindowMinimized || oldState == Qt::WindowMaximized) {
+ if ((oldState & Qt::WindowMinimized) || (oldState & Qt::WindowMaximized)) {
const QRect nf = normalFrameGeometry(m_data.hwnd);
if (nf.isValid())
m_savedFrameGeometry = nf;
@@ -1826,6 +1930,8 @@ void QWindowsWindow::setWindowState_sys(Qt::WindowState newState)
m_savedFrameGeometry = frameGeometry_sys();
}
}
+ if (newState & Qt::WindowMaximized)
+ setFlag(MaximizeToFullScreen);
if (m_savedStyle & WS_SYSMENU)
newStyle |= WS_SYSMENU;
if (visible)
@@ -1839,15 +1945,23 @@ void QWindowsWindow::setWindowState_sys(Qt::WindowState newState)
if (!screen)
screen = QGuiApplication::primaryScreen();
const QRect r = screen ? QHighDpi::toNativePixels(screen->geometry(), window()) : m_savedFrameGeometry;
- const UINT swpf = SWP_FRAMECHANGED | SWP_NOACTIVATE;
- const bool wasSync = testFlag(SynchronousGeometryChangeEvent);
- setFlag(SynchronousGeometryChangeEvent);
- SetWindowPos(m_data.hwnd, HWND_TOP, r.left(), r.top(), r.width(), r.height(), swpf);
- if (!wasSync)
- clearFlag(SynchronousGeometryChangeEvent);
- QWindowSystemInterface::handleGeometryChange(window(), r);
- QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);
- } else if (newState != Qt::WindowMinimized) {
+
+ if (newState & Qt::WindowMinimized) {
+ setMinimizedGeometry(m_data.hwnd, r);
+ if (stateChange & Qt::WindowMaximized)
+ setRestoreMaximizedFlag(m_data.hwnd, newState & Qt::WindowMaximized);
+ } else {
+ const UINT swpf = SWP_FRAMECHANGED | SWP_NOACTIVATE;
+ const bool wasSync = testFlag(SynchronousGeometryChangeEvent);
+ setFlag(SynchronousGeometryChangeEvent);
+ SetWindowPos(m_data.hwnd, HWND_TOP, r.left(), r.top(), r.width(), r.height(), swpf);
+ if (!wasSync)
+ clearFlag(SynchronousGeometryChangeEvent);
+ clearFlag(MaximizeToFullScreen);
+ QWindowSystemInterface::handleGeometryChange(window(), r);
+ QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);
+ }
+ } else {
// Restore saved state.
unsigned newStyle = m_savedStyle ? m_savedStyle : style();
if (visible)
@@ -1861,43 +1975,58 @@ void QWindowsWindow::setWindowState_sys(Qt::WindowState newState)
if (!screen->geometry().intersects(m_savedFrameGeometry))
m_savedFrameGeometry.moveTo(screen->geometry().topLeft());
- UINT swpf = SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOACTIVATE;
- if (!m_savedFrameGeometry.isValid())
- swpf |= SWP_NOSIZE | SWP_NOMOVE;
- const bool wasSync = testFlag(SynchronousGeometryChangeEvent);
- setFlag(SynchronousGeometryChangeEvent);
- // After maximized/fullscreen; the window can be in a maximized state. Clear
- // it before applying the normal geometry.
- if (windowVisibility_sys(m_data.hwnd) == QWindow::Maximized)
- ShowWindow(m_data.hwnd, SW_SHOWNOACTIVATE);
- SetWindowPos(m_data.hwnd, 0, m_savedFrameGeometry.x(), m_savedFrameGeometry.y(),
- m_savedFrameGeometry.width(), m_savedFrameGeometry.height(), swpf);
- if (!wasSync)
- clearFlag(SynchronousGeometryChangeEvent);
- // preserve maximized state
- if (visible) {
- setFlag(WithinMaximize);
- ShowWindow(m_data.hwnd, (newState == Qt::WindowMaximized) ? SW_MAXIMIZE : SW_SHOWNA);
- clearFlag(WithinMaximize);
+ if (newState & Qt::WindowMinimized) {
+ setMinimizedGeometry(m_data.hwnd, m_savedFrameGeometry);
+ if (stateChange & Qt::WindowMaximized)
+ setRestoreMaximizedFlag(m_data.hwnd, newState & Qt::WindowMaximized);
+ } else {
+ UINT swpf = SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOACTIVATE;
+ if (!m_savedFrameGeometry.isValid())
+ swpf |= SWP_NOSIZE | SWP_NOMOVE;
+ const bool wasSync = testFlag(SynchronousGeometryChangeEvent);
+ setFlag(SynchronousGeometryChangeEvent);
+ // After maximized/fullscreen; the window can be in a maximized state. Clear
+ // it before applying the normal geometry.
+ if (windowVisibility_sys(m_data.hwnd) == QWindow::Maximized)
+ ShowWindow(m_data.hwnd, SW_SHOWNOACTIVATE);
+ SetWindowPos(m_data.hwnd, 0, m_savedFrameGeometry.x(), m_savedFrameGeometry.y(),
+ m_savedFrameGeometry.width(), m_savedFrameGeometry.height(), swpf);
+ if (!wasSync)
+ clearFlag(SynchronousGeometryChangeEvent);
+ // preserve maximized state
+ if (visible) {
+ setFlag(WithinMaximize);
+ ShowWindow(m_data.hwnd,
+ (newState & Qt::WindowMaximized) ? SW_MAXIMIZE : SW_SHOWNA);
+ clearFlag(WithinMaximize);
+ }
}
m_savedStyle = 0;
m_savedFrameGeometry = QRect();
}
- } else if ((oldState == Qt::WindowMaximized) != (newState == Qt::WindowMaximized)) {
- if (visible && !(newState == Qt::WindowMinimized)) {
+ } else if ((oldState & Qt::WindowMaximized) != (newState & Qt::WindowMaximized)) {
+ if (visible && !(newState & Qt::WindowMinimized)) {
setFlag(WithinMaximize);
- if (newState == Qt::WindowFullScreen)
+ if (newState & Qt::WindowFullScreen)
setFlag(MaximizeToFullScreen);
- ShowWindow(m_data.hwnd, (newState == Qt::WindowMaximized) ? SW_MAXIMIZE : SW_SHOWNOACTIVATE);
+ ShowWindow(m_data.hwnd,
+ (newState & Qt::WindowMaximized) ? SW_MAXIMIZE : SW_SHOWNOACTIVATE);
clearFlag(WithinMaximize);
clearFlag(MaximizeToFullScreen);
+ } else if (visible && (oldState & newState & Qt::WindowMinimized)) {
+ // change of the maximized state while keeping minimized
+ setRestoreMaximizedFlag(m_data.hwnd, newState & Qt::WindowMaximized);
}
}
- if ((oldState == Qt::WindowMinimized) != (newState == Qt::WindowMinimized)) {
- if (visible)
- ShowWindow(m_data.hwnd, (newState == Qt::WindowMinimized) ? SW_MINIMIZE :
- (newState == Qt::WindowMaximized) ? SW_MAXIMIZE : SW_SHOWNORMAL);
+ if (stateChange & Qt::WindowMinimized) {
+ if (visible) {
+ ShowWindow(m_data.hwnd,
+ (newState & Qt::WindowMinimized) ? SW_MINIMIZE :
+ (newState & Qt::WindowMaximized) ? SW_MAXIMIZE : SW_SHOWNORMAL);
+ if ((newState & Qt::WindowMinimized) && (stateChange & Qt::WindowMaximized))
+ setRestoreMaximizedFlag(m_data.hwnd, newState & Qt::WindowMaximized);
+ }
}
qCDebug(lcQpaWindows) << '<' << __FUNCTION__ << this << window() << newState;
}
@@ -1942,9 +2071,17 @@ void QWindowsWindow::propagateSizeHints()
bool QWindowsWindow::handleGeometryChangingMessage(MSG *message, const QWindow *qWindow, const QMargins &margins)
{
+ WINDOWPOS *windowPos = reinterpret_cast<WINDOWPOS *>(message->lParam);
+ if ((windowPos->flags & SWP_NOZORDER) == 0) {
+ if (QWindowsWindow *platformWindow = QWindowsWindow::windowsWindowOf(qWindow)) {
+ QWindow *parentWindow = qWindow->parent();
+ HWND parentHWND = GetAncestor(windowPos->hwnd, GA_PARENT);
+ HWND desktopHWND = GetDesktopWindow();
+ platformWindow->m_data.embedded = !parentWindow && parentHWND && (parentHWND != desktopHWND);
+ }
+ }
if (!qWindow->isTopLevel()) // Implement hasHeightForWidth().
return false;
- WINDOWPOS *windowPos = reinterpret_cast<WINDOWPOS *>(message->lParam);
if ((windowPos->flags & (SWP_NOCOPYBITS | SWP_NOSIZE)))
return false;
const QRect suggestedFrameGeometry(windowPos->x, windowPos->y,
@@ -1989,7 +2126,7 @@ void QWindowsWindow::setOpacity(qreal level)
m_opacity = level;
if (m_data.hwnd)
setWindowOpacity(m_data.hwnd, m_data.flags,
- window()->format().hasAlpha(), testFlag(OpenGLSurface),
+ window()->format().hasAlpha(), testFlag(OpenGLSurface) || testFlag(VulkanSurface),
level);
}
}
@@ -2140,6 +2277,16 @@ bool QWindowsWindow::startSystemResize(const QPoint &, Qt::Corner corner)
return true;
}
+bool QWindowsWindow::startSystemMove(const QPoint &)
+{
+ if (!GetSystemMenu(m_data.hwnd, FALSE))
+ return false;
+
+ ReleaseCapture();
+ PostMessage(m_data.hwnd, WM_SYSCOMMAND, 0xF012 /*SC_DRAGMOVE*/, 0);
+ return true;
+}
+
void QWindowsWindow::setFrameStrutEventsEnabled(bool enabled)
{
if (enabled) {
@@ -2158,7 +2305,7 @@ void QWindowsWindow::getSizeHints(MINMAXINFO *mmi) const
hint.applyToMinMaxInfo(m_data.hwnd, mmi);
}
- if ((testFlag(WithinMaximize) || (window()->windowState() == Qt::WindowMinimized))
+ if ((testFlag(WithinMaximize) || (window()->windowStates() & Qt::WindowMinimized))
&& (m_data.flags & Qt::FramelessWindowHint)) {
// This block fixes QTBUG-8361: Frameless windows shouldn't cover the
// taskbar when maximized
@@ -2188,7 +2335,7 @@ bool QWindowsWindow::handleNonClientHitTest(const QPoint &globalPos, LRESULT *re
// QTBUG-32663, suppress resize cursor for fixed size windows.
const QWindow *w = window();
if (!w->isTopLevel() // Task 105852, minimized windows need to respond to user input.
- || (m_windowState != Qt::WindowNoState && m_windowState != Qt::WindowActive)
+ || !(m_windowState & ~Qt::WindowActive)
|| (m_data.flags & Qt::FramelessWindowHint)) {
return false;
}
@@ -2379,6 +2526,16 @@ bool QWindowsWindow::isTopLevel() const
return window()->isTopLevel() && !m_data.embedded;
}
+QWindowsMenuBar *QWindowsWindow::menuBar() const
+{
+ return m_menuBar.data();
+}
+
+void QWindowsWindow::setMenuBar(QWindowsMenuBar *mb)
+{
+ m_menuBar = mb;
+}
+
/*!
\brief Sets custom margins to be added to the default margins determined by
the windows style in the handling of the WM_NCCALCSIZE message.
@@ -2407,11 +2564,27 @@ void QWindowsWindow::setCustomMargins(const QMargins &newCustomMargins)
void *QWindowsWindow::surface(void *nativeConfig, int *err)
{
-#ifdef QT_NO_OPENGL
+#if QT_CONFIG(vulkan)
+ Q_UNUSED(nativeConfig);
+ Q_UNUSED(err);
+ if (window()->surfaceType() == QSurface::VulkanSurface) {
+ if (!m_vkSurface) {
+ QVulkanInstance *inst = window()->vulkanInstance();
+ if (inst)
+ m_vkSurface = static_cast<QWindowsVulkanInstance *>(inst->handle())->createSurface(handle());
+ else
+ qWarning("Attempted to create Vulkan surface without an instance; was QWindow::setVulkanInstance() called?");
+ }
+ // Different semantics for VkSurfaces: the return value is the address,
+ // not the value, given that this is a 64-bit handle even on x86.
+ return &m_vkSurface;
+ }
+#elif defined(QT_NO_OPENGL)
Q_UNUSED(err)
Q_UNUSED(nativeConfig)
return 0;
-#else
+#endif
+#ifndef QT_NO_OPENGL
if (!m_surface) {
if (QWindowsStaticOpenGLContext *staticOpenGLContext = QWindowsIntegration::staticOpenGLContext())
m_surface = staticOpenGLContext->createWindowSurface(m_data.hwnd, nativeConfig, err);
@@ -2423,6 +2596,14 @@ void *QWindowsWindow::surface(void *nativeConfig, int *err)
void QWindowsWindow::invalidateSurface()
{
+#if QT_CONFIG(vulkan)
+ if (m_vkSurface) {
+ QVulkanInstance *inst = window()->vulkanInstance();
+ if (inst)
+ static_cast<QWindowsVulkanInstance *>(inst->handle())->destroySurface(m_vkSurface);
+ m_vkSurface = 0;
+ }
+#endif
#ifndef QT_NO_OPENGL
if (m_surface) {
if (QWindowsStaticOpenGLContext *staticOpenGLContext = QWindowsIntegration::staticOpenGLContext())
@@ -2444,12 +2625,12 @@ void QWindowsWindow::registerTouchWindow(QWindowsWindowFunctions::TouchWindowTou
if ((QWindowsContext::instance()->systemInfo() & QWindowsContext::SI_SupportsTouch)
&& !testFlag(TouchRegistered)) {
ULONG touchFlags = 0;
- const bool ret = QWindowsContext::user32dll.isTouchWindow(m_data.hwnd, &touchFlags);
+ const bool ret = IsTouchWindow(m_data.hwnd, &touchFlags);
// Return if it is not a touch window or the flags are already set by a hook
// such as HCBT_CREATEWND
if (ret || touchFlags != 0)
return;
- if (QWindowsContext::user32dll.registerTouchWindow(m_data.hwnd, ULONG(touchTypes)))
+ if (RegisterTouchWindow(m_data.hwnd, ULONG(touchTypes)))
setFlag(TouchRegistered);
else
qErrnoWarning("RegisterTouchWindow() failed for window '%s'.", qPrintable(window()->objectName()));
diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h
index 2b447751ba..3732255738 100644
--- a/src/plugins/platforms/windows/qwindowswindow.h
+++ b/src/plugins/platforms/windows/qwindowswindow.h
@@ -41,14 +41,20 @@
#define QWINDOWSWINDOW_H
#include <QtCore/qt_windows.h>
+#include <QtCore/QPointer>
#include "qwindowscursor.h"
#include <qpa/qplatformwindow.h>
#include <QtPlatformHeaders/qwindowswindowfunctions.h>
+#if QT_CONFIG(vulkan)
+#include "qwindowsvulkaninstance.h"
+#endif
+
QT_BEGIN_NAMESPACE
class QWindowsOleDropTarget;
+class QWindowsMenuBar;
class QDebug;
struct QWindowsGeometryHint
@@ -75,9 +81,10 @@ struct QWindowsGeometryHint
struct QWindowCreationContext
{
- QWindowCreationContext(const QWindow *w, const QRect &r,
- const QMargins &customMargins,
- DWORD style, DWORD exStyle);
+ explicit QWindowCreationContext(const QWindow *w,
+ const QRect &geometryIn, const QRect &geometry,
+ const QMargins &customMargins,
+ DWORD style, DWORD exStyle);
void applyToMinMaxInfo(MINMAXINFO *mmi) const
{ geometryHint.applyToMinMaxInfo(style, exStyle, mmi); }
@@ -85,7 +92,8 @@ struct QWindowCreationContext
const QWindow *window;
DWORD style;
DWORD exStyle;
- QRect requestedGeometry;
+ QRect requestedGeometryIn; // QWindow scaled
+ QRect requestedGeometry; // after QPlatformWindow::initialGeometry()
QRect obtainedGeometry;
QMargins margins;
QMargins customMargins; // User-defined, additional frame for WM_NCCALCSIZE
@@ -188,6 +196,7 @@ public:
{
AutoMouseCapture = 0x1, //! Automatic mouse capture on button press.
WithinSetParent = 0x2,
+ WithinSetGeometry = 0x8,
OpenGLSurface = 0x10,
OpenGL_ES2 = 0x20,
OpenGLDoubleBuffered = 0x40,
@@ -208,12 +217,15 @@ public:
Compositing = 0x200000,
HasBorderInFullScreen = 0x400000,
WithinDpiChanged = 0x800000,
+ VulkanSurface = 0x1000000,
ResizeMoveActive = 0x2000000
};
QWindowsWindow(QWindow *window, const QWindowsWindowData &data);
~QWindowsWindow();
+ void initialize() override;
+
using QPlatformWindow::screenForGeometry;
QSurfaceFormat format() const override { return m_format; }
@@ -231,7 +243,7 @@ public:
QPoint mapFromGlobal(const QPoint &pos) const override;
void setWindowFlags(Qt::WindowFlags flags) override;
- void setWindowState(Qt::WindowState state) override;
+ void setWindowState(Qt::WindowStates state) override;
void setParent(const QPlatformWindow *window) override;
@@ -257,6 +269,7 @@ public:
inline bool hasMouseCapture() const { return GetCapture() == m_data.hwnd; }
bool startSystemResize(const QPoint &pos, Qt::Corner corner) override;
+ bool startSystemMove(const QPoint &pos) override;
void setFrameStrutEventsEnabled(bool enabled) override;
bool frameStrutEventsEnabled() const override { return testFlag(FrameStrutEventsEnabled); }
@@ -265,6 +278,9 @@ public:
HWND handle() const override { return m_data.hwnd; }
bool isTopLevel() const override;
+ QWindowsMenuBar *menuBar() const;
+ void setMenuBar(QWindowsMenuBar *mb);
+
QMargins customMargins() const { return m_data.customMargins; }
void setCustomMargins(const QMargins &m);
@@ -328,7 +344,7 @@ private:
inline void show_sys() const;
inline QWindowsWindowData setWindowFlags_sys(Qt::WindowFlags wt, unsigned flags = 0) const;
inline bool isFullScreen_sys() const;
- inline void setWindowState_sys(Qt::WindowState newState);
+ inline void setWindowState_sys(Qt::WindowStates newState);
inline void setParent_sys(const QPlatformWindow *parent);
inline void updateTransientParent() const;
void destroyWindow();
@@ -336,14 +352,15 @@ private:
void setDropSiteEnabled(bool enabled);
void updateDropSite(bool topLevel);
void handleGeometryChange();
- void handleWindowStateChange(Qt::WindowState state);
+ void handleWindowStateChange(Qt::WindowStates state);
inline void destroyIcon();
void fireExpose(const QRegion &region, bool force=false);
mutable QWindowsWindowData m_data;
+ QPointer<QWindowsMenuBar> m_menuBar;
mutable unsigned m_flags = WithinCreate;
HDC m_hdc = 0;
- Qt::WindowState m_windowState = Qt::WindowNoState;
+ Qt::WindowStates m_windowState = Qt::WindowNoState;
qreal m_opacity = 1;
#ifndef QT_NO_CURSOR
CursorHandlePtr m_cursor;
@@ -355,6 +372,11 @@ private:
HICON m_iconSmall = 0;
HICON m_iconBig = 0;
void *m_surface = nullptr;
+
+#if QT_CONFIG(vulkan)
+ // note: intentionally not using void * in order to avoid breaking x86
+ VkSurfaceKHR m_vkSurface = 0;
+#endif
};
#ifndef QT_NO_DEBUG_STREAM
@@ -364,6 +386,7 @@ QDebug operator<<(QDebug d, const MINMAXINFO &i);
QDebug operator<<(QDebug d, const NCCALCSIZE_PARAMS &p);
QDebug operator<<(QDebug d, const WINDOWPLACEMENT &);
QDebug operator<<(QDebug d, const WINDOWPOS &);
+QDebug operator<<(QDebug d, const GUID &guid);
#endif // !QT_NO_DEBUG_STREAM
// ---------- QWindowsGeometryHint inline functions.
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.cpp
new file mode 100644
index 0000000000..907883bf5b
--- /dev/null
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.cpp
@@ -0,0 +1,140 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/QtConfig>
+#ifndef QT_NO_ACCESSIBILITY
+
+#include "qwindowsuiaaccessibility.h"
+#include "qwindowsuiamainprovider.h"
+#include "qwindowsuiautils.h"
+
+#include <QtGui/QAccessible>
+#include <QtGui/QWindow>
+#include <QtGui/QGuiApplication>
+#include <QtGui/private/qguiapplication_p.h>
+#include <QtCore/qt_windows.h>
+#include <qpa/qplatformintegration.h>
+#include <QtWindowsUIAutomationSupport/private/qwindowsuiawrapper_p.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QWindowsUiAutomation;
+
+
+QWindowsUiaAccessibility::QWindowsUiaAccessibility()
+{
+}
+
+QWindowsUiaAccessibility::~QWindowsUiaAccessibility()
+{
+}
+
+// Handles UI Automation window messages.
+bool QWindowsUiaAccessibility::handleWmGetObject(HWND hwnd, WPARAM wParam, LPARAM lParam, LRESULT *lResult)
+{
+ if (lParam == LPARAM(UiaRootObjectId)) {
+
+ // Start handling accessibility internally
+ QGuiApplicationPrivate::platformIntegration()->accessibility()->setActive(true);
+
+ // Ignoring all requests while starting up / shutting down
+ if (QCoreApplication::startingUp() || QCoreApplication::closingDown())
+ return false;
+
+ if (QWindow *window = QWindowsContext::instance()->findWindow(hwnd)) {
+ if (QAccessibleInterface *accessible = window->accessibleRoot()) {
+ QWindowsUiaMainProvider *provider = QWindowsUiaMainProvider::providerForAccessible(accessible);
+ *lResult = QWindowsUiaWrapper::instance()->returnRawElementProvider(hwnd, wParam, lParam, provider);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+// Handles accessibility update notifications.
+void QWindowsUiaAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event)
+{
+ if (!event)
+ return;
+
+ QAccessibleInterface *accessible = event->accessibleInterface();
+ if (!isActive() || !accessible || !accessible->isValid())
+ return;
+
+ // Ensures QWindowsUiaWrapper is properly initialized.
+ if (!QWindowsUiaWrapper::instance()->ready())
+ return;
+
+ // No need to do anything when nobody is listening.
+ if (!QWindowsUiaWrapper::instance()->clientsAreListening())
+ return;
+
+ switch (event->type()) {
+
+ case QAccessible::Focus:
+ QWindowsUiaMainProvider::notifyFocusChange(event);
+ break;
+
+ case QAccessible::StateChanged:
+ QWindowsUiaMainProvider::notifyStateChange(static_cast<QAccessibleStateChangeEvent *>(event));
+ break;
+
+ case QAccessible::ValueChanged:
+ QWindowsUiaMainProvider::notifyValueChange(static_cast<QAccessibleValueChangeEvent *>(event));
+ break;
+
+ case QAccessible::TextAttributeChanged:
+ case QAccessible::TextColumnChanged:
+ case QAccessible::TextInserted:
+ case QAccessible::TextRemoved:
+ case QAccessible::TextUpdated:
+ case QAccessible::TextSelectionChanged:
+ case QAccessible::TextCaretMoved:
+ QWindowsUiaMainProvider::notifyTextChange(event);
+ break;
+
+ default:
+ break;
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ACCESSIBILITY
diff --git a/src/plugins/platforms/windows/accessible/qwindowsaccessibility.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.h
index 8621e93120..bbb81d596b 100644
--- a/src/plugins/platforms/windows/accessible/qwindowsaccessibility.h
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
@@ -37,27 +37,29 @@
**
****************************************************************************/
-#ifndef QWINDOWSACCESSIBILITY_H
-#define QWINDOWSACCESSIBILITY_H
+#ifndef QWINDOWSUIAACCESSIBILITY_H
+#define QWINDOWSUIAACCESSIBILITY_H
-#include "../qtwindowsglobal.h"
-#include "../qwindowscontext.h"
-#include <qpa/qplatformaccessibility.h>
+#include <QtCore/QtConfig>
+#ifndef QT_NO_ACCESSIBILITY
-#include <oleacc.h>
+#include "qwindowscontext.h"
+#include <qpa/qplatformaccessibility.h>
QT_BEGIN_NAMESPACE
-class QWindowsAccessibility : public QPlatformAccessibility
+// Windows plataform accessibility implemented over UI Automation.
+class QWindowsUiaAccessibility : public QPlatformAccessibility
{
public:
- QWindowsAccessibility();
- static bool handleAccessibleObjectFromWindowRequest(HWND hwnd, WPARAM wParam, LPARAM lParam, LRESULT *lResult);
+ explicit QWindowsUiaAccessibility();
+ virtual ~QWindowsUiaAccessibility();
+ static bool handleWmGetObject(HWND hwnd, WPARAM wParam, LPARAM lParam, LRESULT *lResult);
void notifyAccessibilityUpdate(QAccessibleEvent *event) override;
- static IAccessible *wrap(QAccessibleInterface *acc);
- static QWindow *windowHelper(const QAccessibleInterface *iface);
};
QT_END_NAMESPACE
-#endif // QWINDOWSACCESSIBILITY_H
+#endif // QT_NO_ACCESSIBILITY
+
+#endif // QWINDOWSUIAACCESSIBILITY_H
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiabaseprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiabaseprovider.cpp
new file mode 100644
index 0000000000..1e1fc49c0f
--- /dev/null
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiabaseprovider.cpp
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/QtConfig>
+#ifndef QT_NO_ACCESSIBILITY
+
+#include "qwindowsuiabaseprovider.h"
+#include "qwindowsuiautils.h"
+#include "qwindowscontext.h"
+
+#include <QtGui/QAccessible>
+#include <QtGui/QAccessibleInterface>
+#include <QtCore/QDebug>
+#include <QtCore/QString>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QWindowsUiAutomation;
+
+
+QWindowsUiaBaseProvider::QWindowsUiaBaseProvider(QAccessible::Id id) :
+ m_id(id)
+{
+}
+
+QWindowsUiaBaseProvider::~QWindowsUiaBaseProvider()
+{
+}
+
+QAccessibleInterface *QWindowsUiaBaseProvider::accessibleInterface() const
+{
+ QAccessibleInterface *accessible = QAccessible::accessibleInterface(m_id);
+ if (accessible && accessible->isValid())
+ return accessible;
+ return nullptr;
+}
+
+QAccessible::Id QWindowsUiaBaseProvider::id() const
+{
+ return m_id;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ACCESSIBILITY
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiabaseprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiabaseprovider.h
new file mode 100644
index 0000000000..3ae403e8c5
--- /dev/null
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiabaseprovider.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSUIABASEPROVIDER_H
+#define QWINDOWSUIABASEPROVIDER_H
+
+#include <QtCore/QtConfig>
+#ifndef QT_NO_ACCESSIBILITY
+
+#include <QtGui/QAccessible>
+#include <QtGui/QAccessibleInterface>
+#include <QtCore/QPointer>
+
+#include <qwindowscombase.h>
+#include <QtWindowsUIAutomationSupport/private/qwindowsuiawrapper_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAccessibleInterface;
+class QDebug;
+
+// Base class for UI Automation providers.
+class QWindowsUiaBaseProvider : public QObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(QWindowsUiaBaseProvider)
+public:
+ explicit QWindowsUiaBaseProvider(QAccessible::Id id);
+ virtual ~QWindowsUiaBaseProvider();
+
+ QAccessibleInterface *accessibleInterface() const;
+ QAccessible::Id id() const;
+
+private:
+ QAccessible::Id m_id;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ACCESSIBILITY
+
+#endif // QWINDOWSUIABASEPROVIDER_H
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiagriditemprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiagriditemprovider.cpp
new file mode 100644
index 0000000000..e0502c00f3
--- /dev/null
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiagriditemprovider.cpp
@@ -0,0 +1,176 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/QtConfig>
+#ifndef QT_NO_ACCESSIBILITY
+
+#include "qwindowsuiagriditemprovider.h"
+#include "qwindowsuiamainprovider.h"
+#include "qwindowsuiautils.h"
+#include "qwindowscontext.h"
+
+#include <QtGui/QAccessible>
+#include <QtGui/QAccessibleInterface>
+#include <QtCore/QDebug>
+#include <QtCore/QString>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QWindowsUiAutomation;
+
+
+QWindowsUiaGridItemProvider::QWindowsUiaGridItemProvider(QAccessible::Id id) :
+ QWindowsUiaBaseProvider(id)
+{
+}
+
+QWindowsUiaGridItemProvider::~QWindowsUiaGridItemProvider()
+{
+}
+
+// Returns the row index of the item.
+HRESULT STDMETHODCALLTYPE QWindowsUiaGridItemProvider::get_Row(int *pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = 0;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QAccessibleTableCellInterface *tableCellInterface = accessible->tableCellInterface();
+ if (!tableCellInterface)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ *pRetVal = tableCellInterface->rowIndex();
+ return S_OK;
+}
+
+// Returns the column index of the item.
+HRESULT STDMETHODCALLTYPE QWindowsUiaGridItemProvider::get_Column(int *pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = 0;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QAccessibleTableCellInterface *tableCellInterface = accessible->tableCellInterface();
+ if (!tableCellInterface)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ *pRetVal = tableCellInterface->columnIndex();
+ return S_OK;
+}
+
+// Returns the number of rows occupied by the item.
+HRESULT STDMETHODCALLTYPE QWindowsUiaGridItemProvider::get_RowSpan(int *pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = 0;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QAccessibleTableCellInterface *tableCellInterface = accessible->tableCellInterface();
+ if (!tableCellInterface)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ *pRetVal = tableCellInterface->rowExtent();
+ return S_OK;
+}
+
+// Returns the number of columns occupied by the item.
+HRESULT STDMETHODCALLTYPE QWindowsUiaGridItemProvider::get_ColumnSpan(int *pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = 0;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QAccessibleTableCellInterface *tableCellInterface = accessible->tableCellInterface();
+ if (!tableCellInterface)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ *pRetVal = tableCellInterface->columnExtent();
+ return S_OK;
+}
+
+// Returns the provider for the cointaining table/tree.
+HRESULT STDMETHODCALLTYPE QWindowsUiaGridItemProvider::get_ContainingGrid(IRawElementProviderSimple **pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = nullptr;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QAccessibleTableCellInterface *tableCellInterface = accessible->tableCellInterface();
+ if (!tableCellInterface)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ if (QAccessibleInterface *table = tableCellInterface->table()) {
+ *pRetVal = QWindowsUiaMainProvider::providerForAccessible(table);
+ }
+ return S_OK;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ACCESSIBILITY
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiagriditemprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiagriditemprovider.h
new file mode 100644
index 0000000000..a93b50ef97
--- /dev/null
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiagriditemprovider.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSUIAGRIDITEMPROVIDER_H
+#define QWINDOWSUIAGRIDITEMPROVIDER_H
+
+#include <QtCore/QtConfig>
+#ifndef QT_NO_ACCESSIBILITY
+
+#include "qwindowsuiabaseprovider.h"
+
+QT_BEGIN_NAMESPACE
+
+// Implements the Grid Item control pattern provider. Used by items within a table/tree.
+class QWindowsUiaGridItemProvider : public QWindowsUiaBaseProvider,
+ public QWindowsComBase<IGridItemProvider>
+{
+ Q_DISABLE_COPY(QWindowsUiaGridItemProvider)
+public:
+ explicit QWindowsUiaGridItemProvider(QAccessible::Id id);
+ virtual ~QWindowsUiaGridItemProvider();
+
+ // IGridItemProvider
+ HRESULT STDMETHODCALLTYPE get_Row(int *pRetVal);
+ HRESULT STDMETHODCALLTYPE get_Column(int *pRetVal);
+ HRESULT STDMETHODCALLTYPE get_RowSpan(int *pRetVal);
+ HRESULT STDMETHODCALLTYPE get_ColumnSpan(int *pRetVal);
+ HRESULT STDMETHODCALLTYPE get_ContainingGrid(IRawElementProviderSimple **pRetVal);
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ACCESSIBILITY
+
+#endif // QWINDOWSUIAGRIDITEMPROVIDER_H
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiagridprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiagridprovider.cpp
new file mode 100644
index 0000000000..65c2df703b
--- /dev/null
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiagridprovider.cpp
@@ -0,0 +1,136 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/QtConfig>
+#ifndef QT_NO_ACCESSIBILITY
+
+#include "qwindowsuiagridprovider.h"
+#include "qwindowsuiamainprovider.h"
+#include "qwindowsuiautils.h"
+#include "qwindowscontext.h"
+
+#include <QtGui/QAccessible>
+#include <QtGui/QAccessibleInterface>
+#include <QtCore/QDebug>
+#include <QtCore/QString>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QWindowsUiAutomation;
+
+
+QWindowsUiaGridProvider::QWindowsUiaGridProvider(QAccessible::Id id) :
+ QWindowsUiaBaseProvider(id)
+{
+}
+
+QWindowsUiaGridProvider::~QWindowsUiaGridProvider()
+{
+}
+
+// Returns the provider for an item within a table/tree.
+HRESULT STDMETHODCALLTYPE QWindowsUiaGridProvider::GetItem(int row, int column, IRawElementProviderSimple **pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = nullptr;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QAccessibleTableInterface *tableInterface = accessible->tableInterface();
+ if (!tableInterface)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ if ((row >= 0) && (row < tableInterface->rowCount()) && (column >= 0) && (column < tableInterface->columnCount())) {
+ if (QAccessibleInterface *cell = tableInterface->cellAt(row, column)) {
+ *pRetVal = QWindowsUiaMainProvider::providerForAccessible(cell);
+ }
+ }
+ return S_OK;
+}
+
+// Returns the number of rows.
+HRESULT STDMETHODCALLTYPE QWindowsUiaGridProvider::get_RowCount(int *pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = 0;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QAccessibleTableInterface *tableInterface = accessible->tableInterface();
+ if (!tableInterface)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ *pRetVal = tableInterface->rowCount();
+ return S_OK;
+}
+
+// Returns the number of columns.
+HRESULT STDMETHODCALLTYPE QWindowsUiaGridProvider::get_ColumnCount(int *pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = 0;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QAccessibleTableInterface *tableInterface = accessible->tableInterface();
+ if (!tableInterface)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ *pRetVal = tableInterface->columnCount();
+ return S_OK;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ACCESSIBILITY
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiagridprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiagridprovider.h
new file mode 100644
index 0000000000..15521f98b3
--- /dev/null
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiagridprovider.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSUIAGRIDPROVIDER_H
+#define QWINDOWSUIAGRIDPROVIDER_H
+
+#include <QtCore/QtConfig>
+#ifndef QT_NO_ACCESSIBILITY
+
+#include "qwindowsuiabaseprovider.h"
+
+QT_BEGIN_NAMESPACE
+
+// Implements the Grid control pattern provider. Used by tables/trees.
+class QWindowsUiaGridProvider : public QWindowsUiaBaseProvider,
+ public QWindowsComBase<IGridProvider>
+{
+ Q_DISABLE_COPY(QWindowsUiaGridProvider)
+public:
+ explicit QWindowsUiaGridProvider(QAccessible::Id id);
+ virtual ~QWindowsUiaGridProvider();
+
+ // IGridProvider
+ HRESULT STDMETHODCALLTYPE GetItem(int row, int column, IRawElementProviderSimple **pRetVal);
+ HRESULT STDMETHODCALLTYPE get_RowCount(int *pRetVal);
+ HRESULT STDMETHODCALLTYPE get_ColumnCount(int *pRetVal);
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ACCESSIBILITY
+
+#endif // QWINDOWSUIAGRIDPROVIDER_H
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiainvokeprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiainvokeprovider.cpp
new file mode 100644
index 0000000000..2af883c4f6
--- /dev/null
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiainvokeprovider.cpp
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/QtConfig>
+#ifndef QT_NO_ACCESSIBILITY
+
+#include "qwindowsuiainvokeprovider.h"
+#include "qwindowsuiautils.h"
+#include "qwindowscontext.h"
+
+#include <QtGui/QAccessible>
+#include <QtGui/QAccessibleInterface>
+#include <QtCore/QDebug>
+#include <QtCore/QString>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QWindowsUiAutomation;
+
+
+QWindowsUiaInvokeProvider::QWindowsUiaInvokeProvider(QAccessible::Id id) :
+ QWindowsUiaBaseProvider(id)
+{
+}
+
+QWindowsUiaInvokeProvider::~QWindowsUiaInvokeProvider()
+{
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsUiaInvokeProvider::Invoke()
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QAccessibleActionInterface *actionInterface = accessible->actionInterface();
+ if (!actionInterface)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ actionInterface->doAction(QAccessibleActionInterface::pressAction());
+ return S_OK;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ACCESSIBILITY
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiainvokeprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiainvokeprovider.h
new file mode 100644
index 0000000000..2b8a646983
--- /dev/null
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiainvokeprovider.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSUIAINVOKEPROVIDER_H
+#define QWINDOWSUIAINVOKEPROVIDER_H
+
+#include <QtCore/QtConfig>
+#ifndef QT_NO_ACCESSIBILITY
+
+#include "qwindowsuiabaseprovider.h"
+
+QT_BEGIN_NAMESPACE
+
+// Implements the Invoke control pattern provider.
+class QWindowsUiaInvokeProvider : public QWindowsUiaBaseProvider,
+ public QWindowsComBase<IInvokeProvider>
+{
+ Q_DISABLE_COPY(QWindowsUiaInvokeProvider)
+public:
+ explicit QWindowsUiaInvokeProvider(QAccessible::Id id);
+ virtual ~QWindowsUiaInvokeProvider();
+
+ // IInvokeProvider
+ HRESULT STDMETHODCALLTYPE Invoke();
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ACCESSIBILITY
+
+#endif // QWINDOWSUIAINVOKEPROVIDER_H
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp
new file mode 100644
index 0000000000..46f73f81a0
--- /dev/null
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp
@@ -0,0 +1,638 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/QtConfig>
+#ifndef QT_NO_ACCESSIBILITY
+
+#include "qwindowsuiamainprovider.h"
+#include "qwindowsuiavalueprovider.h"
+#include "qwindowsuiarangevalueprovider.h"
+#include "qwindowsuiatextprovider.h"
+#include "qwindowsuiatoggleprovider.h"
+#include "qwindowsuiainvokeprovider.h"
+#include "qwindowsuiaselectionprovider.h"
+#include "qwindowsuiaselectionitemprovider.h"
+#include "qwindowsuiatableprovider.h"
+#include "qwindowsuiatableitemprovider.h"
+#include "qwindowsuiagridprovider.h"
+#include "qwindowsuiagriditemprovider.h"
+#include "qwindowscombase.h"
+#include "qwindowscontext.h"
+#include "qwindowsuiautils.h"
+#include "qwindowsuiaprovidercache.h"
+
+#include <QtCore/QDebug>
+#include <QtGui/QAccessible>
+#include <QtGui/QGuiApplication>
+#include <QtGui/QWindow>
+
+#if !defined(Q_CC_BOR) && !defined (Q_CC_GNU)
+#include <comdef.h>
+#endif
+
+#include <QtCore/qt_windows.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QWindowsUiAutomation;
+
+
+// Returns a cached instance of the provider for a specific acessible interface.
+QWindowsUiaMainProvider *QWindowsUiaMainProvider::providerForAccessible(QAccessibleInterface *accessible)
+{
+ if (!accessible)
+ return nullptr;
+
+ QAccessible::Id id = QAccessible::uniqueId(accessible);
+ QWindowsUiaProviderCache *providerCache = QWindowsUiaProviderCache::instance();
+ QWindowsUiaMainProvider *provider = qobject_cast<QWindowsUiaMainProvider *>(providerCache->providerForId(id));
+
+ if (provider) {
+ provider->AddRef();
+ } else {
+ provider = new QWindowsUiaMainProvider(accessible);
+ providerCache->insert(id, provider);
+ }
+ return provider;
+}
+
+QWindowsUiaMainProvider::QWindowsUiaMainProvider(QAccessibleInterface *a, int initialRefCount)
+ : QWindowsUiaBaseProvider(QAccessible::uniqueId(a)),
+ m_ref(initialRefCount)
+{
+}
+
+QWindowsUiaMainProvider::~QWindowsUiaMainProvider()
+{
+}
+
+void QWindowsUiaMainProvider::notifyFocusChange(QAccessibleEvent *event)
+{
+ if (QAccessibleInterface *accessible = event->accessibleInterface()) {
+ if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) {
+ QWindowsUiaWrapper::instance()->raiseAutomationEvent(provider, UIA_AutomationFocusChangedEventId);
+ }
+ }
+}
+
+void QWindowsUiaMainProvider::notifyStateChange(QAccessibleStateChangeEvent *event)
+{
+ if (QAccessibleInterface *accessible = event->accessibleInterface()) {
+ if (event->changedStates().checked || event->changedStates().checkStateMixed) {
+ // Notifies states changes in checkboxes.
+ if (accessible->role() == QAccessible::CheckBox) {
+ if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) {
+ VARIANT oldVal, newVal;
+ clearVariant(&oldVal);
+ int toggleState = ToggleState_Off;
+ if (accessible->state().checked)
+ toggleState = accessible->state().checkStateMixed ? ToggleState_Indeterminate : ToggleState_On;
+ setVariantI4(toggleState, &newVal);
+ QWindowsUiaWrapper::instance()->raiseAutomationPropertyChangedEvent(provider, UIA_ToggleToggleStatePropertyId, oldVal, newVal);
+ }
+ }
+ }
+ if (event->changedStates().active) {
+ if (accessible->role() == QAccessible::Window) {
+ // Notifies window opened/closed.
+ if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) {
+ if (accessible->state().active) {
+ QWindowsUiaWrapper::instance()->raiseAutomationEvent(provider, UIA_Window_WindowOpenedEventId);
+ } else {
+ QWindowsUiaWrapper::instance()->raiseAutomationEvent(provider, UIA_Window_WindowClosedEventId);
+ }
+ }
+ }
+ }
+ }
+}
+
+void QWindowsUiaMainProvider::notifyValueChange(QAccessibleValueChangeEvent *event)
+{
+ if (QAccessibleInterface *accessible = event->accessibleInterface()) {
+ if (QAccessibleValueInterface *valueInterface = accessible->valueInterface()) {
+ // Notifies changes in values of controls supporting the value interface.
+ if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) {
+ VARIANT oldVal, newVal;
+ clearVariant(&oldVal);
+ setVariantDouble(valueInterface->currentValue().toDouble(), &newVal);
+ QWindowsUiaWrapper::instance()->raiseAutomationPropertyChangedEvent(provider, UIA_RangeValueValuePropertyId, oldVal, newVal);
+ }
+ }
+ }
+}
+
+// Notifies changes in text content and selection state of text controls.
+void QWindowsUiaMainProvider::notifyTextChange(QAccessibleEvent *event)
+{
+ if (QAccessibleInterface *accessible = event->accessibleInterface()) {
+ if (accessible->textInterface()) {
+ if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) {
+ if (event->type() == QAccessible::TextSelectionChanged) {
+ QWindowsUiaWrapper::instance()->raiseAutomationEvent(provider, UIA_Text_TextSelectionChangedEventId);
+ } else if (event->type() == QAccessible::TextCaretMoved) {
+ if (!accessible->state().readOnly) {
+ QWindowsUiaWrapper::instance()->raiseAutomationEvent(provider, UIA_Text_TextSelectionChangedEventId);
+ }
+ } else {
+ QWindowsUiaWrapper::instance()->raiseAutomationEvent(provider, UIA_Text_TextChangedEventId);
+ }
+ }
+ }
+ }
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsUiaMainProvider::QueryInterface(REFIID iid, LPVOID *iface)
+{
+ if (!iface)
+ return E_INVALIDARG;
+ *iface = nullptr;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+
+ const bool result = qWindowsComQueryUnknownInterfaceMulti<IRawElementProviderSimple>(this, iid, iface)
+ || qWindowsComQueryInterface<IRawElementProviderSimple>(this, iid, iface)
+ || qWindowsComQueryInterface<IRawElementProviderFragment>(this, iid, iface)
+ || (accessible && hwndForAccessible(accessible) && qWindowsComQueryInterface<IRawElementProviderFragmentRoot>(this, iid, iface));
+ return result ? S_OK : E_NOINTERFACE;
+}
+
+ULONG QWindowsUiaMainProvider::AddRef()
+{
+ return ++m_ref;
+}
+
+ULONG STDMETHODCALLTYPE QWindowsUiaMainProvider::Release()
+{
+ if (!--m_ref) {
+ delete this;
+ return 0;
+ }
+ return m_ref;
+}
+
+HRESULT QWindowsUiaMainProvider::get_ProviderOptions(ProviderOptions *pRetVal)
+{
+ if (!pRetVal)
+ return E_INVALIDARG;
+ // We are STA, (OleInitialize()).
+ *pRetVal = static_cast<ProviderOptions>(ProviderOptions_ServerSideProvider | ProviderOptions_UseComThreading);
+ return S_OK;
+}
+
+// Return providers for specific control patterns
+HRESULT QWindowsUiaMainProvider::GetPatternProvider(PATTERNID idPattern, IUnknown **pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__ << idPattern;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = nullptr;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ switch (idPattern) {
+ case UIA_TextPatternId:
+ case UIA_TextPattern2Id:
+ // All text controls.
+ if (accessible->textInterface()) {
+ *pRetVal = new QWindowsUiaTextProvider(id());
+ }
+ break;
+ case UIA_ValuePatternId:
+ // All accessible controls return text(QAccessible::Value) (which may be empty).
+ *pRetVal = new QWindowsUiaValueProvider(id());
+ break;
+ case UIA_RangeValuePatternId:
+ // Controls providing a numeric value within a range (e.g., sliders, scroll bars, dials).
+ if (accessible->valueInterface()) {
+ *pRetVal = new QWindowsUiaRangeValueProvider(id());
+ }
+ break;
+ case UIA_TogglePatternId:
+ // Checkbox controls.
+ if (accessible->role() == QAccessible::CheckBox) {
+ *pRetVal = new QWindowsUiaToggleProvider(id());
+ }
+ break;
+ case UIA_SelectionPatternId:
+ // Lists of items.
+ if (accessible->role() == QAccessible::List) {
+ *pRetVal = new QWindowsUiaSelectionProvider(id());
+ }
+ break;
+ case UIA_SelectionItemPatternId:
+ // Items within a list and radio buttons.
+ if ((accessible->role() == QAccessible::RadioButton)
+ || (accessible->role() == QAccessible::ListItem)) {
+ *pRetVal = new QWindowsUiaSelectionItemProvider(id());
+ }
+ break;
+ case UIA_TablePatternId:
+ // Table/tree.
+ if (accessible->tableInterface()
+ && ((accessible->role() == QAccessible::Table) || (accessible->role() == QAccessible::Tree))) {
+ *pRetVal = new QWindowsUiaTableProvider(id());
+ }
+ break;
+ case UIA_TableItemPatternId:
+ // Item within a table/tree.
+ if (accessible->tableCellInterface()
+ && ((accessible->role() == QAccessible::Cell) || (accessible->role() == QAccessible::TreeItem))) {
+ *pRetVal = new QWindowsUiaTableItemProvider(id());
+ }
+ break;
+ case UIA_GridPatternId:
+ // Table/tree.
+ if (accessible->tableInterface()
+ && ((accessible->role() == QAccessible::Table) || (accessible->role() == QAccessible::Tree))) {
+ *pRetVal = new QWindowsUiaGridProvider(id());
+ }
+ break;
+ case UIA_GridItemPatternId:
+ // Item within a table/tree.
+ if (accessible->tableCellInterface()
+ && ((accessible->role() == QAccessible::Cell) || (accessible->role() == QAccessible::TreeItem))) {
+ *pRetVal = new QWindowsUiaGridItemProvider(id());
+ }
+ break;
+ case UIA_InvokePatternId:
+ // Things that have an invokable action (e.g., simple buttons).
+ if (accessible->actionInterface()) {
+ *pRetVal = new QWindowsUiaInvokeProvider(id());
+ }
+ break;
+ default:
+ break;
+ }
+
+ return S_OK;
+}
+
+HRESULT QWindowsUiaMainProvider::GetPropertyValue(PROPERTYID idProp, VARIANT *pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__ << idProp;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ clearVariant(pRetVal);
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ bool clientTopLevel = (accessible->role() == QAccessible::Client)
+ && accessible->parent() && (accessible->parent()->role() == QAccessible::Application);
+
+ switch (idProp) {
+ case UIA_ProcessIdPropertyId:
+ // PID
+ setVariantI4(int(GetCurrentProcessId()), pRetVal);
+ break;
+ case UIA_AccessKeyPropertyId:
+ // Accelerator key.
+ setVariantString(accessible->text(QAccessible::Accelerator), pRetVal);
+ break;
+ case UIA_AutomationIdPropertyId:
+ // Automation ID, which can be used by tools to select a specific control in the UI.
+ setVariantString(automationIdForAccessible(accessible), pRetVal);
+ break;
+ case UIA_ClassNamePropertyId:
+ // Class name.
+ if (QObject *o = accessible->object()) {
+ QString className = QLatin1String(o->metaObject()->className());
+ setVariantString(className, pRetVal);
+ }
+ break;
+ case UIA_FrameworkIdPropertyId:
+ setVariantString(QStringLiteral("Qt"), pRetVal);
+ break;
+ case UIA_ControlTypePropertyId:
+ if (clientTopLevel) {
+ // Reports a top-level widget as a window, instead of "custom".
+ setVariantI4(UIA_WindowControlTypeId, pRetVal);
+ } else {
+ // Control type converted from role.
+ setVariantI4(roleToControlTypeId(accessible->role()), pRetVal);
+ }
+ break;
+ case UIA_HelpTextPropertyId:
+ setVariantString(accessible->text(QAccessible::Help), pRetVal);
+ break;
+ case UIA_HasKeyboardFocusPropertyId:
+ setVariantBool(accessible->state().focused, pRetVal);
+ break;
+ case UIA_IsKeyboardFocusablePropertyId:
+ setVariantBool(accessible->state().focusable, pRetVal);
+ break;
+ case UIA_IsOffscreenPropertyId:
+ setVariantBool(false, pRetVal);
+ break;
+ case UIA_IsContentElementPropertyId:
+ setVariantBool(true, pRetVal);
+ break;
+ case UIA_IsControlElementPropertyId:
+ setVariantBool(true, pRetVal);
+ break;
+ case UIA_IsEnabledPropertyId:
+ setVariantBool(!accessible->state().disabled, pRetVal);
+ break;
+ case UIA_IsPasswordPropertyId:
+ setVariantBool(accessible->role() == QAccessible::EditableText
+ && accessible->state().passwordEdit, pRetVal);
+ break;
+ case UIA_IsPeripheralPropertyId:
+ // True for peripheral UIs.
+ if (QWindow *window = windowForAccessible(accessible)) {
+ const Qt::WindowType wt = window->type();
+ setVariantBool(wt == Qt::Popup || wt == Qt::ToolTip || wt == Qt::SplashScreen, pRetVal);
+ }
+ break;
+ case UIA_FullDescriptionPropertyId:
+ setVariantString(accessible->text(QAccessible::Description), pRetVal);
+ break;
+ case UIA_NamePropertyId: {
+ QString name = accessible->text(QAccessible::Name);
+ if (name.isEmpty() && clientTopLevel) {
+ name = QCoreApplication::applicationName();
+ }
+ setVariantString(name, pRetVal);
+ break;
+ }
+ default:
+ break;
+ }
+ return S_OK;
+}
+
+// Generates an ID based on the name of the controls and their parents.
+QString QWindowsUiaMainProvider::automationIdForAccessible(const QAccessibleInterface *accessible)
+{
+ QString result;
+ if (accessible) {
+ QObject *obj = accessible->object();
+ while (obj) {
+ QString name = obj->objectName();
+ if (name.isEmpty())
+ return QString();
+ if (!result.isEmpty())
+ result.prepend(QLatin1Char('.'));
+ result.prepend(name);
+ obj = obj->parent();
+ }
+ }
+ return result;
+}
+
+HRESULT QWindowsUiaMainProvider::get_HostRawElementProvider(IRawElementProviderSimple **pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = nullptr;
+
+ // Returns a host provider only for controls associated with a native window handle. Others should return NULL.
+ if (QAccessibleInterface *accessible = accessibleInterface()) {
+ if (HWND hwnd = hwndForAccessible(accessible)) {
+ return QWindowsUiaWrapper::instance()->hostProviderFromHwnd(hwnd, pRetVal);
+ }
+ }
+ return S_OK;
+}
+
+// Navigates within the tree of accessible controls.
+HRESULT QWindowsUiaMainProvider::Navigate(NavigateDirection direction, IRawElementProviderFragment **pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__ << direction << " this: " << this;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = nullptr;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QAccessibleInterface *targetacc = nullptr;
+
+ switch (direction) {
+ case NavigateDirection_Parent:
+ targetacc = accessible->parent();
+ if (targetacc && (targetacc->role() == QAccessible::Application)) {
+ targetacc = nullptr; // The app's children are considered top level objects.
+ }
+ break;
+ case NavigateDirection_FirstChild:
+ targetacc = accessible->child(0);
+ break;
+ case NavigateDirection_LastChild:
+ targetacc = accessible->child(accessible->childCount() - 1);
+ break;
+ case NavigateDirection_NextSibling:
+ case NavigateDirection_PreviousSibling:
+ if (QAccessibleInterface *parent = accessible->parent()) {
+ if (parent->isValid()) {
+ int index = parent->indexOfChild(accessible);
+ index += (direction == NavigateDirection_NextSibling) ? 1 : -1;
+ if (index >= 0 && index < parent->childCount())
+ targetacc = parent->child(index);
+ }
+ }
+ break;
+ }
+
+ if (targetacc)
+ *pRetVal = providerForAccessible(targetacc);
+ return S_OK;
+}
+
+// Returns a unique id assigned to the UI element, used as key by the UI Automation framework.
+HRESULT QWindowsUiaMainProvider::GetRuntimeId(SAFEARRAY **pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = nullptr;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ // The UiaAppendRuntimeId constant is used to make then ID unique
+ // among multiple instances running on the system.
+ int rtId[] = { UiaAppendRuntimeId, int(id()) };
+
+ if ((*pRetVal = SafeArrayCreateVector(VT_I4, 0, 2))) {
+ for (LONG i = 0; i < 2; ++i)
+ SafeArrayPutElement(*pRetVal, &i, &rtId[i]);
+ }
+ return S_OK;
+}
+
+// Returns the bounding rectangle for the accessible control.
+HRESULT QWindowsUiaMainProvider::get_BoundingRectangle(UiaRect *pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QWindow *window = windowForAccessible(accessible);
+ if (!window)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ rectToNativeUiaRect(accessible->rect(), window, pRetVal);
+ return S_OK;
+}
+
+HRESULT QWindowsUiaMainProvider::GetEmbeddedFragmentRoots(SAFEARRAY **pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = nullptr;
+ // No embedded roots.
+ return S_OK;
+}
+
+// Sets focus to the control.
+HRESULT QWindowsUiaMainProvider::SetFocus()
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QAccessibleActionInterface *actionInterface = accessible->actionInterface();
+ if (!actionInterface)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ actionInterface->doAction(QAccessibleActionInterface::setFocusAction());
+ return S_OK;
+}
+
+HRESULT QWindowsUiaMainProvider::get_FragmentRoot(IRawElementProviderFragmentRoot **pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = nullptr;
+
+ // Our UI Automation implementation considers the window as the root for
+ // non-native controls/fragments.
+ if (QAccessibleInterface *accessible = accessibleInterface()) {
+ if (QWindow *window = windowForAccessible(accessible)) {
+ if (QAccessibleInterface *rootacc = window->accessibleRoot()) {
+ *pRetVal = providerForAccessible(rootacc);
+ }
+ }
+ }
+ return S_OK;
+}
+
+// Returns a provider for the UI element present at the specified screen coordinates.
+HRESULT QWindowsUiaMainProvider::ElementProviderFromPoint(double x, double y, IRawElementProviderFragment **pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__ << x << y;
+
+ if (!pRetVal) {
+ return E_INVALIDARG;
+ }
+ *pRetVal = nullptr;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QWindow *window = windowForAccessible(accessible);
+ if (!window)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ // Scales coordinates from High DPI screens.
+ UiaPoint uiaPoint = {x, y};
+ QPoint point;
+ nativeUiaPointToPoint(uiaPoint, window, &point);
+
+ QAccessibleInterface *targetacc = accessible->childAt(point.x(), point.y());
+
+ if (targetacc) {
+ QAccessibleInterface *acc = targetacc;
+ // Controls can be embedded within grouping elements. By default returns the innermost control.
+ while (acc) {
+ targetacc = acc;
+ // For accessibility tools it may be better to return the text element instead of its subcomponents.
+ if (targetacc->textInterface()) break;
+ acc = acc->childAt(point.x(), point.y());
+ }
+ *pRetVal = providerForAccessible(targetacc);
+ }
+ return S_OK;
+}
+
+// Returns the fragment with focus.
+HRESULT QWindowsUiaMainProvider::GetFocus(IRawElementProviderFragment **pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = nullptr;
+
+ if (QAccessibleInterface *accessible = accessibleInterface()) {
+ if (QAccessibleInterface *focusacc = accessible->focusChild()) {
+ *pRetVal = providerForAccessible(focusacc);
+ }
+ }
+ return S_OK;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ACCESSIBILITY
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.h
new file mode 100644
index 0000000000..893cbf7f8a
--- /dev/null
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.h
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSUIAMAINPROVIDER_H
+#define QWINDOWSUIAMAINPROVIDER_H
+
+#include <QtCore/QtConfig>
+#ifndef QT_NO_ACCESSIBILITY
+
+#include "qwindowsuiabaseprovider.h"
+
+#include <QtCore/QPointer>
+#include <QtCore/QSharedPointer>
+#include <QtCore/qt_windows.h>
+#include <QtGui/QAccessible>
+
+QT_BEGIN_NAMESPACE
+
+// The main UI Automation class.
+class QWindowsUiaMainProvider :
+ public QWindowsUiaBaseProvider,
+ public IRawElementProviderSimple,
+ public IRawElementProviderFragment,
+ public IRawElementProviderFragmentRoot
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(QWindowsUiaMainProvider)
+public:
+ static QWindowsUiaMainProvider *providerForAccessible(QAccessibleInterface *accessible);
+ explicit QWindowsUiaMainProvider(QAccessibleInterface *a, int initialRefCount = 1);
+ virtual ~QWindowsUiaMainProvider();
+ static void notifyFocusChange(QAccessibleEvent *event);
+ static void notifyStateChange(QAccessibleStateChangeEvent *event);
+ static void notifyValueChange(QAccessibleValueChangeEvent *event);
+ static void notifyTextChange(QAccessibleEvent *event);
+
+ // IUnknown
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID id, LPVOID *iface);
+ ULONG STDMETHODCALLTYPE AddRef();
+ ULONG STDMETHODCALLTYPE Release();
+
+ // IRawElementProviderSimple methods
+ HRESULT STDMETHODCALLTYPE get_ProviderOptions(ProviderOptions *pRetVal);
+ HRESULT STDMETHODCALLTYPE GetPatternProvider(PATTERNID idPattern, IUnknown **pRetVal);
+ HRESULT STDMETHODCALLTYPE GetPropertyValue(PROPERTYID idProp, VARIANT *pRetVal);
+ HRESULT STDMETHODCALLTYPE get_HostRawElementProvider(IRawElementProviderSimple **pRetVal);
+
+ // IRawElementProviderFragment methods
+ HRESULT STDMETHODCALLTYPE Navigate(NavigateDirection direction, IRawElementProviderFragment **pRetVal);
+ HRESULT STDMETHODCALLTYPE GetRuntimeId(SAFEARRAY **pRetVal);
+ HRESULT STDMETHODCALLTYPE get_BoundingRectangle(UiaRect *pRetVal);
+ HRESULT STDMETHODCALLTYPE GetEmbeddedFragmentRoots(SAFEARRAY **pRetVal);
+ HRESULT STDMETHODCALLTYPE SetFocus();
+ HRESULT STDMETHODCALLTYPE get_FragmentRoot(IRawElementProviderFragmentRoot **pRetVal);
+
+ // IRawElementProviderFragmentRoot methods
+ HRESULT STDMETHODCALLTYPE ElementProviderFromPoint(double x, double y, IRawElementProviderFragment **pRetVal);
+ HRESULT STDMETHODCALLTYPE GetFocus(IRawElementProviderFragment **pRetVal);
+
+private:
+ QString automationIdForAccessible(const QAccessibleInterface *accessible);
+ ULONG m_ref;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ACCESSIBILITY
+
+#endif // QWINDOWSUIAMAINPROVIDER_H
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiaprovidercache.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiaprovidercache.cpp
new file mode 100644
index 0000000000..9f0a1e126f
--- /dev/null
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiaprovidercache.cpp
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/QtConfig>
+#ifndef QT_NO_ACCESSIBILITY
+
+#include "qwindowsuiaprovidercache.h"
+#include "qwindowsuiautils.h"
+#include "qwindowscontext.h"
+
+#include <QtCore/QDebug>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QWindowsUiAutomation;
+
+
+// Private constructor
+QWindowsUiaProviderCache::QWindowsUiaProviderCache()
+{
+}
+
+// shared instance
+QWindowsUiaProviderCache *QWindowsUiaProviderCache::instance()
+{
+ static QWindowsUiaProviderCache providerCache;
+ return &providerCache;
+}
+
+// Returns the provider instance associated with the ID, or nullptr.
+QWindowsUiaBaseProvider *QWindowsUiaProviderCache::providerForId(QAccessible::Id id) const
+{
+ return providerTable.value(id);
+}
+
+// Inserts a provider in the cache and associates it with an accessibility ID.
+void QWindowsUiaProviderCache::insert(QAccessible::Id id, QWindowsUiaBaseProvider *provider)
+{
+ remove(id);
+ if (provider) {
+ providerTable[id] = provider;
+ inverseTable[provider] = id;
+ // Connects the destroyed signal to our slot, to remove deleted objects from the cache.
+ QObject::connect(provider, &QObject::destroyed, this, &QWindowsUiaProviderCache::objectDestroyed);
+ }
+}
+
+// Removes deleted provider objects from the cache.
+void QWindowsUiaProviderCache::objectDestroyed(QObject *obj)
+{
+ // We have to use the inverse table to map the object address back to its ID,
+ // since at this point (called from QObject destructor), it has already been
+ // partially destroyed and we cannot treat it as a provider.
+ auto it = inverseTable.find(obj);
+ if (it != inverseTable.end()) {
+ providerTable.remove(*it);
+ inverseTable.remove(obj);
+ }
+}
+
+// Removes a provider with a given id from the cache.
+void QWindowsUiaProviderCache::remove(QAccessible::Id id)
+{
+ inverseTable.remove(providerTable.value(id));
+ providerTable.remove(id);
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ACCESSIBILITY
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiaprovidercache.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiaprovidercache.h
new file mode 100644
index 0000000000..7ad30ac39c
--- /dev/null
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiaprovidercache.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSUIAPROVIDERCACHE_H
+#define QWINDOWSUIAPROVIDERCACHE_H
+
+#include <QtCore/QtConfig>
+#ifndef QT_NO_ACCESSIBILITY
+
+#include "qwindowsuiabaseprovider.h"
+
+#include <QtCore/QHash>
+#include <QtGui/QAccessible>
+#include <QtGui/QAccessibleInterface>
+
+QT_BEGIN_NAMESPACE
+
+// Singleton used to cache provider instances using the accessibility ID as the key.
+class QWindowsUiaProviderCache : public QObject
+{
+ QWindowsUiaProviderCache();
+ Q_OBJECT
+public:
+ static QWindowsUiaProviderCache *instance();
+ QWindowsUiaBaseProvider *providerForId(QAccessible::Id id) const;
+ void insert(QAccessible::Id id, QWindowsUiaBaseProvider *provider);
+ void remove(QAccessible::Id id);
+
+private Q_SLOTS:
+ void objectDestroyed(QObject *obj);
+
+private:
+ QHash<QAccessible::Id, QWindowsUiaBaseProvider *> providerTable;
+ QHash<QObject *, QAccessible::Id> inverseTable;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ACCESSIBILITY
+
+#endif // QWINDOWSUIAPROVIDERCACHE_H
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiarangevalueprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiarangevalueprovider.cpp
new file mode 100644
index 0000000000..0cd09c3f0a
--- /dev/null
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiarangevalueprovider.cpp
@@ -0,0 +1,190 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/QtConfig>
+#ifndef QT_NO_ACCESSIBILITY
+
+#include "qwindowsuiarangevalueprovider.h"
+#include "qwindowsuiautils.h"
+#include "qwindowscontext.h"
+
+#include <QtGui/QAccessible>
+#include <QtGui/QAccessibleInterface>
+#include <QtCore/QDebug>
+#include <QtCore/QString>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QWindowsUiAutomation;
+
+
+QWindowsUiaRangeValueProvider::QWindowsUiaRangeValueProvider(QAccessible::Id id) :
+ QWindowsUiaBaseProvider(id)
+{
+}
+
+QWindowsUiaRangeValueProvider::~QWindowsUiaRangeValueProvider()
+{
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsUiaRangeValueProvider::SetValue(double val)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QAccessibleValueInterface *valueInterface = accessible->valueInterface();
+ if (!valueInterface)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ double minimum = valueInterface->minimumValue().toDouble();
+ double maximum = valueInterface->maximumValue().toDouble();
+ if ((val < minimum) || (val > maximum))
+ return E_INVALIDARG;
+
+ valueInterface->setCurrentValue(QVariant(val));
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsUiaRangeValueProvider::get_Value(double *pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QAccessibleValueInterface *valueInterface = accessible->valueInterface();
+ if (!valueInterface)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QVariant varValue = valueInterface->currentValue();
+ *pRetVal = varValue.toDouble();
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsUiaRangeValueProvider::get_IsReadOnly(BOOL *pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ *pRetVal = accessible->state().readOnly;
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsUiaRangeValueProvider::get_Maximum(double *pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QAccessibleValueInterface *valueInterface = accessible->valueInterface();
+ if (!valueInterface)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QVariant varValue = valueInterface->maximumValue();
+ *pRetVal = varValue.toDouble();
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsUiaRangeValueProvider::get_Minimum(double *pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QAccessibleValueInterface *valueInterface = accessible->valueInterface();
+ if (!valueInterface)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QVariant varValue = valueInterface->minimumValue();
+ *pRetVal = varValue.toDouble();
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsUiaRangeValueProvider::get_LargeChange(double *pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+ return get_SmallChange(pRetVal);
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsUiaRangeValueProvider::get_SmallChange(double *pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QAccessibleValueInterface *valueInterface = accessible->valueInterface();
+ if (!valueInterface)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QVariant varValue = valueInterface->minimumStepSize();
+ *pRetVal = varValue.toDouble();
+ return S_OK;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ACCESSIBILITY
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiarangevalueprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiarangevalueprovider.h
new file mode 100644
index 0000000000..f742ef99c2
--- /dev/null
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiarangevalueprovider.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSUIARANGEVALUEPROVIDER_H
+#define QWINDOWSUIARANGEVALUEPROVIDER_H
+
+#include <QtCore/QtConfig>
+#ifndef QT_NO_ACCESSIBILITY
+
+#include "qwindowsuiabaseprovider.h"
+
+QT_BEGIN_NAMESPACE
+
+// Implements the Range Value control pattern provider.
+class QWindowsUiaRangeValueProvider : public QWindowsUiaBaseProvider,
+ public QWindowsComBase<IRangeValueProvider>
+{
+ Q_DISABLE_COPY(QWindowsUiaRangeValueProvider)
+public:
+ explicit QWindowsUiaRangeValueProvider(QAccessible::Id id);
+ virtual ~QWindowsUiaRangeValueProvider();
+
+ // IRangeValueProvider
+ HRESULT STDMETHODCALLTYPE SetValue(double val);
+ HRESULT STDMETHODCALLTYPE get_Value(double *pRetVal);
+ HRESULT STDMETHODCALLTYPE get_IsReadOnly(BOOL *pRetVal);
+ HRESULT STDMETHODCALLTYPE get_Maximum(double *pRetVal);
+ HRESULT STDMETHODCALLTYPE get_Minimum(double *pRetVal);
+ HRESULT STDMETHODCALLTYPE get_LargeChange(double *pRetVal);
+ HRESULT STDMETHODCALLTYPE get_SmallChange(double *pRetVal);
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ACCESSIBILITY
+
+#endif // QWINDOWSUIARANGEVALUEPROVIDER_H
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionitemprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionitemprovider.cpp
new file mode 100644
index 0000000000..45216a6d1c
--- /dev/null
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionitemprovider.cpp
@@ -0,0 +1,201 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/QtConfig>
+#ifndef QT_NO_ACCESSIBILITY
+
+#include "qwindowsuiaselectionitemprovider.h"
+#include "qwindowsuiamainprovider.h"
+#include "qwindowsuiautils.h"
+#include "qwindowscontext.h"
+
+#include <QtGui/QAccessible>
+#include <QtGui/QAccessibleInterface>
+#include <QtCore/QDebug>
+#include <QtCore/QString>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QWindowsUiAutomation;
+
+
+QWindowsUiaSelectionItemProvider::QWindowsUiaSelectionItemProvider(QAccessible::Id id) :
+ QWindowsUiaBaseProvider(id)
+{
+}
+
+QWindowsUiaSelectionItemProvider::~QWindowsUiaSelectionItemProvider()
+{
+}
+
+// Selects the element (deselecting all others).
+HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionItemProvider::Select()
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QAccessibleActionInterface *actionInterface = accessible->actionInterface();
+ if (!actionInterface)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ if (accessible->role() == QAccessible::RadioButton) {
+ // For radio buttons we just invoke the selection action; others are automatically deselected.
+ actionInterface->doAction(QAccessibleActionInterface::pressAction());
+ } else {
+ // Toggle list item if not already selected. It must be done first to support all selection modes.
+ if (!accessible->state().selected) {
+ actionInterface->doAction(QAccessibleActionInterface::toggleAction());
+ }
+ // Toggle selected siblings.
+ if (QAccessibleInterface *parent = accessible->parent()) {
+ for (int i = 0; i < parent->childCount(); ++i) {
+ if (QAccessibleInterface *sibling = parent->child(i)) {
+ if ((sibling != accessible) && (sibling->state().selected)) {
+ if (QAccessibleActionInterface *siblingAction = sibling->actionInterface()) {
+ siblingAction->doAction(QAccessibleActionInterface::toggleAction());
+ }
+ }
+ }
+ }
+ }
+ }
+ return S_OK;
+}
+
+// Adds the element to the list of selected elements.
+HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionItemProvider::AddToSelection()
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QAccessibleActionInterface *actionInterface = accessible->actionInterface();
+ if (!actionInterface)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ if (accessible->role() == QAccessible::RadioButton) {
+ // For radio buttons we invoke the selection action.
+ actionInterface->doAction(QAccessibleActionInterface::pressAction());
+ } else {
+ // Toggle list item if not already selected.
+ if (!accessible->state().selected) {
+ actionInterface->doAction(QAccessibleActionInterface::toggleAction());
+ }
+ }
+ return S_OK;
+}
+
+// Removes a list item from selection.
+HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionItemProvider::RemoveFromSelection()
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QAccessibleActionInterface *actionInterface = accessible->actionInterface();
+ if (!actionInterface)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ if (accessible->role() != QAccessible::RadioButton) {
+ if (accessible->state().selected) {
+ actionInterface->doAction(QAccessibleActionInterface::toggleAction());
+ }
+ }
+
+ return S_OK;
+}
+
+// Returns true if element is currently selected.
+HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionItemProvider::get_IsSelected(BOOL *pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = FALSE;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ if (accessible->role() == QAccessible::RadioButton)
+ *pRetVal = accessible->state().checked;
+ else
+ *pRetVal = accessible->state().selected;
+ return S_OK;
+}
+
+// Returns the provider for the container element (e.g., the list for the list item).
+HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionItemProvider::get_SelectionContainer(IRawElementProviderSimple **pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = nullptr;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QAccessibleActionInterface *actionInterface = accessible->actionInterface();
+ if (!actionInterface)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ // Radio buttons do not require a container.
+ if (accessible->role() == QAccessible::ListItem) {
+ if (QAccessibleInterface *parent = accessible->parent()) {
+ if (parent->role() == QAccessible::List) {
+ *pRetVal = QWindowsUiaMainProvider::providerForAccessible(parent);
+ }
+ }
+ }
+ return S_OK;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ACCESSIBILITY
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionitemprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionitemprovider.h
new file mode 100644
index 0000000000..6a9b5b1e4b
--- /dev/null
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionitemprovider.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSUIASELECTIONITEMPROVIDER_H
+#define QWINDOWSUIASELECTIONITEMPROVIDER_H
+
+#include <QtCore/QtConfig>
+#ifndef QT_NO_ACCESSIBILITY
+
+#include "qwindowsuiabaseprovider.h"
+
+QT_BEGIN_NAMESPACE
+
+// Implements the Selection Item control pattern provider. Used for List items and radio buttons.
+class QWindowsUiaSelectionItemProvider : public QWindowsUiaBaseProvider,
+ public QWindowsComBase<ISelectionItemProvider>
+{
+ Q_DISABLE_COPY(QWindowsUiaSelectionItemProvider)
+public:
+ explicit QWindowsUiaSelectionItemProvider(QAccessible::Id id);
+ virtual ~QWindowsUiaSelectionItemProvider();
+
+ // ISelectionItemProvider
+ HRESULT STDMETHODCALLTYPE Select();
+ HRESULT STDMETHODCALLTYPE AddToSelection();
+ HRESULT STDMETHODCALLTYPE RemoveFromSelection();
+ HRESULT STDMETHODCALLTYPE get_IsSelected(BOOL *pRetVal);
+ HRESULT STDMETHODCALLTYPE get_SelectionContainer(IRawElementProviderSimple **pRetVal);
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ACCESSIBILITY
+
+#endif // QWINDOWSUIASELECTIONITEMPROVIDER_H
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionprovider.cpp
new file mode 100644
index 0000000000..1c06503bfc
--- /dev/null
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionprovider.cpp
@@ -0,0 +1,147 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/QtConfig>
+#ifndef QT_NO_ACCESSIBILITY
+
+#include "qwindowsuiaselectionprovider.h"
+#include "qwindowsuiamainprovider.h"
+#include "qwindowsuiautils.h"
+#include "qwindowscontext.h"
+
+#include <QtGui/QAccessible>
+#include <QtGui/QAccessibleInterface>
+#include <QtCore/QDebug>
+#include <QtCore/QString>
+#include <QList>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QWindowsUiAutomation;
+
+
+QWindowsUiaSelectionProvider::QWindowsUiaSelectionProvider(QAccessible::Id id) :
+ QWindowsUiaBaseProvider(id)
+{
+}
+
+QWindowsUiaSelectionProvider::~QWindowsUiaSelectionProvider()
+{
+}
+
+// Returns an array of providers with the selected items.
+HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionProvider::GetSelection(SAFEARRAY **pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = nullptr;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ // First put selected items in a list, then build a safe array with the right size.
+ QList<QAccessibleInterface *> selectedList;
+ for (int i = 0; i < accessible->childCount(); ++i) {
+ if (QAccessibleInterface *child = accessible->child(i)) {
+ if (child->state().selected) {
+ selectedList.append(child);
+ }
+ }
+ }
+
+ if ((*pRetVal = SafeArrayCreateVector(VT_UNKNOWN, 0, selectedList.size()))) {
+ for (LONG i = 0; i < selectedList.size(); ++i) {
+ if (QWindowsUiaMainProvider *childProvider = QWindowsUiaMainProvider::providerForAccessible(selectedList.at(i))) {
+ SafeArrayPutElement(*pRetVal, &i, static_cast<IRawElementProviderSimple *>(childProvider));
+ childProvider->Release();
+ }
+ }
+ }
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionProvider::get_CanSelectMultiple(BOOL *pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = FALSE;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ *pRetVal = accessible->state().multiSelectable;
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsUiaSelectionProvider::get_IsSelectionRequired(BOOL *pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = FALSE;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ // Initially returns false if none are selected. After the first selection, it may be required.
+ bool anySelected = false;
+ for (int i = 0; i < accessible->childCount(); ++i) {
+ if (QAccessibleInterface *child = accessible->child(i)) {
+ if (child->state().selected) {
+ anySelected = true;
+ break;
+ }
+ }
+ }
+
+ *pRetVal = anySelected && !accessible->state().multiSelectable && !accessible->state().extSelectable;
+ return S_OK;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ACCESSIBILITY
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionprovider.h
new file mode 100644
index 0000000000..5a07a82ac8
--- /dev/null
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiaselectionprovider.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSUIASELECTIONPROVIDER_H
+#define QWINDOWSUIASELECTIONPROVIDER_H
+
+#include <QtCore/QtConfig>
+#ifndef QT_NO_ACCESSIBILITY
+
+#include "qwindowsuiabaseprovider.h"
+
+QT_BEGIN_NAMESPACE
+
+// Implements the Selection control pattern provider. Used for Lists.
+class QWindowsUiaSelectionProvider : public QWindowsUiaBaseProvider,
+ public QWindowsComBase<ISelectionProvider>
+{
+ Q_DISABLE_COPY(QWindowsUiaSelectionProvider)
+public:
+ explicit QWindowsUiaSelectionProvider(QAccessible::Id id);
+ virtual ~QWindowsUiaSelectionProvider();
+
+ // ISelectionProvider
+ HRESULT STDMETHODCALLTYPE GetSelection(SAFEARRAY **pRetVal);
+ HRESULT STDMETHODCALLTYPE get_CanSelectMultiple(BOOL *pRetVal);
+ HRESULT STDMETHODCALLTYPE get_IsSelectionRequired(BOOL *pRetVal);
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ACCESSIBILITY
+
+#endif // QWINDOWSUIASELECTIONPROVIDER_H
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiatableitemprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiatableitemprovider.cpp
new file mode 100644
index 0000000000..3ea29fc86c
--- /dev/null
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiatableitemprovider.cpp
@@ -0,0 +1,129 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/QtConfig>
+#ifndef QT_NO_ACCESSIBILITY
+
+#include "qwindowsuiatableitemprovider.h"
+#include "qwindowsuiamainprovider.h"
+#include "qwindowsuiautils.h"
+#include "qwindowscontext.h"
+
+#include <QtGui/QAccessible>
+#include <QtGui/QAccessibleInterface>
+#include <QtCore/QDebug>
+#include <QtCore/QString>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QWindowsUiAutomation;
+
+
+QWindowsUiaTableItemProvider::QWindowsUiaTableItemProvider(QAccessible::Id id) :
+ QWindowsUiaBaseProvider(id)
+{
+}
+
+QWindowsUiaTableItemProvider::~QWindowsUiaTableItemProvider()
+{
+}
+
+// Returns the providers for the row headers associated with the item.
+HRESULT STDMETHODCALLTYPE QWindowsUiaTableItemProvider::GetRowHeaderItems(SAFEARRAY **pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = nullptr;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QAccessibleTableCellInterface *tableCellInterface = accessible->tableCellInterface();
+ if (!tableCellInterface)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QList<QAccessibleInterface *> headers = tableCellInterface->rowHeaderCells();
+
+ if ((*pRetVal = SafeArrayCreateVector(VT_UNKNOWN, 0, headers.size()))) {
+ for (LONG i = 0; i < headers.size(); ++i) {
+ if (QWindowsUiaMainProvider *headerProvider = QWindowsUiaMainProvider::providerForAccessible(headers.at(i))) {
+ SafeArrayPutElement(*pRetVal, &i, static_cast<IRawElementProviderSimple *>(headerProvider));
+ headerProvider->Release();
+ }
+ }
+ }
+ return S_OK;
+}
+
+// Returns the providers for the column headers associated with the item.
+HRESULT STDMETHODCALLTYPE QWindowsUiaTableItemProvider::GetColumnHeaderItems(SAFEARRAY **pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = nullptr;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QAccessibleTableCellInterface *tableCellInterface = accessible->tableCellInterface();
+ if (!tableCellInterface)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QList<QAccessibleInterface *> headers = tableCellInterface->columnHeaderCells();
+
+ if ((*pRetVal = SafeArrayCreateVector(VT_UNKNOWN, 0, headers.size()))) {
+ for (LONG i = 0; i < headers.size(); ++i) {
+ if (QWindowsUiaMainProvider *headerProvider = QWindowsUiaMainProvider::providerForAccessible(headers.at(i))) {
+ SafeArrayPutElement(*pRetVal, &i, static_cast<IRawElementProviderSimple *>(headerProvider));
+ headerProvider->Release();
+ }
+ }
+ }
+ return S_OK;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ACCESSIBILITY
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiatableitemprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiatableitemprovider.h
new file mode 100644
index 0000000000..277884c980
--- /dev/null
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiatableitemprovider.h
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSUIATABLEITEMPROVIDER_H
+#define QWINDOWSUIATABLEITEMPROVIDER_H
+
+#include <QtCore/QtConfig>
+#ifndef QT_NO_ACCESSIBILITY
+
+#include "qwindowsuiabaseprovider.h"
+
+QT_BEGIN_NAMESPACE
+
+// Implements the Table Item control pattern provider. Used by items within a table/tree.
+class QWindowsUiaTableItemProvider : public QWindowsUiaBaseProvider,
+ public QWindowsComBase<ITableItemProvider>
+{
+ Q_DISABLE_COPY(QWindowsUiaTableItemProvider)
+public:
+ explicit QWindowsUiaTableItemProvider(QAccessible::Id id);
+ virtual ~QWindowsUiaTableItemProvider();
+
+ // ITableItemProvider
+ HRESULT STDMETHODCALLTYPE GetRowHeaderItems(SAFEARRAY **pRetVal);
+ HRESULT STDMETHODCALLTYPE GetColumnHeaderItems(SAFEARRAY **pRetVal);
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ACCESSIBILITY
+
+#endif // QWINDOWSUIATABLEITEMPROVIDER_H
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiatableprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiatableprovider.cpp
new file mode 100644
index 0000000000..f79a24536b
--- /dev/null
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiatableprovider.cpp
@@ -0,0 +1,154 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/QtConfig>
+#ifndef QT_NO_ACCESSIBILITY
+
+#include "qwindowsuiatableprovider.h"
+#include "qwindowsuiamainprovider.h"
+#include "qwindowsuiautils.h"
+#include "qwindowscontext.h"
+
+#include <QtGui/QAccessible>
+#include <QtGui/QAccessibleInterface>
+#include <QtCore/QDebug>
+#include <QtCore/QString>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QWindowsUiAutomation;
+
+
+QWindowsUiaTableProvider::QWindowsUiaTableProvider(QAccessible::Id id) :
+ QWindowsUiaBaseProvider(id)
+{
+}
+
+QWindowsUiaTableProvider::~QWindowsUiaTableProvider()
+{
+}
+
+// Gets the providers for all the row headers in the table.
+HRESULT STDMETHODCALLTYPE QWindowsUiaTableProvider::GetRowHeaders(SAFEARRAY **pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = nullptr;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QAccessibleTableInterface *tableInterface = accessible->tableInterface();
+ if (!tableInterface)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QList<QAccessibleInterface *> headers;
+
+ for (int i = 0; i < tableInterface->rowCount(); ++i) {
+ if (QAccessibleInterface *cell = tableInterface->cellAt(i, 0)) {
+ if (QAccessibleTableCellInterface *tableCellInterface = cell->tableCellInterface()) {
+ headers.append(tableCellInterface->rowHeaderCells());
+ }
+ }
+ }
+ if ((*pRetVal = SafeArrayCreateVector(VT_UNKNOWN, 0, headers.size()))) {
+ for (LONG i = 0; i < headers.size(); ++i) {
+ if (QWindowsUiaMainProvider *headerProvider = QWindowsUiaMainProvider::providerForAccessible(headers.at(i))) {
+ SafeArrayPutElement(*pRetVal, &i, static_cast<IRawElementProviderSimple *>(headerProvider));
+ headerProvider->Release();
+ }
+ }
+ }
+ return S_OK;
+}
+
+// Gets the providers for all the column headers in the table.
+HRESULT STDMETHODCALLTYPE QWindowsUiaTableProvider::GetColumnHeaders(SAFEARRAY **pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = nullptr;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QAccessibleTableInterface *tableInterface = accessible->tableInterface();
+ if (!tableInterface)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QList<QAccessibleInterface *> headers;
+
+ for (int i = 0; i < tableInterface->columnCount(); ++i) {
+ if (QAccessibleInterface *cell = tableInterface->cellAt(0, i)) {
+ if (QAccessibleTableCellInterface *tableCellInterface = cell->tableCellInterface()) {
+ headers.append(tableCellInterface->columnHeaderCells());
+ }
+ }
+ }
+ if ((*pRetVal = SafeArrayCreateVector(VT_UNKNOWN, 0, headers.size()))) {
+ for (LONG i = 0; i < headers.size(); ++i) {
+ if (QWindowsUiaMainProvider *headerProvider = QWindowsUiaMainProvider::providerForAccessible(headers.at(i))) {
+ SafeArrayPutElement(*pRetVal, &i, static_cast<IRawElementProviderSimple *>(headerProvider));
+ headerProvider->Release();
+ }
+ }
+ }
+ return S_OK;
+}
+
+// Returns the primary direction of traversal for the table.
+HRESULT STDMETHODCALLTYPE QWindowsUiaTableProvider::get_RowOrColumnMajor(enum RowOrColumnMajor *pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = RowOrColumnMajor_Indeterminate;
+ return S_OK;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ACCESSIBILITY
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiatableprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiatableprovider.h
new file mode 100644
index 0000000000..8cd0acda03
--- /dev/null
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiatableprovider.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSUIATABLEPROVIDER_H
+#define QWINDOWSUIATABLEPROVIDER_H
+
+#include <QtCore/QtConfig>
+#ifndef QT_NO_ACCESSIBILITY
+
+#include "qwindowsuiabaseprovider.h"
+
+QT_BEGIN_NAMESPACE
+
+// Implements the Table control pattern provider. Used by tables/trees.
+class QWindowsUiaTableProvider : public QWindowsUiaBaseProvider,
+ public QWindowsComBase<ITableProvider>
+{
+ Q_DISABLE_COPY(QWindowsUiaTableProvider)
+public:
+ explicit QWindowsUiaTableProvider(QAccessible::Id id);
+ virtual ~QWindowsUiaTableProvider();
+
+ // ITableProvider
+ HRESULT STDMETHODCALLTYPE GetRowHeaders(SAFEARRAY **pRetVal);
+ HRESULT STDMETHODCALLTYPE GetColumnHeaders(SAFEARRAY **pRetVal);
+ HRESULT STDMETHODCALLTYPE get_RowOrColumnMajor(enum RowOrColumnMajor *pRetVal);
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ACCESSIBILITY
+
+#endif // QWINDOWSUIATABLEPROVIDER_H
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiatextprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiatextprovider.cpp
new file mode 100644
index 0000000000..e1622933af
--- /dev/null
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiatextprovider.cpp
@@ -0,0 +1,261 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/QtConfig>
+#ifndef QT_NO_ACCESSIBILITY
+
+#include "qwindowsuiatextprovider.h"
+#include "qwindowsuiautils.h"
+#include "qwindowscontext.h"
+
+#include <QtGui/QAccessible>
+#include <QtGui/QAccessibleInterface>
+#include <QtCore/QDebug>
+#include <QtCore/QString>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QWindowsUiAutomation;
+
+
+QWindowsUiaTextProvider::QWindowsUiaTextProvider(QAccessible::Id id) :
+ QWindowsUiaBaseProvider(id)
+{
+}
+
+QWindowsUiaTextProvider::~QWindowsUiaTextProvider()
+{
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsUiaTextProvider::QueryInterface(REFIID iid, LPVOID *iface)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
+
+ if (!iface)
+ return E_INVALIDARG;
+ *iface = nullptr;
+
+ const bool result = qWindowsComQueryUnknownInterfaceMulti<ITextProvider>(this, iid, iface)
+ || qWindowsComQueryInterface<ITextProvider>(this, iid, iface)
+ || qWindowsComQueryInterface<ITextProvider2>(this, iid, iface);
+ return result ? S_OK : E_NOINTERFACE;
+}
+
+// Returns an array of providers for the selected text ranges.
+HRESULT STDMETHODCALLTYPE QWindowsUiaTextProvider::GetSelection(SAFEARRAY **pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = nullptr;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QAccessibleTextInterface *textInterface = accessible->textInterface();
+ if (!textInterface)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ int selCount = textInterface->selectionCount();
+ if (selCount > 0) {
+ // Build a safe array with the text range providers.
+ if ((*pRetVal = SafeArrayCreateVector(VT_UNKNOWN, 0, selCount))) {
+ for (LONG i = 0; i < selCount; ++i) {
+ int startOffset = 0, endOffset = 0;
+ textInterface->selection((int)i, &startOffset, &endOffset);
+ QWindowsUiaTextRangeProvider *textRangeProvider = new QWindowsUiaTextRangeProvider(id(), startOffset, endOffset);
+ SafeArrayPutElement(*pRetVal, &i, static_cast<IUnknown *>(textRangeProvider));
+ textRangeProvider->Release();
+ }
+ }
+ } else {
+ // If there is no selection, we return an array with a single degenerate (empty) text range at the cursor position.
+ if ((*pRetVal = SafeArrayCreateVector(VT_UNKNOWN, 0, 1))) {
+ LONG i = 0;
+ int cursorPosition = textInterface->cursorPosition();
+ QWindowsUiaTextRangeProvider *textRangeProvider = new QWindowsUiaTextRangeProvider(id(), cursorPosition, cursorPosition);
+ SafeArrayPutElement(*pRetVal, &i, static_cast<IUnknown *>(textRangeProvider));
+ textRangeProvider->Release();
+ }
+ }
+ return S_OK;
+}
+
+// Returns an array of providers for the visible text ranges.
+HRESULT STDMETHODCALLTYPE QWindowsUiaTextProvider::GetVisibleRanges(SAFEARRAY **pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = nullptr;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QAccessibleTextInterface *textInterface = accessible->textInterface();
+ if (!textInterface)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ // Considering the entire text as visible.
+ if ((*pRetVal = SafeArrayCreateVector(VT_UNKNOWN, 0, 1))) {
+ LONG i = 0;
+ QWindowsUiaTextRangeProvider *textRangeProvider = new QWindowsUiaTextRangeProvider(id(), 0, textInterface->characterCount());
+ SafeArrayPutElement(*pRetVal, &i, static_cast<IUnknown *>(textRangeProvider));
+ textRangeProvider->Release();
+ }
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsUiaTextProvider::RangeFromChild(IRawElementProviderSimple * /*childElement*/,
+ ITextRangeProvider **pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = nullptr;
+ // No children supported.
+ return S_OK;
+}
+
+// Returns a degenerate text range at the specified point.
+HRESULT STDMETHODCALLTYPE QWindowsUiaTextProvider::RangeFromPoint(UiaPoint point, ITextRangeProvider **pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = nullptr;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QAccessibleTextInterface *textInterface = accessible->textInterface();
+ if (!textInterface)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QWindow *window = windowForAccessible(accessible);
+ if (!window)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QPoint pt;
+ nativeUiaPointToPoint(point, window, &pt);
+
+ int offset = textInterface->offsetAtPoint(pt);
+ if ((offset >= 0) && (offset < textInterface->characterCount())) {
+ *pRetVal = new QWindowsUiaTextRangeProvider(id(), offset, offset);
+ }
+ return S_OK;
+}
+
+// Returns a text range provider for the entire text.
+HRESULT STDMETHODCALLTYPE QWindowsUiaTextProvider::get_DocumentRange(ITextRangeProvider **pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = nullptr;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QAccessibleTextInterface *textInterface = accessible->textInterface();
+ if (!textInterface)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ *pRetVal = new QWindowsUiaTextRangeProvider(id(), 0, textInterface->characterCount());
+ return S_OK;
+}
+
+// Currently supporting single selection.
+HRESULT STDMETHODCALLTYPE QWindowsUiaTextProvider::get_SupportedTextSelection(SupportedTextSelection *pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = SupportedTextSelection_Single;
+ return S_OK;
+}
+
+// Not supporting annotations.
+HRESULT STDMETHODCALLTYPE QWindowsUiaTextProvider::RangeFromAnnotation(IRawElementProviderSimple * /*annotationElement*/, ITextRangeProvider **pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = nullptr;
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsUiaTextProvider::GetCaretRange(BOOL *isActive, ITextRangeProvider **pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
+
+ if (!isActive || !pRetVal)
+ return E_INVALIDARG;
+ *isActive = FALSE;
+ *pRetVal = nullptr;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QAccessibleTextInterface *textInterface = accessible->textInterface();
+ if (!textInterface)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ *isActive = accessible->state().focused;
+
+ int cursorPosition = textInterface->cursorPosition();
+ *pRetVal = new QWindowsUiaTextRangeProvider(id(), cursorPosition, cursorPosition);
+ return S_OK;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ACCESSIBILITY
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiatextprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiatextprovider.h
new file mode 100644
index 0000000000..a6d10027fa
--- /dev/null
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiatextprovider.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSUIATEXTPROVIDER_H
+#define QWINDOWSUIATEXTPROVIDER_H
+
+#include <QtCore/QtConfig>
+#ifndef QT_NO_ACCESSIBILITY
+
+#include "qwindowsuiabaseprovider.h"
+#include "qwindowsuiatextrangeprovider.h"
+
+QT_BEGIN_NAMESPACE
+
+// Implements the Text control pattern provider. Used for text controls.
+class QWindowsUiaTextProvider : public QWindowsUiaBaseProvider,
+ public QWindowsComBase<ITextProvider2>
+{
+ Q_DISABLE_COPY(QWindowsUiaTextProvider)
+public:
+ explicit QWindowsUiaTextProvider(QAccessible::Id id);
+ ~QWindowsUiaTextProvider();
+
+ // IUnknown overrides
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID id, LPVOID *iface);
+
+ // ITextProvider
+ HRESULT STDMETHODCALLTYPE GetSelection(SAFEARRAY **pRetVal);
+ HRESULT STDMETHODCALLTYPE GetVisibleRanges(SAFEARRAY **pRetVal);
+ HRESULT STDMETHODCALLTYPE RangeFromChild(IRawElementProviderSimple *childElement, ITextRangeProvider **pRetVal);
+ HRESULT STDMETHODCALLTYPE RangeFromPoint(UiaPoint point, ITextRangeProvider **pRetVal);
+ HRESULT STDMETHODCALLTYPE get_DocumentRange(ITextRangeProvider **pRetVal);
+ HRESULT STDMETHODCALLTYPE get_SupportedTextSelection(SupportedTextSelection *pRetVal);
+
+ // ITextProvider2
+ HRESULT STDMETHODCALLTYPE RangeFromAnnotation(IRawElementProviderSimple *annotationElement, ITextRangeProvider **pRetVal);
+ HRESULT STDMETHODCALLTYPE GetCaretRange(BOOL *isActive, ITextRangeProvider **pRetVal);
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ACCESSIBILITY
+
+#endif // QWINDOWSUIATEXTPROVIDER_H
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiatextrangeprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiatextrangeprovider.cpp
new file mode 100644
index 0000000000..dae7cbdd5f
--- /dev/null
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiatextrangeprovider.cpp
@@ -0,0 +1,554 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/QtConfig>
+#ifndef QT_NO_ACCESSIBILITY
+
+#include "qwindowsuiatextrangeprovider.h"
+#include "qwindowsuiamainprovider.h"
+#include "qwindowsuiautils.h"
+#include "qwindowscontext.h"
+
+#include <QtGui/QAccessible>
+#include <QtGui/QAccessibleInterface>
+#include <QtCore/QDebug>
+#include <QtCore/QString>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QWindowsUiAutomation;
+
+
+QWindowsUiaTextRangeProvider::QWindowsUiaTextRangeProvider(QAccessible::Id id, int startOffset, int endOffset) :
+ QWindowsUiaBaseProvider(id),
+ m_startOffset(startOffset),
+ m_endOffset(endOffset)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this << startOffset << endOffset;
+}
+
+QWindowsUiaTextRangeProvider::~QWindowsUiaTextRangeProvider()
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
+}
+
+HRESULT QWindowsUiaTextRangeProvider::AddToSelection()
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
+ return Select();
+}
+
+HRESULT QWindowsUiaTextRangeProvider::Clone(ITextRangeProvider **pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+
+ *pRetVal = new QWindowsUiaTextRangeProvider(id(), m_startOffset, m_endOffset);
+ return S_OK;
+}
+
+// Two ranges are considered equal if their start/end points are the same.
+HRESULT QWindowsUiaTextRangeProvider::Compare(ITextRangeProvider *range, BOOL *pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
+
+ if (!range || !pRetVal)
+ return E_INVALIDARG;
+
+ QWindowsUiaTextRangeProvider *targetProvider = static_cast<QWindowsUiaTextRangeProvider *>(range);
+ *pRetVal = ((targetProvider->m_startOffset == m_startOffset) && (targetProvider->m_endOffset == m_endOffset));
+ return S_OK;
+}
+
+// Compare different endpoinds between two providers.
+HRESULT QWindowsUiaTextRangeProvider::CompareEndpoints(TextPatternRangeEndpoint endpoint,
+ ITextRangeProvider *targetRange,
+ TextPatternRangeEndpoint targetEndpoint,
+ int *pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__
+ << "endpoint=" << endpoint << "targetRange=" << targetRange
+ << "targetEndpoint=" << targetEndpoint << "this: " << this;
+
+ if (!targetRange || !pRetVal)
+ return E_INVALIDARG;
+
+ QWindowsUiaTextRangeProvider *targetProvider = static_cast<QWindowsUiaTextRangeProvider *>(targetRange);
+
+ int point = (endpoint == TextPatternRangeEndpoint_Start) ? m_startOffset : m_endOffset;
+ int targetPoint = (targetEndpoint == TextPatternRangeEndpoint_Start) ?
+ targetProvider->m_startOffset : targetProvider->m_endOffset;
+ *pRetVal = point - targetPoint;
+ return S_OK;
+}
+
+// Expands/normalizes the range for a given text unit.
+HRESULT QWindowsUiaTextRangeProvider::ExpandToEnclosingUnit(TextUnit unit)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__ << "unit=" << unit << "this: " << this;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QAccessibleTextInterface *textInterface = accessible->textInterface();
+ if (!textInterface)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ int len = textInterface->characterCount();
+ if (len < 1) {
+ m_startOffset = 0;
+ m_endOffset = 0;
+ } else {
+ if (unit == TextUnit_Character) {
+ m_startOffset = qBound(0, m_startOffset, len - 1);
+ m_endOffset = m_startOffset + 1;
+ } else {
+ QString text = textInterface->text(0, len);
+ for (int t = m_startOffset; t >= 0; --t) {
+ if (!isTextUnitSeparator(unit, text[t]) && ((t == 0) || isTextUnitSeparator(unit, text[t - 1]))) {
+ m_startOffset = t;
+ break;
+ }
+ }
+ for (int t = m_startOffset; t < len; ++t) {
+ if ((t == len - 1) || (isTextUnitSeparator(unit, text[t]) && ((unit == TextUnit_Word) || !isTextUnitSeparator(unit, text[t + 1])))) {
+ m_endOffset = t + 1;
+ break;
+ }
+ }
+ }
+ }
+ return S_OK;
+}
+
+// Not supported.
+HRESULT QWindowsUiaTextRangeProvider::FindAttribute(TEXTATTRIBUTEID /* attributeId */,
+ VARIANT /* val */, BOOL /* backward */,
+ ITextRangeProvider **pRetVal)
+{
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = nullptr;
+ return S_OK;
+}
+
+// Returns the value of a given attribute.
+HRESULT STDMETHODCALLTYPE QWindowsUiaTextRangeProvider::GetAttributeValue(TEXTATTRIBUTEID attributeId,
+ VARIANT *pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__ << "attributeId=" << attributeId << "this: " << this;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ clearVariant(pRetVal);
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QAccessibleTextInterface *textInterface = accessible->textInterface();
+ if (!textInterface)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ switch (attributeId) {
+ case UIA_IsReadOnlyAttributeId:
+ setVariantBool(accessible->state().readOnly, pRetVal);
+ break;
+ case UIA_CaretPositionAttributeId:
+ if (textInterface->cursorPosition() == 0)
+ setVariantI4(CaretPosition_BeginningOfLine, pRetVal);
+ else if (textInterface->cursorPosition() == textInterface->characterCount())
+ setVariantI4(CaretPosition_EndOfLine, pRetVal);
+ else
+ setVariantI4(CaretPosition_Unknown, pRetVal);
+ break;
+ default:
+ break;
+ }
+ return S_OK;
+}
+
+// Returns an array of bounding rectangles for text lines within the range.
+HRESULT QWindowsUiaTextRangeProvider::GetBoundingRectangles(SAFEARRAY **pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QAccessibleTextInterface *textInterface = accessible->textInterface();
+ if (!textInterface)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QWindow *window = windowForAccessible(accessible);
+ if (!window)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ int len = textInterface->characterCount();
+ QList<QRect> rectList;
+
+ if ((m_startOffset >= 0) && (m_endOffset <= len) && (m_startOffset < m_endOffset)) {
+ int start, end;
+ textInterface->textAtOffset(m_startOffset, QAccessible::LineBoundary, &start, &end);
+ while ((start >= 0) && (end >= 0)) {
+ int startRange = qMax(start, m_startOffset);
+ int endRange = qMin(end, m_endOffset);
+ if (startRange < endRange) {
+ // Calculates a bounding rectangle for the line and adds it to the list.
+ QRect startRect = textInterface->characterRect(startRange);
+ QRect endRect = textInterface->characterRect(endRange - 1);
+ QRect lineRect(qMin(startRect.x(), endRect.x()),
+ qMin(startRect.y(), endRect.y()),
+ qMax(startRect.x() + startRect.width(), endRect.x() + endRect.width()) - qMin(startRect.x(), endRect.x()),
+ qMax(startRect.y() + startRect.height(), endRect.y() + endRect.height()) - qMin(startRect.y(), endRect.y()));
+ rectList.append(lineRect);
+ }
+ if (end >= len) break;
+ textInterface->textAfterOffset(end + 1, QAccessible::LineBoundary, &start, &end);
+ }
+ }
+
+ if ((*pRetVal = SafeArrayCreateVector(VT_R8, 0, 4 * rectList.size()))) {
+ for (int i = 0; i < rectList.size(); ++i) {
+ // Scale rect for high DPI screens.
+ UiaRect uiaRect;
+ rectToNativeUiaRect(rectList[i], window, &uiaRect);
+ double coords[4] = { uiaRect.left, uiaRect.top, uiaRect.width, uiaRect.height };
+ for (int j = 0; j < 4; ++j) {
+ LONG idx = 4 * i + j;
+ SafeArrayPutElement(*pRetVal, &idx, &coords[j]);
+ }
+ }
+ }
+ return S_OK;
+}
+
+// Returns an array of children elements embedded within the range.
+HRESULT QWindowsUiaTextRangeProvider::GetChildren(SAFEARRAY **pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ // Not supporting any children.
+ *pRetVal = SafeArrayCreateVector(VT_UNKNOWN, 0, 0);
+ return S_OK;
+}
+
+// Returns a provider for the enclosing element (text to which the range belongs).
+HRESULT QWindowsUiaTextRangeProvider::GetEnclosingElement(IRawElementProviderSimple **pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ *pRetVal = QWindowsUiaMainProvider::providerForAccessible(accessible);
+ return S_OK;
+}
+
+// Gets the text within the range.
+HRESULT QWindowsUiaTextRangeProvider::GetText(int maxLength, BSTR *pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__ << maxLength << "this: " << this;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = nullptr;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QAccessibleTextInterface *textInterface = accessible->textInterface();
+ if (!textInterface)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ int len = textInterface->characterCount();
+ QString rangeText;
+ if ((m_startOffset >= 0) && (m_endOffset <= len) && (m_startOffset < m_endOffset))
+ rangeText = textInterface->text(m_startOffset, m_endOffset);
+
+ if ((maxLength > -1) && (rangeText.size() > maxLength))
+ rangeText.truncate(maxLength);
+ *pRetVal = bStrFromQString(rangeText);
+ return S_OK;
+}
+
+// Moves the range a specified number of units (and normalizes it).
+HRESULT QWindowsUiaTextRangeProvider::Move(TextUnit unit, int count, int *pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__ << "unit=" << unit << "count=" << count << "this: " << this;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = 0;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QAccessibleTextInterface *textInterface = accessible->textInterface();
+ if (!textInterface)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ int len = textInterface->characterCount();
+
+ if (len < 1)
+ return S_OK;
+
+ if (unit == TextUnit_Character) {
+ // Moves the start point, ensuring it lies within the bounds.
+ int start = qBound(0, m_startOffset + count, len - 1);
+ // If range was initially empty, leaves it as is; otherwise, normalizes it to one char.
+ m_endOffset = (m_endOffset > m_startOffset) ? start + 1 : start;
+ *pRetVal = start - m_startOffset; // Returns the actually moved distance.
+ m_startOffset = start;
+ } else {
+ if (count > 0) {
+ MoveEndpointByUnit(TextPatternRangeEndpoint_End, unit, count, pRetVal);
+ MoveEndpointByUnit(TextPatternRangeEndpoint_Start, unit, count, pRetVal);
+ } else {
+ MoveEndpointByUnit(TextPatternRangeEndpoint_Start, unit, count, pRetVal);
+ MoveEndpointByUnit(TextPatternRangeEndpoint_End, unit, count, pRetVal);
+ }
+ }
+ return S_OK;
+}
+
+// Copies the value of an end point from one range to another.
+HRESULT QWindowsUiaTextRangeProvider::MoveEndpointByRange(TextPatternRangeEndpoint endpoint,
+ ITextRangeProvider *targetRange,
+ TextPatternRangeEndpoint targetEndpoint)
+{
+ if (!targetRange)
+ return E_INVALIDARG;
+
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__
+ << "endpoint=" << endpoint << "targetRange=" << targetRange << "targetEndpoint=" << targetEndpoint << "this: " << this;
+
+ QWindowsUiaTextRangeProvider *targetProvider = static_cast<QWindowsUiaTextRangeProvider *>(targetRange);
+
+ int targetPoint = (targetEndpoint == TextPatternRangeEndpoint_Start) ?
+ targetProvider->m_startOffset : targetProvider->m_endOffset;
+
+ // If the moved endpoint crosses the other endpoint, that one is moved too.
+ if (endpoint == TextPatternRangeEndpoint_Start) {
+ m_startOffset = targetPoint;
+ if (m_endOffset < m_startOffset)
+ m_endOffset = m_startOffset;
+ } else {
+ m_endOffset = targetPoint;
+ if (m_endOffset < m_startOffset)
+ m_startOffset = m_endOffset;
+ }
+ return S_OK;
+}
+
+// Moves an endpoint an specific number of units.
+HRESULT QWindowsUiaTextRangeProvider::MoveEndpointByUnit(TextPatternRangeEndpoint endpoint,
+ TextUnit unit, int count,
+ int *pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__
+ << "endpoint=" << endpoint << "unit=" << unit << "count=" << count << "this: " << this;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = 0;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QAccessibleTextInterface *textInterface = accessible->textInterface();
+ if (!textInterface)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ int len = textInterface->characterCount();
+
+ if (len < 1)
+ return S_OK;
+
+ if (unit == TextUnit_Character) {
+ if (endpoint == TextPatternRangeEndpoint_Start) {
+ int boundedValue = qBound(0, m_startOffset + count, len - 1);
+ *pRetVal = boundedValue - m_startOffset;
+ m_startOffset = boundedValue;
+ m_endOffset = qBound(m_startOffset, m_endOffset, len);
+ } else {
+ int boundedValue = qBound(0, m_endOffset + count, len);
+ *pRetVal = boundedValue - m_endOffset;
+ m_endOffset = boundedValue;
+ m_startOffset = qBound(0, m_startOffset, m_endOffset);
+ }
+ } else {
+ QString text = textInterface->text(0, len);
+ int moved = 0;
+
+ if (endpoint == TextPatternRangeEndpoint_Start) {
+ if (count > 0) {
+ for (int t = m_startOffset; (t < len - 1) && (moved < count); ++t) {
+ if (isTextUnitSeparator(unit, text[t]) && !isTextUnitSeparator(unit, text[t + 1])) {
+ m_startOffset = t + 1;
+ ++moved;
+ }
+ }
+ m_endOffset = qBound(m_startOffset, m_endOffset, len);
+ } else {
+ for (int t = m_startOffset - 1; (t >= 0) && (moved > count); --t) {
+ if (!isTextUnitSeparator(unit, text[t]) && ((t == 0) || isTextUnitSeparator(unit, text[t - 1]))) {
+ m_startOffset = t;
+ --moved;
+ }
+ }
+ }
+ } else {
+ if (count > 0) {
+ for (int t = m_endOffset; (t < len) && (moved < count); ++t) {
+ if ((t == len - 1) || (isTextUnitSeparator(unit, text[t]) && ((unit == TextUnit_Word) || !isTextUnitSeparator(unit, text[t + 1])))) {
+ m_endOffset = t + 1;
+ ++moved;
+ }
+ }
+ } else {
+ int end = 0;
+ for (int t = m_endOffset - 2; (t > 0) && (moved > count); --t) {
+ if (isTextUnitSeparator(unit, text[t]) && ((unit == TextUnit_Word) || !isTextUnitSeparator(unit, text[t + 1]))) {
+ end = t + 1;
+ --moved;
+ }
+ }
+ m_endOffset = end;
+ m_startOffset = qBound(0, m_startOffset, m_endOffset);
+ }
+ }
+ *pRetVal = moved;
+ }
+ return S_OK;
+}
+
+HRESULT QWindowsUiaTextRangeProvider::RemoveFromSelection()
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+ // unselects all
+ return unselect();
+}
+
+// Scrolls the range into view.
+HRESULT QWindowsUiaTextRangeProvider::ScrollIntoView(BOOL alignToTop)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__ << "alignToTop=" << alignToTop << "this: " << this;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QAccessibleTextInterface *textInterface = accessible->textInterface();
+ if (!textInterface)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ textInterface->scrollToSubstring(m_startOffset, m_endOffset);
+ return S_OK;
+}
+
+// Selects the range.
+HRESULT QWindowsUiaTextRangeProvider::Select()
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QAccessibleTextInterface *textInterface = accessible->textInterface();
+ if (!textInterface)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ // unselects all and adds a new selection
+ unselect();
+ textInterface->addSelection(m_startOffset, m_endOffset);
+ return S_OK;
+}
+
+// Not supported.
+HRESULT QWindowsUiaTextRangeProvider::FindTextW(BSTR /* text */, BOOL /* backward */,
+ BOOL /* ignoreCase */,
+ ITextRangeProvider **pRetVal)
+{
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = nullptr;
+ return S_OK;
+}
+
+// Removes all selected ranges from the text element.
+HRESULT QWindowsUiaTextRangeProvider::unselect()
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QAccessibleTextInterface *textInterface = accessible->textInterface();
+ if (!textInterface)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ int selCount = textInterface->selectionCount();
+
+ for (int i = selCount - 1; i >= 0; --i)
+ textInterface->removeSelection(i);
+ return S_OK;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ACCESSIBILITY
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiatextrangeprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiatextrangeprovider.h
new file mode 100644
index 0000000000..6fe6502c41
--- /dev/null
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiatextrangeprovider.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSUIATEXTRANGEPROVIDER_H
+#define QWINDOWSUIATEXTRANGEPROVIDER_H
+
+#include <QtCore/QtConfig>
+#ifndef QT_NO_ACCESSIBILITY
+
+#include "qwindowsuiabaseprovider.h"
+
+QT_BEGIN_NAMESPACE
+
+// Implements the Text Range control pattern provider. Used for text controls.
+class QWindowsUiaTextRangeProvider : public QWindowsUiaBaseProvider,
+ public QWindowsComBase<ITextRangeProvider>
+{
+ Q_DISABLE_COPY(QWindowsUiaTextRangeProvider)
+public:
+ explicit QWindowsUiaTextRangeProvider(QAccessible::Id id, int startOffset, int endOffset);
+ virtual ~QWindowsUiaTextRangeProvider();
+
+ HRESULT STDMETHODCALLTYPE AddToSelection();
+ HRESULT STDMETHODCALLTYPE Clone(ITextRangeProvider **pRetVal);
+ HRESULT STDMETHODCALLTYPE Compare(ITextRangeProvider *range, BOOL *pRetVal);
+ HRESULT STDMETHODCALLTYPE CompareEndpoints(TextPatternRangeEndpoint endpoint, ITextRangeProvider *targetRange, TextPatternRangeEndpoint targetEndpoint, int *pRetVal);
+ HRESULT STDMETHODCALLTYPE ExpandToEnclosingUnit(TextUnit unit);
+ HRESULT STDMETHODCALLTYPE FindAttribute(TEXTATTRIBUTEID attributeId, VARIANT val, BOOL backward, ITextRangeProvider **pRetVal);
+ HRESULT STDMETHODCALLTYPE FindText(BSTR text, BOOL backward, BOOL ignoreCase, ITextRangeProvider **pRetVal);
+ HRESULT STDMETHODCALLTYPE GetAttributeValue(TEXTATTRIBUTEID attributeId, VARIANT *pRetVal);
+ HRESULT STDMETHODCALLTYPE GetBoundingRectangles(SAFEARRAY **pRetVal);
+ HRESULT STDMETHODCALLTYPE GetChildren(SAFEARRAY **pRetVal);
+ HRESULT STDMETHODCALLTYPE GetEnclosingElement(IRawElementProviderSimple **pRetVal);
+ HRESULT STDMETHODCALLTYPE GetText(int maxLength, BSTR *pRetVal);
+ HRESULT STDMETHODCALLTYPE Move(TextUnit unit, int count, int *pRetVal);
+ HRESULT STDMETHODCALLTYPE MoveEndpointByRange(TextPatternRangeEndpoint endpoint, ITextRangeProvider *targetRange, TextPatternRangeEndpoint targetEndpoint);
+ HRESULT STDMETHODCALLTYPE MoveEndpointByUnit(TextPatternRangeEndpoint endpoint, TextUnit unit, int count, int *pRetVal);
+ HRESULT STDMETHODCALLTYPE RemoveFromSelection();
+ HRESULT STDMETHODCALLTYPE ScrollIntoView(BOOL alignToTop);
+ HRESULT STDMETHODCALLTYPE Select();
+
+private:
+ HRESULT unselect();
+ int m_startOffset;
+ int m_endOffset;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ACCESSIBILITY
+
+#endif // QWINDOWSUIATEXTRANGEPROVIDER_H
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiatoggleprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiatoggleprovider.cpp
new file mode 100644
index 0000000000..01cdfd7e91
--- /dev/null
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiatoggleprovider.cpp
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/QtConfig>
+#ifndef QT_NO_ACCESSIBILITY
+
+#include "qwindowsuiatoggleprovider.h"
+#include "qwindowsuiautils.h"
+#include "qwindowscontext.h"
+
+#include <QtGui/QAccessible>
+#include <QtGui/QAccessibleInterface>
+#include <QtCore/QDebug>
+#include <QtCore/QString>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QWindowsUiAutomation;
+
+
+QWindowsUiaToggleProvider::QWindowsUiaToggleProvider(QAccessible::Id id) :
+ QWindowsUiaBaseProvider(id)
+{
+}
+
+QWindowsUiaToggleProvider::~QWindowsUiaToggleProvider()
+{
+}
+
+// toggles the state by invoking the toggle action
+HRESULT STDMETHODCALLTYPE QWindowsUiaToggleProvider::Toggle()
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ QAccessibleActionInterface *actionInterface = accessible->actionInterface();
+ if (!actionInterface)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ actionInterface->doAction(QAccessibleActionInterface::toggleAction());
+ return S_OK;
+}
+
+// Gets the current toggle state.
+HRESULT STDMETHODCALLTYPE QWindowsUiaToggleProvider::get_ToggleState(ToggleState *pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = ToggleState_Off;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ if (accessible->state().checked)
+ *pRetVal = accessible->state().checkStateMixed ? ToggleState_Indeterminate : ToggleState_On;
+ else
+ *pRetVal = ToggleState_Off;
+ return S_OK;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ACCESSIBILITY
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiatoggleprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiatoggleprovider.h
new file mode 100644
index 0000000000..a0df983e40
--- /dev/null
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiatoggleprovider.h
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSUIATOGGLEPROVIDER_H
+#define QWINDOWSUIATOGGLEPROVIDER_H
+
+#include <QtCore/QtConfig>
+#ifndef QT_NO_ACCESSIBILITY
+
+#include "qwindowsuiabaseprovider.h"
+
+QT_BEGIN_NAMESPACE
+
+// Implements the Toggle control pattern provider. Used for checkboxes.
+class QWindowsUiaToggleProvider : public QWindowsUiaBaseProvider,
+ public QWindowsComBase<IToggleProvider>
+{
+ Q_DISABLE_COPY(QWindowsUiaToggleProvider)
+public:
+ explicit QWindowsUiaToggleProvider(QAccessible::Id id);
+ virtual ~QWindowsUiaToggleProvider();
+
+ // IToggleProvider
+ HRESULT STDMETHODCALLTYPE Toggle();
+ HRESULT STDMETHODCALLTYPE get_ToggleState(ToggleState *pRetVal);
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ACCESSIBILITY
+
+#endif // QWINDOWSUIATOGGLEPROVIDER_H
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp
new file mode 100644
index 0000000000..89e5075dcb
--- /dev/null
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp
@@ -0,0 +1,221 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/QtConfig>
+#ifndef QT_NO_ACCESSIBILITY
+
+#include "qwindowsuiautils.h"
+#include "qwindowscontext.h"
+#include "qwindowswindow.h"
+
+#include <QtGui/QWindow>
+#include <QtGui/private/qhighdpiscaling_p.h>
+#include <cmath>
+
+QT_BEGIN_NAMESPACE
+
+namespace QWindowsUiAutomation {
+
+// Returns the window containing the element (usually the top window),
+QWindow *windowForAccessible(const QAccessibleInterface *accessible)
+{
+ QWindow *window = accessible->window();
+ if (!window) {
+ QAccessibleInterface *acc = accessible->parent();
+ while (acc && acc->isValid() && !window) {
+ window = acc->window();
+ QAccessibleInterface *par = acc->parent();
+ acc = par;
+ }
+ }
+ return window;
+}
+
+// Returns the native window handle associated with the element, if any.
+// Usually it will be NULL, as Qt5 by default uses alien widgets with no native windows.
+HWND hwndForAccessible(const QAccessibleInterface *accessible)
+{
+ if (QWindow *window = accessible->window()) {
+ if (!accessible->parent() || (accessible->parent()->window() != window)) {
+ return QWindowsBaseWindow::handleOf(window);
+ }
+ }
+ return NULL;
+}
+
+void clearVariant(VARIANT *variant)
+{
+ variant->vt = VT_EMPTY;
+ variant->punkVal = nullptr;
+}
+
+void setVariantI4(int value, VARIANT *variant)
+{
+ variant->vt = VT_I4;
+ variant->lVal = value;
+}
+
+void setVariantBool(bool value, VARIANT *variant)
+{
+ variant->vt = VT_BOOL;
+ variant->boolVal = value ? -1 : 0;
+}
+
+void setVariantDouble(double value, VARIANT *variant)
+{
+ variant->vt = VT_R8;
+ variant->boolVal = value;
+}
+
+BSTR bStrFromQString(const QString &value)
+{
+ return SysAllocString(reinterpret_cast<const wchar_t *>(value.utf16()));
+}
+
+void setVariantString(const QString &value, VARIANT *variant)
+{
+ variant->vt = VT_BSTR;
+ variant->bstrVal = bStrFromQString(value);
+}
+
+// Scales a rect to native coordinates, according to high dpi settings.
+void rectToNativeUiaRect(const QRect &rect, const QWindow *w, UiaRect *uiaRect)
+{
+ if (w && uiaRect) {
+ const qreal factor = QHighDpiScaling::factor(w);
+ uiaRect->left = qreal(rect.x()) * factor;
+ uiaRect->top = qreal(rect.y()) * factor;
+ uiaRect->width = qreal(rect.width()) * factor;
+ uiaRect->height = qreal(rect.height()) * factor;
+ }
+}
+
+// Scales a point from native coordinates, according to high dpi settings.
+void nativeUiaPointToPoint(const UiaPoint &uiaPoint, const QWindow *w, QPoint *point)
+{
+ if (w && point) {
+ const qreal factor = QHighDpiScaling::factor(w);
+ point->setX(int(std::lround(uiaPoint.x / factor)));
+ point->setY(int(std::lround(uiaPoint.y / factor)));
+ }
+}
+
+// Maps an accessibility role ID to an UI Automation control type ID.
+long roleToControlTypeId(QAccessible::Role role)
+{
+ static const QHash<QAccessible::Role, long> mapping {
+ {QAccessible::TitleBar, UIA_TitleBarControlTypeId},
+ {QAccessible::MenuBar, UIA_MenuBarControlTypeId},
+ {QAccessible::ScrollBar, UIA_ScrollBarControlTypeId},
+ {QAccessible::Grip, UIA_ThumbControlTypeId},
+ {QAccessible::Sound, UIA_CustomControlTypeId},
+ {QAccessible::Cursor, UIA_CustomControlTypeId},
+ {QAccessible::Caret, UIA_CustomControlTypeId},
+ {QAccessible::AlertMessage, UIA_CustomControlTypeId},
+ {QAccessible::Window, UIA_WindowControlTypeId},
+ {QAccessible::Client, UIA_CustomControlTypeId},
+ {QAccessible::PopupMenu, UIA_MenuControlTypeId},
+ {QAccessible::MenuItem, UIA_MenuItemControlTypeId},
+ {QAccessible::ToolTip, UIA_ToolTipControlTypeId},
+ {QAccessible::Application, UIA_CustomControlTypeId},
+ {QAccessible::Document, UIA_DocumentControlTypeId},
+ {QAccessible::Pane, UIA_PaneControlTypeId},
+ {QAccessible::Chart, UIA_CustomControlTypeId},
+ {QAccessible::Dialog, UIA_WindowControlTypeId},
+ {QAccessible::Border, UIA_CustomControlTypeId},
+ {QAccessible::Grouping, UIA_GroupControlTypeId},
+ {QAccessible::Separator, UIA_SeparatorControlTypeId},
+ {QAccessible::ToolBar, UIA_ToolBarControlTypeId},
+ {QAccessible::StatusBar, UIA_StatusBarControlTypeId},
+ {QAccessible::Table, UIA_TableControlTypeId},
+ {QAccessible::ColumnHeader, UIA_HeaderControlTypeId},
+ {QAccessible::RowHeader, UIA_HeaderControlTypeId},
+ {QAccessible::Column, UIA_HeaderItemControlTypeId},
+ {QAccessible::Row, UIA_HeaderItemControlTypeId},
+ {QAccessible::Cell, UIA_DataItemControlTypeId},
+ {QAccessible::Link, UIA_HyperlinkControlTypeId},
+ {QAccessible::HelpBalloon, UIA_ToolTipControlTypeId},
+ {QAccessible::Assistant, UIA_CustomControlTypeId},
+ {QAccessible::List, UIA_ListControlTypeId},
+ {QAccessible::ListItem, UIA_ListItemControlTypeId},
+ {QAccessible::Tree, UIA_TreeControlTypeId},
+ {QAccessible::TreeItem, UIA_TreeItemControlTypeId},
+ {QAccessible::PageTab, UIA_TabItemControlTypeId},
+ {QAccessible::PropertyPage, UIA_CustomControlTypeId},
+ {QAccessible::Indicator, UIA_CustomControlTypeId},
+ {QAccessible::Graphic, UIA_ImageControlTypeId},
+ {QAccessible::StaticText, UIA_EditControlTypeId},
+ {QAccessible::EditableText, UIA_EditControlTypeId},
+ {QAccessible::Button, UIA_ButtonControlTypeId},
+ {QAccessible::CheckBox, UIA_CheckBoxControlTypeId},
+ {QAccessible::RadioButton, UIA_RadioButtonControlTypeId},
+ {QAccessible::ComboBox, UIA_ComboBoxControlTypeId},
+ {QAccessible::ProgressBar, UIA_ProgressBarControlTypeId},
+ {QAccessible::Dial, UIA_CustomControlTypeId},
+ {QAccessible::HotkeyField, UIA_CustomControlTypeId},
+ {QAccessible::Slider, UIA_SliderControlTypeId},
+ {QAccessible::SpinBox, UIA_SpinnerControlTypeId},
+ {QAccessible::Canvas, UIA_CustomControlTypeId},
+ {QAccessible::Animation, UIA_CustomControlTypeId},
+ {QAccessible::Equation, UIA_CustomControlTypeId},
+ {QAccessible::ButtonDropDown, UIA_ButtonControlTypeId},
+ {QAccessible::ButtonMenu, UIA_ButtonControlTypeId},
+ {QAccessible::ButtonDropGrid, UIA_ButtonControlTypeId},
+ {QAccessible::Whitespace, UIA_CustomControlTypeId},
+ {QAccessible::PageTabList, UIA_TabControlTypeId},
+ {QAccessible::Clock, UIA_CustomControlTypeId},
+ {QAccessible::Splitter, UIA_CustomControlTypeId},
+ };
+
+ return mapping.value(role, UIA_CustomControlTypeId);
+}
+
+// True if a character can be a separator for a text unit.
+bool isTextUnitSeparator(TextUnit unit, const QChar &ch)
+{
+ return (((unit == TextUnit_Word) || (unit == TextUnit_Format)) && ch.isSpace())
+ || ((unit == TextUnit_Line) && (ch.toLatin1() == '\n'));
+}
+
+} // namespace QWindowsUiAutomation
+
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ACCESSIBILITY
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.h
new file mode 100644
index 0000000000..15f4d6e8ba
--- /dev/null
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSUIAUTILS_H
+#define QWINDOWSUIAUTILS_H
+
+#include <QtCore/QtConfig>
+#ifndef QT_NO_ACCESSIBILITY
+
+#include <QtCore/QString>
+#include <QtCore/qt_windows.h>
+#include <QtGui/QAccessible>
+#include <QtGui/QAccessibleInterface>
+#include <QtGui/QWindow>
+#include <QtCore/QDebug>
+#include <QtCore/QRect>
+#include <QtWindowsUIAutomationSupport/private/qwindowsuiawrapper_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QWindowsUiAutomation {
+
+QWindow *windowForAccessible(const QAccessibleInterface *accessible);
+
+HWND hwndForAccessible(const QAccessibleInterface *accessible);
+
+void rectToNativeUiaRect(const QRect &rect, const QWindow *w, UiaRect *uiaRect);
+
+void nativeUiaPointToPoint(const UiaPoint &uiaPoint, const QWindow *w, QPoint *point);
+
+long roleToControlTypeId(QAccessible::Role role);
+
+bool isTextUnitSeparator(TextUnit unit, const QChar &ch);
+
+void clearVariant(VARIANT *variant);
+
+void setVariantI4(int value, VARIANT *variant);
+
+void setVariantBool(bool value, VARIANT *variant);
+
+void setVariantDouble(double value, VARIANT *variant);
+
+BSTR bStrFromQString(const QString &value);
+
+void setVariantString(const QString &value, VARIANT *variant);
+
+} // namespace QWindowsUiAutomation
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ACCESSIBILITY
+
+#endif // QWINDOWSUIAUTILS_H
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiavalueprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiavalueprovider.cpp
new file mode 100644
index 0000000000..ef7d564e22
--- /dev/null
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiavalueprovider.cpp
@@ -0,0 +1,132 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/QtConfig>
+#ifndef QT_NO_ACCESSIBILITY
+
+#include "qwindowsuiavalueprovider.h"
+#include "qwindowsuiautils.h"
+#include "qwindowscontext.h"
+
+#include <QtGui/QAccessible>
+#include <QtGui/QAccessibleInterface>
+#include <QtCore/QDebug>
+#include <QtCore/QString>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QWindowsUiAutomation;
+
+
+QWindowsUiaValueProvider::QWindowsUiaValueProvider(QAccessible::Id id) :
+ QWindowsUiaBaseProvider(id)
+{
+}
+
+QWindowsUiaValueProvider::~QWindowsUiaValueProvider()
+{
+}
+
+// Sets the value associated with the control.
+HRESULT STDMETHODCALLTYPE QWindowsUiaValueProvider::SetValue(LPCWSTR val)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ // First sets the value as a text.
+ QString strVal = QString::fromUtf16(reinterpret_cast<const ushort *>(val));
+ accessible->setText(QAccessible::Value, strVal);
+
+ // Then, if the control supports the value interface (range value)
+ // and the supplied text can be converted to a number, and that number
+ // lies within the min/max limits, sets it as the control's current (numeric) value.
+ if (QAccessibleValueInterface *valueInterface = accessible->valueInterface()) {
+ bool ok = false;
+ double numval = strVal.toDouble(&ok);
+ if (ok) {
+ double minimum = valueInterface->minimumValue().toDouble();
+ double maximum = valueInterface->maximumValue().toDouble();
+ if ((numval >= minimum) && (numval <= maximum)) {
+ valueInterface->setCurrentValue(QVariant(numval));
+ }
+ }
+ }
+ return S_OK;
+}
+
+// True for read-only controls.
+HRESULT STDMETHODCALLTYPE QWindowsUiaValueProvider::get_IsReadOnly(BOOL *pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = FALSE;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ *pRetVal = accessible->state().readOnly;
+ return S_OK;
+}
+
+// Returns the value in text form.
+HRESULT STDMETHODCALLTYPE QWindowsUiaValueProvider::get_Value(BSTR *pRetVal)
+{
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+
+ if (!pRetVal)
+ return E_INVALIDARG;
+ *pRetVal = nullptr;
+
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible)
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ *pRetVal = bStrFromQString(accessible->text(QAccessible::Value));
+ return S_OK;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ACCESSIBILITY
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiavalueprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiavalueprovider.h
new file mode 100644
index 0000000000..db54fc0a46
--- /dev/null
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiavalueprovider.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSUIAVALUEPROVIDER_H
+#define QWINDOWSUIAVALUEPROVIDER_H
+
+#include <QtCore/QtConfig>
+#ifndef QT_NO_ACCESSIBILITY
+
+#include "qwindowsuiabaseprovider.h"
+
+QT_BEGIN_NAMESPACE
+
+// Implements the Value control pattern provider.
+// Supported for all controls that can return text(QAccessible::Value).
+class QWindowsUiaValueProvider : public QWindowsUiaBaseProvider,
+ public QWindowsComBase<IValueProvider>
+{
+ Q_DISABLE_COPY(QWindowsUiaValueProvider)
+public:
+ explicit QWindowsUiaValueProvider(QAccessible::Id id);
+ virtual ~QWindowsUiaValueProvider();
+
+ // IValueProvider
+ HRESULT STDMETHODCALLTYPE SetValue(LPCWSTR val);
+ HRESULT STDMETHODCALLTYPE get_IsReadOnly(BOOL *pRetVal);
+ HRESULT STDMETHODCALLTYPE get_Value(BSTR *pRetVal);
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_ACCESSIBILITY
+
+#endif // QWINDOWSUIAVALUEPROVIDER_H
diff --git a/src/plugins/platforms/windows/uiautomation/uiautomation.pri b/src/plugins/platforms/windows/uiautomation/uiautomation.pri
new file mode 100644
index 0000000000..e3071766d9
--- /dev/null
+++ b/src/plugins/platforms/windows/uiautomation/uiautomation.pri
@@ -0,0 +1,43 @@
+qtHaveModule(windowsuiautomation_support-private): \
+ QT += windowsuiautomation_support-private
+
+SOURCES += \
+ $$PWD/qwindowsuiaaccessibility.cpp \
+ $$PWD/qwindowsuiaprovidercache.cpp \
+ $$PWD/qwindowsuiamainprovider.cpp \
+ $$PWD/qwindowsuiabaseprovider.cpp \
+ $$PWD/qwindowsuiavalueprovider.cpp \
+ $$PWD/qwindowsuiatextprovider.cpp \
+ $$PWD/qwindowsuiatextrangeprovider.cpp \
+ $$PWD/qwindowsuiatoggleprovider.cpp \
+ $$PWD/qwindowsuiaselectionprovider.cpp \
+ $$PWD/qwindowsuiaselectionitemprovider.cpp \
+ $$PWD/qwindowsuiainvokeprovider.cpp \
+ $$PWD/qwindowsuiarangevalueprovider.cpp \
+ $$PWD/qwindowsuiatableprovider.cpp \
+ $$PWD/qwindowsuiatableitemprovider.cpp \
+ $$PWD/qwindowsuiagridprovider.cpp \
+ $$PWD/qwindowsuiagriditemprovider.cpp \
+ $$PWD/qwindowsuiautils.cpp
+
+HEADERS += \
+ $$PWD/qwindowsuiaaccessibility.h \
+ $$PWD/qwindowsuiaprovidercache.h \
+ $$PWD/qwindowsuiamainprovider.h \
+ $$PWD/qwindowsuiabaseprovider.h \
+ $$PWD/qwindowsuiavalueprovider.h \
+ $$PWD/qwindowsuiatextprovider.h \
+ $$PWD/qwindowsuiatextrangeprovider.h \
+ $$PWD/qwindowsuiatoggleprovider.h \
+ $$PWD/qwindowsuiaselectionprovider.h \
+ $$PWD/qwindowsuiaselectionitemprovider.h \
+ $$PWD/qwindowsuiainvokeprovider.h \
+ $$PWD/qwindowsuiarangevalueprovider.h \
+ $$PWD/qwindowsuiatableprovider.h \
+ $$PWD/qwindowsuiatableitemprovider.h \
+ $$PWD/qwindowsuiagridprovider.h \
+ $$PWD/qwindowsuiagriditemprovider.h \
+ $$PWD/qwindowsuiautils.h
+
+mingw: LIBS *= -luuid
+
diff --git a/src/plugins/platforms/windows/windows.pri b/src/plugins/platforms/windows/windows.pri
index 6d01d05fcc..f4c396f7c5 100644
--- a/src/plugins/platforms/windows/windows.pri
+++ b/src/plugins/platforms/windows/windows.pri
@@ -19,11 +19,13 @@ SOURCES += \
$$PWD/qwindowskeymapper.cpp \
$$PWD/qwindowsmousehandler.cpp \
$$PWD/qwindowsole.cpp \
+ $$PWD/qwindowsdropdataobject.cpp \
$$PWD/qwindowsmime.cpp \
$$PWD/qwindowsinternalmimedata.cpp \
$$PWD/qwindowscursor.cpp \
$$PWD/qwindowsinputcontext.cpp \
$$PWD/qwindowstheme.cpp \
+ $$PWD/qwindowsmenu.cpp \
$$PWD/qwindowsdialoghelpers.cpp \
$$PWD/qwindowsservices.cpp \
$$PWD/qwindowsnativeinterface.cpp \
@@ -31,6 +33,7 @@ SOURCES += \
$$PWD/qwin10helpers.cpp
HEADERS += \
+ $$PWD/qwindowscombase.h \
$$PWD/qwindowswindow.h \
$$PWD/qwindowsintegration.h \
$$PWD/qwindowscontext.h \
@@ -39,16 +42,18 @@ HEADERS += \
$$PWD/qwindowsmousehandler.h \
$$PWD/qtwindowsglobal.h \
$$PWD/qwindowsole.h \
+ $$PWD/qwindowsdropdataobject.h \
$$PWD/qwindowsmime.h \
$$PWD/qwindowsinternalmimedata.h \
$$PWD/qwindowscursor.h \
$$PWD/qwindowsinputcontext.h \
$$PWD/qwindowstheme.h \
+ $$PWD/qwindowsmenu.h \
$$PWD/qwindowsdialoghelpers.h \
$$PWD/qwindowsservices.h \
$$PWD/qwindowsnativeinterface.h \
$$PWD/qwindowsopengltester.h \
- $$PWD/qwindowsthreadpoolrunner.h
+ $$PWD/qwindowsthreadpoolrunner.h \
$$PWD/qwin10helpers.h
INCLUDEPATH += $$PWD
@@ -69,6 +74,16 @@ qtConfig(dynamicgl) {
HEADERS += $$PWD/qwindowseglcontext.h
}
+qtConfig(systemtrayicon) {
+ SOURCES += $$PWD/qwindowssystemtrayicon.cpp
+ HEADERS += $$PWD/qwindowssystemtrayicon.h
+}
+
+qtConfig(vulkan) {
+ SOURCES += $$PWD/qwindowsvulkaninstance.cpp
+ HEADERS += $$PWD/qwindowsvulkaninstance.h
+}
+
qtConfig(clipboard) {
SOURCES += $$PWD/qwindowsclipboard.cpp
HEADERS += $$PWD/qwindowsclipboard.h
@@ -94,7 +109,7 @@ qtConfig(imageformat_png):RESOURCES += $$PWD/cursors.qrc
RESOURCES += $$PWD/openglblacklists.qrc
-qtConfig(accessibility): include($$PWD/accessible/accessible.pri)
+qtConfig(accessibility): include($$PWD/uiautomation/uiautomation.pri)
qtConfig(combined-angle-lib) {
DEFINES *= LIBEGL_NAME=$${LIBQTANGLE_NAME}
diff --git a/src/plugins/platforms/windows/windows.pro b/src/plugins/platforms/windows/windows.pro
index c5d76c5d1d..174bc7b609 100644
--- a/src/plugins/platforms/windows/windows.pro
+++ b/src/plugins/platforms/windows/windows.pro
@@ -6,6 +6,7 @@ QT += \
fontdatabase_support-private theme_support-private
qtConfig(accessibility): QT += accessibility_support-private
+qtConfig(vulkan): QT += vulkan_support-private
LIBS += -lgdi32 -ldwmapi
diff --git a/src/plugins/platforms/winrt/qwinrtclipboard.cpp b/src/plugins/platforms/winrt/qwinrtclipboard.cpp
index 117cb515df..05c34b82f8 100644
--- a/src/plugins/platforms/winrt/qwinrtclipboard.cpp
+++ b/src/plugins/platforms/winrt/qwinrtclipboard.cpp
@@ -59,7 +59,7 @@ typedef IEventHandler<IInspectable *> ContentChangedHandler;
QT_BEGIN_NAMESPACE
QWinRTClipboard::QWinRTClipboard()
- : m_mimeData(Q_NULLPTR)
+ : m_mimeData(nullptr)
{
QEventDispatcherWinRT::runOnXamlThread([this]() {
HRESULT hr;
diff --git a/src/plugins/platforms/winrt/qwinrtcursor.cpp b/src/plugins/platforms/winrt/qwinrtcursor.cpp
index 28a5f73e6e..3c918df935 100644
--- a/src/plugins/platforms/winrt/qwinrtcursor.cpp
+++ b/src/plugins/platforms/winrt/qwinrtcursor.cpp
@@ -89,7 +89,7 @@ void QWinRTCursor::changeCursor(QCursor *windowCursor, QWindow *window)
switch (windowCursor ? windowCursor->shape() : Qt::ArrowCursor) {
case Qt::BlankCursor:
hr = QEventDispatcherWinRT::runOnXamlThread([coreWindow]() {
- coreWindow->put_PointerCursor(Q_NULLPTR);
+ coreWindow->put_PointerCursor(nullptr);
return S_OK;
});
RETURN_VOID_IF_FAILED("Failed to set blank native cursor");
diff --git a/src/plugins/platforms/winrt/qwinrtdrag.cpp b/src/plugins/platforms/winrt/qwinrtdrag.cpp
index 15ae024d20..0c918230b3 100644
--- a/src/plugins/platforms/winrt/qwinrtdrag.cpp
+++ b/src/plugins/platforms/winrt/qwinrtdrag.cpp
@@ -104,7 +104,7 @@ class DragThreadTransferData : public QObject
public slots:
void handleDrag();
public:
- explicit DragThreadTransferData(QObject *parent = Q_NULLPTR);
+ explicit DragThreadTransferData(QObject *parent = nullptr);
QWindow *window;
QWinRTInternalMimeData *mime;
QPoint point;
@@ -773,12 +773,6 @@ void QWinRTDrag::setDropTarget(QWindow *target)
m_dragTarget = target;
}
-QMimeData *QWinRTDrag::platformDropData()
-{
- qCDebug(lcQpaMime) << __FUNCTION__;
- return m_mimeData;
-}
-
void QWinRTDrag::setUiElement(ComPtr<ABI::Windows::UI::Xaml::IUIElement> &element)
{
qCDebug(lcQpaMime) << __FUNCTION__;
diff --git a/src/plugins/platforms/winrt/qwinrtdrag.h b/src/plugins/platforms/winrt/qwinrtdrag.h
index 6cbabfbf41..2371201507 100644
--- a/src/plugins/platforms/winrt/qwinrtdrag.h
+++ b/src/plugins/platforms/winrt/qwinrtdrag.h
@@ -94,7 +94,6 @@ public:
virtual ~QWinRTDrag();
static QWinRTDrag *instance();
- QMimeData *platformDropData(void) override;
Qt::DropAction drag(QDrag *) override;
void setDropTarget(QWindow *target);
diff --git a/src/plugins/platforms/winrt/qwinrtfiledialoghelper.cpp b/src/plugins/platforms/winrt/qwinrtfiledialoghelper.cpp
index 62eacba89b..3c90334c8c 100644
--- a/src/plugins/platforms/winrt/qwinrtfiledialoghelper.cpp
+++ b/src/plugins/platforms/winrt/qwinrtfiledialoghelper.cpp
@@ -83,7 +83,7 @@ public:
}
HRESULT __stdcall GetView(IVectorView<HSTRING> **view)
{
- *view = Q_NULLPTR;
+ *view = nullptr;
return E_NOTIMPL;
}
HRESULT __stdcall IndexOf(HSTRING value, quint32 *index, boolean *found)
diff --git a/src/plugins/platforms/winrt/qwinrtfileengine.cpp b/src/plugins/platforms/winrt/qwinrtfileengine.cpp
index 58375d331c..76efdf6cc8 100644
--- a/src/plugins/platforms/winrt/qwinrtfileengine.cpp
+++ b/src/plugins/platforms/winrt/qwinrtfileengine.cpp
@@ -144,7 +144,7 @@ QAbstractFileEngine *QWinRTFileEngineHandler::create(const QString &fileName) co
if (file != d->files.end())
return new QWinRTFileEngine(fileName, file.value().Get());
- return Q_NULLPTR;
+ return nullptr;
}
static HRESULT getDestinationFolder(const QString &fileName, const QString &newFileName,
@@ -414,10 +414,11 @@ QDateTime QWinRTFileEngine::fileTime(FileTime type) const
HRESULT hr;
DateTime dateTime = { 0 };
switch (type) {
- case CreationTime:
+ case BirthTime:
hr = d->file->get_DateCreated(&dateTime);
RETURN_IF_FAILED("Failed to get file creation time", return QDateTime());
break;
+ case MetadataChangeTime:
case ModificationTime:
case AccessTime: {
ComPtr<IAsyncOperation<FileProperties::BasicProperties *>> op;
diff --git a/src/plugins/platforms/winrt/qwinrtscreen.cpp b/src/plugins/platforms/winrt/qwinrtscreen.cpp
index c370c2ec50..e37aeb0bc5 100644
--- a/src/plugins/platforms/winrt/qwinrtscreen.cpp
+++ b/src/plugins/platforms/winrt/qwinrtscreen.cpp
@@ -47,6 +47,7 @@
#endif
#include "qwinrtwindow.h"
#include <private/qeventdispatcher_winrt_p.h>
+#include <private/qhighdpiscaling_p.h>
#include <QtCore/QLoggingCategory>
#include <QtGui/QSurfaceFormat>
@@ -487,7 +488,8 @@ public:
#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP)
QAtomicPointer<QWinRTWindow> mouseGrabWindow;
QAtomicPointer<QWinRTWindow> keyboardGrabWindow;
- QWindow *currentPressWindow = 0;
+ QWindow *currentPressWindow = nullptr;
+ QWindow *currentTargetWindow = nullptr;
};
// To be called from the XAML thread
@@ -497,7 +499,7 @@ QWinRTScreen::QWinRTScreen()
Q_D(QWinRTScreen);
qCDebug(lcQpaWindows) << __FUNCTION__;
d->orientation = Qt::PrimaryOrientation;
- d->touchDevice = Q_NULLPTR;
+ d->touchDevice = nullptr;
HRESULT hr;
ComPtr<Xaml::IWindowStatics> windowStatics;
@@ -540,7 +542,7 @@ QWinRTScreen::QWinRTScreen()
Q_ASSERT_SUCCEEDED(hr);
d->nativeOrientation = static_cast<Qt::ScreenOrientation>(static_cast<int>(qtOrientationsFromNative(displayOrientation)));
// Set initial pixel density
- onDpiChanged(Q_NULLPTR, Q_NULLPTR);
+ onDpiChanged(nullptr, nullptr);
d->orientation = d->nativeOrientation;
ComPtr<IApplicationViewStatics2> applicationViewStatics;
@@ -764,7 +766,7 @@ void QWinRTScreen::initialize()
Q_ASSERT_SUCCEEDED(hr);
hr = d->displayInformation->add_DpiChanged(Callback<DisplayInformationHandler>(this, &QWinRTScreen::onDpiChanged).Get(), &d->displayTokens[&IDisplayInformation::remove_DpiChanged]);
Q_ASSERT_SUCCEEDED(hr);
- onOrientationChanged(Q_NULLPTR, Q_NULLPTR);
+ onOrientationChanged(nullptr, nullptr);
onVisibilityChanged(nullptr, nullptr);
hr = d->redirect->add_PointerRoutedReleased(Callback<RedirectHandler>(this, &QWinRTScreen::onRedirectReleased).Get(), &d->redirectTokens[&ICorePointerRedirector::remove_PointerRoutedReleased]);
@@ -864,7 +866,7 @@ void QWinRTScreen::removeWindow(QWindow *window)
const Qt::WindowType type = window->type();
if (wasTopWindow && type != Qt::Popup && type != Qt::ToolTip && type != Qt::Tool)
- QWindowSystemInterface::handleWindowActivated(Q_NULLPTR, Qt::OtherFocusReason);
+ QWindowSystemInterface::handleWindowActivated(nullptr, Qt::OtherFocusReason);
handleExpose();
QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);
#ifndef QT_NO_DRAGANDDROP
@@ -1081,11 +1083,11 @@ HRESULT QWinRTScreen::onPointerEntered(ICoreWindow *, IPointerEventArgs *args)
pointerPoint->get_Position(&point);
QPoint pos(point.X * d->scaleFactor, point.Y * d->scaleFactor);
- QWindow *targetWindow = topWindow();
+ d->currentTargetWindow = topWindow();
if (d->mouseGrabWindow)
- targetWindow = d->mouseGrabWindow.load()->window();
+ d->currentTargetWindow = d->mouseGrabWindow.load()->window();
- QWindowSystemInterface::handleEnterEvent(targetWindow, pos, pos);
+ QWindowSystemInterface::handleEnterEvent(d->currentTargetWindow, pos, pos);
}
return S_OK;
}
@@ -1104,11 +1106,11 @@ HRESULT QWinRTScreen::onPointerExited(ICoreWindow *, IPointerEventArgs *args)
d->touchPoints.remove(id);
- QWindow *targetWindow = nullptr;
if (d->mouseGrabWindow)
- targetWindow = d->mouseGrabWindow.load()->window();
+ d->currentTargetWindow = d->mouseGrabWindow.load()->window();
- QWindowSystemInterface::handleLeaveEvent(targetWindow);
+ QWindowSystemInterface::handleLeaveEvent(d->currentTargetWindow);
+ d->currentTargetWindow = nullptr;
return S_OK;
}
@@ -1126,19 +1128,19 @@ HRESULT QWinRTScreen::onPointerUpdated(ICoreWindow *, IPointerEventArgs *args)
// Common traits - point, modifiers, properties
Point point;
pointerPoint->get_Position(&point);
- QPointF pos(point.X * d->scaleFactor, point.Y * d->scaleFactor);
+ const QPointF pos(point.X * d->scaleFactor, point.Y * d->scaleFactor);
QPointF localPos = pos;
const QPoint posPoint = pos.toPoint();
- QWindow *windowUnderPointer = windowAt(posPoint);
- QWindow *targetWindow = windowUnderPointer;
+ QWindow *windowUnderPointer = windowAt(QHighDpiScaling::mapPositionFromNative(posPoint, this));
+ d->currentTargetWindow = windowUnderPointer;
if (d->mouseGrabWindow)
- targetWindow = d->mouseGrabWindow.load()->window();
+ d->currentTargetWindow = d->mouseGrabWindow.load()->window();
- if (targetWindow) {
+ if (d->currentTargetWindow) {
const QPointF globalPosDelta = pos - posPoint;
- localPos = targetWindow->mapFromGlobal(posPoint) + globalPosDelta;
+ localPos = d->currentTargetWindow->mapFromGlobal(posPoint) + globalPosDelta;
}
VirtualKeyModifiers modifiers;
@@ -1173,7 +1175,7 @@ HRESULT QWinRTScreen::onPointerUpdated(ICoreWindow *, IPointerEventArgs *args)
boolean isHorizontal;
properties->get_IsHorizontalMouseWheel(&isHorizontal);
QPoint angleDelta(isHorizontal ? delta : 0, isHorizontal ? 0 : delta);
- QWindowSystemInterface::handleWheelEvent(targetWindow, localPos, pos, QPoint(), angleDelta, mods);
+ QWindowSystemInterface::handleWheelEvent(d->currentTargetWindow, localPos, pos, QPoint(), angleDelta, mods);
break;
}
@@ -1207,15 +1209,22 @@ HRESULT QWinRTScreen::onPointerUpdated(ICoreWindow *, IPointerEventArgs *args)
// menus.
if (buttons != Qt::NoButton && d->currentPressWindow == nullptr && !d->mouseGrabWindow)
d->currentPressWindow = windowUnderPointer;
- if (!isPressed && d->currentPressWindow && d->mouseGrabWindow) {
+ if (buttons == Qt::NoButton && d->currentPressWindow && d->mouseGrabWindow) {
const QPointF globalPosDelta = pos - posPoint;
const QPointF localPressPos = d->currentPressWindow->mapFromGlobal(posPoint) + globalPosDelta;
QWindowSystemInterface::handleMouseEvent(d->currentPressWindow, localPressPos, pos, buttons, mods);
d->currentPressWindow = nullptr;
}
+ // If the mouse button is released outside of a window, targetWindow is 0, but the event
+ // has to be delivered to the window, that initially received the mouse press. Do not reset
+ // d->currentTargetWindow though, as it is used (and reset) in onPointerExited.
+ if (buttons == Qt::NoButton && d->currentPressWindow && !d->currentTargetWindow) {
+ d->currentTargetWindow = d->currentPressWindow;
+ d->currentPressWindow = nullptr;
+ }
- QWindowSystemInterface::handleMouseEvent(targetWindow, localPos, pos, buttons, mods);
+ QWindowSystemInterface::handleMouseEvent(d->currentTargetWindow, localPos, pos, buttons, mods);
break;
}
@@ -1269,7 +1278,7 @@ HRESULT QWinRTScreen::onPointerUpdated(ICoreWindow *, IPointerEventArgs *args)
it.value().normalPosition = QPointF(point.X/d->logicalRect.width(), point.Y/d->logicalRect.height());
it.value().pressure = pressure;
- QWindowSystemInterface::handleTouchEvent(targetWindow, d->touchDevice, d->touchPoints.values(), mods);
+ QWindowSystemInterface::handleTouchEvent(d->currentTargetWindow, d->touchDevice, d->touchPoints.values(), mods);
// Fall-through for pen to generate tablet event
if (pointerDeviceType != PointerDeviceType_Pen)
@@ -1288,7 +1297,7 @@ HRESULT QWinRTScreen::onPointerUpdated(ICoreWindow *, IPointerEventArgs *args)
float rotation;
properties->get_Twist(&rotation);
- QWindowSystemInterface::handleTabletEvent(targetWindow, isPressed, pos, pos, 0,
+ QWindowSystemInterface::handleTabletEvent(d->currentTargetWindow, isPressed, pos, pos, 0,
pointerType, pressure, xTilt, yTilt,
0, rotation, 0, id, mods);
diff --git a/src/plugins/platforms/winrt/qwinrtwindow.cpp b/src/plugins/platforms/winrt/qwinrtwindow.cpp
index c40a1b8c45..cbf0ba36c9 100644
--- a/src/plugins/platforms/winrt/qwinrtwindow.cpp
+++ b/src/plugins/platforms/winrt/qwinrtwindow.cpp
@@ -91,7 +91,7 @@ public:
QSurfaceFormat surfaceFormat;
QString windowTitle;
- Qt::WindowState state;
+ Qt::WindowStates state;
EGLDisplay display;
EGLSurface surface;
@@ -158,7 +158,7 @@ QWinRTWindow::QWinRTWindow(QWindow *window)
Q_ASSERT_SUCCEEDED(hr);
setWindowFlags(window->flags());
- setWindowState(window->windowState());
+ setWindowState(window->windowStates());
setWindowTitle(window->title());
setGeometry(window->geometry());
@@ -323,7 +323,7 @@ qreal QWinRTWindow::devicePixelRatio() const
return screen()->devicePixelRatio();
}
-void QWinRTWindow::setWindowState(Qt::WindowState state)
+void QWinRTWindow::setWindowState(Qt::WindowStates state)
{
Q_D(QWinRTWindow);
qCDebug(lcQpaWindows) << __FUNCTION__ << this << state;
@@ -331,7 +331,13 @@ void QWinRTWindow::setWindowState(Qt::WindowState state)
if (d->state == state)
return;
- if (state == Qt::WindowFullScreen) {
+ if (state & Qt::WindowMinimized) {
+ setUIElementVisibility(d->uiElement.Get(), false);
+ d->state = state;
+ return;
+ }
+
+ if (state & Qt::WindowFullScreen) {
HRESULT hr;
boolean success;
hr = QEventDispatcherWinRT::runOnXamlThread([&hr, &success]() {
@@ -356,7 +362,7 @@ void QWinRTWindow::setWindowState(Qt::WindowState state)
return;
}
- if (d->state == Qt::WindowFullScreen) {
+ if (d->state & Qt::WindowFullScreen) {
HRESULT hr;
hr = QEventDispatcherWinRT::runOnXamlThread([&hr]() {
ComPtr<IApplicationViewStatics2> applicationViewStatics;
@@ -378,10 +384,7 @@ void QWinRTWindow::setWindowState(Qt::WindowState state)
}
}
- if (state == Qt::WindowMinimized)
- setUIElementVisibility(d->uiElement.Get(), false);
-
- if (d->state == Qt::WindowMinimized || state == Qt::WindowNoState || state == Qt::WindowActive)
+ if (d->state & Qt::WindowMinimized || state == Qt::WindowNoState || state == Qt::WindowActive)
setUIElementVisibility(d->uiElement.Get(), true);
d->state = state;
diff --git a/src/plugins/platforms/winrt/qwinrtwindow.h b/src/plugins/platforms/winrt/qwinrtwindow.h
index 26c2fa800d..9604b4bbaa 100644
--- a/src/plugins/platforms/winrt/qwinrtwindow.h
+++ b/src/plugins/platforms/winrt/qwinrtwindow.h
@@ -68,10 +68,10 @@ public:
WId winId() const override;
qreal devicePixelRatio() const override;
- void setWindowState(Qt::WindowState state) override;
+ void setWindowState(Qt::WindowStates state) override;
- bool setMouseGrabEnabled(bool grab) Q_DECL_OVERRIDE;
- bool setKeyboardGrabEnabled(bool grab) Q_DECL_OVERRIDE;
+ bool setMouseGrabEnabled(bool grab) override;
+ bool setKeyboardGrabEnabled(bool grab) override;
EGLSurface eglSurface() const;
void createEglSurface(EGLDisplay display, EGLConfig config);
diff --git a/src/plugins/platforms/xcb/gl_integrations/qxcbglintegration.h b/src/plugins/platforms/xcb/gl_integrations/qxcbglintegration.h
index 926e5e22df..07e983a499 100644
--- a/src/plugins/platforms/xcb/gl_integrations/qxcbglintegration.h
+++ b/src/plugins/platforms/xcb/gl_integrations/qxcbglintegration.h
@@ -69,7 +69,7 @@ public:
#endif
virtual QPlatformOffscreenSurface *createPlatformOffscreenSurface(QOffscreenSurface *surface) const = 0;
- virtual QXcbNativeInterfaceHandler *nativeInterfaceHandler() const { return Q_NULLPTR; }
+ virtual QXcbNativeInterfaceHandler *nativeInterfaceHandler() const { return nullptr; }
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/gl_integrations/qxcbnativeinterfacehandler.cpp b/src/plugins/platforms/xcb/gl_integrations/qxcbnativeinterfacehandler.cpp
index ac992b859d..e18656c6ec 100644
--- a/src/plugins/platforms/xcb/gl_integrations/qxcbnativeinterfacehandler.cpp
+++ b/src/plugins/platforms/xcb/gl_integrations/qxcbnativeinterfacehandler.cpp
@@ -56,37 +56,37 @@ QXcbNativeInterfaceHandler::~QXcbNativeInterfaceHandler()
QPlatformNativeInterface::NativeResourceForIntegrationFunction QXcbNativeInterfaceHandler::nativeResourceFunctionForIntegration(const QByteArray &resource) const
{
Q_UNUSED(resource);
- return Q_NULLPTR;
+ return nullptr;
}
QPlatformNativeInterface::NativeResourceForContextFunction QXcbNativeInterfaceHandler::nativeResourceFunctionForContext(const QByteArray &resource) const
{
Q_UNUSED(resource);
- return Q_NULLPTR;
+ return nullptr;
}
QPlatformNativeInterface::NativeResourceForScreenFunction QXcbNativeInterfaceHandler::nativeResourceFunctionForScreen(const QByteArray &resource) const
{
Q_UNUSED(resource);
- return Q_NULLPTR;
+ return nullptr;
}
QPlatformNativeInterface::NativeResourceForWindowFunction QXcbNativeInterfaceHandler::nativeResourceFunctionForWindow(const QByteArray &resource) const
{
Q_UNUSED(resource);
- return Q_NULLPTR;
+ return nullptr;
}
QPlatformNativeInterface::NativeResourceForBackingStoreFunction QXcbNativeInterfaceHandler::nativeResourceFunctionForBackingStore(const QByteArray &resource) const
{
Q_UNUSED(resource);
- return Q_NULLPTR;
+ return nullptr;
}
QFunctionPointer QXcbNativeInterfaceHandler::platformFunction(const QByteArray &function) const
{
Q_UNUSED(function);
- return Q_NULLPTR;
+ return nullptr;
}
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglcontext.h b/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglcontext.h
index 48e774bbb2..c3ce8d8745 100644
--- a/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglcontext.h
+++ b/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglcontext.h
@@ -47,38 +47,36 @@
QT_BEGIN_NAMESPACE
-//####todo remove the noops (looks like their where there in the initial commit)
class QXcbEglContext : public QEGLPlatformContext
{
public:
QXcbEglContext(const QSurfaceFormat &glFormat, QPlatformOpenGLContext *share,
- EGLDisplay display, QXcbConnection *c, const QVariant &nativeHandle)
+ EGLDisplay display, const QVariant &nativeHandle)
: QEGLPlatformContext(glFormat, share, display, 0, nativeHandle)
- , m_connection(c)
{
- Q_XCB_NOOP(m_connection);
}
void swapBuffers(QPlatformSurface *surface)
{
- Q_XCB_NOOP(m_connection);
QEGLPlatformContext::swapBuffers(surface);
- Q_XCB_NOOP(m_connection);
+ if (surface->surface()->surfaceClass() == QSurface::Window) {
+ QXcbWindow *platformWindow = static_cast<QXcbWindow *>(surface);
+ // OpenGL context might be bound to a non-gui thread use QueuedConnection to sync
+ // the window from the platformWindow's thread as QXcbWindow is no QObject, an
+ // event is sent to QXcbConnection. (this is faster than a metacall)
+ if (platformWindow->needsSync())
+ platformWindow->postSyncWindowRequest();
+ }
}
bool makeCurrent(QPlatformSurface *surface)
{
- Q_XCB_NOOP(m_connection);
- bool ret = QEGLPlatformContext::makeCurrent(surface);
- Q_XCB_NOOP(m_connection);
- return ret;
+ return QEGLPlatformContext::makeCurrent(surface);
}
void doneCurrent()
{
- Q_XCB_NOOP(m_connection);
QEGLPlatformContext::doneCurrent();
- Q_XCB_NOOP(m_connection);
}
EGLSurface eglSurfaceForPlatformSurface(QPlatformSurface *surface)
@@ -92,9 +90,6 @@ public:
QVariant nativeHandle() const {
return QVariant::fromValue<QEGLNativeContext>(QEGLNativeContext(eglContext(), eglDisplay()));
}
-
-private:
- QXcbConnection *m_connection;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglintegration.cpp b/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglintegration.cpp
index 9c52733120..fe18bc24db 100644
--- a/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglintegration.cpp
+++ b/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglintegration.cpp
@@ -49,7 +49,7 @@
QT_BEGIN_NAMESPACE
QXcbEglIntegration::QXcbEglIntegration()
- : m_connection(Q_NULLPTR)
+ : m_connection(nullptr)
, m_egl_display(EGL_NO_DISPLAY)
{
qCDebug(lcQpaGl) << "Xcb EGL gl-integration created";
@@ -102,7 +102,6 @@ QPlatformOpenGLContext *QXcbEglIntegration::createPlatformOpenGLContext(QOpenGLC
QXcbEglContext *platformContext = new QXcbEglContext(screen->surfaceFormatFor(context->format()),
context->shareHandle(),
eglDisplay(),
- screen->connection(),
context->nativeHandle());
context->setNativeHandle(platformContext->nativeHandle());
return platformContext;
diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglnativeinterfacehandler.cpp b/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglnativeinterfacehandler.cpp
index 30f5e3a00d..c0e3f820fe 100644
--- a/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglnativeinterfacehandler.cpp
+++ b/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglnativeinterfacehandler.cpp
@@ -77,7 +77,7 @@ QPlatformNativeInterface::NativeResourceForIntegrationFunction QXcbEglNativeInte
default:
break;
}
- return Q_NULLPTR;
+ return nullptr;
}
QPlatformNativeInterface::NativeResourceForContextFunction QXcbEglNativeInterfaceHandler::nativeResourceFunctionForContext(const QByteArray &resource) const
@@ -90,7 +90,7 @@ QPlatformNativeInterface::NativeResourceForContextFunction QXcbEglNativeInterfac
default:
break;
}
- return Q_NULLPTR;
+ return nullptr;
}
QPlatformNativeInterface::NativeResourceForWindowFunction QXcbEglNativeInterfaceHandler::nativeResourceFunctionForWindow(const QByteArray &resource) const
@@ -101,7 +101,7 @@ QPlatformNativeInterface::NativeResourceForWindowFunction QXcbEglNativeInterface
default:
break;
}
- return Q_NULLPTR;
+ return nullptr;
}
void *QXcbEglNativeInterfaceHandler::eglDisplay()
@@ -114,11 +114,11 @@ void *QXcbEglNativeInterfaceHandler::eglDisplay()
void *QXcbEglNativeInterfaceHandler::eglDisplayForWindow(QWindow *window)
{
Q_ASSERT(window);
- if (window->supportsOpenGL() && window->handle() == Q_NULLPTR)
+ if (window->supportsOpenGL() && window->handle() == nullptr)
return eglDisplay();
else if (window->supportsOpenGL())
return static_cast<QXcbEglWindow *>(window->handle())->glIntegration()->eglDisplay();
- return Q_NULLPTR;
+ return nullptr;
}
void *QXcbEglNativeInterfaceHandler::eglContextForContext(QOpenGLContext *context)
diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglwindow.cpp b/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglwindow.cpp
index 9c3fd26d49..65beac227c 100644
--- a/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglwindow.cpp
+++ b/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglwindow.cpp
@@ -49,7 +49,7 @@ QT_BEGIN_NAMESPACE
QXcbEglWindow::QXcbEglWindow(QWindow *window, QXcbEglIntegration *glIntegration)
: QXcbWindow(window)
, m_glIntegration(glIntegration)
- , m_config(Q_NULLPTR)
+ , m_config(nullptr)
, m_surface(EGL_NO_SURFACE)
{
}
diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp
index fc1806f982..21024385b0 100644
--- a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp
+++ b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp
@@ -171,7 +171,7 @@ static void updateFormatFromContext(QSurfaceFormat &format)
QGLXContext::QGLXContext(QXcbScreen *screen, const QSurfaceFormat &format, QPlatformOpenGLContext *share,
const QVariant &nativeHandle)
: QPlatformOpenGLContext()
- , m_display(DISPLAY_FROM_XCB(screen))
+ , m_display(static_cast<Display *>(screen->connection()->xlib_display()))
, m_config(0)
, m_context(0)
, m_shareContext(0)
@@ -200,7 +200,7 @@ void QGLXContext::init(QXcbScreen *screen, QPlatformOpenGLContext *share)
if (share)
m_shareContext = static_cast<const QGLXContext*>(share)->glxContext();
- GLXFBConfig config = qglx_findConfig(DISPLAY_FROM_XCB(screen),screen->screenNumber(),m_format);
+ GLXFBConfig config = qglx_findConfig(m_display, screen->screenNumber(), m_format);
m_config = config;
XVisualInfo *visualInfo = 0;
Window window = 0; // Temporary window used to query OpenGL context
@@ -308,10 +308,10 @@ void QGLXContext::init(QXcbScreen *screen, QPlatformOpenGLContext *share)
// Get the basic surface format details
if (m_context)
- qglx_surfaceFormatFromGLXFBConfig(&m_format, DISPLAY_FROM_XCB(screen), config);
+ qglx_surfaceFormatFromGLXFBConfig(&m_format, m_display, config);
// Create a temporary window so that we can make the new context current
- window = createDummyWindow(DISPLAY_FROM_XCB(screen), config, screen->screenNumber(), screen->root());
+ window = createDummyWindow(m_display, config, screen->screenNumber(), screen->root());
} else {
// requesting an OpenGL ES context requires glXCreateContextAttribsARB, so bail out
if (m_format.renderableType() == QSurfaceFormat::OpenGLES)
@@ -325,11 +325,11 @@ void QGLXContext::init(QXcbScreen *screen, QPlatformOpenGLContext *share)
if (!m_context && m_shareContext) {
// re-try without a shared glx context
m_shareContext = 0;
- m_context = glXCreateContext(m_display, visualInfo, Q_NULLPTR, true);
+ m_context = glXCreateContext(m_display, visualInfo, nullptr, true);
}
// Create a temporary window so that we can make the new context current
- window = createDummyWindow(DISPLAY_FROM_XCB(screen), visualInfo, screen->screenNumber(), screen->root());
+ window = createDummyWindow(m_display, visualInfo, screen->screenNumber(), screen->root());
XFree(visualInfo);
}
@@ -364,7 +364,7 @@ void QGLXContext::init(QXcbScreen *screen, QPlatformOpenGLContext *share, const
// Use the provided Display, if available. If not, use our own. It may still work.
Display *dpy = handle.display();
if (!dpy)
- dpy = DISPLAY_FROM_XCB(screen);
+ dpy = m_display;
// Legacy contexts created using glXCreateContext are created using a visual
// and the FBConfig cannot be queried. The only way to adapt these contexts
@@ -474,7 +474,7 @@ static QXcbScreen *screenForPlatformSurface(QPlatformSurface *surface)
} else if (surfaceClass == QSurface::Offscreen) {
return static_cast<QXcbScreen *>(static_cast<QGLXPbuffer *>(surface)->screen());
}
- return Q_NULLPTR;
+ return nullptr;
}
QVariant QGLXContext::nativeHandle() const
@@ -669,8 +669,10 @@ void QGLXContext::queryDummyContext()
Display *display = glXGetCurrentDisplay();
if (!display) {
// FIXME: Since Qt 5.6 we don't need to check whether primary screen is NULL
- if (QScreen *screen = QGuiApplication::primaryScreen())
- display = DISPLAY_FROM_XCB(static_cast<QXcbScreen *>(screen->handle()));
+ if (QScreen *screen = QGuiApplication::primaryScreen()) {
+ QXcbScreen *xcbScreen = static_cast<QXcbScreen *>(screen->handle());
+ display = static_cast<Display *>(xcbScreen->connection()->xlib_display());
+ }
}
const char *glxvendor = glXGetClientString(display, GLX_VENDOR);
if (glxvendor && !strcmp(glxvendor, "ATI")) {
@@ -733,8 +735,7 @@ void QGLXContext::queryDummyContext()
bool QGLXContext::supportsThreading()
{
- if (!m_queriedDummyContext)
- queryDummyContext();
+ queryDummyContext();
return m_supportsThreading;
}
@@ -742,9 +743,10 @@ QGLXPbuffer::QGLXPbuffer(QOffscreenSurface *offscreenSurface)
: QPlatformOffscreenSurface(offscreenSurface)
, m_screen(static_cast<QXcbScreen *>(offscreenSurface->screen()->handle()))
, m_format(m_screen->surfaceFormatFor(offscreenSurface->requestedFormat()))
+ , m_display(static_cast<Display *>(m_screen->connection()->xlib_display()))
, m_pbuffer(0)
{
- GLXFBConfig config = qglx_findConfig(DISPLAY_FROM_XCB(m_screen), m_screen->screenNumber(), m_format);
+ GLXFBConfig config = qglx_findConfig(m_display, m_screen->screenNumber(), m_format);
if (config) {
const int attributes[] = {
@@ -755,17 +757,17 @@ QGLXPbuffer::QGLXPbuffer(QOffscreenSurface *offscreenSurface)
None
};
- m_pbuffer = glXCreatePbuffer(DISPLAY_FROM_XCB(m_screen), config, attributes);
+ m_pbuffer = glXCreatePbuffer(m_display, config, attributes);
if (m_pbuffer)
- qglx_surfaceFormatFromGLXFBConfig(&m_format, DISPLAY_FROM_XCB(m_screen), config);
+ qglx_surfaceFormatFromGLXFBConfig(&m_format, m_display, config);
}
}
QGLXPbuffer::~QGLXPbuffer()
{
if (m_pbuffer)
- glXDestroyPbuffer(DISPLAY_FROM_XCB(m_screen), m_pbuffer);
+ glXDestroyPbuffer(m_display, m_pbuffer);
}
diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.h b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.h
index 3dfe0ac618..f6372582db 100644
--- a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.h
+++ b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.h
@@ -108,6 +108,7 @@ public:
private:
QXcbScreen *m_screen;
QSurfaceFormat m_format;
+ Display *m_display;
GLXPbuffer m_pbuffer;
};
diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxintegration.cpp b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxintegration.cpp
index fea365cabc..13f03f8bf3 100644
--- a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxintegration.cpp
+++ b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxintegration.cpp
@@ -87,7 +87,7 @@ QT_BEGIN_NAMESPACE
#endif
QXcbGlxIntegration::QXcbGlxIntegration()
- : m_connection(Q_NULLPTR)
+ : m_connection(nullptr)
, m_glx_first_event(0)
{
qCDebug(lcQpaGl) << "Xcb GLX gl-integration created";
@@ -108,18 +108,13 @@ bool QXcbGlxIntegration::initialize(QXcbConnection *connection)
m_glx_first_event = reply->first_event;
- xcb_generic_error_t *error = 0;
- xcb_glx_query_version_cookie_t xglx_query_cookie = xcb_glx_query_version(m_connection->xcb_connection(),
- XCB_GLX_MAJOR_VERSION,
- XCB_GLX_MINOR_VERSION);
- xcb_glx_query_version_reply_t *xglx_query = xcb_glx_query_version_reply(m_connection->xcb_connection(),
- xglx_query_cookie, &error);
- if (!xglx_query || error) {
+ auto xglx_query = Q_XCB_REPLY(xcb_glx_query_version, m_connection->xcb_connection(),
+ XCB_GLX_MAJOR_VERSION,
+ XCB_GLX_MINOR_VERSION);
+ if (!xglx_query) {
qCWarning(lcQpaGl) << "QXcbConnection: Failed to initialize GLX";
- free(error);
return false;
}
- free(xglx_query);
#endif
m_native_interface_handler.reset(new QXcbGlxNativeInterfaceHandler(connection->nativeInterface()));
diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxnativeinterfacehandler.cpp b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxnativeinterfacehandler.cpp
index 638fdd46b5..e9bb4460ff 100644
--- a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxnativeinterfacehandler.cpp
+++ b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxnativeinterfacehandler.cpp
@@ -72,7 +72,7 @@ QPlatformNativeInterface::NativeResourceForContextFunction QXcbGlxNativeInterfac
default:
break;
}
- return Q_NULLPTR;
+ return nullptr;
}
void *QXcbGlxNativeInterfaceHandler::glxContextForContext(QOpenGLContext *context)
diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxwindow.cpp b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxwindow.cpp
index 8df8b28f72..5e406017ca 100644
--- a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxwindow.cpp
+++ b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxwindow.cpp
@@ -41,6 +41,7 @@
#include "qxcbscreen.h"
#include <QtGlxSupport/private/qglxconvenience_p.h>
+#include <QDebug>
QT_BEGIN_NAMESPACE
@@ -57,14 +58,29 @@ const xcb_visualtype_t *QXcbGlxWindow::createVisual()
{
QXcbScreen *scr = xcbScreen();
if (!scr)
- return Q_NULLPTR;
- XVisualInfo *visualInfo = qglx_findVisualInfo(DISPLAY_FROM_XCB(scr), scr->screenNumber(), &m_format);
+ return nullptr;
+
+ qCDebug(lcQpaGl) << "Requested format before FBConfig/Visual selection:" << m_format;
+
+ Display *dpy = static_cast<Display *>(scr->connection()->xlib_display());
+ const char *glxExts = glXQueryExtensionsString(dpy, scr->screenNumber());
+ int flags = 0;
+ if (glxExts) {
+ qCDebug(lcQpaGl, "Available GLX extensions: %s", glxExts);
+ if (strstr(glxExts, "GLX_EXT_framebuffer_sRGB") || strstr(glxExts, "GLX_ARB_framebuffer_sRGB"))
+ flags |= QGLX_SUPPORTS_SRGB;
+ }
+
+ XVisualInfo *visualInfo = qglx_findVisualInfo(dpy, scr->screenNumber(), &m_format, GLX_WINDOW_BIT, flags);
if (!visualInfo) {
qWarning() << "No XVisualInfo for format" << m_format;
- return Q_NULLPTR;
+ return nullptr;
}
const xcb_visualtype_t *xcb_visualtype = scr->visualForId(visualInfo->visualid);
XFree(visualInfo);
+
+ qCDebug(lcQpaGl) << "Got format:" << m_format;
+
return xcb_visualtype;
}
diff --git a/src/plugins/platforms/xcb/nativepainting/nativepainting.pri b/src/plugins/platforms/xcb/nativepainting/nativepainting.pri
new file mode 100644
index 0000000000..78ed00843f
--- /dev/null
+++ b/src/plugins/platforms/xcb/nativepainting/nativepainting.pri
@@ -0,0 +1,22 @@
+qtConfig(xcb-native-painting) {
+ qtConfig(xrender): QMAKE_USE += xrender
+ qtConfig(fontconfig): QMAKE_USE_PRIVATE += freetype
+
+ INCLUDEPATH += $$PWD
+ HEADERS += \
+ $$PWD/qtessellator_p.h \
+ $$PWD/qpixmap_x11_p.h \
+ $$PWD/qpaintengine_x11_p.h \
+ $$PWD/qt_x11_p.h \
+ $$PWD/qcolormap_x11_p.h \
+ $$PWD/qbackingstore_x11_p.h \
+ $$PWD/qxcbnativepainting.h
+
+ SOURCES += \
+ $$PWD/qtessellator.cpp \
+ $$PWD/qpixmap_x11.cpp \
+ $$PWD/qpaintengine_x11.cpp \
+ $$PWD/qcolormap_x11.cpp \
+ $$PWD/qbackingstore_x11.cpp \
+ $$PWD/qxcbnativepainting.cpp
+}
diff --git a/src/plugins/platforms/xcb/nativepainting/qbackingstore_x11.cpp b/src/plugins/platforms/xcb/nativepainting/qbackingstore_x11.cpp
new file mode 100644
index 0000000000..9b31998620
--- /dev/null
+++ b/src/plugins/platforms/xcb/nativepainting/qbackingstore_x11.cpp
@@ -0,0 +1,209 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qbackingstore_x11_p.h"
+#include "qxcbwindow.h"
+#include "qpixmap_x11_p.h"
+
+#include <private/qhighdpiscaling_p.h>
+#include <QPainter>
+
+#if QT_CONFIG(xrender)
+# include <X11/extensions/Xrender.h>
+#endif
+
+#include <X11/Xlib.h>
+
+#ifndef None
+#define None 0L
+#endif
+
+QT_BEGIN_NAMESPACE
+
+QXcbNativeBackingStore::QXcbNativeBackingStore(QWindow *window)
+ : QPlatformBackingStore(window)
+ , m_translucentBackground(false)
+{
+ if (QXcbWindow *w = static_cast<QXcbWindow *>(window->handle()))
+ m_translucentBackground = w->connection()->hasXRender() && QImage::toPixelFormat(w->imageFormat()).alphaSize() > 0;
+}
+
+QXcbNativeBackingStore::~QXcbNativeBackingStore()
+{}
+
+QPaintDevice *QXcbNativeBackingStore::paintDevice()
+{
+ return &m_pixmap;
+}
+
+void QXcbNativeBackingStore::flush(QWindow *window, const QRegion &region, const QPoint &offset)
+{
+ if (m_pixmap.isNull())
+ return;
+
+ QSize pixmapSize = m_pixmap.size();
+
+ QRegion clipped = region;
+ clipped &= QRect(QPoint(), QHighDpi::toNativePixels(window->size(), window));
+ clipped &= QRect(0, 0, pixmapSize.width(), pixmapSize.height()).translated(-offset);
+
+ QRect br = clipped.boundingRect();
+ if (br.isNull())
+ return;
+
+ QXcbWindow *platformWindow = static_cast<QXcbWindow *>(window->handle());
+ if (!platformWindow) {
+ qWarning("QXcbBackingStore::flush: QWindow has no platform window (QTBUG-32681)");
+ return;
+ }
+
+ Window wid = platformWindow->xcb_window();
+ Pixmap pid = qt_x11PixmapHandle(m_pixmap);
+
+ QVector<XRectangle> clipRects = qt_region_to_xrectangles(clipped);
+
+#if QT_CONFIG(xrender)
+ if (m_translucentBackground)
+ {
+ XWindowAttributes attrib;
+ XGetWindowAttributes(display(), wid, &attrib);
+ XRenderPictFormat *format = XRenderFindVisualFormat(display(), attrib.visual);
+
+ Picture srcPic = qt_x11PictureHandle(m_pixmap);
+ Picture dstPic = XRenderCreatePicture(display(), wid, format, 0, 0);
+
+ XRenderSetPictureClipRectangles(display(), dstPic, 0, 0, clipRects.constData(), clipRects.size());
+
+ XRenderComposite(display(), PictOpSrc, srcPic, None, dstPic,
+ br.x() + offset.x(), br.y() + offset.y(),
+ 0, 0,
+ br.x(), br.y(),
+ br.width(), br.height());
+
+ XRenderFreePicture(display(), dstPic);
+ }
+ else
+#endif
+ {
+ GC gc = XCreateGC(display(), wid, 0, nullptr);
+
+ if (clipRects.size() != 1)
+ XSetClipRectangles(display(), gc, 0, 0, clipRects.data(), clipRects.size(), YXBanded);
+
+ XCopyArea(display(), pid, wid, gc, br.x() + offset.x(), br.y() + offset.y(), br.width(), br.height(), br.x(), br.y());
+ XFreeGC(display(), gc);
+ }
+
+
+ if (platformWindow->needsSync()) {
+ platformWindow->updateSyncRequestCounter();
+ } else {
+ XFlush(display());
+ }
+}
+
+QImage QXcbNativeBackingStore::toImage() const
+{
+ return m_pixmap.toImage();
+}
+
+void QXcbNativeBackingStore::resize(const QSize &size, const QRegion &staticContents)
+{
+ if (size == m_pixmap.size())
+ return;
+
+ QPixmap newPixmap(size);
+
+#if QT_CONFIG(xrender)
+ if (m_translucentBackground && newPixmap.depth() != 32)
+ qt_x11Pixmap(newPixmap)->convertToARGB32();
+#endif
+
+ if (!m_pixmap.isNull()) {
+ Pixmap from = qt_x11PixmapHandle(m_pixmap);
+ Pixmap to = qt_x11PixmapHandle(newPixmap);
+ QRect br = staticContents.boundingRect().intersected(QRect(QPoint(0, 0), size));
+
+ if (!br.isEmpty()) {
+ GC gc = XCreateGC(display(), to, 0, nullptr);
+ XCopyArea(display(), from, to, gc, br.x(), br.y(), br.width(), br.height(), br.x(), br.y());
+ XFreeGC(display(), gc);
+ }
+ }
+
+ m_pixmap = newPixmap;
+}
+
+bool QXcbNativeBackingStore::scroll(const QRegion &area, int dx, int dy)
+{
+ if (m_pixmap.isNull())
+ return false;
+
+ QRect rect = area.boundingRect();
+ Pixmap pix = qt_x11PixmapHandle(m_pixmap);
+
+ GC gc = XCreateGC(display(), pix, 0, nullptr);
+ XCopyArea(display(), pix, pix, gc,
+ rect.x(), rect.y(), rect.width(), rect.height(),
+ rect.x()+dx, rect.y()+dy);
+ XFreeGC(display(), gc);
+ return true;
+}
+
+void QXcbNativeBackingStore::beginPaint(const QRegion &region)
+{
+#if QT_CONFIG(xrender)
+ if (m_translucentBackground) {
+ const QVector<XRectangle> xrects = qt_region_to_xrectangles(region);
+ const XRenderColor color = { 0, 0, 0, 0 };
+ XRenderFillRectangles(display(), PictOpSrc,
+ qt_x11PictureHandle(m_pixmap), &color,
+ xrects.constData(), xrects.size());
+ }
+#else
+ Q_UNUSED(region);
+#endif
+}
+
+Display *QXcbNativeBackingStore::display() const
+{
+ return static_cast<Display *>(static_cast<QXcbWindow *>(window()->handle())->connection()->xlib_display());
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/nativepainting/qbackingstore_x11_p.h b/src/plugins/platforms/xcb/nativepainting/qbackingstore_x11_p.h
new file mode 100644
index 0000000000..f3653a9438
--- /dev/null
+++ b/src/plugins/platforms/xcb/nativepainting/qbackingstore_x11_p.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QBACKINGSTORE_X11_H
+#define QBACKINGSTORE_X11_H
+
+#include <qpa/qplatformbackingstore.h>
+
+typedef struct _XDisplay Display;
+
+QT_BEGIN_NAMESPACE
+
+class QXcbWindow;
+
+class QXcbNativeBackingStore : public QPlatformBackingStore
+{
+public:
+ QXcbNativeBackingStore(QWindow *window);
+ ~QXcbNativeBackingStore();
+
+ QPaintDevice *paintDevice() override;
+ void flush(QWindow *window, const QRegion &region, const QPoint &offset) override;
+
+ QImage toImage() const override;
+
+ void resize(const QSize &size, const QRegion &staticContents) override;
+ bool scroll(const QRegion &area, int dx, int dy) override;
+
+ void beginPaint(const QRegion &region) override;
+
+private:
+ Display *display() const;
+
+ QPixmap m_pixmap;
+ bool m_translucentBackground;
+};
+
+QT_END_NAMESPACE
+
+#endif // QBACKINGSTORE_X11_H
diff --git a/src/plugins/platforms/xcb/nativepainting/qcolormap_x11.cpp b/src/plugins/platforms/xcb/nativepainting/qcolormap_x11.cpp
new file mode 100644
index 0000000000..fe9d1fcde9
--- /dev/null
+++ b/src/plugins/platforms/xcb/nativepainting/qcolormap_x11.cpp
@@ -0,0 +1,650 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QVarLengthArray>
+
+#include <private/qguiapplication_p.h>
+
+#include "qcolormap_x11_p.h"
+#include "qxcbnativepainting.h"
+#include "qt_x11_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QXcbColormapPrivate
+{
+public:
+ QXcbColormapPrivate()
+ : ref(1), mode(QXcbColormap::Direct), depth(0),
+ colormap(0), defaultColormap(true),
+ visual(0), defaultVisual(true),
+ r_max(0), g_max(0), b_max(0),
+ r_shift(0), g_shift(0), b_shift(0)
+ {}
+
+ QAtomicInt ref;
+
+ QXcbColormap::Mode mode;
+ int depth;
+
+ Colormap colormap;
+ bool defaultColormap;
+
+ Visual *visual;
+ bool defaultVisual;
+
+ int r_max;
+ int g_max;
+ int b_max;
+
+ uint r_shift;
+ uint g_shift;
+ uint b_shift;
+
+ QVector<QColor> colors;
+ QVector<int> pixels;
+};
+
+static uint right_align(uint v)
+{
+ while (!(v & 0x1))
+ v >>= 1;
+ return v;
+}
+
+static int cube_root(int v)
+{
+ if (v == 1)
+ return 1;
+ // brute force algorithm
+ int i = 1;
+ for (;;) {
+ const int b = i * i * i;
+ if (b <= v) {
+ ++i;
+ } else {
+ --i;
+ break;
+ }
+ }
+ return i;
+}
+
+static Visual *find_visual(Display *display,
+ int screen,
+ int visual_class,
+ int visual_id,
+ int *depth,
+ bool *defaultVisual)
+{
+ XVisualInfo *vi, rvi;
+ int count;
+
+ uint mask = VisualScreenMask;
+ rvi.screen = screen;
+
+ if (visual_class != -1) {
+ rvi.c_class = visual_class;
+ mask |= VisualClassMask;
+ }
+ if (visual_id != -1) {
+ rvi.visualid = visual_id;
+ mask |= VisualIDMask;
+ }
+
+ Visual *visual = DefaultVisual(display, screen);
+ *defaultVisual = true;
+ *depth = DefaultDepth(display, screen);
+
+ vi = XGetVisualInfo(display, mask, &rvi, &count);
+ if (vi) {
+ int best = 0;
+ for (int x = 0; x < count; ++x) {
+ if (vi[x].depth > vi[best].depth)
+ best = x;
+ }
+ if (best >= 0 && best <= count && vi[best].visualid != XVisualIDFromVisual(visual)) {
+ visual = vi[best].visual;
+ *defaultVisual = (visual == DefaultVisual(display, screen));
+ *depth = vi[best].depth;
+ }
+ }
+ if (vi)
+ XFree((char *)vi);
+ return visual;
+}
+
+static void query_colormap(QXcbColormapPrivate *d, int screen)
+{
+ Display *display = X11->display;
+
+ // query existing colormap
+ int q_colors = (((1u << d->depth) > 256u) ? 256u : (1u << d->depth));
+ XColor queried[256];
+ memset(queried, 0, sizeof(queried));
+ for (int x = 0; x < q_colors; ++x)
+ queried[x].pixel = x;
+ XQueryColors(display, d->colormap, queried, q_colors);
+
+ d->colors.resize(q_colors);
+ for (int x = 0; x < q_colors; ++x) {
+ if (queried[x].red == 0
+ && queried[x].green == 0
+ && queried[x].blue == 0
+ && queried[x].pixel != BlackPixel(display, screen)) {
+ // unallocated color cell, skip it
+ continue;
+ }
+
+ d->colors[x] = QColor::fromRgbF(queried[x].red / float(USHRT_MAX),
+ queried[x].green / float(USHRT_MAX),
+ queried[x].blue / float(USHRT_MAX));
+ }
+
+ // for missing colors, find the closest color in the existing colormap
+ Q_ASSERT(d->pixels.size());
+ for (int x = 0; x < d->pixels.size(); ++x) {
+ if (d->pixels.at(x) != -1)
+ continue;
+
+ QRgb rgb;
+ if (d->mode == QXcbColormap::Indexed) {
+ const int r = (x / (d->g_max * d->b_max)) % d->r_max;
+ const int g = (x / d->b_max) % d->g_max;
+ const int b = x % d->b_max;
+ rgb = qRgb((r * 0xff + (d->r_max - 1) / 2) / (d->r_max - 1),
+ (g * 0xff + (d->g_max - 1) / 2) / (d->g_max - 1),
+ (b * 0xff + (d->b_max - 1) / 2) / (d->b_max - 1));
+ } else {
+ rgb = qRgb(x, x, x);
+ }
+
+ // find closest color
+ int mindist = INT_MAX, best = -1;
+ for (int y = 0; y < q_colors; ++y) {
+ int r = qRed(rgb) - (queried[y].red >> 8);
+ int g = qGreen(rgb) - (queried[y].green >> 8);
+ int b = qBlue(rgb) - (queried[y].blue >> 8);
+ int dist = (r * r) + (g * g) + (b * b);
+ if (dist < mindist) {
+ mindist = dist;
+ best = y;
+ }
+ }
+
+ Q_ASSERT(best >= 0 && best < q_colors);
+ if (d->visual->c_class & 1) {
+ XColor xcolor;
+ xcolor.red = queried[best].red;
+ xcolor.green = queried[best].green;
+ xcolor.blue = queried[best].blue;
+ xcolor.pixel = queried[best].pixel;
+
+ if (XAllocColor(display, d->colormap, &xcolor)) {
+ d->pixels[x] = xcolor.pixel;
+ } else {
+ // some weird stuff is going on...
+ d->pixels[x] = (qGray(rgb) < 127
+ ? BlackPixel(display, screen)
+ : WhitePixel(display, screen));
+ }
+ } else {
+ d->pixels[x] = best;
+ }
+ }
+}
+
+static void init_gray(QXcbColormapPrivate *d, int screen)
+{
+ d->pixels.resize(d->r_max);
+
+ for (int g = 0; g < d->g_max; ++g) {
+ const int gray = (g * 0xff + (d->r_max - 1) / 2) / (d->r_max - 1);
+ const QRgb rgb = qRgb(gray, gray, gray);
+
+ d->pixels[g] = -1;
+
+ if (d->visual->c_class & 1) {
+ XColor xcolor;
+ xcolor.red = qRed(rgb) * 0x101;
+ xcolor.green = qGreen(rgb) * 0x101;
+ xcolor.blue = qBlue(rgb) * 0x101;
+ xcolor.pixel = 0ul;
+
+ if (XAllocColor(X11->display, d->colormap, &xcolor))
+ d->pixels[g] = xcolor.pixel;
+ }
+ }
+
+ query_colormap(d, screen);
+}
+
+static void init_indexed(QXcbColormapPrivate *d, int screen)
+{
+ d->pixels.resize(d->r_max * d->g_max * d->b_max);
+
+ // create color cube
+ for (int x = 0, r = 0; r < d->r_max; ++r) {
+ for (int g = 0; g < d->g_max; ++g) {
+ for (int b = 0; b < d->b_max; ++b, ++x) {
+ const QRgb rgb = qRgb((r * 0xff + (d->r_max - 1) / 2) / (d->r_max - 1),
+ (g * 0xff + (d->g_max - 1) / 2) / (d->g_max - 1),
+ (b * 0xff + (d->b_max - 1) / 2) / (d->b_max - 1));
+
+ d->pixels[x] = -1;
+
+ if (d->visual->c_class & 1) {
+ XColor xcolor;
+ xcolor.red = qRed(rgb) * 0x101;
+ xcolor.green = qGreen(rgb) * 0x101;
+ xcolor.blue = qBlue(rgb) * 0x101;
+ xcolor.pixel = 0ul;
+
+ if (XAllocColor(X11->display, d->colormap, &xcolor))
+ d->pixels[x] = xcolor.pixel;
+ }
+ }
+ }
+ }
+
+ query_colormap(d, screen);
+}
+
+static void init_direct(QXcbColormapPrivate *d, bool ownColormap)
+{
+ if (d->visual->c_class != DirectColor || !ownColormap)
+ return;
+
+ // preallocate 768 on the stack, so that we don't have to malloc
+ // for the common case (<= 24 bpp)
+ QVarLengthArray<XColor, 768> colorTable(d->r_max + d->g_max + d->b_max);
+ int i = 0;
+
+ for (int r = 0; r < d->r_max; ++r) {
+ colorTable[i].red = r << 8 | r;
+ colorTable[i].pixel = r << d->r_shift;
+ colorTable[i].flags = DoRed;
+ ++i;
+ }
+
+ for (int g = 0; g < d->g_max; ++g) {
+ colorTable[i].green = g << 8 | g;
+ colorTable[i].pixel = g << d->g_shift;
+ colorTable[i].flags = DoGreen;
+ ++i;
+ }
+
+ for (int b = 0; b < d->b_max; ++b) {
+ colorTable[i].blue = (b << 8 | b);
+ colorTable[i].pixel = b << d->b_shift;
+ colorTable[i].flags = DoBlue;
+ ++i;
+ }
+
+ XStoreColors(X11->display, d->colormap, colorTable.data(), colorTable.count());
+}
+
+static QXcbColormap **cmaps = 0;
+
+void QXcbColormap::initialize()
+{
+ Display *display = X11->display;
+ const int screens = ScreenCount(display);
+
+ cmaps = new QXcbColormap*[screens];
+
+ for (int i = 0; i < screens; ++i) {
+ cmaps[i] = new QXcbColormap;
+ QXcbColormapPrivate * const d = cmaps[i]->d;
+
+ bool use_stdcmap = false;
+ int color_count = X11->color_count;
+
+ // defaults
+ d->depth = DefaultDepth(display, i);
+ d->colormap = DefaultColormap(display, i);
+ d->defaultColormap = true;
+ d->visual = DefaultVisual(display, i);
+ d->defaultVisual = true;
+
+ Visual *argbVisual = 0;
+
+ if (X11->visual && i == DefaultScreen(display)) {
+ // only use the outside colormap on the default screen
+ d->visual = find_visual(display, i, X11->visual->c_class,
+ XVisualIDFromVisual(X11->visual),
+ &d->depth, &d->defaultVisual);
+ } else if ((X11->visual_class != -1 && X11->visual_class >= 0 && X11->visual_class < 6)
+ || (X11->visual_id != -1)) {
+ // look for a specific visual or type of visual
+ d->visual = find_visual(display, i, X11->visual_class, X11->visual_id,
+ &d->depth, &d->defaultVisual);
+ } else if (!X11->custom_cmap) {
+ XStandardColormap *stdcmap = 0;
+ int ncmaps = 0;
+
+#if QT_CONFIG(xrender)
+ if (X11->use_xrender) {
+ int nvi;
+ XVisualInfo templ;
+ templ.screen = i;
+ templ.depth = 32;
+ templ.c_class = TrueColor;
+ XVisualInfo *xvi = XGetVisualInfo(X11->display, VisualScreenMask |
+ VisualDepthMask |
+ VisualClassMask, &templ, &nvi);
+ for (int idx = 0; idx < nvi; ++idx) {
+ XRenderPictFormat *format = XRenderFindVisualFormat(X11->display,
+ xvi[idx].visual);
+ if (format->type == PictTypeDirect && format->direct.alphaMask) {
+ argbVisual = xvi[idx].visual;
+ break;
+ }
+ }
+ XFree(xvi);
+ }
+#endif
+ if (XGetRGBColormaps(display, RootWindow(display, i),
+ &stdcmap, &ncmaps, XA_RGB_DEFAULT_MAP)) {
+ if (stdcmap) {
+ for (int c = 0; c < ncmaps; ++c) {
+ if (!stdcmap[c].red_max ||
+ !stdcmap[c].green_max ||
+ !stdcmap[c].blue_max ||
+ !stdcmap[c].red_mult ||
+ !stdcmap[c].green_mult ||
+ !stdcmap[c].blue_mult)
+ continue; // invalid stdcmap
+
+ XVisualInfo proto;
+ proto.visualid = stdcmap[c].visualid;
+ proto.screen = i;
+
+ int nvisuals = 0;
+ XVisualInfo *vi = XGetVisualInfo(display, VisualIDMask | VisualScreenMask,
+ &proto, &nvisuals);
+ if (vi) {
+ if (nvisuals > 0) {
+ use_stdcmap = true;
+
+ d->mode = ((vi[0].visual->c_class < StaticColor)
+ ? Gray
+ : ((vi[0].visual->c_class < TrueColor)
+ ? Indexed
+ : Direct));
+
+ d->depth = vi[0].depth;
+ d->colormap = stdcmap[c].colormap;
+ d->defaultColormap = true;
+ d->visual = vi[0].visual;
+ d->defaultVisual = (d->visual == DefaultVisual(display, i));
+
+ d->r_max = stdcmap[c].red_max + 1;
+ d->g_max = stdcmap[c].green_max + 1;
+ d->b_max = stdcmap[c].blue_max + 1;
+
+ if (d->mode == Direct) {
+ // calculate offsets
+ d->r_shift = lowest_bit(d->visual->red_mask);
+ d->g_shift = lowest_bit(d->visual->green_mask);
+ d->b_shift = lowest_bit(d->visual->blue_mask);
+ } else {
+ d->r_shift = 0;
+ d->g_shift = 0;
+ d->b_shift = 0;
+ }
+ }
+ XFree(vi);
+ }
+ break;
+ }
+ XFree(stdcmap);
+ }
+ }
+ }
+ if (!use_stdcmap) {
+ switch (d->visual->c_class) {
+ case StaticGray:
+ d->mode = Gray;
+
+ d->r_max = d->g_max = d->b_max = d->visual->map_entries;
+ break;
+
+ case XGrayScale:
+ d->mode = Gray;
+
+ // follow precedent set in libXmu...
+ if (color_count != 0)
+ d->r_max = d->g_max = d->b_max = color_count;
+ else if (d->visual->map_entries > 65000)
+ d->r_max = d->g_max = d->b_max = 4096;
+ else if (d->visual->map_entries > 4000)
+ d->r_max = d->g_max = d->b_max = 512;
+ else if (d->visual->map_entries > 250)
+ d->r_max = d->g_max = d->b_max = 12;
+ else
+ d->r_max = d->g_max = d->b_max = 4;
+ break;
+
+ case StaticColor:
+ d->mode = Indexed;
+
+ d->r_max = right_align(d->visual->red_mask) + 1;
+ d->g_max = right_align(d->visual->green_mask) + 1;
+ d->b_max = right_align(d->visual->blue_mask) + 1;
+ break;
+
+ case PseudoColor:
+ d->mode = Indexed;
+
+ // follow precedent set in libXmu...
+ if (color_count != 0)
+ d->r_max = d->g_max = d->b_max = cube_root(color_count);
+ else if (d->visual->map_entries > 65000)
+ d->r_max = d->g_max = d->b_max = 27;
+ else if (d->visual->map_entries > 4000)
+ d->r_max = d->g_max = d->b_max = 12;
+ else if (d->visual->map_entries > 250)
+ d->r_max = d->g_max = d->b_max = cube_root(d->visual->map_entries - 125);
+ else
+ d->r_max = d->g_max = d->b_max = cube_root(d->visual->map_entries);
+ break;
+
+ case TrueColor:
+ case DirectColor:
+ d->mode = Direct;
+
+ d->r_max = right_align(d->visual->red_mask) + 1;
+ d->g_max = right_align(d->visual->green_mask) + 1;
+ d->b_max = right_align(d->visual->blue_mask) + 1;
+
+ d->r_shift = lowest_bit(d->visual->red_mask);
+ d->g_shift = lowest_bit(d->visual->green_mask);
+ d->b_shift = lowest_bit(d->visual->blue_mask);
+ break;
+ }
+ }
+
+ bool ownColormap = false;
+ if (X11->colormap && i == DefaultScreen(display)) {
+ // only use the outside colormap on the default screen
+ d->colormap = X11->colormap;
+ d->defaultColormap = (d->colormap == DefaultColormap(display, i));
+ } else if ((!use_stdcmap
+ && (((d->visual->c_class & 1) && X11->custom_cmap)
+ || d->visual != DefaultVisual(display, i)))
+ || d->visual->c_class == DirectColor) {
+ // allocate custom colormap (we always do this when using DirectColor visuals)
+ d->colormap =
+ XCreateColormap(display, RootWindow(display, i), d->visual,
+ d->visual->c_class == DirectColor ? AllocAll : AllocNone);
+ d->defaultColormap = false;
+ ownColormap = true;
+ }
+
+ switch (d->mode) {
+ case Gray:
+ init_gray(d, i);
+ break;
+ case Indexed:
+ init_indexed(d, i);
+ break;
+ case Direct:
+ init_direct(d, ownColormap);
+ break;
+ }
+
+ QX11InfoData *screen = X11->screens + i;
+ screen->depth = d->depth;
+ screen->visual = d->visual;
+ screen->defaultVisual = d->defaultVisual;
+ screen->colormap = d->colormap;
+ screen->defaultColormap = d->defaultColormap;
+ screen->cells = screen->visual->map_entries;
+
+ if (argbVisual) {
+ X11->argbVisuals[i] = argbVisual;
+ X11->argbColormaps[i] = XCreateColormap(display, RootWindow(display, i), argbVisual, AllocNone);
+ }
+
+ // ###
+ // We assume that 8bpp == pseudocolor, but this is not
+ // always the case (according to the X server), so we need
+ // to make sure that our internal data is setup in a way
+ // that is compatible with our assumptions
+ if (screen->visual->c_class == TrueColor && screen->depth == 8 && screen->cells == 8)
+ screen->cells = 256;
+ }
+}
+
+void QXcbColormap::cleanup()
+{
+ Display *display = X11->display;
+ const int screens = ScreenCount(display);
+
+ for (int i = 0; i < screens; ++i)
+ delete cmaps[i];
+
+ delete [] cmaps;
+ cmaps = 0;
+}
+
+
+QXcbColormap QXcbColormap::instance(int screen)
+{
+ if (screen == -1)
+ screen = QXcbX11Info::appScreen();
+ return *cmaps[screen];
+}
+
+/*! \internal
+ Constructs a new colormap.
+*/
+QXcbColormap::QXcbColormap()
+ : d(new QXcbColormapPrivate)
+{}
+
+QXcbColormap::QXcbColormap(const QXcbColormap &colormap)
+ :d (colormap.d)
+{ d->ref.ref(); }
+
+QXcbColormap::~QXcbColormap()
+{
+ if (!d->ref.deref()) {
+ if (!d->defaultColormap)
+ XFreeColormap(X11->display, d->colormap);
+ delete d;
+ }
+}
+
+QXcbColormap::Mode QXcbColormap::mode() const
+{ return d->mode; }
+
+int QXcbColormap::depth() const
+{ return d->depth; }
+
+int QXcbColormap::size() const
+{
+ return (d->mode == Gray
+ ? d->r_max
+ : (d->mode == Indexed
+ ? d->r_max * d->g_max * d->b_max
+ : -1));
+}
+
+uint QXcbColormap::pixel(const QColor &color) const
+{
+ const QRgba64 rgba64 = color.rgba64();
+ // XXX We emulate the raster engine here by getting the
+ // 8-bit values, but we could instead use the 16-bit
+ // values for slightly better color accuracy
+ const uint r = (rgba64.red8() * d->r_max) >> 8;
+ const uint g = (rgba64.green8() * d->g_max) >> 8;
+ const uint b = (rgba64.blue8() * d->b_max) >> 8;
+ if (d->mode != Direct) {
+ if (d->mode == Gray)
+ return d->pixels.at((r * 30 + g * 59 + b * 11) / 100);
+ return d->pixels.at(r * d->g_max * d->b_max + g * d->b_max + b);
+ }
+ return (r << d->r_shift) + (g << d->g_shift) + (b << d->b_shift);
+}
+
+const QColor QXcbColormap::colorAt(uint pixel) const
+{
+ if (d->mode != Direct) {
+ Q_ASSERT(pixel <= (uint)d->colors.size());
+ return d->colors.at(pixel);
+ }
+
+ const int r = (((pixel & d->visual->red_mask) >> d->r_shift) << 8) / d->r_max;
+ const int g = (((pixel & d->visual->green_mask) >> d->g_shift) << 8) / d->g_max;
+ const int b = (((pixel & d->visual->blue_mask) >> d->b_shift) << 8) / d->b_max;
+ return QColor(r, g, b);
+}
+
+const QVector<QColor> QXcbColormap::colormap() const
+{ return d->colors; }
+
+QXcbColormap &QXcbColormap::operator=(const QXcbColormap &colormap)
+{
+ qAtomicAssign(d, colormap.d);
+ return *this;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/nativepainting/qcolormap_x11_p.h b/src/plugins/platforms/xcb/nativepainting/qcolormap_x11_p.h
new file mode 100644
index 0000000000..573a0f28ea
--- /dev/null
+++ b/src/plugins/platforms/xcb/nativepainting/qcolormap_x11_p.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOLORMAP_X11_H
+#define QCOLORMAP_X11_H
+
+#include <QColor>
+#include <QVector>
+
+QT_BEGIN_NAMESPACE
+
+class QXcbColormapPrivate;
+class QXcbColormap
+{
+public:
+ enum Mode { Direct, Indexed, Gray };
+
+ static void initialize();
+ static void cleanup();
+
+ static QXcbColormap instance(int screen = -1);
+
+ QXcbColormap(const QXcbColormap &colormap);
+ ~QXcbColormap();
+
+ QXcbColormap &operator=(const QXcbColormap &colormap);
+
+ Mode mode() const;
+
+ int depth() const;
+ int size() const;
+
+ uint pixel(const QColor &color) const;
+ const QColor colorAt(uint pixel) const;
+
+ const QVector<QColor> colormap() const;
+
+private:
+ QXcbColormap();
+ QXcbColormapPrivate *d;
+};
+
+QT_END_NAMESPACE
+
+#endif // QCOLORMAP_X11_H
diff --git a/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11.cpp b/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11.cpp
new file mode 100644
index 0000000000..8b63e5431d
--- /dev/null
+++ b/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11.cpp
@@ -0,0 +1,2843 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qpixmapcache_p.h>
+#include <private/qpaintengine_p.h>
+#include <private/qpolygonclipper_p.h>
+#include <private/qpainterpath_p.h>
+#include <private/qdrawhelper_p.h>
+#include <private/qfontengineglyphcache_p.h>
+
+#if QT_CONFIG(fontconfig)
+#include <private/qfontengine_ft_p.h>
+#endif
+
+#include "qpaintengine_x11_p.h"
+#include "qtessellator_p.h"
+#include "qpixmap_x11_p.h"
+#include "qcolormap_x11_p.h"
+#include "qt_x11_p.h"
+#include "qxcbexport.h"
+#include "qxcbnativepainting.h"
+
+QT_BEGIN_NAMESPACE
+
+#if QT_CONFIG(xrender)
+
+class QXRenderTessellator : public QTessellator
+{
+public:
+ QXRenderTessellator() : traps(0), allocated(0), size(0) {}
+ ~QXRenderTessellator() { free(traps); }
+ XTrapezoid *traps;
+ int allocated;
+ int size;
+ void addTrap(const Trapezoid &trap);
+ QRect tessellate(const QPointF *points, int nPoints, bool winding) {
+ size = 0;
+ setWinding(winding);
+ return QTessellator::tessellate(points, nPoints).toRect();
+ }
+ void done() {
+ if (allocated > 64) {
+ free(traps);
+ traps = 0;
+ allocated = 0;
+ }
+ }
+};
+
+void QXRenderTessellator::addTrap(const Trapezoid &trap)
+{
+ if (size == allocated) {
+ allocated = qMax(2*allocated, 64);
+ traps = q_check_ptr((XTrapezoid *)realloc(traps, allocated * sizeof(XTrapezoid)));
+ }
+ traps[size].top = Q27Dot5ToXFixed(trap.top);
+ traps[size].bottom = Q27Dot5ToXFixed(trap.bottom);
+ traps[size].left.p1.x = Q27Dot5ToXFixed(trap.topLeft->x);
+ traps[size].left.p1.y = Q27Dot5ToXFixed(trap.topLeft->y);
+ traps[size].left.p2.x = Q27Dot5ToXFixed(trap.bottomLeft->x);
+ traps[size].left.p2.y = Q27Dot5ToXFixed(trap.bottomLeft->y);
+ traps[size].right.p1.x = Q27Dot5ToXFixed(trap.topRight->x);
+ traps[size].right.p1.y = Q27Dot5ToXFixed(trap.topRight->y);
+ traps[size].right.p2.x = Q27Dot5ToXFixed(trap.bottomRight->x);
+ traps[size].right.p2.y = Q27Dot5ToXFixed(trap.bottomRight->y);
+ ++size;
+}
+
+#endif // QT_CONFIG(xrender)
+
+class QX11PaintEnginePrivate : public QPaintEnginePrivate
+{
+ Q_DECLARE_PUBLIC(QX11PaintEngine)
+public:
+ QX11PaintEnginePrivate()
+ {
+ opacity = 1.0;
+ scrn = -1;
+ hd = 0;
+ picture = 0;
+ gc = gc_brush = 0;
+ dpy = 0;
+ xinfo = 0;
+ txop = QTransform::TxNone;
+ has_clipping = false;
+ render_hints = 0;
+ xform_scale = 1;
+#if QT_CONFIG(xrender)
+ tessellator = 0;
+#endif
+ }
+ enum GCMode {
+ PenGC,
+ BrushGC
+ };
+
+ void init();
+ void fillPolygon_translated(const QPointF *points, int pointCount, GCMode gcMode,
+ QPaintEngine::PolygonDrawMode mode);
+ void fillPolygon_dev(const QPointF *points, int pointCount, GCMode gcMode,
+ QPaintEngine::PolygonDrawMode mode);
+ void fillPath(const QPainterPath &path, GCMode gcmode, bool transform);
+ void strokePolygon_dev(const QPointF *points, int pointCount, bool close);
+ void strokePolygon_translated(const QPointF *points, int pointCount, bool close);
+ void setupAdaptedOrigin(const QPoint &p);
+ void resetAdaptedOrigin();
+ void decidePathFallback() {
+ use_path_fallback = has_alpha_brush
+ || has_alpha_pen
+ || has_custom_pen
+ || has_complex_xform
+ || (render_hints & QPainter::Antialiasing);
+ }
+ void decideCoordAdjust() {
+ adjust_coords = !(render_hints & QPainter::Antialiasing)
+ && (render_hints & QPainter::Qt4CompatiblePainting)
+ && (has_alpha_pen
+ || (has_alpha_brush && has_pen && !has_alpha_pen)
+ || (cpen.style() > Qt::SolidLine));
+ }
+ void clipPolygon_dev(const QPolygonF &poly, QPolygonF *clipped_poly);
+ void systemStateChanged() override;
+ inline bool isCosmeticPen() const {
+ if ((render_hints & QPainter::Qt4CompatiblePainting) && cpen == QPen())
+ return true;
+ return cpen.isCosmetic();
+ }
+
+ Display *dpy;
+ int scrn;
+ int pdev_depth;
+ unsigned long hd;
+ QPixmap brush_pm;
+#if QT_CONFIG(xrender)
+ unsigned long picture;
+ unsigned long current_brush;
+ QPixmap bitmap_texture;
+ int composition_mode;
+#else
+ unsigned long picture;
+#endif
+ GC gc;
+ GC gc_brush;
+
+ QPen cpen;
+ QBrush cbrush;
+ QRegion crgn;
+ QTransform matrix;
+ qreal opacity;
+
+ uint has_complex_xform : 1;
+ uint has_scaling_xform : 1;
+ uint has_non_scaling_xform : 1;
+ uint has_custom_pen : 1;
+ uint use_path_fallback : 1;
+ uint adjust_coords : 1;
+ uint has_clipping : 1;
+ uint adapted_brush_origin : 1;
+ uint adapted_pen_origin : 1;
+ uint has_pen : 1;
+ uint has_brush : 1;
+ uint has_texture : 1;
+ uint has_alpha_texture : 1;
+ uint has_pattern : 1;
+ uint has_alpha_pen : 1;
+ uint has_alpha_brush : 1;
+ uint render_hints;
+
+ const QXcbX11Info *xinfo;
+ QPointF bg_origin;
+ QTransform::TransformationType txop;
+ qreal xform_scale;
+
+ struct qt_float_point
+ {
+ qreal x, y;
+ };
+ QPolygonClipper<qt_float_point, qt_float_point, float> polygonClipper;
+
+ int xlibMaxLinePoints;
+#if QT_CONFIG(xrender)
+ QXRenderTessellator *tessellator;
+#endif
+};
+
+#if QT_CONFIG(xrender)
+class QXRenderGlyphCache : public QFontEngineGlyphCache
+{
+public:
+ QXRenderGlyphCache(QXcbX11Info x, QFontEngine::GlyphFormat format, const QTransform &matrix);
+ ~QXRenderGlyphCache();
+
+ bool addGlyphs(const QTextItemInt &ti, QVarLengthArray<glyph_t> glyphs, QVarLengthArray<QFixedPoint> positions);
+ bool draw(Drawable src, Drawable dst, const QTransform &matrix, const QTextItemInt &ti);
+
+ inline GlyphSet glyphSet();
+ inline int glyphBufferSize(const QFontEngineFT::Glyph &glyph) const;
+
+ inline QImage::Format imageFormat() const;
+ inline const XRenderPictFormat *renderPictFormat() const;
+
+ static inline QFontEngine::GlyphFormat glyphFormatForDepth(QFontEngine *fontEngine, int depth);
+ static inline Glyph glyphId(glyph_t glyph, QFixed subPixelPosition);
+
+ static inline bool isValidCoordinate(const QFixedPoint &fp);
+
+private:
+ QXcbX11Info xinfo;
+ GlyphSet gset;
+ QSet<Glyph> cachedGlyphs;
+};
+#endif // QT_CONFIG(xrender)
+
+extern QPixmap qt_pixmapForBrush(int brushStyle, bool invert); //in qbrush.cpp
+extern QPixmap qt_toX11Pixmap(const QPixmap &pixmap);
+
+extern "C" {
+Q_XCB_EXPORT Drawable qt_x11Handle(const QPaintDevice *pd)
+{
+ if (!pd)
+ return 0;
+
+// if (pd->devType() == QInternal::Widget)
+// return static_cast<const QWidget *>(pd)->handle();
+
+ if (pd->devType() == QInternal::Pixmap)
+ return qt_x11PixmapHandle(*static_cast<const QPixmap *>(pd));
+
+ return 0;
+}
+}
+
+static const QXcbX11Info *qt_x11Info(const QPaintDevice *pd)
+{
+ if (!pd)
+ return 0;
+
+// if (pd->devType() == QInternal::Widget)
+// return &static_cast<const QWidget *>(pd)->x11Info();
+
+ if (pd->devType() == QInternal::Pixmap)
+ return &qt_x11Info(*static_cast<const QPixmap *>(pd));
+
+ return 0;
+}
+
+// use the same rounding as in qrasterizer.cpp (6 bit fixed point)
+static const qreal aliasedCoordinateDelta = 0.5 - 0.015625;
+
+#undef X11 // defined in qt_x11_p.h
+extern "C" {
+/*!
+ Returns the X11 specific pen GC for the painter \a p. Note that
+ QPainter::begin() must be called before this function returns a
+ valid GC.
+*/
+GC Q_XCB_EXPORT qt_x11_get_pen_gc(QPainter *p)
+{
+ if (p && p->paintEngine()
+ && p->paintEngine()->isActive()
+ && p->paintEngine()->type() == QPaintEngine::X11) {
+ return static_cast<QX11PaintEngine *>(p->paintEngine())->d_func()->gc;
+ }
+ return 0;
+}
+
+/*!
+ Returns the X11 specific brush GC for the painter \a p. Note that
+ QPainter::begin() must be called before this function returns a
+ valid GC.
+*/
+GC Q_XCB_EXPORT qt_x11_get_brush_gc(QPainter *p)
+{
+ if (p && p->paintEngine()
+ && p->paintEngine()->isActive()
+ && p->paintEngine()->type() == QPaintEngine::X11) {
+ return static_cast<QX11PaintEngine *>(p->paintEngine())->d_func()->gc_brush;
+ }
+ return 0;
+}
+}
+#define X11 qt_x11Data
+
+// internal helper. Converts an integer value to an unique string token
+template <typename T>
+ struct HexString
+{
+ inline HexString(const T t)
+ : val(t)
+ {}
+
+ inline void write(QChar *&dest) const
+ {
+ const ushort hexChars[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+ const char *c = reinterpret_cast<const char *>(&val);
+ for (uint i = 0; i < sizeof(T); ++i) {
+ *dest++ = hexChars[*c & 0xf];
+ *dest++ = hexChars[(*c & 0xf0) >> 4];
+ ++c;
+ }
+ }
+ const T val;
+};
+
+// specialization to enable fast concatenating of our string tokens to a string
+template <typename T>
+ struct QConcatenable<HexString<T> >
+{
+ typedef HexString<T> type;
+ enum { ExactSize = true };
+ static int size(const HexString<T> &) { return sizeof(T) * 2; }
+ static inline void appendTo(const HexString<T> &str, QChar *&out) { str.write(out); }
+ typedef QString ConvertTo;
+};
+
+#if QT_CONFIG(xrender)
+static const int compositionModeToRenderOp[QPainter::CompositionMode_Xor + 1] = {
+ PictOpOver, //CompositionMode_SourceOver,
+ PictOpOverReverse, //CompositionMode_DestinationOver,
+ PictOpClear, //CompositionMode_Clear,
+ PictOpSrc, //CompositionMode_Source,
+ PictOpDst, //CompositionMode_Destination,
+ PictOpIn, //CompositionMode_SourceIn,
+ PictOpInReverse, //CompositionMode_DestinationIn,
+ PictOpOut, //CompositionMode_SourceOut,
+ PictOpOutReverse, //CompositionMode_DestinationOut,
+ PictOpAtop, //CompositionMode_SourceAtop,
+ PictOpAtopReverse, //CompositionMode_DestinationAtop,
+ PictOpXor //CompositionMode_Xor
+};
+
+static inline int qpainterOpToXrender(QPainter::CompositionMode mode)
+{
+ Q_ASSERT(mode <= QPainter::CompositionMode_Xor);
+ return compositionModeToRenderOp[mode];
+}
+
+static inline bool complexPictOp(int op)
+{
+ return op != PictOpOver && op != PictOpSrc;
+}
+#endif
+
+static inline void x11SetClipRegion(Display *dpy, GC gc, GC gc2,
+#if QT_CONFIG(xrender)
+ Picture picture,
+#else
+ Qt::HANDLE picture,
+#endif
+ const QRegion &r)
+{
+// int num;
+// XRectangle *rects = (XRectangle *)qt_getClipRects(r, num);
+ QVector<XRectangle> rects = qt_region_to_xrectangles(r);
+ int num = rects.size();
+
+ if (gc)
+ XSetClipRectangles( dpy, gc, 0, 0, rects.data(), num, Unsorted );
+ if (gc2)
+ XSetClipRectangles( dpy, gc2, 0, 0, rects.data(), num, Unsorted );
+
+#if QT_CONFIG(xrender)
+ if (picture)
+ XRenderSetPictureClipRectangles(dpy, picture, 0, 0, rects.data(), num);
+#else
+ Q_UNUSED(picture);
+#endif // QT_CONFIG(xrender)
+}
+
+
+static inline void x11ClearClipRegion(Display *dpy, GC gc, GC gc2,
+#if QT_CONFIG(xrender)
+ Picture picture
+#else
+ Qt::HANDLE picture
+#endif
+ )
+{
+ if (gc)
+ XSetClipMask(dpy, gc, XNone);
+ if (gc2)
+ XSetClipMask(dpy, gc2, XNone);
+
+#if QT_CONFIG(xrender)
+ if (picture) {
+ XRenderPictureAttributes attrs;
+ attrs.clip_mask = XNone;
+ XRenderChangePicture (dpy, picture, CPClipMask, &attrs);
+ }
+#else
+ Q_UNUSED(picture);
+#endif // QT_CONFIG(xrender)
+}
+
+
+#define DITHER_SIZE 16
+static const uchar base_dither_matrix[DITHER_SIZE][DITHER_SIZE] = {
+ { 0,192, 48,240, 12,204, 60,252, 3,195, 51,243, 15,207, 63,255 },
+ { 128, 64,176,112,140, 76,188,124,131, 67,179,115,143, 79,191,127 },
+ { 32,224, 16,208, 44,236, 28,220, 35,227, 19,211, 47,239, 31,223 },
+ { 160, 96,144, 80,172,108,156, 92,163, 99,147, 83,175,111,159, 95 },
+ { 8,200, 56,248, 4,196, 52,244, 11,203, 59,251, 7,199, 55,247 },
+ { 136, 72,184,120,132, 68,180,116,139, 75,187,123,135, 71,183,119 },
+ { 40,232, 24,216, 36,228, 20,212, 43,235, 27,219, 39,231, 23,215 },
+ { 168,104,152, 88,164,100,148, 84,171,107,155, 91,167,103,151, 87 },
+ { 2,194, 50,242, 14,206, 62,254, 1,193, 49,241, 13,205, 61,253 },
+ { 130, 66,178,114,142, 78,190,126,129, 65,177,113,141, 77,189,125 },
+ { 34,226, 18,210, 46,238, 30,222, 33,225, 17,209, 45,237, 29,221 },
+ { 162, 98,146, 82,174,110,158, 94,161, 97,145, 81,173,109,157, 93 },
+ { 10,202, 58,250, 6,198, 54,246, 9,201, 57,249, 5,197, 53,245 },
+ { 138, 74,186,122,134, 70,182,118,137, 73,185,121,133, 69,181,117 },
+ { 42,234, 26,218, 38,230, 22,214, 41,233, 25,217, 37,229, 21,213 },
+ { 170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85 }
+};
+
+static QPixmap qt_patternForAlpha(uchar alpha, int screen)
+{
+ QPixmap pm;
+ QString key = QLatin1Literal("$qt-alpha-brush$")
+ % HexString<uchar>(alpha)
+ % HexString<int>(screen);
+
+ if (!QPixmapCache::find(key, pm)) {
+ // #### why not use a mono image here????
+ QImage pattern(DITHER_SIZE, DITHER_SIZE, QImage::Format_ARGB32);
+ pattern.fill(0xffffffff);
+ for (int y = 0; y < DITHER_SIZE; ++y) {
+ for (int x = 0; x < DITHER_SIZE; ++x) {
+ if (base_dither_matrix[x][y] <= alpha)
+ pattern.setPixel(x, y, 0x00000000);
+ }
+ }
+ pm = QBitmap::fromImage(pattern);
+ qt_x11SetScreen(pm, screen);
+ //pm.x11SetScreen(screen);
+ QPixmapCache::insert(key, pm);
+ }
+ return pm;
+}
+
+
+#if QT_CONFIG(xrender)
+static Picture getPatternFill(int screen, const QBrush &b)
+{
+ if (!X11->use_xrender)
+ return XNone;
+
+ XRenderColor color = X11->preMultiply(b.color());
+ XRenderColor bg_color;
+
+ bg_color = X11->preMultiply(QColor(0, 0, 0, 0));
+
+ for (int i = 0; i < X11->pattern_fill_count; ++i) {
+ if (X11->pattern_fills[i].screen == screen
+ && X11->pattern_fills[i].opaque == false
+ && X11->pattern_fills[i].style == b.style()
+ && X11->pattern_fills[i].color.alpha == color.alpha
+ && X11->pattern_fills[i].color.red == color.red
+ && X11->pattern_fills[i].color.green == color.green
+ && X11->pattern_fills[i].color.blue == color.blue
+ && X11->pattern_fills[i].bg_color.alpha == bg_color.alpha
+ && X11->pattern_fills[i].bg_color.red == bg_color.red
+ && X11->pattern_fills[i].bg_color.green == bg_color.green
+ && X11->pattern_fills[i].bg_color.blue == bg_color.blue)
+ return X11->pattern_fills[i].picture;
+ }
+ // none found, replace one
+ int i = qrand() % 16;
+
+ if (X11->pattern_fills[i].screen != screen && X11->pattern_fills[i].picture) {
+ XRenderFreePicture (QXcbX11Info::display(), X11->pattern_fills[i].picture);
+ X11->pattern_fills[i].picture = 0;
+ }
+
+ if (!X11->pattern_fills[i].picture) {
+ Pixmap pixmap = XCreatePixmap (QXcbX11Info::display(), RootWindow (QXcbX11Info::display(), screen), 8, 8, 32);
+ XRenderPictureAttributes attrs;
+ attrs.repeat = True;
+ X11->pattern_fills[i].picture = XRenderCreatePicture (QXcbX11Info::display(), pixmap,
+ XRenderFindStandardFormat(QXcbX11Info::display(), PictStandardARGB32),
+ CPRepeat, &attrs);
+ XFreePixmap (QXcbX11Info::display(), pixmap);
+ }
+
+ X11->pattern_fills[i].screen = screen;
+ X11->pattern_fills[i].color = color;
+ X11->pattern_fills[i].bg_color = bg_color;
+ X11->pattern_fills[i].opaque = false;
+ X11->pattern_fills[i].style = b.style();
+
+ XRenderFillRectangle(QXcbX11Info::display(), PictOpSrc, X11->pattern_fills[i].picture, &bg_color, 0, 0, 8, 8);
+
+ QPixmap pattern(qt_pixmapForBrush(b.style(), true));
+ XRenderPictureAttributes attrs;
+ attrs.repeat = true;
+ XRenderChangePicture(QXcbX11Info::display(), qt_x11PictureHandle(pattern), CPRepeat, &attrs);
+
+ Picture fill_fg = X11->getSolidFill(screen, b.color());
+ XRenderComposite(QXcbX11Info::display(), PictOpOver, fill_fg, qt_x11PictureHandle(pattern),
+ X11->pattern_fills[i].picture,
+ 0, 0, 0, 0, 0, 0, 8, 8);
+
+ return X11->pattern_fills[i].picture;
+}
+
+static void qt_render_bitmap(Display *dpy, int scrn, Picture src, Picture dst,
+ int sx, int sy, int x, int y, int sw, int sh,
+ const QPen &pen)
+{
+ Picture fill_fg = X11->getSolidFill(scrn, pen.color());
+ XRenderComposite(dpy, PictOpOver,
+ fill_fg, src, dst, sx, sy, sx, sy, x, y, sw, sh);
+}
+#endif
+
+void QX11PaintEnginePrivate::init()
+{
+ dpy = 0;
+ scrn = 0;
+ hd = 0;
+ picture = 0;
+ xinfo = 0;
+#if QT_CONFIG(xrender)
+ current_brush = 0;
+ composition_mode = PictOpOver;
+ tessellator = new QXRenderTessellator;
+#endif
+}
+
+void QX11PaintEnginePrivate::setupAdaptedOrigin(const QPoint &p)
+{
+ if (adapted_pen_origin)
+ XSetTSOrigin(dpy, gc, p.x(), p.y());
+ if (adapted_brush_origin)
+ XSetTSOrigin(dpy, gc_brush, p.x(), p.y());
+}
+
+void QX11PaintEnginePrivate::resetAdaptedOrigin()
+{
+ if (adapted_pen_origin)
+ XSetTSOrigin(dpy, gc, 0, 0);
+ if (adapted_brush_origin)
+ XSetTSOrigin(dpy, gc_brush, 0, 0);
+}
+
+void QX11PaintEnginePrivate::clipPolygon_dev(const QPolygonF &poly, QPolygonF *clipped_poly)
+{
+ int clipped_count = 0;
+ qt_float_point *clipped_points = 0;
+ polygonClipper.clipPolygon((qt_float_point *) poly.data(), poly.size(),
+ &clipped_points, &clipped_count);
+ clipped_poly->resize(clipped_count);
+ for (int i=0; i<clipped_count; ++i)
+ (*clipped_poly)[i] = *((QPointF *)(&clipped_points[i]));
+}
+
+void QX11PaintEnginePrivate::systemStateChanged()
+{
+ Q_Q(QX11PaintEngine);
+ QPainter *painter = q->state ? q->state->painter() : nullptr;
+ if (painter && painter->hasClipping()) {
+ if (q->testDirty(QPaintEngine::DirtyTransform))
+ q->updateMatrix(q->state->transform());
+ QPolygonF clip_poly_dev(matrix.map(painter->clipPath().toFillPolygon()));
+ QPolygonF clipped_poly_dev;
+ clipPolygon_dev(clip_poly_dev, &clipped_poly_dev);
+ q->updateClipRegion_dev(QRegion(clipped_poly_dev.toPolygon()), Qt::ReplaceClip);
+ } else {
+ q->updateClipRegion_dev(QRegion(), Qt::NoClip);
+ }
+}
+
+static QPaintEngine::PaintEngineFeatures qt_decide_features()
+{
+ QPaintEngine::PaintEngineFeatures features =
+ QPaintEngine::PrimitiveTransform
+ | QPaintEngine::PatternBrush
+ | QPaintEngine::AlphaBlend
+ | QPaintEngine::PainterPaths
+ | QPaintEngine::RasterOpModes;
+
+ if (X11->use_xrender) {
+ features |= QPaintEngine::Antialiasing;
+ features |= QPaintEngine::PorterDuff;
+ features |= QPaintEngine::MaskedBrush;
+#if 0
+ if (X11->xrender_version > 10) {
+ features |= QPaintEngine::LinearGradientFill;
+ // ###
+ }
+#endif
+ }
+
+ return features;
+}
+
+/*
+ * QX11PaintEngine members
+ */
+
+QX11PaintEngine::QX11PaintEngine()
+ : QPaintEngine(*(new QX11PaintEnginePrivate), qt_decide_features())
+{
+ Q_D(QX11PaintEngine);
+ d->init();
+}
+
+QX11PaintEngine::QX11PaintEngine(QX11PaintEnginePrivate &dptr)
+ : QPaintEngine(dptr, qt_decide_features())
+{
+ Q_D(QX11PaintEngine);
+ d->init();
+}
+
+QX11PaintEngine::~QX11PaintEngine()
+{
+#if QT_CONFIG(xrender)
+ Q_D(QX11PaintEngine);
+ delete d->tessellator;
+#endif
+}
+
+bool QX11PaintEngine::begin(QPaintDevice *pdev)
+{
+ Q_D(QX11PaintEngine);
+ d->xinfo = qt_x11Info(pdev);
+#if QT_CONFIG(xrender)
+ if (pdev->devType() == QInternal::Pixmap) {
+ const QPixmap *pm = static_cast<const QPixmap *>(pdev);
+ QX11PlatformPixmap *data = static_cast<QX11PlatformPixmap*>(pm->handle());
+ if (X11->use_xrender && data->depth() != 32 && data->x11_mask)
+ data->convertToARGB32();
+ d->picture = qt_x11PictureHandle(*static_cast<const QPixmap *>(pdev));
+ }
+#else
+ d->picture = 0;
+#endif
+ d->hd = qt_x11Handle(pdev);
+
+ Q_ASSERT(d->xinfo != 0);
+ d->dpy = d->xinfo->display(); // get display variable
+ d->scrn = d->xinfo->screen(); // get screen variable
+
+ d->crgn = QRegion();
+ d->gc = XCreateGC(d->dpy, d->hd, 0, 0);
+ d->gc_brush = XCreateGC(d->dpy, d->hd, 0, 0);
+ d->has_alpha_brush = false;
+ d->has_alpha_pen = false;
+ d->has_clipping = false;
+ d->has_complex_xform = false;
+ d->has_scaling_xform = false;
+ d->has_non_scaling_xform = true;
+ d->xform_scale = 1;
+ d->has_custom_pen = false;
+ d->matrix = QTransform();
+ d->pdev_depth = d->pdev->depth();
+ d->render_hints = 0;
+ d->txop = QTransform::TxNone;
+ d->use_path_fallback = false;
+#if QT_CONFIG(xrender)
+ d->composition_mode = PictOpOver;
+#endif
+ d->xlibMaxLinePoints = 32762; // a safe number used to avoid, call to XMaxRequestSize(d->dpy) - 3;
+ d->opacity = 1;
+
+ // Set up the polygon clipper. Note: This will only work in
+ // polyline mode as long as we have a buffer zone, since a
+ // polyline may be clipped into several non-connected polylines.
+ const int BUFFERZONE = 1000;
+ QRect devClipRect(-BUFFERZONE, -BUFFERZONE,
+ pdev->width() + 2*BUFFERZONE, pdev->height() + 2*BUFFERZONE);
+ d->polygonClipper.setBoundingRect(devClipRect);
+
+ setSystemClip(systemClip());
+ d->systemStateChanged();
+
+ qt_x11SetDefaultScreen(d->xinfo->screen());
+
+ updatePen(QPen(Qt::black));
+ updateBrush(QBrush(Qt::white), QPoint());
+
+ setDirty(QPaintEngine::DirtyClipRegion);
+ setDirty(QPaintEngine::DirtyPen);
+ setDirty(QPaintEngine::DirtyBrush);
+ setDirty(QPaintEngine::DirtyBackground);
+
+ setActive(true);
+ return true;
+}
+
+bool QX11PaintEngine::end()
+{
+ Q_D(QX11PaintEngine);
+
+#if QT_CONFIG(xrender)
+ if (d->picture) {
+ // reset clipping/subwindow mode on our render picture
+ XRenderPictureAttributes attrs;
+ attrs.subwindow_mode = ClipByChildren;
+ attrs.clip_mask = XNone;
+ XRenderChangePicture(d->dpy, d->picture, CPClipMask|CPSubwindowMode, &attrs);
+ }
+#endif
+
+ if (d->gc_brush && d->pdev->painters < 2) {
+ XFreeGC(d->dpy, d->gc_brush);
+ d->gc_brush = 0;
+ }
+
+ if (d->gc && d->pdev->painters < 2) {
+ XFreeGC(d->dpy, d->gc);
+ d->gc = 0;
+ }
+
+ // Restore system clip for alien widgets painting outside the paint event.
+// if (d->pdev->devType() == QInternal::Widget && !static_cast<QWidget *>(d->pdev)->internalWinId())
+ d->currentClipDevice = nullptr;
+ setSystemClip(QRegion());
+
+ setActive(false);
+ return true;
+}
+
+static bool clipLine(QLineF *line, const QRect &rect)
+{
+ qreal x1 = line->x1();
+ qreal x2 = line->x2();
+ qreal y1 = line->y1();
+ qreal y2 = line->y2();
+
+ qreal left = rect.x();
+ qreal right = rect.x() + rect.width() - 1;
+ qreal top = rect.y();
+ qreal bottom = rect.y() + rect.height() - 1;
+
+ enum { Left, Right, Top, Bottom };
+ // clip the lines, after cohen-sutherland, see e.g. http://www.nondot.org/~sabre/graphpro/line6.html
+ int p1 = ((x1 < left) << Left)
+ | ((x1 > right) << Right)
+ | ((y1 < top) << Top)
+ | ((y1 > bottom) << Bottom);
+ int p2 = ((x2 < left) << Left)
+ | ((x2 > right) << Right)
+ | ((y2 < top) << Top)
+ | ((y2 > bottom) << Bottom);
+
+ if (p1 & p2)
+ // completely outside
+ return false;
+
+ if (p1 | p2) {
+ qreal dx = x2 - x1;
+ qreal dy = y2 - y1;
+
+ // clip x coordinates
+ if (x1 < left) {
+ y1 += dy/dx * (left - x1);
+ x1 = left;
+ } else if (x1 > right) {
+ y1 -= dy/dx * (x1 - right);
+ x1 = right;
+ }
+ if (x2 < left) {
+ y2 += dy/dx * (left - x2);
+ x2 = left;
+ } else if (x2 > right) {
+ y2 -= dy/dx * (x2 - right);
+ x2 = right;
+ }
+ p1 = ((y1 < top) << Top)
+ | ((y1 > bottom) << Bottom);
+ p2 = ((y2 < top) << Top)
+ | ((y2 > bottom) << Bottom);
+ if (p1 & p2)
+ return false;
+ // clip y coordinates
+ if (y1 < top) {
+ x1 += dx/dy * (top - y1);
+ y1 = top;
+ } else if (y1 > bottom) {
+ x1 -= dx/dy * (y1 - bottom);
+ y1 = bottom;
+ }
+ if (y2 < top) {
+ x2 += dx/dy * (top - y2);
+ y2 = top;
+ } else if (y2 > bottom) {
+ x2 -= dx/dy * (y2 - bottom);
+ y2 = bottom;
+ }
+ *line = QLineF(QPointF(x1, y1), QPointF(x2, y2));
+ }
+ return true;
+}
+
+void QX11PaintEngine::drawLines(const QLine *lines, int lineCount)
+{
+ Q_ASSERT(lines);
+ Q_ASSERT(lineCount);
+ Q_D(QX11PaintEngine);
+
+ if (d->has_alpha_brush
+ || d->has_alpha_pen
+ || d->has_custom_pen
+ || (d->cpen.widthF() > 0 && d->has_complex_xform
+ && !d->has_non_scaling_xform)
+ || (d->render_hints & QPainter::Antialiasing)) {
+ for (int i = 0; i < lineCount; ++i) {
+ QPainterPath path(lines[i].p1());
+ path.lineTo(lines[i].p2());
+ drawPath(path);
+ }
+ return;
+ }
+
+ if (d->has_pen) {
+ for (int i = 0; i < lineCount; ++i) {
+ QLineF linef;
+ if (d->txop == QTransform::TxNone) {
+ linef = lines[i];
+ } else {
+ linef = d->matrix.map(QLineF(lines[i]));
+ }
+ if (clipLine(&linef, d->polygonClipper.boundingRect())) {
+ int x1 = qRound(linef.x1() + aliasedCoordinateDelta);
+ int y1 = qRound(linef.y1() + aliasedCoordinateDelta);
+ int x2 = qRound(linef.x2() + aliasedCoordinateDelta);
+ int y2 = qRound(linef.y2() + aliasedCoordinateDelta);
+
+ XDrawLine(d->dpy, d->hd, d->gc, x1, y1, x2, y2);
+ }
+ }
+ }
+}
+
+void QX11PaintEngine::drawLines(const QLineF *lines, int lineCount)
+{
+ Q_ASSERT(lines);
+ Q_ASSERT(lineCount);
+ Q_D(QX11PaintEngine);
+
+ if (d->has_alpha_brush
+ || d->has_alpha_pen
+ || d->has_custom_pen
+ || (d->cpen.widthF() > 0 && d->has_complex_xform
+ && !d->has_non_scaling_xform)
+ || (d->render_hints & QPainter::Antialiasing)) {
+ for (int i = 0; i < lineCount; ++i) {
+ QPainterPath path(lines[i].p1());
+ path.lineTo(lines[i].p2());
+ drawPath(path);
+ }
+ return;
+ }
+
+ if (d->has_pen) {
+ for (int i = 0; i < lineCount; ++i) {
+ QLineF linef = d->matrix.map(lines[i]);
+ if (clipLine(&linef, d->polygonClipper.boundingRect())) {
+ int x1 = qRound(linef.x1() + aliasedCoordinateDelta);
+ int y1 = qRound(linef.y1() + aliasedCoordinateDelta);
+ int x2 = qRound(linef.x2() + aliasedCoordinateDelta);
+ int y2 = qRound(linef.y2() + aliasedCoordinateDelta);
+
+ XDrawLine(d->dpy, d->hd, d->gc, x1, y1, x2, y2);
+ }
+ }
+ }
+}
+
+static inline QLine clipStraightLine(const QRect &clip, const QLine &l)
+{
+ if (l.p1().x() == l.p2().x()) {
+ int x = qBound(clip.left(), l.p1().x(), clip.right());
+ int y1 = qBound(clip.top(), l.p1().y(), clip.bottom());
+ int y2 = qBound(clip.top(), l.p2().y(), clip.bottom());
+
+ return QLine(x, y1, x, y2);
+ } else {
+ Q_ASSERT(l.p1().y() == l.p2().y());
+
+ int x1 = qBound(clip.left(), l.p1().x(), clip.right());
+ int x2 = qBound(clip.left(), l.p2().x(), clip.right());
+ int y = qBound(clip.top(), l.p1().y(), clip.bottom());
+
+ return QLine(x1, y, x2, y);
+ }
+}
+
+void QX11PaintEngine::drawRects(const QRectF *rects, int rectCount)
+{
+ Q_D(QX11PaintEngine);
+ Q_ASSERT(rects);
+ Q_ASSERT(rectCount);
+
+ if (rectCount != 1
+ || d->has_pen
+ || d->has_alpha_brush
+ || d->has_complex_xform
+ || d->has_custom_pen
+ || d->cbrush.style() != Qt::SolidPattern
+#if QT_CONFIG(xrender)
+ || complexPictOp(d->composition_mode)
+#endif
+ )
+ {
+ QPaintEngine::drawRects(rects, rectCount);
+ return;
+ }
+
+ QPoint alignedOffset;
+ if (d->txop == QTransform::TxTranslate) {
+ QPointF offset(d->matrix.dx(), d->matrix.dy());
+ alignedOffset = offset.toPoint();
+ if (offset != QPointF(alignedOffset)) {
+ QPaintEngine::drawRects(rects, rectCount);
+ return;
+ }
+ }
+
+ const QRectF& r = rects[0];
+ QRect alignedRect = r.toAlignedRect();
+ if (r != QRectF(alignedRect)) {
+ QPaintEngine::drawRects(rects, rectCount);
+ return;
+ }
+ alignedRect.translate(alignedOffset);
+
+ QRect clip(d->polygonClipper.boundingRect());
+ alignedRect = alignedRect.intersected(clip);
+ if (alignedRect.isEmpty())
+ return;
+
+ // simple-case:
+ // the rectangle is pixel-aligned
+ // the fill brush is just a solid non-alpha color
+ // the painter transform is only integer translation
+ // ignore: antialiasing and just XFillRectangles directly
+ XRectangle xrect;
+ xrect.x = short(alignedRect.x());
+ xrect.y = short(alignedRect.y());
+ xrect.width = ushort(alignedRect.width());
+ xrect.height = ushort(alignedRect.height());
+ XFillRectangles(d->dpy, d->hd, d->gc_brush, &xrect, 1);
+}
+
+void QX11PaintEngine::drawRects(const QRect *rects, int rectCount)
+{
+ Q_D(QX11PaintEngine);
+ Q_ASSERT(rects);
+ Q_ASSERT(rectCount);
+
+ if (d->has_alpha_pen
+ || d->has_complex_xform
+ || d->has_custom_pen
+ || (d->render_hints & QPainter::Antialiasing))
+ {
+ for (int i = 0; i < rectCount; ++i) {
+ QPainterPath path;
+ path.addRect(rects[i]);
+ drawPath(path);
+ }
+ return;
+ }
+
+ QRect clip(d->polygonClipper.boundingRect());
+ QPoint offset(qRound(d->matrix.dx()), qRound(d->matrix.dy()));
+#if QT_CONFIG(xrender)
+ ::Picture pict = d->picture;
+
+ if (X11->use_xrender && pict && d->has_brush && d->pdev_depth != 1
+ && (d->has_texture || d->has_alpha_brush || complexPictOp(d->composition_mode)))
+ {
+ XRenderColor xc;
+ if (!d->has_texture && !d->has_pattern)
+ xc = X11->preMultiply(d->cbrush.color());
+
+ for (int i = 0; i < rectCount; ++i) {
+ QRect r(rects[i]);
+ if (d->txop == QTransform::TxTranslate)
+ r.translate(offset);
+
+ if (r.width() == 0 || r.height() == 0) {
+ if (d->has_pen) {
+ const QLine l = clipStraightLine(clip, QLine(r.left(), r.top(), r.left() + r.width(), r.top() + r.height()));
+ XDrawLine(d->dpy, d->hd, d->gc, l.p1().x(), l.p1().y(), l.p2().x(), l.p2().y());
+ }
+ continue;
+ }
+
+ r = r.intersected(clip);
+ if (r.isEmpty())
+ continue;
+ if (d->has_texture || d->has_pattern) {
+ XRenderComposite(d->dpy, d->composition_mode, d->current_brush, 0, pict,
+ qRound(r.x() - d->bg_origin.x()), qRound(r.y() - d->bg_origin.y()),
+ 0, 0, r.x(), r.y(), r.width(), r.height());
+ } else {
+ XRenderFillRectangle(d->dpy, d->composition_mode, pict, &xc, r.x(), r.y(), r.width(), r.height());
+ }
+ if (d->has_pen)
+ XDrawRectangle(d->dpy, d->hd, d->gc, r.x(), r.y(), r.width(), r.height());
+ }
+ } else
+#endif // QT_CONFIG(xrender)
+ {
+ if (d->has_brush && d->has_pen) {
+ for (int i = 0; i < rectCount; ++i) {
+ QRect r(rects[i]);
+ if (d->txop == QTransform::TxTranslate)
+ r.translate(offset);
+
+ if (r.width() == 0 || r.height() == 0) {
+ const QLine l = clipStraightLine(clip, QLine(r.left(), r.top(), r.left() + r.width(), r.top() + r.height()));
+ XDrawLine(d->dpy, d->hd, d->gc, l.p1().x(), l.p1().y(), l.p2().x(), l.p2().y());
+ continue;
+ }
+
+ r = r.intersected(clip);
+ if (r.isEmpty())
+ continue;
+ d->setupAdaptedOrigin(r.topLeft());
+ XFillRectangle(d->dpy, d->hd, d->gc_brush, r.x(), r.y(), r.width(), r.height());
+ XDrawRectangle(d->dpy, d->hd, d->gc, r.x(), r.y(), r.width(), r.height());
+ }
+ d->resetAdaptedOrigin();
+ } else {
+ QVarLengthArray<XRectangle> xrects(rectCount);
+ int numClipped = rectCount;
+ for (int i = 0; i < rectCount; ++i) {
+ QRect r(rects[i]);
+ if (d->txop == QTransform::TxTranslate)
+ r.translate(offset);
+
+ if (r.width() == 0 || r.height() == 0) {
+ --numClipped;
+ if (d->has_pen) {
+ const QLine l = clipStraightLine(clip, QLine(r.left(), r.top(), r.left() + r.width(), r.top() + r.height()));
+ XDrawLine(d->dpy, d->hd, d->gc, l.p1().x(), l.p1().y(), l.p2().x(), l.p2().y());
+ }
+ continue;
+ }
+
+ r = r.intersected(clip);
+ if (r.isEmpty()) {
+ --numClipped;
+ continue;
+ }
+ xrects[i].x = short(r.x());
+ xrects[i].y = short(r.y());
+ xrects[i].width = ushort(r.width());
+ xrects[i].height = ushort(r.height());
+ }
+ if (numClipped) {
+ d->setupAdaptedOrigin(rects[0].topLeft());
+ if (d->has_brush)
+ XFillRectangles(d->dpy, d->hd, d->gc_brush, xrects.data(), numClipped);
+ else if (d->has_pen)
+ XDrawRectangles(d->dpy, d->hd, d->gc, xrects.data(), numClipped);
+ d->resetAdaptedOrigin();
+ }
+ }
+ }
+}
+
+static inline void setCapStyle(int cap_style, GC gc)
+{
+ ulong mask = GCCapStyle;
+ XGCValues vals;
+ vals.cap_style = cap_style;
+ XChangeGC(QXcbX11Info::display(), gc, mask, &vals);
+}
+
+void QX11PaintEngine::drawPoints(const QPoint *points, int pointCount)
+{
+ Q_ASSERT(points);
+ Q_ASSERT(pointCount);
+ Q_D(QX11PaintEngine);
+
+ if (!d->has_pen)
+ return;
+
+ // use the same test here as in drawPath to ensure that we don't use the path fallback
+ // and end up in XDrawLines for pens with width <= 1
+ if (d->cpen.widthF() > 1.0f
+ || (X11->use_xrender && (d->has_alpha_pen || (d->render_hints & QPainter::Antialiasing)))
+ || (!d->isCosmeticPen() && d->txop > QTransform::TxTranslate))
+ {
+ Qt::PenCapStyle capStyle = d->cpen.capStyle();
+ if (capStyle == Qt::FlatCap) {
+ setCapStyle(CapProjecting, d->gc);
+ d->cpen.setCapStyle(Qt::SquareCap);
+ }
+ const QPoint *end = points + pointCount;
+ while (points < end) {
+ QPainterPath path;
+ path.moveTo(*points);
+ path.lineTo(points->x()+.005, points->y());
+ drawPath(path);
+ ++points;
+ }
+
+ if (capStyle == Qt::FlatCap) {
+ setCapStyle(CapButt, d->gc);
+ d->cpen.setCapStyle(capStyle);
+ }
+ return;
+ }
+
+ static const int BUF_SIZE = 1024;
+ XPoint xPoints[BUF_SIZE];
+ int i = 0, j = 0;
+ while (i < pointCount) {
+ while (i < pointCount && j < BUF_SIZE) {
+ const QPoint &xformed = d->matrix.map(points[i]);
+ int x = xformed.x();
+ int y = xformed.y();
+ if (x >= SHRT_MIN && y >= SHRT_MIN && x < SHRT_MAX && y < SHRT_MAX) {
+ xPoints[j].x = x;
+ xPoints[j].y = y;
+ ++j;
+ }
+ ++i;
+ }
+ if (j)
+ XDrawPoints(d->dpy, d->hd, d->gc, xPoints, j, CoordModeOrigin);
+
+ j = 0;
+ }
+}
+
+void QX11PaintEngine::drawPoints(const QPointF *points, int pointCount)
+{
+ Q_ASSERT(points);
+ Q_ASSERT(pointCount);
+ Q_D(QX11PaintEngine);
+
+ if (!d->has_pen)
+ return;
+
+ // use the same test here as in drawPath to ensure that we don't use the path fallback
+ // and end up in XDrawLines for pens with width <= 1
+ if (d->cpen.widthF() > 1.0f
+ || (X11->use_xrender && (d->has_alpha_pen || (d->render_hints & QPainter::Antialiasing)))
+ || (!d->isCosmeticPen() && d->txop > QTransform::TxTranslate))
+ {
+ Qt::PenCapStyle capStyle = d->cpen.capStyle();
+ if (capStyle == Qt::FlatCap) {
+ setCapStyle(CapProjecting, d->gc);
+ d->cpen.setCapStyle(Qt::SquareCap);
+ }
+
+ const QPointF *end = points + pointCount;
+ while (points < end) {
+ QPainterPath path;
+ path.moveTo(*points);
+ path.lineTo(points->x() + 0.005, points->y());
+ drawPath(path);
+ ++points;
+ }
+ if (capStyle == Qt::FlatCap) {
+ setCapStyle(CapButt, d->gc);
+ d->cpen.setCapStyle(capStyle);
+ }
+ return;
+ }
+
+ static const int BUF_SIZE = 1024;
+ XPoint xPoints[BUF_SIZE];
+ int i = 0, j = 0;
+ while (i < pointCount) {
+ while (i < pointCount && j < BUF_SIZE) {
+ const QPointF &xformed = d->matrix.map(points[i]);
+ int x = qFloor(xformed.x());
+ int y = qFloor(xformed.y());
+
+ if (x >= SHRT_MIN && y >= SHRT_MIN && x < SHRT_MAX && y < SHRT_MAX) {
+ xPoints[j].x = x;
+ xPoints[j].y = y;
+ ++j;
+ }
+ ++i;
+ }
+ if (j)
+ XDrawPoints(d->dpy, d->hd, d->gc, xPoints, j, CoordModeOrigin);
+
+ j = 0;
+ }
+}
+
+QPainter::RenderHints QX11PaintEngine::supportedRenderHints() const
+{
+#if QT_CONFIG(xrender)
+ if (X11->use_xrender)
+ return QPainter::Antialiasing;
+#endif
+ return QFlag(0);
+}
+
+void QX11PaintEngine::updateState(const QPaintEngineState &state)
+{
+ Q_D(QX11PaintEngine);
+ QPaintEngine::DirtyFlags flags = state.state();
+
+
+ if (flags & DirtyOpacity) {
+ d->opacity = state.opacity();
+ // Force update pen/brush as to get proper alpha colors propagated
+ flags |= DirtyPen;
+ flags |= DirtyBrush;
+ }
+
+ if (flags & DirtyTransform) updateMatrix(state.transform());
+ if (flags & DirtyPen) updatePen(state.pen());
+ if (flags & (DirtyBrush | DirtyBrushOrigin)) updateBrush(state.brush(), state.brushOrigin());
+ if (flags & DirtyFont) updateFont(state.font());
+
+ if (state.state() & DirtyClipEnabled) {
+ if (state.isClipEnabled()) {
+ QPolygonF clip_poly_dev(d->matrix.map(painter()->clipPath().toFillPolygon()));
+ QPolygonF clipped_poly_dev;
+ d->clipPolygon_dev(clip_poly_dev, &clipped_poly_dev);
+ updateClipRegion_dev(QRegion(clipped_poly_dev.toPolygon()), Qt::ReplaceClip);
+ } else {
+ updateClipRegion_dev(QRegion(), Qt::NoClip);
+ }
+ }
+
+ if (flags & DirtyClipPath) {
+ QPolygonF clip_poly_dev(d->matrix.map(state.clipPath().toFillPolygon()));
+ QPolygonF clipped_poly_dev;
+ d->clipPolygon_dev(clip_poly_dev, &clipped_poly_dev);
+ updateClipRegion_dev(QRegion(clipped_poly_dev.toPolygon(), state.clipPath().fillRule()),
+ state.clipOperation());
+ } else if (flags & DirtyClipRegion) {
+ extern QPainterPath qt_regionToPath(const QRegion &region);
+ QPainterPath clip_path = qt_regionToPath(state.clipRegion());
+ QPolygonF clip_poly_dev(d->matrix.map(clip_path.toFillPolygon()));
+ QPolygonF clipped_poly_dev;
+ d->clipPolygon_dev(clip_poly_dev, &clipped_poly_dev);
+ updateClipRegion_dev(QRegion(clipped_poly_dev.toPolygon()), state.clipOperation());
+ }
+ if (flags & DirtyHints) updateRenderHints(state.renderHints());
+ if (flags & DirtyCompositionMode) {
+ int function = GXcopy;
+ if (state.compositionMode() >= QPainter::RasterOp_SourceOrDestination) {
+ switch (state.compositionMode()) {
+ case QPainter::RasterOp_SourceOrDestination:
+ function = GXor;
+ break;
+ case QPainter::RasterOp_SourceAndDestination:
+ function = GXand;
+ break;
+ case QPainter::RasterOp_SourceXorDestination:
+ function = GXxor;
+ break;
+ case QPainter::RasterOp_NotSourceAndNotDestination:
+ function = GXnor;
+ break;
+ case QPainter::RasterOp_NotSourceOrNotDestination:
+ function = GXnand;
+ break;
+ case QPainter::RasterOp_NotSourceXorDestination:
+ function = GXequiv;
+ break;
+ case QPainter::RasterOp_NotSource:
+ function = GXcopyInverted;
+ break;
+ case QPainter::RasterOp_SourceAndNotDestination:
+ function = GXandReverse;
+ break;
+ case QPainter::RasterOp_NotSourceAndDestination:
+ function = GXandInverted;
+ break;
+ default:
+ function = GXcopy;
+ }
+ }
+#if QT_CONFIG(xrender)
+ else {
+ d->composition_mode =
+ qpainterOpToXrender(state.compositionMode());
+ }
+#endif
+ XSetFunction(X11->display, d->gc, function);
+ XSetFunction(X11->display, d->gc_brush, function);
+ }
+ d->decidePathFallback();
+ d->decideCoordAdjust();
+}
+
+void QX11PaintEngine::updateRenderHints(QPainter::RenderHints hints)
+{
+ Q_D(QX11PaintEngine);
+ d->render_hints = hints;
+
+#if QT_CONFIG(xrender)
+ if (X11->use_xrender && d->picture) {
+ XRenderPictureAttributes attrs;
+ attrs.poly_edge = (hints & QPainter::Antialiasing) ? PolyEdgeSmooth : PolyEdgeSharp;
+ XRenderChangePicture(d->dpy, d->picture, CPPolyEdge, &attrs);
+ }
+#endif
+}
+
+void QX11PaintEngine::updatePen(const QPen &pen)
+{
+ Q_D(QX11PaintEngine);
+ d->cpen = pen;
+ int cp = CapButt;
+ int jn = JoinMiter;
+ int ps = pen.style();
+
+ if (d->opacity < 1.0) {
+ QColor c = d->cpen.color();
+ c.setAlpha(qRound(c.alpha()*d->opacity));
+ d->cpen.setColor(c);
+ }
+
+ d->has_pen = (ps != Qt::NoPen);
+ d->has_alpha_pen = (pen.color().alpha() != 255);
+
+ switch (pen.capStyle()) {
+ case Qt::SquareCap:
+ cp = CapProjecting;
+ break;
+ case Qt::RoundCap:
+ cp = CapRound;
+ break;
+ case Qt::FlatCap:
+ default:
+ cp = CapButt;
+ break;
+ }
+ switch (pen.joinStyle()) {
+ case Qt::BevelJoin:
+ jn = JoinBevel;
+ break;
+ case Qt::RoundJoin:
+ jn = JoinRound;
+ break;
+ case Qt::MiterJoin:
+ default:
+ jn = JoinMiter;
+ break;
+ }
+
+ d->adapted_pen_origin = false;
+
+ char dashes[10]; // custom pen dashes
+ int dash_len = 0; // length of dash list
+ int xStyle = LineSolid;
+
+ /*
+ We are emulating Windows here. Windows treats cpen.width() == 1
+ (or 0) as a very special case. The fudge variable unifies this
+ case with the general case.
+ */
+ qreal pen_width = pen.widthF();
+ int scale = qRound(pen_width < 1 ? 1 : pen_width);
+ int space = (pen_width < 1 && pen_width > 0 ? 1 : (2 * scale));
+ int dot = 1 * scale;
+ int dash = 4 * scale;
+
+ d->has_custom_pen = false;
+
+ switch (ps) {
+ case Qt::NoPen:
+ case Qt::SolidLine:
+ xStyle = LineSolid;
+ break;
+ case Qt::DashLine:
+ dashes[0] = dash;
+ dashes[1] = space;
+ dash_len = 2;
+ xStyle = LineOnOffDash;
+ break;
+ case Qt::DotLine:
+ dashes[0] = dot;
+ dashes[1] = space;
+ dash_len = 2;
+ xStyle = LineOnOffDash;
+ break;
+ case Qt::DashDotLine:
+ dashes[0] = dash;
+ dashes[1] = space;
+ dashes[2] = dot;
+ dashes[3] = space;
+ dash_len = 4;
+ xStyle = LineOnOffDash;
+ break;
+ case Qt::DashDotDotLine:
+ dashes[0] = dash;
+ dashes[1] = space;
+ dashes[2] = dot;
+ dashes[3] = space;
+ dashes[4] = dot;
+ dashes[5] = space;
+ dash_len = 6;
+ xStyle = LineOnOffDash;
+ break;
+ case Qt::CustomDashLine:
+ d->has_custom_pen = true;
+ break;
+ }
+
+ ulong mask = GCForeground | GCBackground | GCGraphicsExposures | GCLineWidth
+ | GCCapStyle | GCJoinStyle | GCLineStyle;
+ XGCValues vals;
+ vals.graphics_exposures = false;
+ if (d->pdev_depth == 1) {
+ vals.foreground = qGray(pen.color().rgb()) > 127 ? 0 : 1;
+ vals.background = qGray(QColor(Qt::transparent).rgb()) > 127 ? 0 : 1;
+ } else if (d->pdev->devType() == QInternal::Pixmap && d->pdev_depth == 32
+ && X11->use_xrender) {
+ vals.foreground = pen.color().rgba();
+ vals.background = QColor(Qt::transparent).rgba();
+ } else {
+ QXcbColormap cmap = QXcbColormap::instance(d->scrn);
+ vals.foreground = cmap.pixel(pen.color());
+ vals.background = cmap.pixel(QColor(Qt::transparent));
+ }
+
+
+ vals.line_width = qRound(pen.widthF());
+ vals.cap_style = cp;
+ vals.join_style = jn;
+ vals.line_style = xStyle;
+
+ XChangeGC(d->dpy, d->gc, mask, &vals);
+
+ if (dash_len) { // make dash list
+ XSetDashes(d->dpy, d->gc, 0, dashes, dash_len);
+ }
+
+ if (!d->has_clipping) { // if clipping is set the paintevent clip region is merged with the clip region
+ QRegion sysClip = systemClip();
+ if (!sysClip.isEmpty())
+ x11SetClipRegion(d->dpy, d->gc, 0, d->picture, sysClip);
+ else
+ x11ClearClipRegion(d->dpy, d->gc, 0, d->picture);
+ }
+}
+
+void QX11PaintEngine::updateBrush(const QBrush &brush, const QPointF &origin)
+{
+ Q_D(QX11PaintEngine);
+ d->cbrush = brush;
+ d->bg_origin = origin;
+ d->adapted_brush_origin = false;
+#if QT_CONFIG(xrender)
+ d->current_brush = 0;
+#endif
+ if (d->opacity < 1.0) {
+ QColor c = d->cbrush.color();
+ c.setAlpha(qRound(c.alpha()*d->opacity));
+ d->cbrush.setColor(c);
+ }
+
+ int s = FillSolid;
+ int bs = d->cbrush.style();
+ d->has_brush = (bs != Qt::NoBrush);
+ d->has_pattern = bs >= Qt::Dense1Pattern && bs <= Qt::DiagCrossPattern;
+ d->has_texture = bs == Qt::TexturePattern;
+ d->has_alpha_brush = brush.color().alpha() != 255;
+ d->has_alpha_texture = d->has_texture && d->cbrush.texture().hasAlphaChannel();
+
+ ulong mask = GCForeground | GCBackground | GCGraphicsExposures
+ | GCLineStyle | GCCapStyle | GCJoinStyle | GCFillStyle;
+ XGCValues vals;
+ vals.graphics_exposures = false;
+ if (d->pdev_depth == 1) {
+ vals.foreground = qGray(d->cbrush.color().rgb()) > 127 ? 0 : 1;
+ vals.background = qGray(QColor(Qt::transparent).rgb()) > 127 ? 0 : 1;
+ } else if (X11->use_xrender && d->pdev->devType() == QInternal::Pixmap
+ && d->pdev_depth == 32) {
+ vals.foreground = d->cbrush.color().rgba();
+ vals.background = QColor(Qt::transparent).rgba();
+ } else {
+ QXcbColormap cmap = QXcbColormap::instance(d->scrn);
+ vals.foreground = cmap.pixel(d->cbrush.color());
+ vals.background = cmap.pixel(QColor(Qt::transparent));
+
+ if (!X11->use_xrender && d->has_brush && !d->has_pattern && !brush.isOpaque()) {
+ QPixmap pattern = qt_patternForAlpha(brush.color().alpha(), d->scrn);
+ mask |= GCStipple;
+ vals.stipple = qt_x11PixmapHandle(pattern);
+ s = FillStippled;
+ d->adapted_brush_origin = true;
+ }
+ }
+ vals.cap_style = CapButt;
+ vals.join_style = JoinMiter;
+ vals.line_style = LineSolid;
+
+ if (d->has_pattern || d->has_texture) {
+ if (bs == Qt::TexturePattern) {
+ d->brush_pm = qt_toX11Pixmap(d->cbrush.texture());
+#if QT_CONFIG(xrender)
+ if (X11->use_xrender) {
+ XRenderPictureAttributes attrs;
+ attrs.repeat = true;
+ XRenderChangePicture(d->dpy, qt_x11PictureHandle(d->brush_pm), CPRepeat, &attrs);
+ QX11PlatformPixmap *data = static_cast<QX11PlatformPixmap*>(d->brush_pm.handle());
+ if (data->mask_picture)
+ XRenderChangePicture(d->dpy, data->mask_picture, CPRepeat, &attrs);
+ }
+#endif
+ } else {
+ d->brush_pm = qt_toX11Pixmap(qt_pixmapForBrush(bs, true));
+ }
+ qt_x11SetScreen(d->brush_pm, d->scrn);
+ if (d->brush_pm.depth() == 1) {
+ mask |= GCStipple;
+ vals.stipple = qt_x11PixmapHandle(d->brush_pm);
+ s = FillStippled;
+#if QT_CONFIG(xrender)
+ if (X11->use_xrender) {
+ d->bitmap_texture = QPixmap(d->brush_pm.size());
+ d->bitmap_texture.fill(Qt::transparent);
+ d->bitmap_texture = qt_toX11Pixmap(d->bitmap_texture);
+ qt_x11SetScreen(d->bitmap_texture, d->scrn);
+
+ ::Picture src = X11->getSolidFill(d->scrn, d->cbrush.color());
+ XRenderComposite(d->dpy, PictOpSrc, src, qt_x11PictureHandle(d->brush_pm),
+ qt_x11PictureHandle(d->bitmap_texture),
+ 0, 0, d->brush_pm.width(), d->brush_pm.height(),
+ 0, 0, d->brush_pm.width(), d->brush_pm.height());
+
+ XRenderPictureAttributes attrs;
+ attrs.repeat = true;
+ XRenderChangePicture(d->dpy, qt_x11PictureHandle(d->bitmap_texture), CPRepeat, &attrs);
+
+ d->current_brush = qt_x11PictureHandle(d->bitmap_texture);
+ }
+#endif
+ } else {
+ mask |= GCTile;
+#if QT_CONFIG(xrender)
+ if (d->pdev_depth == 32 && d->brush_pm.depth() != 32) {
+ d->brush_pm.detach();
+ QX11PlatformPixmap *brushData = static_cast<QX11PlatformPixmap*>(d->brush_pm.handle());
+ brushData->convertToARGB32();
+ }
+#endif
+ vals.tile = (d->brush_pm.depth() == d->pdev_depth
+ ? qt_x11PixmapHandle(d->brush_pm)
+ : static_cast<QX11PlatformPixmap*>(d->brush_pm.handle())->x11ConvertToDefaultDepth());
+ s = FillTiled;
+#if QT_CONFIG(xrender)
+ d->current_brush = qt_x11PictureHandle(d->cbrush.texture());
+#endif
+ }
+
+ mask |= GCTileStipXOrigin | GCTileStipYOrigin;
+ vals.ts_x_origin = qRound(origin.x());
+ vals.ts_y_origin = qRound(origin.y());
+ }
+#if QT_CONFIG(xrender)
+ else if (d->has_alpha_brush) {
+ d->current_brush = X11->getSolidFill(d->scrn, d->cbrush.color());
+ }
+#endif
+
+ vals.fill_style = s;
+ XChangeGC(d->dpy, d->gc_brush, mask, &vals);
+ if (!d->has_clipping) {
+ QRegion sysClip = systemClip();
+ if (!sysClip.isEmpty())
+ x11SetClipRegion(d->dpy, d->gc_brush, 0, d->picture, sysClip);
+ else
+ x11ClearClipRegion(d->dpy, d->gc_brush, 0, d->picture);
+ }
+}
+
+void QX11PaintEngine::drawEllipse(const QRectF &rect)
+{
+ QRect aligned = rect.toAlignedRect();
+ if (aligned == rect)
+ drawEllipse(aligned);
+ else
+ QPaintEngine::drawEllipse(rect);
+}
+
+void QX11PaintEngine::drawEllipse(const QRect &rect)
+{
+ if (rect.isEmpty()) {
+ drawRects(&rect, 1);
+ return;
+ }
+
+ Q_D(QX11PaintEngine);
+ QRect devclip(SHRT_MIN, SHRT_MIN, SHRT_MAX*2 - 1, SHRT_MAX*2 - 1);
+ QRect r(rect);
+ if (d->txop < QTransform::TxRotate) {
+ r = d->matrix.mapRect(rect);
+ } else if (d->txop == QTransform::TxRotate && rect.width() == rect.height()) {
+ QPainterPath path;
+ path.addEllipse(rect);
+ r = d->matrix.map(path).boundingRect().toRect();
+ }
+
+ if (d->has_alpha_brush || d->has_alpha_pen || d->has_custom_pen || (d->render_hints & QPainter::Antialiasing)
+ || d->has_alpha_texture || devclip.intersected(r) != r
+ || (d->has_complex_xform
+ && !(d->has_non_scaling_xform && rect.width() == rect.height())))
+ {
+ QPainterPath path;
+ path.addEllipse(rect);
+ drawPath(path);
+ return;
+ }
+
+ int x = r.x();
+ int y = r.y();
+ int w = r.width();
+ int h = r.height();
+ if (w < 1 || h < 1)
+ return;
+ if (w == 1 && h == 1) {
+ XDrawPoint(d->dpy, d->hd, d->has_pen ? d->gc : d->gc_brush, x, y);
+ return;
+ }
+ d->setupAdaptedOrigin(rect.topLeft());
+ if (d->has_brush) { // draw filled ellipse
+ XFillArc(d->dpy, d->hd, d->gc_brush, x, y, w, h, 0, 360*64);
+ if (!d->has_pen) // make smoother outline
+ XDrawArc(d->dpy, d->hd, d->gc_brush, x, y, w-1, h-1, 0, 360*64);
+ }
+ if (d->has_pen) // draw outline
+ XDrawArc(d->dpy, d->hd, d->gc, x, y, w, h, 0, 360*64);
+ d->resetAdaptedOrigin();
+}
+
+
+
+void QX11PaintEnginePrivate::fillPolygon_translated(const QPointF *polygonPoints, int pointCount,
+ QX11PaintEnginePrivate::GCMode gcMode,
+ QPaintEngine::PolygonDrawMode mode)
+{
+
+ QVarLengthArray<QPointF> translated_points(pointCount);
+ QPointF offset(matrix.dx(), matrix.dy());
+
+ qreal offs = adjust_coords ? aliasedCoordinateDelta : 0.0;
+ if (!X11->use_xrender || !(render_hints & QPainter::Antialiasing))
+ offset += QPointF(aliasedCoordinateDelta, aliasedCoordinateDelta);
+
+ for (int i = 0; i < pointCount; ++i) {
+ translated_points[i] = polygonPoints[i] + offset;
+
+ translated_points[i].rx() = qRound(translated_points[i].x()) + offs;
+ translated_points[i].ry() = qRound(translated_points[i].y()) + offs;
+ }
+
+ fillPolygon_dev(translated_points.data(), pointCount, gcMode, mode);
+}
+
+#if QT_CONFIG(xrender)
+static void qt_XRenderCompositeTrapezoids(Display *dpy,
+ int op,
+ Picture src,
+ Picture dst,
+ _Xconst XRenderPictFormat *maskFormat,
+ int xSrc,
+ int ySrc,
+ const XTrapezoid *traps, int size)
+{
+ const int MAX_TRAPS = 50000;
+ while (size) {
+ int to_draw = size;
+ if (to_draw > MAX_TRAPS)
+ to_draw = MAX_TRAPS;
+ XRenderCompositeTrapezoids(dpy, op, src, dst,
+ maskFormat,
+ xSrc, ySrc,
+ traps, to_draw);
+ size -= to_draw;
+ traps += to_draw;
+ }
+}
+#endif
+
+void QX11PaintEnginePrivate::fillPolygon_dev(const QPointF *polygonPoints, int pointCount,
+ QX11PaintEnginePrivate::GCMode gcMode,
+ QPaintEngine::PolygonDrawMode mode)
+{
+ Q_Q(QX11PaintEngine);
+
+ int clippedCount = 0;
+ qt_float_point *clippedPoints = 0;
+
+#if QT_CONFIG(xrender)
+ //can change if we switch to pen if gcMode != BrushGC
+ bool has_fill_texture = has_texture;
+ bool has_fill_pattern = has_pattern;
+ ::Picture src;
+#endif
+ QBrush fill;
+ GC fill_gc;
+ if (gcMode == BrushGC) {
+ fill = cbrush;
+ fill_gc = gc_brush;
+#if QT_CONFIG(xrender)
+ if (current_brush)
+ src = current_brush;
+ else
+ src = X11->getSolidFill(scrn, fill.color());
+#endif
+ } else {
+ fill = QBrush(cpen.brush());
+ fill_gc = gc;
+#if QT_CONFIG(xrender)
+ //we use the pens brush
+ has_fill_texture = (fill.style() == Qt::TexturePattern);
+ has_fill_pattern = (fill.style() >= Qt::Dense1Pattern && fill.style() <= Qt::DiagCrossPattern);
+ if (has_fill_texture)
+ src = qt_x11PictureHandle(fill.texture());
+ else if (has_fill_pattern)
+ src = getPatternFill(scrn, fill);
+ else
+ src = X11->getSolidFill(scrn, fill.color());
+#endif
+ }
+
+ polygonClipper.clipPolygon((qt_float_point *) polygonPoints, pointCount,
+ &clippedPoints, &clippedCount);
+
+#if QT_CONFIG(xrender)
+ bool solid_fill = fill.color().alpha() == 255;
+ if (has_fill_texture && fill.texture().depth() == 1 && solid_fill) {
+ has_fill_texture = false;
+ has_fill_pattern = true;
+ }
+
+ bool antialias = render_hints & QPainter::Antialiasing;
+
+ if (X11->use_xrender
+ && picture
+ && !has_fill_pattern
+ && (clippedCount > 0)
+ && (fill.style() != Qt::NoBrush)
+ && ((has_fill_texture && fill.texture().hasAlpha()) || antialias || !solid_fill || has_alpha_pen != has_alpha_brush))
+ {
+ tessellator->tessellate((QPointF *)clippedPoints, clippedCount,
+ mode == QPaintEngine::WindingMode);
+ if (tessellator->size > 0) {
+ XRenderPictureAttributes attrs;
+ attrs.poly_edge = antialias ? PolyEdgeSmooth : PolyEdgeSharp;
+ XRenderChangePicture(dpy, picture, CPPolyEdge, &attrs);
+ int x_offset = int(XFixedToDouble(tessellator->traps[0].left.p1.x) - bg_origin.x());
+ int y_offset = int(XFixedToDouble(tessellator->traps[0].left.p1.y) - bg_origin.y());
+ qt_XRenderCompositeTrapezoids(dpy, composition_mode, src, picture,
+ antialias
+ ? XRenderFindStandardFormat(dpy, PictStandardA8)
+ : XRenderFindStandardFormat(dpy, PictStandardA1),
+ x_offset, y_offset,
+ tessellator->traps, tessellator->size);
+ tessellator->done();
+ }
+ } else
+#endif
+ if (fill.style() != Qt::NoBrush) {
+ if (clippedCount > 200000) {
+ QPolygon poly;
+ for (int i = 0; i < clippedCount; ++i)
+ poly << QPoint(qFloor(clippedPoints[i].x), qFloor(clippedPoints[i].y));
+
+ const QRect bounds = poly.boundingRect();
+ const QRect aligned = bounds
+ & QRect(QPoint(), QSize(pdev->width(), pdev->height()));
+
+ QImage img(aligned.size(), QImage::Format_ARGB32_Premultiplied);
+ img.fill(0);
+
+ QPainter painter(&img);
+ painter.translate(-aligned.x(), -aligned.y());
+ painter.setPen(Qt::NoPen);
+ painter.setBrush(fill);
+ if (gcMode == BrushGC)
+ painter.setBrushOrigin(q->painter()->brushOrigin());
+ painter.drawPolygon(poly);
+ painter.end();
+
+ q->drawImage(aligned, img, img.rect(), Qt::AutoColor);
+ } else if (clippedCount > 0) {
+ QVarLengthArray<XPoint> xpoints(clippedCount);
+ for (int i = 0; i < clippedCount; ++i) {
+ xpoints[i].x = qFloor(clippedPoints[i].x);
+ xpoints[i].y = qFloor(clippedPoints[i].y);
+ }
+ if (mode == QPaintEngine::WindingMode)
+ XSetFillRule(dpy, fill_gc, WindingRule);
+ setupAdaptedOrigin(QPoint(xpoints[0].x, xpoints[0].y));
+ XFillPolygon(dpy, hd, fill_gc,
+ xpoints.data(), clippedCount,
+ mode == QPaintEngine::ConvexMode ? Convex : Complex, CoordModeOrigin);
+ resetAdaptedOrigin();
+ if (mode == QPaintEngine::WindingMode)
+ XSetFillRule(dpy, fill_gc, EvenOddRule);
+ }
+ }
+}
+
+void QX11PaintEnginePrivate::strokePolygon_translated(const QPointF *polygonPoints, int pointCount, bool close)
+{
+ QVarLengthArray<QPointF> translated_points(pointCount);
+ QPointF offset(matrix.dx(), matrix.dy());
+ for (int i = 0; i < pointCount; ++i)
+ translated_points[i] = polygonPoints[i] + offset;
+ strokePolygon_dev(translated_points.data(), pointCount, close);
+}
+
+void QX11PaintEnginePrivate::strokePolygon_dev(const QPointF *polygonPoints, int pointCount, bool close)
+{
+ int clippedCount = 0;
+ qt_float_point *clippedPoints = 0;
+ polygonClipper.clipPolygon((qt_float_point *) polygonPoints, pointCount,
+ &clippedPoints, &clippedCount, close);
+
+ if (clippedCount > 0) {
+ QVarLengthArray<XPoint> xpoints(clippedCount);
+ for (int i = 0; i < clippedCount; ++i) {
+ xpoints[i].x = qRound(clippedPoints[i].x + aliasedCoordinateDelta);
+ xpoints[i].y = qRound(clippedPoints[i].y + aliasedCoordinateDelta);
+ }
+ uint numberPoints = qMin(clippedCount, xlibMaxLinePoints);
+ XPoint *pts = xpoints.data();
+ XDrawLines(dpy, hd, gc, pts, numberPoints, CoordModeOrigin);
+ pts += numberPoints;
+ clippedCount -= numberPoints;
+ numberPoints = qMin(clippedCount, xlibMaxLinePoints-1);
+ while (clippedCount) {
+ XDrawLines(dpy, hd, gc, pts-1, numberPoints+1, CoordModeOrigin);
+ pts += numberPoints;
+ clippedCount -= numberPoints;
+ numberPoints = qMin(clippedCount, xlibMaxLinePoints-1);
+ }
+ }
+}
+
+void QX11PaintEngine::drawPolygon(const QPointF *polygonPoints, int pointCount, PolygonDrawMode mode)
+{
+ Q_D(QX11PaintEngine);
+
+ if (d->use_path_fallback) {
+ QPainterPath path(polygonPoints[0]);
+ for (int i = 1; i < pointCount; ++i)
+ path.lineTo(polygonPoints[i]);
+ if (mode == PolylineMode) {
+ QBrush oldBrush = d->cbrush;
+ d->cbrush = QBrush(Qt::NoBrush);
+ path.setFillRule(Qt::WindingFill);
+ drawPath(path);
+ d->cbrush = oldBrush;
+ } else {
+ path.setFillRule(mode == OddEvenMode ? Qt::OddEvenFill : Qt::WindingFill);
+ path.closeSubpath();
+ drawPath(path);
+ }
+ return;
+ }
+ if (mode != PolylineMode && d->has_brush)
+ d->fillPolygon_translated(polygonPoints, pointCount, QX11PaintEnginePrivate::BrushGC, mode);
+
+ if (d->has_pen)
+ d->strokePolygon_translated(polygonPoints, pointCount, mode != PolylineMode);
+}
+
+
+void QX11PaintEnginePrivate::fillPath(const QPainterPath &path, QX11PaintEnginePrivate::GCMode gc_mode, bool transform)
+{
+ qreal offs = adjust_coords ? aliasedCoordinateDelta : 0.0;
+
+ QPainterPath clippedPath;
+ QPainterPath clipPath;
+ clipPath.addRect(polygonClipper.boundingRect());
+
+ if (transform)
+ clippedPath = (path*matrix).intersected(clipPath);
+ else
+ clippedPath = path.intersected(clipPath);
+
+ QList<QPolygonF> polys = clippedPath.toFillPolygons();
+ for (int i = 0; i < polys.size(); ++i) {
+ QVarLengthArray<QPointF> translated_points(polys.at(i).size());
+
+ for (int j = 0; j < polys.at(i).size(); ++j) {
+ translated_points[j] = polys.at(i).at(j);
+ if (!X11->use_xrender || !(render_hints & QPainter::Antialiasing)) {
+ translated_points[j].rx() = qRound(translated_points[j].rx() + aliasedCoordinateDelta) + offs;
+ translated_points[j].ry() = qRound(translated_points[j].ry() + aliasedCoordinateDelta) + offs;
+ }
+ }
+
+ fillPolygon_dev(translated_points.data(), polys.at(i).size(), gc_mode,
+ path.fillRule() == Qt::OddEvenFill ? QPaintEngine::OddEvenMode : QPaintEngine::WindingMode);
+ }
+}
+
+void QX11PaintEngine::drawPath(const QPainterPath &path)
+{
+ Q_D(QX11PaintEngine);
+ if (path.isEmpty())
+ return;
+
+ if (d->has_brush)
+ d->fillPath(path, QX11PaintEnginePrivate::BrushGC, true);
+ if (d->has_pen
+ && ((X11->use_xrender && (d->has_alpha_pen || (d->render_hints & QPainter::Antialiasing)))
+ || (!d->isCosmeticPen() && d->txop > QTransform::TxTranslate
+ && !d->has_non_scaling_xform)
+ || (d->cpen.style() == Qt::CustomDashLine))) {
+ QPainterPathStroker stroker;
+ if (d->cpen.style() == Qt::CustomDashLine) {
+ stroker.setDashPattern(d->cpen.dashPattern());
+ stroker.setDashOffset(d->cpen.dashOffset());
+ } else {
+ stroker.setDashPattern(d->cpen.style());
+ }
+ stroker.setCapStyle(d->cpen.capStyle());
+ stroker.setJoinStyle(d->cpen.joinStyle());
+ QPainterPath stroke;
+ qreal width = d->cpen.widthF();
+ QPolygonF poly;
+ QRectF deviceRect(0, 0, d->pdev->width(), d->pdev->height());
+ // necessary to get aliased alphablended primitives to be drawn correctly
+ if (d->isCosmeticPen() || d->has_scaling_xform) {
+ if (d->isCosmeticPen())
+ stroker.setWidth(width == 0 ? 1 : width);
+ else
+ stroker.setWidth(width * d->xform_scale);
+ stroker.d_ptr->stroker.setClipRect(deviceRect);
+ stroke = stroker.createStroke(path * d->matrix);
+ if (stroke.isEmpty())
+ return;
+ stroke.setFillRule(Qt::WindingFill);
+ d->fillPath(stroke, QX11PaintEnginePrivate::PenGC, false);
+ } else {
+ stroker.setWidth(width);
+ stroker.d_ptr->stroker.setClipRect(d->matrix.inverted().mapRect(deviceRect));
+ stroke = stroker.createStroke(path);
+ if (stroke.isEmpty())
+ return;
+ stroke.setFillRule(Qt::WindingFill);
+ d->fillPath(stroke, QX11PaintEnginePrivate::PenGC, true);
+ }
+ } else if (d->has_pen) {
+ // if we have a cosmetic pen - use XDrawLine() for speed
+ QList<QPolygonF> polys = path.toSubpathPolygons(d->matrix);
+ for (int i = 0; i < polys.size(); ++i)
+ d->strokePolygon_dev(polys.at(i).data(), polys.at(i).size(), false);
+ }
+}
+
+Q_GUI_EXPORT void qt_x11_drawImage(const QRect &rect, const QPoint &pos, const QImage &image,
+ Drawable hd, GC gc, Display *dpy, Visual *visual, int depth)
+{
+ Q_ASSERT(image.format() == QImage::Format_RGB32);
+ Q_ASSERT(image.depth() == 32);
+
+ XImage *xi;
+ // Note: this code assumes either RGB or BGR, 8 bpc server layouts
+ const uint red_mask = (uint) visual->red_mask;
+ bool bgr_layout = (red_mask == 0xff);
+
+ const int w = rect.width();
+ const int h = rect.height();
+
+ QImage im;
+ int image_byte_order = ImageByteOrder(QXcbX11Info::display());
+ if ((QSysInfo::ByteOrder == QSysInfo::BigEndian && ((image_byte_order == LSBFirst) || bgr_layout))
+ || (image_byte_order == MSBFirst && QSysInfo::ByteOrder == QSysInfo::LittleEndian)
+ || (image_byte_order == LSBFirst && bgr_layout))
+ {
+ im = image.copy(rect);
+ const int iw = im.bytesPerLine() / 4;
+ uint *data = (uint *)im.bits();
+ for (int i=0; i < h; i++) {
+ uint *p = data;
+ uint *end = p + w;
+ if (bgr_layout && image_byte_order == MSBFirst && QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
+ while (p < end) {
+ *p = ((*p << 8) & 0xffffff00) | ((*p >> 24) & 0x000000ff);
+ p++;
+ }
+ } else if ((image_byte_order == LSBFirst && QSysInfo::ByteOrder == QSysInfo::BigEndian)
+ || (image_byte_order == MSBFirst && QSysInfo::ByteOrder == QSysInfo::LittleEndian)) {
+ while (p < end) {
+ *p = ((*p << 24) & 0xff000000) | ((*p << 8) & 0x00ff0000)
+ | ((*p >> 8) & 0x0000ff00) | ((*p >> 24) & 0x000000ff);
+ p++;
+ }
+ } else if ((image_byte_order == MSBFirst && QSysInfo::ByteOrder == QSysInfo::BigEndian)
+ || (image_byte_order == LSBFirst && bgr_layout))
+ {
+ while (p < end) {
+ *p = ((*p << 16) & 0x00ff0000) | ((*p >> 16) & 0x000000ff)
+ | ((*p ) & 0xff00ff00);
+ p++;
+ }
+ }
+ data += iw;
+ }
+ xi = XCreateImage(dpy, visual, depth, ZPixmap,
+ 0, (char *) im.bits(), w, h, 32, im.bytesPerLine());
+ } else {
+ xi = XCreateImage(dpy, visual, depth, ZPixmap,
+ 0, (char *) image.scanLine(rect.y())+rect.x()*sizeof(uint), w, h, 32, image.bytesPerLine());
+ }
+ XPutImage(dpy, hd, gc, xi, 0, 0, pos.x(), pos.y(), w, h);
+ xi->data = 0; // QImage owns these bits
+ XDestroyImage(xi);
+}
+
+void QX11PaintEngine::drawImage(const QRectF &r, const QImage &image, const QRectF &sr, Qt::ImageConversionFlags flags)
+{
+ Q_D(QX11PaintEngine);
+
+ if (image.format() == QImage::Format_RGB32
+ && d->pdev_depth >= 24 && image.depth() == 32
+ && r.size() == sr.size())
+ {
+ int sx = qRound(sr.x());
+ int sy = qRound(sr.y());
+ int x = qRound(r.x());
+ int y = qRound(r.y());
+ int w = qRound(r.width());
+ int h = qRound(r.height());
+
+ qt_x11_drawImage(QRect(sx, sy, w, h), QPoint(x, y), image, d->hd, d->gc, d->dpy,
+ (Visual *)d->xinfo->visual(), d->pdev_depth);
+ } else {
+ QPaintEngine::drawImage(r, image, sr, flags);
+ }
+}
+
+void QX11PaintEngine::drawPixmap(const QRectF &r, const QPixmap &px, const QRectF &_sr)
+{
+ Q_D(QX11PaintEngine);
+ QRectF sr = _sr;
+ int x = qRound(r.x());
+ int y = qRound(r.y());
+ int sx = qRound(sr.x());
+ int sy = qRound(sr.y());
+ int sw = qRound(sr.width());
+ int sh = qRound(sr.height());
+
+ QPixmap pixmap = qt_toX11Pixmap(px);
+ if (pixmap.isNull())
+ return;
+
+ if ((d->xinfo && d->xinfo->screen() != qt_x11Info(pixmap).screen())
+ || (qt_x11Info(pixmap).screen() != DefaultScreen(QXcbX11Info::display()))) {
+ qt_x11SetScreen(pixmap, d->xinfo ? d->xinfo->screen() : DefaultScreen(X11->display));
+ }
+
+ qt_x11SetDefaultScreen(qt_x11Info(pixmap).screen());
+
+#if QT_CONFIG(xrender)
+ ::Picture src_pict = qt_x11PictureHandle(pixmap);
+ if (src_pict && d->picture) {
+ const int pDepth = pixmap.depth();
+ if (pDepth == 1 && (d->has_alpha_pen)) {
+ qt_render_bitmap(d->dpy, d->scrn, src_pict, d->picture,
+ sx, sy, x, y, sw, sh, d->cpen);
+ return;
+ } else if (pDepth != 1 && (pDepth == 32 || pDepth != d->pdev_depth)) {
+ XRenderComposite(d->dpy, d->composition_mode,
+ src_pict, 0, d->picture, sx, sy, 0, 0, x, y, sw, sh);
+ return;
+ }
+ }
+#endif
+
+ bool mono_src = pixmap.depth() == 1;
+ bool mono_dst = d->pdev_depth == 1;
+ bool restore_clip = false;
+
+ if (static_cast<QX11PlatformPixmap*>(pixmap.handle())->x11_mask) { // pixmap has a mask
+ QBitmap comb(sw, sh);
+ GC cgc = XCreateGC(d->dpy, qt_x11PixmapHandle(comb), 0, 0);
+ XSetForeground(d->dpy, cgc, 0);
+ XFillRectangle(d->dpy, qt_x11PixmapHandle(comb), cgc, 0, 0, sw, sh);
+ XSetBackground(d->dpy, cgc, 0);
+ XSetForeground(d->dpy, cgc, 1);
+ if (!d->crgn.isEmpty()) {
+ QVector<XRectangle> rects = qt_region_to_xrectangles(d->crgn);
+ XSetClipRectangles(d->dpy, cgc, -x, -y, rects.data(), rects.size(), Unsorted);
+ } else if (d->has_clipping) {
+ XSetClipRectangles(d->dpy, cgc, 0, 0, 0, 0, Unsorted);
+ }
+ XSetFillStyle(d->dpy, cgc, FillOpaqueStippled);
+ XSetTSOrigin(d->dpy, cgc, -sx, -sy);
+ XSetStipple(d->dpy, cgc,
+ static_cast<QX11PlatformPixmap*>(pixmap.handle())->x11_mask);
+ XFillRectangle(d->dpy, qt_x11PixmapHandle(comb), cgc, 0, 0, sw, sh);
+ XFreeGC(d->dpy, cgc);
+
+ XSetClipOrigin(d->dpy, d->gc, x, y);
+ XSetClipMask(d->dpy, d->gc, qt_x11PixmapHandle(comb));
+ restore_clip = true;
+ }
+
+ if (mono_src) {
+ if (!d->crgn.isEmpty()) {
+ Pixmap comb = XCreatePixmap(d->dpy, d->hd, sw, sh, 1);
+ GC cgc = XCreateGC(d->dpy, comb, 0, 0);
+ XSetForeground(d->dpy, cgc, 0);
+ XFillRectangle(d->dpy, comb, cgc, 0, 0, sw, sh);
+ QVector<XRectangle> rects = qt_region_to_xrectangles(d->crgn);
+ XSetClipRectangles(d->dpy, cgc, -x, -y, rects.data(), rects.size(), Unsorted);
+ XCopyArea(d->dpy, qt_x11PixmapHandle(pixmap), comb, cgc, sx, sy, sw, sh, 0, 0);
+ XFreeGC(d->dpy, cgc);
+
+ XSetClipMask(d->dpy, d->gc, comb);
+ XSetClipOrigin(d->dpy, d->gc, x, y);
+ XFreePixmap(d->dpy, comb);
+ } else {
+ XSetClipMask(d->dpy, d->gc, qt_x11PixmapHandle(pixmap));
+ XSetClipOrigin(d->dpy, d->gc, x - sx, y - sy);
+ }
+
+ if (mono_dst) {
+ XSetForeground(d->dpy, d->gc, qGray(d->cpen.color().rgb()) > 127 ? 0 : 1);
+ } else {
+ QXcbColormap cmap = QXcbColormap::instance(d->scrn);
+ XSetForeground(d->dpy, d->gc, cmap.pixel(d->cpen.color()));
+ }
+ XFillRectangle(d->dpy, d->hd, d->gc, x, y, sw, sh);
+ restore_clip = true;
+ } else if (mono_dst && !mono_src) {
+ QBitmap bitmap(pixmap);
+ XCopyArea(d->dpy, qt_x11PixmapHandle(bitmap), d->hd, d->gc, sx, sy, sw, sh, x, y);
+ } else {
+ XCopyArea(d->dpy, qt_x11PixmapHandle(pixmap), d->hd, d->gc, sx, sy, sw, sh, x, y);
+ }
+
+ if (d->pdev->devType() == QInternal::Pixmap) {
+ const QPixmap *px = static_cast<const QPixmap*>(d->pdev);
+ Pixmap src_mask = static_cast<QX11PlatformPixmap*>(pixmap.handle())->x11_mask;
+ Pixmap dst_mask = static_cast<QX11PlatformPixmap*>(px->handle())->x11_mask;
+ if (dst_mask) {
+ GC cgc = XCreateGC(d->dpy, dst_mask, 0, 0);
+ XSetClipOrigin(d->dpy, cgc, x, y);
+ XSetClipMask(d->dpy, cgc, src_mask);
+ if (src_mask) { // copy src mask into dst mask
+ XCopyArea(d->dpy, src_mask, dst_mask, cgc, sx, sy, sw, sh, x, y);
+ } else { // no src mask, but make sure the area copied is opaque in dest
+ XSetBackground(d->dpy, cgc, 0);
+ XSetForeground(d->dpy, cgc, 1);
+ XFillRectangle(d->dpy, dst_mask, cgc, x, y, sw, sh);
+ }
+ XFreeGC(d->dpy, cgc);
+ }
+ }
+
+ if (restore_clip) {
+ XSetClipOrigin(d->dpy, d->gc, 0, 0);
+ QVector<XRectangle> rects = qt_region_to_xrectangles(d->crgn);
+ if (rects.isEmpty())
+ XSetClipMask(d->dpy, d->gc, XNone);
+ else
+ XSetClipRectangles(d->dpy, d->gc, 0, 0, rects.data(), rects.size(), Unsorted);
+ }
+}
+
+void QX11PaintEngine::updateMatrix(const QTransform &mtx)
+{
+ Q_D(QX11PaintEngine);
+ d->txop = mtx.type();
+ d->matrix = mtx;
+
+ d->has_complex_xform = (d->txop > QTransform::TxTranslate);
+
+ extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale);
+ bool scaling = qt_scaleForTransform(d->matrix, &d->xform_scale);
+ d->has_scaling_xform = scaling && d->xform_scale != 1.0;
+ d->has_non_scaling_xform = scaling && d->xform_scale == 1.0;
+}
+
+/*
+ NB! the clip region is expected to be in dev coordinates
+*/
+void QX11PaintEngine::updateClipRegion_dev(const QRegion &clipRegion, Qt::ClipOperation op)
+{
+ Q_D(QX11PaintEngine);
+ QRegion sysClip = systemClip();
+ if (op == Qt::NoClip) {
+ d->has_clipping = false;
+ d->crgn = sysClip;
+ if (!sysClip.isEmpty()) {
+ x11SetClipRegion(d->dpy, d->gc, d->gc_brush, d->picture, sysClip);
+ } else {
+ x11ClearClipRegion(d->dpy, d->gc, d->gc_brush, d->picture);
+ }
+ return;
+ }
+
+ switch (op) {
+ case Qt::IntersectClip:
+ if (d->has_clipping) {
+ d->crgn &= clipRegion;
+ break;
+ }
+ // fall through
+ case Qt::ReplaceClip:
+ if (!sysClip.isEmpty())
+ d->crgn = clipRegion.intersected(sysClip);
+ else
+ d->crgn = clipRegion;
+ break;
+// case Qt::UniteClip:
+// d->crgn |= clipRegion;
+// if (!sysClip.isEmpty())
+// d->crgn = d->crgn.intersected(sysClip);
+// break;
+ default:
+ break;
+ }
+ d->has_clipping = true;
+ x11SetClipRegion(d->dpy, d->gc, d->gc_brush, d->picture, d->crgn);
+}
+
+void QX11PaintEngine::updateFont(const QFont &)
+{
+}
+
+Drawable QX11PaintEngine::handle() const
+{
+ Q_D(const QX11PaintEngine);
+ Q_ASSERT(isActive());
+ Q_ASSERT(d->hd);
+ return d->hd;
+}
+
+extern void qt_draw_tile(QPaintEngine *, qreal, qreal, qreal, qreal, const QPixmap &,
+ qreal, qreal);
+
+void QX11PaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &p)
+{
+ int x = qRound(r.x());
+ int y = qRound(r.y());
+ int w = qRound(r.width());
+ int h = qRound(r.height());
+ int sx = qRound(p.x());
+ int sy = qRound(p.y());
+
+ bool mono_src = pixmap.depth() == 1;
+ Q_D(QX11PaintEngine);
+
+ if ((d->xinfo && d->xinfo->screen() != qt_x11Info(pixmap).screen())
+ || (qt_x11Info(pixmap).screen() != DefaultScreen(QXcbX11Info::display()))) {
+ QPixmap* p = const_cast<QPixmap *>(&pixmap);
+ qt_x11SetScreen(*p, d->xinfo ? d->xinfo->screen() : DefaultScreen(QXcbX11Info::display()));
+ }
+
+ qt_x11SetDefaultScreen(qt_x11Info(pixmap).screen());
+
+#if QT_CONFIG(xrender)
+ if (X11->use_xrender && d->picture && qt_x11PictureHandle(pixmap)) {
+#if 0
+ // ### Qt 5: enable this
+ XRenderPictureAttributes attrs;
+ attrs.repeat = true;
+ XRenderChangePicture(d->dpy, pixmap.x11PictureHandle(), CPRepeat, &attrs);
+
+ if (mono_src) {
+ qt_render_bitmap(d->dpy, d->scrn, pixmap.x11PictureHandle(), d->picture,
+ sx, sy, x, y, w, h, d->cpen);
+ } else {
+ XRenderComposite(d->dpy, d->composition_mode,
+ pixmap.x11PictureHandle(), XNone, d->picture,
+ sx, sy, 0, 0, x, y, w, h);
+ }
+#else
+ const int numTiles = (w / pixmap.width()) * (h / pixmap.height());
+ if (numTiles < 100) {
+ // this is essentially qt_draw_tile(), inlined for
+ // the XRenderComposite call
+ int yPos, xPos, drawH, drawW, yOff, xOff;
+ yPos = y;
+ yOff = sy;
+ while (yPos < y + h) {
+ drawH = pixmap.height() - yOff; // Cropping first row
+ if (yPos + drawH > y + h) // Cropping last row
+ drawH = y + h - yPos;
+ xPos = x;
+ xOff = sx;
+ while (xPos < x + w) {
+ drawW = pixmap.width() - xOff; // Cropping first column
+ if (xPos + drawW > x + w) // Cropping last column
+ drawW = x + w - xPos;
+ if (mono_src) {
+ qt_render_bitmap(d->dpy, d->scrn, qt_x11PictureHandle(pixmap), d->picture,
+ xOff, yOff, xPos, yPos, drawW, drawH, d->cpen);
+ } else {
+ XRenderComposite(d->dpy, d->composition_mode,
+ qt_x11PictureHandle(pixmap), XNone, d->picture,
+ xOff, yOff, 0, 0, xPos, yPos, drawW, drawH);
+ }
+ xPos += drawW;
+ xOff = 0;
+ }
+ yPos += drawH;
+ yOff = 0;
+ }
+ } else {
+ w = qMin(w, d->pdev->width() - x);
+ h = qMin(h, d->pdev->height() - y);
+ if (w <= 0 || h <= 0)
+ return;
+
+ const int pw = w + sx;
+ const int ph = h + sy;
+ QPixmap pm(pw, ph);
+ if (pixmap.hasAlpha() || mono_src)
+ pm.fill(Qt::transparent);
+
+ const int mode = pixmap.hasAlpha() ? PictOpOver : PictOpSrc;
+ const ::Picture pmPicture = qt_x11PictureHandle(pm);
+
+ // first tile
+ XRenderComposite(d->dpy, mode,
+ qt_x11PictureHandle(pixmap), XNone, pmPicture,
+ 0, 0, 0, 0, 0, 0, qMin(pw, pixmap.width()), qMin(ph, pixmap.height()));
+
+ // first row of tiles
+ int xPos = pixmap.width();
+ const int sh = qMin(ph, pixmap.height());
+ while (xPos < pw) {
+ const int sw = qMin(xPos, pw - xPos);
+ XRenderComposite(d->dpy, mode,
+ pmPicture, XNone, pmPicture,
+ 0, 0, 0, 0, xPos, 0, sw, sh);
+ xPos *= 2;
+ }
+
+ // remaining rows
+ int yPos = pixmap.height();
+ const int sw = pw;
+ while (yPos < ph) {
+ const int sh = qMin(yPos, ph - yPos);
+ XRenderComposite(d->dpy, mode,
+ pmPicture, XNone, pmPicture,
+ 0, 0, 0, 0, 0, yPos, sw, sh);
+ yPos *= 2;
+ }
+
+ // composite
+ if (mono_src)
+ qt_render_bitmap(d->dpy, d->scrn, pmPicture, d->picture,
+ sx, sy, x, y, w, h, d->cpen);
+ else
+ XRenderComposite(d->dpy, d->composition_mode,
+ pmPicture, XNone, d->picture,
+ sx, sy, 0, 0, x, y, w, h);
+ }
+#endif
+ } else
+#endif // QT_CONFIG(xrender)
+ if (pixmap.depth() > 1 && !static_cast<QX11PlatformPixmap*>(pixmap.handle())->x11_mask) {
+ XSetTile(d->dpy, d->gc, qt_x11PixmapHandle(pixmap));
+ XSetFillStyle(d->dpy, d->gc, FillTiled);
+ XSetTSOrigin(d->dpy, d->gc, x-sx, y-sy);
+ XFillRectangle(d->dpy, d->hd, d->gc, x, y, w, h);
+ XSetTSOrigin(d->dpy, d->gc, 0, 0);
+ XSetFillStyle(d->dpy, d->gc, FillSolid);
+ } else {
+ qt_draw_tile(this, x, y, w, h, pixmap, sx, sy);
+ }
+}
+
+bool QX11PaintEngine::drawCachedGlyphs(const QTransform &transform, const QTextItemInt &ti)
+{
+#if QT_CONFIG(xrender)
+ Q_D(QX11PaintEngine);
+ Q_ASSERT(ti.fontEngine->type() == QFontEngine::Freetype);
+
+ if (!X11->use_xrender)
+ return false;
+
+ QFontEngineFT *ft = static_cast<QFontEngineFT *>(ti.fontEngine);
+ QFontEngineFT::QGlyphSet *set = ft->loadGlyphSet(transform);
+
+ if (!set || set->outline_drawing)
+ return false;
+
+ QFontEngine::GlyphFormat glyphFormat = QXRenderGlyphCache::glyphFormatForDepth(ft, d->pdev_depth);
+
+ QXRenderGlyphCache *cache = static_cast<QXRenderGlyphCache *>(ft->glyphCache(set, glyphFormat, transform));
+ if (!cache) {
+ cache = new QXRenderGlyphCache(QXcbX11Info(), glyphFormat, transform);
+ ft->setGlyphCache(set, cache);
+ }
+
+ return cache->draw(X11->getSolidFill(d->scrn, d->cpen.color()), d->picture, transform, ti);
+#else // !QT_CONFIG(xrender)
+ return false;
+#endif // QT_CONFIG(xrender)
+}
+
+void QX11PaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem)
+{
+ Q_D(QX11PaintEngine);
+ const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
+
+ switch (ti.fontEngine->type()) {
+ case QFontEngine::TestFontEngine:
+ case QFontEngine::Box:
+ d->drawBoxTextItem(p, ti);
+ break;
+#if QT_CONFIG(fontconfig)
+ case QFontEngine::Freetype:
+ drawFreetype(p, ti);
+ break;
+#endif
+ default:
+ Q_ASSERT(false);
+ }
+}
+
+#if QT_CONFIG(fontconfig)
+static bool path_for_glyphs(QPainterPath *path,
+ const QVarLengthArray<glyph_t> &glyphs,
+ const QVarLengthArray<QFixedPoint> &positions,
+ const QFontEngineFT *ft)
+{
+ bool result = true;
+ *path = QPainterPath();
+ path->setFillRule(Qt::WindingFill);
+ ft->lockFace();
+ int i = 0;
+ while (i < glyphs.size()) {
+ QFontEngineFT::Glyph *glyph = ft->loadGlyph(glyphs[i], 0, QFontEngineFT::Format_Mono);
+ // #### fix case where we don't get a glyph
+ if (!glyph || glyph->format != QFontEngineFT::Format_Mono) {
+ result = false;
+ break;
+ }
+
+ int n = 0;
+ int h = glyph->height;
+ int xp = qRound(positions[i].x);
+ int yp = qRound(positions[i].y);
+
+ xp += glyph->x;
+ yp += -glyph->y + glyph->height;
+ int pitch = ((glyph->width + 31) & ~31) >> 3;
+
+ uchar *src = glyph->data;
+ while (h--) {
+ for (int x = 0; x < glyph->width; ++x) {
+ bool set = src[x >> 3] & (0x80 >> (x & 7));
+ if (set) {
+ QRect r(xp + x, yp - h, 1, 1);
+ while (x+1 < glyph->width && src[(x+1) >> 3] & (0x80 >> ((x+1) & 7))) {
+ ++x;
+ r.setRight(r.right()+1);
+ }
+
+ path->addRect(r);
+ ++n;
+ }
+ }
+ src += pitch;
+ }
+ ++i;
+ }
+ ft->unlockFace();
+ return result;
+}
+
+void QX11PaintEngine::drawFreetype(const QPointF &p, const QTextItemInt &ti)
+{
+ Q_D(QX11PaintEngine);
+
+ if (!ti.glyphs.numGlyphs)
+ return;
+
+ if (!d->cpen.isSolid()) {
+ QPaintEngine::drawTextItem(p, ti);
+ return;
+ }
+
+ const bool xrenderPath = (X11->use_xrender
+ && !(d->pdev->devType() == QInternal::Pixmap
+ && static_cast<const QPixmap *>(d->pdev)->handle()->pixelType() == QPlatformPixmap::BitmapType));
+
+ if (xrenderPath) {
+ QTransform transform = d->matrix;
+ transform.translate(p.x(), p.y());
+
+ if (drawCachedGlyphs(transform, ti))
+ return;
+ }
+
+ QTransform transform;
+ transform.translate(p.x(), p.y());
+
+ QVarLengthArray<QFixedPoint> positions;
+ QVarLengthArray<glyph_t> glyphs;
+ ti.fontEngine->getGlyphPositions(ti.glyphs, transform, ti.flags, glyphs, positions);
+
+ if (glyphs.count() == 0)
+ return;
+
+ QFontEngineFT *ft = static_cast<QFontEngineFT *>(ti.fontEngine);
+ QFontEngineFT::QGlyphSet *set = ft->loadGlyphSet(transform);
+ QPainterPath path;
+
+ if (!set || set->outline_drawing || !path_for_glyphs(&path, glyphs, positions, ft)) {
+ QPaintEngine::drawTextItem(p, ti);
+ return;
+ }
+
+ if (path.elementCount() <= 1)
+ return;
+
+ Q_ASSERT((path.elementCount() % 5) == 0);
+ if (d->txop >= QTransform::TxScale) {
+ painter()->save();
+ painter()->setBrush(d->cpen.brush());
+ painter()->setPen(Qt::NoPen);
+ painter()->drawPath(path);
+ painter()->restore();
+ return;
+ }
+
+ const int rectcount = 256;
+ XRectangle rects[rectcount];
+ int num_rects = 0;
+
+ QPoint delta(qRound(d->matrix.dx()), qRound(d->matrix.dy()));
+ QRect clip(d->polygonClipper.boundingRect());
+ for (int i=0; i < path.elementCount(); i+=5) {
+ int x = qRound(path.elementAt(i).x);
+ int y = qRound(path.elementAt(i).y);
+ int w = qRound(path.elementAt(i+1).x) - x;
+ int h = qRound(path.elementAt(i+2).y) - y;
+
+ QRect rect = QRect(x + delta.x(), y + delta.y(), w, h);
+ rect = rect.intersected(clip);
+ if (rect.isEmpty())
+ continue;
+
+ rects[num_rects].x = short(rect.x());
+ rects[num_rects].y = short(rect.y());
+ rects[num_rects].width = ushort(rect.width());
+ rects[num_rects].height = ushort(rect.height());
+ ++num_rects;
+ if (num_rects == rectcount) {
+ XFillRectangles(d->dpy, d->hd, d->gc, rects, num_rects);
+ num_rects = 0;
+ }
+ }
+ if (num_rects > 0)
+ XFillRectangles(d->dpy, d->hd, d->gc, rects, num_rects);
+}
+#endif // QT_CONFIG(fontconfig)
+
+#if QT_CONFIG(xrender)
+QXRenderGlyphCache::QXRenderGlyphCache(QXcbX11Info x, QFontEngine::GlyphFormat format, const QTransform &matrix)
+ : QFontEngineGlyphCache(format, matrix)
+ , xinfo(x)
+ , gset(XNone)
+{}
+
+QXRenderGlyphCache::~QXRenderGlyphCache()
+{
+ if (gset != XNone)
+ XRenderFreeGlyphSet(xinfo.display(), gset);
+}
+
+bool QXRenderGlyphCache::addGlyphs(const QTextItemInt &ti, QVarLengthArray<glyph_t> glyphs, QVarLengthArray<QFixedPoint> positions)
+{
+ Q_ASSERT(ti.fontEngine->type() == QFontEngine::Freetype);
+
+ QFontEngineFT *ft = static_cast<QFontEngineFT *>(ti.fontEngine);
+ QFontEngineFT::QGlyphSet *set = ft->loadGlyphSet(transform());
+
+ XGlyphInfo xglyphinfo;
+
+ for (int i = 0; i < glyphs.size(); ++i) {
+ const QFixed spp = ft->subPixelPositionForX(positions[i].x);
+ QFontEngineFT::Glyph *glyph = set->getGlyph(glyphs[i], spp);
+ Glyph xglyphid = qHash(QFontEngineFT::GlyphAndSubPixelPosition(glyphs[i], spp));
+
+ if (glyph && glyph->format == glyphFormat()) {
+ if (cachedGlyphs.contains(xglyphid)) {
+ continue;
+ } else {
+ set->setGlyph(glyphs[i], spp, nullptr);
+ delete glyph;
+ glyph = 0;
+ }
+ }
+
+ glyph = ft->loadGlyphFor(glyphs[i], spp, glyphFormat(), transform());
+
+ if (glyph == 0 || glyph->format != glyphFormat())
+ return false;
+
+ set->setGlyph(glyphs[i], spp, glyph);
+ Q_ASSERT(glyph->data || glyph->width == 0 || glyph->height == 0);
+
+ xglyphinfo.width = glyph->width;
+ xglyphinfo.height = glyph->height;
+ xglyphinfo.x = -glyph->x;
+ xglyphinfo.y = glyph->y;
+ xglyphinfo.xOff = glyph->advance;
+ xglyphinfo.yOff = 0;
+
+ XRenderAddGlyphs(xinfo.display(), glyphSet(), &xglyphid, &xglyphinfo, 1, (const char *) glyph->data, glyphBufferSize(*glyph));
+ cachedGlyphs.insert(xglyphid);
+ }
+
+ return true;
+}
+
+bool QXRenderGlyphCache::draw(Drawable src, Drawable dst, const QTransform &matrix, const QTextItemInt &ti)
+{
+ Q_ASSERT(ti.fontEngine->type() == QFontEngine::Freetype);
+
+ if (ti.glyphs.numGlyphs == 0)
+ return true;
+
+ QFontEngineFT *ft = static_cast<QFontEngineFT *>(ti.fontEngine);
+ QFontEngineFT::QGlyphSet *set = ft->loadGlyphSet(matrix);
+
+ QVarLengthArray<glyph_t> glyphs;
+ QVarLengthArray<QFixedPoint> positions;
+ ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
+
+ if (glyphs.isEmpty())
+ return true;
+
+ if (!addGlyphs(ti, glyphs, positions))
+ return false;
+
+ QVarLengthArray<unsigned int> chars(glyphs.size());
+
+ for (int i = 0; i < glyphs.size(); ++i)
+ chars[i] = glyphId(glyphs[i], ft->subPixelPositionForX(positions[i].x));
+
+ int i = 0;
+ while (i < glyphs.size() && !isValidCoordinate(positions[i]))
+ ++i;
+
+ if (i >= glyphs.size())
+ return true;
+
+ QFixed xp = positions[i].x;
+ QFixed yp = positions[i].y;
+ QFixed offs = QFixed::fromReal(aliasedCoordinateDelta);
+
+ XGlyphElt32 elt;
+ elt.glyphset = gset;
+ elt.chars = &chars[i];
+ elt.nchars = 1;
+ elt.xOff = qRound(xp + offs);
+ elt.yOff = qRound(yp + offs);
+
+ ++i;
+
+ for (; i < glyphs.size(); ++i) {
+ if (!isValidCoordinate(positions[i]))
+ break;
+
+ const QFixed spp = ft->subPixelPositionForX(positions[i].x);
+ QFontEngineFT::Glyph *g = set->getGlyph(glyphs[i], spp);
+
+ if (g
+ && positions[i].x == xp + g->advance
+ && positions[i].y == yp
+ && elt.nchars < 253 // don't draw more than 253 characters as some X servers
+ // hang with it
+ ) {
+ elt.nchars++;
+ xp += g->advance;
+ } else {
+ xp = positions[i].x;
+ yp = positions[i].y;
+
+ XRenderCompositeText32(xinfo.display(), PictOpOver, src, dst,
+ renderPictFormat(), 0, 0, 0, 0,
+ &elt, 1);
+ elt.chars = &chars[i];
+ elt.nchars = 1;
+ elt.xOff = qRound(xp + offs);
+ elt.yOff = qRound(yp + offs);
+ }
+ }
+
+ XRenderCompositeText32(xinfo.display(), PictOpOver, src, dst,
+ renderPictFormat(), 0, 0, 0, 0, &elt, 1);
+
+ return true;
+}
+
+GlyphSet QXRenderGlyphCache::glyphSet()
+{
+ if (gset == XNone)
+ gset = XRenderCreateGlyphSet(xinfo.display(), renderPictFormat());
+
+ Q_ASSERT(gset != XNone);
+ return gset;
+}
+
+int QXRenderGlyphCache::glyphBufferSize(const QFontEngineFT::Glyph &glyph) const
+{
+ int pitch = 0;
+
+ switch (glyphFormat()) {
+ case QFontEngine::Format_Mono:
+ pitch = ((glyph.width + 31) & ~31) >> 3;
+ break;
+ case QFontEngine::Format_A8:
+ pitch = (glyph.width + 3) & ~3;
+ break;
+ default:
+ pitch = glyph.width * 4;
+ break;
+ }
+
+ return pitch * glyph.height;
+}
+
+QImage::Format QXRenderGlyphCache::imageFormat() const
+{
+ switch (glyphFormat()) {
+ case QFontEngine::Format_None:
+ Q_UNREACHABLE();
+ break;
+ case QFontEngine::Format_Mono:
+ return QImage::Format_Mono;
+ break;
+ case QFontEngine::Format_A8:
+ return QImage::Format_Alpha8;
+ break;
+ case QFontEngine::Format_A32:
+ case QFontEngine::Format_ARGB:
+ return QImage::Format_ARGB32_Premultiplied;
+ break;
+ }
+
+ Q_UNREACHABLE();
+}
+
+const XRenderPictFormat *QXRenderGlyphCache::renderPictFormat() const
+{
+ switch (glyphFormat()) {
+ case QFontEngine::Format_None:
+ Q_UNREACHABLE();
+ break;
+ case QFontEngine::Format_Mono:
+ return XRenderFindStandardFormat(xinfo.display(), PictStandardA1);
+ break;
+ case QFontEngine::Format_A8:
+ return XRenderFindStandardFormat(xinfo.display(), PictStandardA8);
+ break;
+ case QFontEngine::Format_A32:
+ case QFontEngine::Format_ARGB:
+ return XRenderFindStandardFormat(xinfo.display(), PictStandardARGB32);
+ break;
+ }
+
+ Q_UNREACHABLE();
+}
+
+QFontEngine::GlyphFormat QXRenderGlyphCache::glyphFormatForDepth(QFontEngine *fontEngine, int depth)
+{
+ QFontEngine::GlyphFormat glyphFormat = fontEngine->glyphFormat;
+
+ if (glyphFormat == QFontEngine::Format_None) {
+ switch (depth) {
+ case 32:
+ glyphFormat = QFontEngine::Format_ARGB;
+ break;
+ case 24:
+ glyphFormat = QFontEngine::Format_A32;
+ break;
+ case 1:
+ glyphFormat = QFontEngine::Format_Mono;
+ break;
+ default:
+ glyphFormat = QFontEngine::Format_A8;
+ break;
+ }
+ }
+
+ return glyphFormat;
+}
+
+Glyph QXRenderGlyphCache::glyphId(glyph_t glyph, QFixed subPixelPosition)
+{
+ return qHash(QFontEngineFT::GlyphAndSubPixelPosition(glyph, subPixelPosition));
+}
+
+bool QXRenderGlyphCache::isValidCoordinate(const QFixedPoint &fp)
+{
+ enum { t_min = SHRT_MIN, t_max = SHRT_MAX };
+ return (fp.x < t_min || fp.x > t_max || fp.y < t_min || fp.y > t_max) ? false : true;
+}
+#endif // QT_CONFIG(xrender)
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11_p.h b/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11_p.h
new file mode 100644
index 0000000000..9b01c0a3fc
--- /dev/null
+++ b/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11_p.h
@@ -0,0 +1,124 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPAINTENGINE_X11_H
+#define QPAINTENGINE_X11_H
+
+#include <QtGui/QPaintEngine>
+
+typedef unsigned long XID;
+typedef XID Drawable;
+typedef struct _XGC *GC;
+
+QT_BEGIN_NAMESPACE
+
+extern "C" {
+Drawable qt_x11Handle(const QPaintDevice *pd);
+GC qt_x11_get_pen_gc(QPainter *);
+GC qt_x11_get_brush_gc(QPainter *);
+}
+
+class QX11PaintEnginePrivate;
+class QX11PaintEngine : public QPaintEngine
+{
+ Q_DECLARE_PRIVATE(QX11PaintEngine)
+public:
+ QX11PaintEngine();
+ ~QX11PaintEngine();
+
+ bool begin(QPaintDevice *pdev) override;
+ bool end() override;
+
+ void updateState(const QPaintEngineState &state) override;
+
+ void updatePen(const QPen &pen);
+ void updateBrush(const QBrush &brush, const QPointF &pt);
+ void updateRenderHints(QPainter::RenderHints hints);
+ void updateFont(const QFont &font);
+ void updateMatrix(const QTransform &matrix);
+ void updateClipRegion_dev(const QRegion &region, Qt::ClipOperation op);
+
+ void drawLines(const QLine *lines, int lineCount) override;
+ void drawLines(const QLineF *lines, int lineCount) override;
+
+ void drawRects(const QRect *rects, int rectCount) override;
+ void drawRects(const QRectF *rects, int rectCount) override;
+
+ void drawPoints(const QPoint *points, int pointCount) override;
+ void drawPoints(const QPointF *points, int pointCount) override;
+
+ void drawEllipse(const QRect &r) override;
+ void drawEllipse(const QRectF &r) override;
+
+ virtual void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode) override;
+ inline void drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode) override
+ { QPaintEngine::drawPolygon(points, pointCount, mode); }
+
+ void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) override;
+ void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s) override;
+ void drawPath(const QPainterPath &path) override;
+ void drawTextItem(const QPointF &p, const QTextItem &textItem) override;
+ void drawImage(const QRectF &r, const QImage &img, const QRectF &sr,
+ Qt::ImageConversionFlags flags = Qt::AutoColor) override;
+
+ virtual Drawable handle() const;
+ inline Type type() const override { return QPaintEngine::X11; }
+
+ QPainter::RenderHints supportedRenderHints() const;
+
+protected:
+ QX11PaintEngine(QX11PaintEnginePrivate &dptr);
+
+#if QT_CONFIG(fontconfig)
+ void drawFreetype(const QPointF &p, const QTextItemInt &ti);
+ bool drawCachedGlyphs(const QTransform &transform, const QTextItemInt &ti);
+#endif // QT_CONFIG(fontconfig)
+
+ friend class QPixmap;
+ friend class QFontEngineBox;
+ friend GC qt_x11_get_pen_gc(QPainter *);
+ friend GC qt_x11_get_brush_gc(QPainter *);
+
+private:
+ Q_DISABLE_COPY(QX11PaintEngine)
+};
+
+QT_END_NAMESPACE
+
+#endif // QPAINTENGINE_X11_H
diff --git a/src/plugins/platforms/xcb/nativepainting/qpixmap_x11.cpp b/src/plugins/platforms/xcb/nativepainting/qpixmap_x11.cpp
new file mode 100644
index 0000000000..86c87e5e30
--- /dev/null
+++ b/src/plugins/platforms/xcb/nativepainting/qpixmap_x11.cpp
@@ -0,0 +1,2114 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QGuiApplication>
+
+#include <private/qdrawhelper_p.h>
+#include <private/qimage_p.h>
+#include <private/qimagepixmapcleanuphooks_p.h>
+
+#include "qxcbnativepainting.h"
+#include "qpixmap_x11_p.h"
+#include "qcolormap_x11_p.h"
+#include "qpaintengine_x11_p.h"
+
+QT_BEGIN_NAMESPACE
+
+#if QT_POINTER_SIZE == 8 // 64-bit versions
+
+Q_ALWAYS_INLINE uint PREMUL(uint x) {
+ uint a = x >> 24;
+ quint64 t = (((quint64(x)) | ((quint64(x)) << 24)) & 0x00ff00ff00ff00ff) * a;
+ t = (t + ((t >> 8) & 0xff00ff00ff00ff) + 0x80008000800080) >> 8;
+ t &= 0x000000ff00ff00ff;
+ return (uint(t)) | (uint(t >> 24)) | (a << 24);
+}
+
+#else // 32-bit versions
+
+Q_ALWAYS_INLINE uint PREMUL(uint x) {
+ uint a = x >> 24;
+ uint t = (x & 0xff00ff) * a;
+ t = (t + ((t >> 8) & 0xff00ff) + 0x800080) >> 8;
+ t &= 0xff00ff;
+
+ x = ((x >> 8) & 0xff) * a;
+ x = (x + ((x >> 8) & 0xff) + 0x80);
+ x &= 0xff00;
+ x |= t | (a << 24);
+ return x;
+}
+#endif
+
+
+
+struct QXImageWrapper
+{
+ XImage *xi;
+};
+
+QPixmap qt_toX11Pixmap(const QImage &image)
+{
+ QPlatformPixmap *data =
+ new QX11PlatformPixmap(image.depth() == 1
+ ? QPlatformPixmap::BitmapType
+ : QPlatformPixmap::PixmapType);
+
+ data->fromImage(image, Qt::AutoColor);
+
+ return QPixmap(data);
+}
+
+QPixmap qt_toX11Pixmap(const QPixmap &pixmap)
+{
+ if (pixmap.isNull())
+ return QPixmap();
+
+ if (QPixmap(pixmap).data_ptr()->classId() == QPlatformPixmap::X11Class)
+ return pixmap;
+
+ return qt_toX11Pixmap(pixmap.toImage());
+}
+
+// For thread-safety:
+// image->data does not belong to X11, so we must free it ourselves.
+
+inline static void qSafeXDestroyImage(XImage *x)
+{
+ if (x->data) {
+ free(x->data);
+ x->data = 0;
+ }
+ XDestroyImage(x);
+}
+
+QBitmap QX11PlatformPixmap::mask_to_bitmap(int screen) const
+{
+ if (!x11_mask)
+ return QBitmap();
+ qt_x11SetDefaultScreen(screen);
+ QBitmap bm(w, h);
+ QX11PlatformPixmap *that = qt_x11Pixmap(bm);
+ const QXcbX11Info *x = that->x11_info();
+ GC gc = XCreateGC(x->display(), that->handle(), 0, 0);
+ XCopyArea(x->display(), x11_mask, that->handle(), gc, 0, 0,
+ that->width(), that->height(), 0, 0);
+ XFreeGC(x->display(), gc);
+ return bm;
+}
+
+void QX11PlatformPixmap::bitmapFromImage(const QImage &image)
+{
+ w = image.width();
+ h = image.height();
+ d = 1;
+ is_null = (w <= 0 || h <= 0);
+ hd = createBitmapFromImage(image);
+#if QT_CONFIG(xrender)
+ if (X11->use_xrender)
+ picture = XRenderCreatePicture(xinfo.display(), hd,
+ XRenderFindStandardFormat(xinfo.display(), PictStandardA1), 0, 0);
+#endif // QT_CONFIG(xrender)
+}
+
+bool QX11PlatformPixmap::canTakeQImageFromXImage(const QXImageWrapper &xiWrapper) const
+{
+ XImage *xi = xiWrapper.xi;
+
+ if (xi->format != ZPixmap)
+ return false;
+
+ // ARGB32_Premultiplied
+ if (picture && depth() == 32)
+ return true;
+
+ // RGB32
+ if (depth() == 24 && xi->bits_per_pixel == 32 && xi->red_mask == 0xff0000
+ && xi->green_mask == 0xff00 && xi->blue_mask == 0xff)
+ return true;
+
+ // RGB16
+ if (depth() == 16 && xi->bits_per_pixel == 16 && xi->red_mask == 0xf800
+ && xi->green_mask == 0x7e0 && xi->blue_mask == 0x1f)
+ return true;
+
+ return false;
+}
+
+QImage QX11PlatformPixmap::takeQImageFromXImage(const QXImageWrapper &xiWrapper) const
+{
+ XImage *xi = xiWrapper.xi;
+
+ QImage::Format format = QImage::Format_ARGB32_Premultiplied;
+ if (depth() == 24)
+ format = QImage::Format_RGB32;
+ else if (depth() == 16)
+ format = QImage::Format_RGB16;
+
+ QImage image((uchar *)xi->data, xi->width, xi->height, xi->bytes_per_line, format);
+ image.setDevicePixelRatio(devicePixelRatio());
+ // take ownership
+ image.data_ptr()->own_data = true;
+ xi->data = 0;
+
+ // we may have to swap the byte order
+ if ((QSysInfo::ByteOrder == QSysInfo::LittleEndian && xi->byte_order == MSBFirst)
+ || (QSysInfo::ByteOrder == QSysInfo::BigEndian && xi->byte_order == LSBFirst))
+ {
+ for (int i=0; i < image.height(); i++) {
+ if (depth() == 16) {
+ ushort *p = (ushort*)image.scanLine(i);
+ ushort *end = p + image.width();
+ while (p < end) {
+ *p = ((*p << 8) & 0xff00) | ((*p >> 8) & 0x00ff);
+ p++;
+ }
+ } else {
+ uint *p = (uint*)image.scanLine(i);
+ uint *end = p + image.width();
+ while (p < end) {
+ *p = ((*p << 24) & 0xff000000) | ((*p << 8) & 0x00ff0000)
+ | ((*p >> 8) & 0x0000ff00) | ((*p >> 24) & 0x000000ff);
+ p++;
+ }
+ }
+ }
+ }
+
+ // fix-up alpha channel
+ if (format == QImage::Format_RGB32) {
+ QRgb *p = (QRgb *)image.bits();
+ for (int y = 0; y < xi->height; ++y) {
+ for (int x = 0; x < xi->width; ++x)
+ p[x] |= 0xff000000;
+ p += xi->bytes_per_line / 4;
+ }
+ }
+
+ XDestroyImage(xi);
+ return image;
+}
+
+XID QX11PlatformPixmap::bitmap_to_mask(const QBitmap &bitmap, int screen)
+{
+ if (bitmap.isNull())
+ return 0;
+ QBitmap bm = bitmap;
+ qt_x11SetScreen(bm, screen);
+
+ QX11PlatformPixmap *that = qt_x11Pixmap(bm);
+ const QXcbX11Info *x = that->x11_info();
+ Pixmap mask = XCreatePixmap(x->display(), RootWindow(x->display(), screen),
+ that->width(), that->height(), 1);
+ GC gc = XCreateGC(x->display(), mask, 0, 0);
+ XCopyArea(x->display(), that->handle(), mask, gc, 0, 0,
+ that->width(), that->height(), 0, 0);
+ XFreeGC(x->display(), gc);
+ return mask;
+}
+
+Drawable qt_x11Handle(const QPixmap &pixmap)
+{
+ if (pixmap.isNull())
+ return XNone;
+
+ if (pixmap.handle()->classId() != QPlatformPixmap::X11Class)
+ return XNone;
+
+ return static_cast<const QX11PlatformPixmap *>(pixmap.handle())->handle();
+}
+
+
+/*****************************************************************************
+ Internal functions
+ *****************************************************************************/
+
+//extern const uchar *qt_get_bitflip_array(); // defined in qimage.cpp
+
+// Returns position of highest bit set or -1 if none
+static int highest_bit(uint v)
+{
+ int i;
+ uint b = (uint)1 << 31;
+ for (i=31; ((b & v) == 0) && i>=0; i--)
+ b >>= 1;
+ return i;
+}
+
+// Counts the number of bits set in 'v'
+static uint n_bits(uint v)
+{
+ int i = 0;
+ while (v) {
+ v = v & (v - 1);
+ i++;
+ }
+ return i;
+}
+
+static uint *red_scale_table = 0;
+static uint *green_scale_table = 0;
+static uint *blue_scale_table = 0;
+
+static void cleanup_scale_tables()
+{
+ delete[] red_scale_table;
+ delete[] green_scale_table;
+ delete[] blue_scale_table;
+}
+
+/*
+ Could do smart bitshifting, but the "obvious" algorithm only works for
+ nBits >= 4. This is more robust.
+*/
+static void build_scale_table(uint **table, uint nBits)
+{
+ if (nBits > 7) {
+ qWarning("build_scale_table: internal error, nBits = %i", nBits);
+ return;
+ }
+ if (!*table) {
+ static bool firstTable = true;
+ if (firstTable) {
+ qAddPostRoutine(cleanup_scale_tables);
+ firstTable = false;
+ }
+ *table = new uint[256];
+ }
+ int maxVal = (1 << nBits) - 1;
+ int valShift = 8 - nBits;
+ int i;
+ for (i = 0 ; i < maxVal + 1 ; i++)
+ (*table)[i << valShift] = i*255/maxVal;
+}
+
+static int defaultScreen = -1;
+
+int qt_x11SetDefaultScreen(int screen)
+{
+ int old = defaultScreen;
+ defaultScreen = screen;
+ return old;
+}
+
+void qt_x11SetScreen(QPixmap &pixmap, int screen)
+{
+ if (pixmap.paintingActive()) {
+ qWarning("qt_x11SetScreen(): Cannot change screens during painting");
+ return;
+ }
+
+ if (pixmap.isNull())
+ return;
+
+ if (pixmap.handle()->classId() != QPlatformPixmap::X11Class)
+ return;
+
+ if (screen < 0)
+ screen = QXcbX11Info::appScreen();
+
+ QX11PlatformPixmap *pm = static_cast<QX11PlatformPixmap *>(pixmap.handle());
+ if (screen == pm->xinfo.screen())
+ return; // nothing to do
+
+ if (pixmap.isNull()) {
+ pm->xinfo = QXcbX11Info::fromScreen(screen);
+ return;
+ }
+
+#if 0
+ qDebug("qt_x11SetScreen for %p from %d to %d. Size is %d/%d", pm, pm->xinfo.screen(), screen, pm->width(), pm->height());
+#endif
+
+ qt_x11SetDefaultScreen(screen);
+ pixmap = qt_toX11Pixmap(pixmap.toImage());
+}
+
+/*****************************************************************************
+ QPixmap member functions
+ *****************************************************************************/
+
+QBasicAtomicInt qt_pixmap_serial = Q_BASIC_ATOMIC_INITIALIZER(0);
+int Q_GUI_EXPORT qt_x11_preferred_pixmap_depth = 0;
+
+QX11PlatformPixmap::QX11PlatformPixmap(PixelType pixelType)
+ : QPlatformPixmap(pixelType, X11Class), hd(0),
+ flags(Uninitialized), x11_mask(0), picture(0), mask_picture(0), hd2(0),
+ dpr(1.0), pengine(0)
+{}
+
+QX11PlatformPixmap::~QX11PlatformPixmap()
+{
+ // Cleanup hooks have to be called before the handles are freed
+ if (is_cached) {
+ QImagePixmapCleanupHooks::executePlatformPixmapDestructionHooks(this);
+ is_cached = false;
+ }
+
+ release();
+}
+
+QPlatformPixmap *QX11PlatformPixmap::createCompatiblePlatformPixmap() const
+{
+ QX11PlatformPixmap *p = new QX11PlatformPixmap(pixelType());
+ p->setDevicePixelRatio(devicePixelRatio());
+ return p;
+}
+
+void QX11PlatformPixmap::resize(int width, int height)
+{
+ setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1));
+
+ w = width;
+ h = height;
+ is_null = (w <= 0 || h <= 0);
+
+ if (defaultScreen >= 0 && defaultScreen != xinfo.screen()) {
+ xinfo = QXcbX11Info::fromScreen(defaultScreen);
+ }
+
+ int dd = xinfo.depth();
+
+ if (qt_x11_preferred_pixmap_depth)
+ dd = qt_x11_preferred_pixmap_depth;
+
+ bool make_null = w <= 0 || h <= 0; // create null pixmap
+ d = (pixelType() == BitmapType ? 1 : dd);
+ if (make_null || d == 0) {
+ w = 0;
+ h = 0;
+ is_null = true;
+ hd = 0;
+ picture = 0;
+ d = 0;
+ if (!make_null)
+ qWarning("QPixmap: Invalid pixmap parameters");
+ return;
+ }
+ hd = XCreatePixmap(xinfo.display(),
+ RootWindow(xinfo.display(), xinfo.screen()),
+ w, h, d);
+#if QT_CONFIG(xrender)
+ if (X11->use_xrender) {
+ XRenderPictFormat *format = d == 1
+ ? XRenderFindStandardFormat(xinfo.display(), PictStandardA1)
+ : XRenderFindVisualFormat(xinfo.display(), (Visual *) xinfo.visual());
+ picture = XRenderCreatePicture(xinfo.display(), hd, format, 0, 0);
+ }
+#endif // QT_CONFIG(xrender)
+}
+
+struct QX11AlphaDetector
+{
+ bool hasAlpha() const {
+ if (checked)
+ return has;
+ // Will implicitly also check format and return quickly for opaque types...
+ checked = true;
+ has = image->isNull() ? false : const_cast<QImage *>(image)->data_ptr()->checkForAlphaPixels();
+ return has;
+ }
+
+ bool hasXRenderAndAlpha() const {
+ if (!X11->use_xrender)
+ return false;
+ return hasAlpha();
+ }
+
+ QX11AlphaDetector(const QImage *i, Qt::ImageConversionFlags flags)
+ : image(i), checked(false), has(false)
+ {
+ if (flags & Qt::NoOpaqueDetection) {
+ checked = true;
+ has = image->hasAlphaChannel();
+ }
+ }
+
+ const QImage *image;
+ mutable bool checked;
+ mutable bool has;
+};
+
+void QX11PlatformPixmap::fromImage(const QImage &img, Qt::ImageConversionFlags flags)
+{
+ setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1));
+
+ w = img.width();
+ h = img.height();
+ d = img.depth();
+ is_null = (w <= 0 || h <= 0);
+ setDevicePixelRatio(img.devicePixelRatio());
+
+ if (is_null) {
+ w = h = 0;
+ return;
+ }
+
+ if (defaultScreen >= 0 && defaultScreen != xinfo.screen()) {
+ xinfo = QXcbX11Info::fromScreen(defaultScreen);
+ }
+
+ if (pixelType() == BitmapType) {
+ bitmapFromImage(img);
+ return;
+ }
+
+ if (uint(w) >= 32768 || uint(h) >= 32768) {
+ w = h = 0;
+ is_null = true;
+ return;
+ }
+
+ QX11AlphaDetector alphaCheck(&img, flags);
+ int dd = alphaCheck.hasXRenderAndAlpha() ? 32 : xinfo.depth();
+
+ if (qt_x11_preferred_pixmap_depth)
+ dd = qt_x11_preferred_pixmap_depth;
+
+ QImage image = img;
+
+ // must be monochrome
+ if (dd == 1 || (flags & Qt::ColorMode_Mask) == Qt::MonoOnly) {
+ if (d != 1) {
+ // dither
+ image = image.convertToFormat(QImage::Format_MonoLSB, flags);
+ d = 1;
+ }
+ } else { // can be both
+ bool conv8 = false;
+ if (d > 8 && dd <= 8) { // convert to 8 bit
+ if ((flags & Qt::DitherMode_Mask) == Qt::AutoDither)
+ flags = (flags & ~Qt::DitherMode_Mask)
+ | Qt::PreferDither;
+ conv8 = true;
+ } else if ((flags & Qt::ColorMode_Mask) == Qt::ColorOnly) {
+ conv8 = (d == 1); // native depth wanted
+ } else if (d == 1) {
+ if (image.colorCount() == 2) {
+ QRgb c0 = image.color(0); // Auto: convert to best
+ QRgb c1 = image.color(1);
+ conv8 = qMin(c0,c1) != qRgb(0,0,0) || qMax(c0,c1) != qRgb(255,255,255);
+ } else {
+ // eg. 1-color monochrome images (they do exist).
+ conv8 = true;
+ }
+ }
+ if (conv8) {
+ image = image.convertToFormat(QImage::Format_Indexed8, flags);
+ d = 8;
+ }
+ }
+
+ if (d == 1 || image.format() > QImage::Format_ARGB32_Premultiplied) {
+ QImage::Format fmt = QImage::Format_RGB32;
+ if (alphaCheck.hasXRenderAndAlpha() && d > 1)
+ fmt = QImage::Format_ARGB32_Premultiplied;
+ image = image.convertToFormat(fmt, flags);
+ fromImage(image, Qt::AutoColor);
+ return;
+ }
+
+ Display *dpy = xinfo.display();
+ Visual *visual = (Visual *)xinfo.visual();
+ XImage *xi = 0;
+ bool trucol = (visual->c_class >= TrueColor);
+ size_t nbytes = image.sizeInBytes();
+ uchar *newbits= 0;
+
+#if QT_CONFIG(xrender)
+ if (alphaCheck.hasXRenderAndAlpha()) {
+ const QImage &cimage = image;
+
+ d = 32;
+
+ if (QXcbX11Info::appDepth() != d) {
+ xinfo.setDepth(d);
+ }
+
+ hd = XCreatePixmap(dpy, RootWindow(dpy, xinfo.screen()), w, h, d);
+ picture = XRenderCreatePicture(dpy, hd,
+ XRenderFindStandardFormat(dpy, PictStandardARGB32), 0, 0);
+
+ xi = XCreateImage(dpy, visual, d, ZPixmap, 0, 0, w, h, 32, 0);
+ Q_CHECK_PTR(xi);
+ newbits = (uchar *)malloc(xi->bytes_per_line*h);
+ Q_CHECK_PTR(newbits);
+ xi->data = (char *)newbits;
+
+ switch (cimage.format()) {
+ case QImage::Format_Indexed8: {
+ QVector<QRgb> colorTable = cimage.colorTable();
+ uint *xidata = (uint *)xi->data;
+ for (int y = 0; y < h; ++y) {
+ const uchar *p = cimage.scanLine(y);
+ for (int x = 0; x < w; ++x) {
+ const QRgb rgb = colorTable[p[x]];
+ const int a = qAlpha(rgb);
+ if (a == 0xff)
+ *xidata = rgb;
+ else
+ // RENDER expects premultiplied alpha
+ *xidata = qRgba(qt_div_255(qRed(rgb) * a),
+ qt_div_255(qGreen(rgb) * a),
+ qt_div_255(qBlue(rgb) * a),
+ a);
+ ++xidata;
+ }
+ }
+ }
+ break;
+ case QImage::Format_RGB32: {
+ uint *xidata = (uint *)xi->data;
+ for (int y = 0; y < h; ++y) {
+ const QRgb *p = (const QRgb *) cimage.scanLine(y);
+ for (int x = 0; x < w; ++x)
+ *xidata++ = p[x] | 0xff000000;
+ }
+ }
+ break;
+ case QImage::Format_ARGB32: {
+ uint *xidata = (uint *)xi->data;
+ for (int y = 0; y < h; ++y) {
+ const QRgb *p = (const QRgb *) cimage.scanLine(y);
+ for (int x = 0; x < w; ++x) {
+ const QRgb rgb = p[x];
+ const int a = qAlpha(rgb);
+ if (a == 0xff)
+ *xidata = rgb;
+ else
+ // RENDER expects premultiplied alpha
+ *xidata = qRgba(qt_div_255(qRed(rgb) * a),
+ qt_div_255(qGreen(rgb) * a),
+ qt_div_255(qBlue(rgb) * a),
+ a);
+ ++xidata;
+ }
+ }
+
+ }
+ break;
+ case QImage::Format_ARGB32_Premultiplied: {
+ uint *xidata = (uint *)xi->data;
+ for (int y = 0; y < h; ++y) {
+ const QRgb *p = (const QRgb *) cimage.scanLine(y);
+ memcpy(xidata, p, w*sizeof(QRgb));
+ xidata += w;
+ }
+ }
+ break;
+ default:
+ Q_ASSERT(false);
+ }
+
+ if ((xi->byte_order == MSBFirst) != (QSysInfo::ByteOrder == QSysInfo::BigEndian)) {
+ uint *xidata = (uint *)xi->data;
+ uint *xiend = xidata + w*h;
+ while (xidata < xiend) {
+ *xidata = (*xidata >> 24)
+ | ((*xidata >> 8) & 0xff00)
+ | ((*xidata << 8) & 0xff0000)
+ | (*xidata << 24);
+ ++xidata;
+ }
+ }
+
+ GC gc = XCreateGC(dpy, hd, 0, 0);
+ XPutImage(dpy, hd, gc, xi, 0, 0, 0, 0, w, h);
+ XFreeGC(dpy, gc);
+
+ qSafeXDestroyImage(xi);
+
+ return;
+ }
+#endif // QT_CONFIG(xrender)
+
+ if (trucol) { // truecolor display
+ if (image.format() == QImage::Format_ARGB32_Premultiplied)
+ image = image.convertToFormat(QImage::Format_ARGB32);
+
+ const QImage &cimage = image;
+ QRgb pix[256]; // pixel translation table
+ const bool d8 = (d == 8);
+ const uint red_mask = (uint)visual->red_mask;
+ const uint green_mask = (uint)visual->green_mask;
+ const uint blue_mask = (uint)visual->blue_mask;
+ const int red_shift = highest_bit(red_mask) - 7;
+ const int green_shift = highest_bit(green_mask) - 7;
+ const int blue_shift = highest_bit(blue_mask) - 7;
+ const uint rbits = highest_bit(red_mask) - lowest_bit(red_mask) + 1;
+ const uint gbits = highest_bit(green_mask) - lowest_bit(green_mask) + 1;
+ const uint bbits = highest_bit(blue_mask) - lowest_bit(blue_mask) + 1;
+
+ if (d8) { // setup pixel translation
+ QVector<QRgb> ctable = cimage.colorTable();
+ for (int i=0; i < cimage.colorCount(); i++) {
+ int r = qRed (ctable[i]);
+ int g = qGreen(ctable[i]);
+ int b = qBlue (ctable[i]);
+ r = red_shift > 0 ? r << red_shift : r >> -red_shift;
+ g = green_shift > 0 ? g << green_shift : g >> -green_shift;
+ b = blue_shift > 0 ? b << blue_shift : b >> -blue_shift;
+ pix[i] = (b & blue_mask) | (g & green_mask) | (r & red_mask)
+ | ~(blue_mask | green_mask | red_mask);
+ }
+ }
+
+ xi = XCreateImage(dpy, visual, dd, ZPixmap, 0, 0, w, h, 32, 0);
+ Q_CHECK_PTR(xi);
+ newbits = (uchar *)malloc(xi->bytes_per_line*h);
+ Q_CHECK_PTR(newbits);
+ if (!newbits) // no memory
+ return;
+ int bppc = xi->bits_per_pixel;
+
+ bool contig_bits = n_bits(red_mask) == rbits &&
+ n_bits(green_mask) == gbits &&
+ n_bits(blue_mask) == bbits;
+ bool dither_tc =
+ // Want it?
+ (flags & Qt::Dither_Mask) != Qt::ThresholdDither &&
+ (flags & Qt::DitherMode_Mask) != Qt::AvoidDither &&
+ // Need it?
+ bppc < 24 && !d8 &&
+ // Can do it? (Contiguous bits?)
+ contig_bits;
+
+ static bool init=false;
+ static int D[16][16];
+ if (dither_tc && !init) {
+ // I also contributed this code to XV - WWA.
+ /*
+ The dither matrix, D, is obtained with this formula:
+
+ D2 = [0 2]
+ [3 1]
+
+
+ D2*n = [4*Dn 4*Dn+2*Un]
+ [4*Dn+3*Un 4*Dn+1*Un]
+ */
+ int n,i,j;
+ init=1;
+
+ /* Set D2 */
+ D[0][0]=0;
+ D[1][0]=2;
+ D[0][1]=3;
+ D[1][1]=1;
+
+ /* Expand using recursive definition given above */
+ for (n=2; n<16; n*=2) {
+ for (i=0; i<n; i++) {
+ for (j=0; j<n; j++) {
+ D[i][j]*=4;
+ D[i+n][j]=D[i][j]+2;
+ D[i][j+n]=D[i][j]+3;
+ D[i+n][j+n]=D[i][j]+1;
+ }
+ }
+ }
+ init=true;
+ }
+
+ enum { BPP8,
+ BPP16_565, BPP16_555,
+ BPP16_MSB, BPP16_LSB,
+ BPP24_888,
+ BPP24_MSB, BPP24_LSB,
+ BPP32_8888,
+ BPP32_MSB, BPP32_LSB
+ } mode = BPP8;
+
+ bool same_msb_lsb = (xi->byte_order == MSBFirst) == (QSysInfo::ByteOrder == QSysInfo::BigEndian);
+
+ if (bppc == 8) // 8 bit
+ mode = BPP8;
+ else if (bppc == 16) { // 16 bit MSB/LSB
+ if (red_shift == 8 && green_shift == 3 && blue_shift == -3 && !d8 && same_msb_lsb)
+ mode = BPP16_565;
+ else if (red_shift == 7 && green_shift == 2 && blue_shift == -3 && !d8 && same_msb_lsb)
+ mode = BPP16_555;
+ else
+ mode = (xi->byte_order == LSBFirst) ? BPP16_LSB : BPP16_MSB;
+ } else if (bppc == 24) { // 24 bit MSB/LSB
+ if (red_shift == 16 && green_shift == 8 && blue_shift == 0 && !d8 && same_msb_lsb)
+ mode = BPP24_888;
+ else
+ mode = (xi->byte_order == LSBFirst) ? BPP24_LSB : BPP24_MSB;
+ } else if (bppc == 32) { // 32 bit MSB/LSB
+ if (red_shift == 16 && green_shift == 8 && blue_shift == 0 && !d8 && same_msb_lsb)
+ mode = BPP32_8888;
+ else
+ mode = (xi->byte_order == LSBFirst) ? BPP32_LSB : BPP32_MSB;
+ } else
+ qFatal("Logic error 3");
+
+#define GET_PIXEL \
+ uint pixel; \
+ if (d8) pixel = pix[*src++]; \
+ else { \
+ int r = qRed (*p); \
+ int g = qGreen(*p); \
+ int b = qBlue (*p++); \
+ r = red_shift > 0 \
+ ? r << red_shift : r >> -red_shift; \
+ g = green_shift > 0 \
+ ? g << green_shift : g >> -green_shift; \
+ b = blue_shift > 0 \
+ ? b << blue_shift : b >> -blue_shift; \
+ pixel = (r & red_mask)|(g & green_mask) | (b & blue_mask) \
+ | ~(blue_mask | green_mask | red_mask); \
+ }
+
+#define GET_PIXEL_DITHER_TC \
+ int r = qRed (*p); \
+ int g = qGreen(*p); \
+ int b = qBlue (*p++); \
+ const int thres = D[x%16][y%16]; \
+ if (r <= (255-(1<<(8-rbits))) && ((r<<rbits) & 255) \
+ > thres) \
+ r += (1<<(8-rbits)); \
+ if (g <= (255-(1<<(8-gbits))) && ((g<<gbits) & 255) \
+ > thres) \
+ g += (1<<(8-gbits)); \
+ if (b <= (255-(1<<(8-bbits))) && ((b<<bbits) & 255) \
+ > thres) \
+ b += (1<<(8-bbits)); \
+ r = red_shift > 0 \
+ ? r << red_shift : r >> -red_shift; \
+ g = green_shift > 0 \
+ ? g << green_shift : g >> -green_shift; \
+ b = blue_shift > 0 \
+ ? b << blue_shift : b >> -blue_shift; \
+ uint pixel = (r & red_mask)|(g & green_mask) | (b & blue_mask);
+
+// again, optimized case
+// can't be optimized that much :(
+#define GET_PIXEL_DITHER_TC_OPT(red_shift,green_shift,blue_shift,red_mask,green_mask,blue_mask, \
+ rbits,gbits,bbits) \
+ const int thres = D[x%16][y%16]; \
+ int r = qRed (*p); \
+ if (r <= (255-(1<<(8-rbits))) && ((r<<rbits) & 255) \
+ > thres) \
+ r += (1<<(8-rbits)); \
+ int g = qGreen(*p); \
+ if (g <= (255-(1<<(8-gbits))) && ((g<<gbits) & 255) \
+ > thres) \
+ g += (1<<(8-gbits)); \
+ int b = qBlue (*p++); \
+ if (b <= (255-(1<<(8-bbits))) && ((b<<bbits) & 255) \
+ > thres) \
+ b += (1<<(8-bbits)); \
+ uint pixel = ((r red_shift) & red_mask) \
+ | ((g green_shift) & green_mask) \
+ | ((b blue_shift) & blue_mask);
+
+#define CYCLE(body) \
+ for (int y=0; y<h; y++) { \
+ const uchar* src = cimage.scanLine(y); \
+ uchar* dst = newbits + xi->bytes_per_line*y; \
+ const QRgb* p = (const QRgb *)src; \
+ body \
+ }
+
+ if (dither_tc) {
+ switch (mode) {
+ case BPP16_565:
+ CYCLE(
+ quint16* dst16 = (quint16*)dst;
+ for (int x=0; x<w; x++) {
+ GET_PIXEL_DITHER_TC_OPT(<<8,<<3,>>3,0xf800,0x7e0,0x1f,5,6,5)
+ *dst16++ = pixel;
+ }
+ )
+ break;
+ case BPP16_555:
+ CYCLE(
+ quint16* dst16 = (quint16*)dst;
+ for (int x=0; x<w; x++) {
+ GET_PIXEL_DITHER_TC_OPT(<<7,<<2,>>3,0x7c00,0x3e0,0x1f,5,5,5)
+ *dst16++ = pixel;
+ }
+ )
+ break;
+ case BPP16_MSB: // 16 bit MSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ GET_PIXEL_DITHER_TC
+ *dst++ = (pixel >> 8);
+ *dst++ = pixel;
+ }
+ )
+ break;
+ case BPP16_LSB: // 16 bit LSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ GET_PIXEL_DITHER_TC
+ *dst++ = pixel;
+ *dst++ = pixel >> 8;
+ }
+ )
+ break;
+ default:
+ qFatal("Logic error");
+ }
+ } else {
+ switch (mode) {
+ case BPP8: // 8 bit
+ CYCLE(
+ Q_UNUSED(p);
+ for (int x=0; x<w; x++)
+ *dst++ = pix[*src++];
+ )
+ break;
+ case BPP16_565:
+ CYCLE(
+ quint16* dst16 = (quint16*)dst;
+ for (int x = 0; x < w; x++) {
+ *dst16++ = ((*p >> 8) & 0xf800)
+ | ((*p >> 5) & 0x7e0)
+ | ((*p >> 3) & 0x1f);
+ ++p;
+ }
+ )
+ break;
+ case BPP16_555:
+ CYCLE(
+ quint16* dst16 = (quint16*)dst;
+ for (int x=0; x<w; x++) {
+ *dst16++ = ((*p >> 9) & 0x7c00)
+ | ((*p >> 6) & 0x3e0)
+ | ((*p >> 3) & 0x1f);
+ ++p;
+ }
+ )
+ break;
+ case BPP16_MSB: // 16 bit MSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ GET_PIXEL
+ *dst++ = (pixel >> 8);
+ *dst++ = pixel;
+ }
+ )
+ break;
+ case BPP16_LSB: // 16 bit LSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ GET_PIXEL
+ *dst++ = pixel;
+ *dst++ = pixel >> 8;
+ }
+ )
+ break;
+ case BPP24_888:
+ CYCLE(
+ if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
+ for (int x=0; x<w; x++) {
+ *dst++ = qRed (*p);
+ *dst++ = qGreen(*p);
+ *dst++ = qBlue (*p++);
+ }
+ } else {
+ for (int x=0; x<w; x++) {
+ *dst++ = qBlue (*p);
+ *dst++ = qGreen(*p);
+ *dst++ = qRed (*p++);
+ }
+ }
+ )
+ break;
+ case BPP24_MSB: // 24 bit MSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ GET_PIXEL
+ *dst++ = pixel >> 16;
+ *dst++ = pixel >> 8;
+ *dst++ = pixel;
+ }
+ )
+ break;
+ case BPP24_LSB: // 24 bit LSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ GET_PIXEL
+ *dst++ = pixel;
+ *dst++ = pixel >> 8;
+ *dst++ = pixel >> 16;
+ }
+ )
+ break;
+ case BPP32_8888:
+ CYCLE(
+ memcpy(dst, p, w * 4);
+ )
+ break;
+ case BPP32_MSB: // 32 bit MSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ GET_PIXEL
+ *dst++ = pixel >> 24;
+ *dst++ = pixel >> 16;
+ *dst++ = pixel >> 8;
+ *dst++ = pixel;
+ }
+ )
+ break;
+ case BPP32_LSB: // 32 bit LSB
+ CYCLE(
+ for (int x=0; x<w; x++) {
+ GET_PIXEL
+ *dst++ = pixel;
+ *dst++ = pixel >> 8;
+ *dst++ = pixel >> 16;
+ *dst++ = pixel >> 24;
+ }
+ )
+ break;
+ default:
+ qFatal("Logic error 2");
+ }
+ }
+ xi->data = (char *)newbits;
+ }
+
+ if (d == 8 && !trucol) { // 8 bit pixmap
+ int pop[256]; // pixel popularity
+
+ if (image.colorCount() == 0)
+ image.setColorCount(1);
+
+ const QImage &cimage = image;
+ memset(pop, 0, sizeof(int)*256); // reset popularity array
+ for (int i = 0; i < h; i++) { // for each scanline...
+ const uchar* p = cimage.scanLine(i);
+ const uchar *end = p + w;
+ while (p < end) // compute popularity
+ pop[*p++]++;
+ }
+
+ newbits = (uchar *)malloc(nbytes); // copy image into newbits
+ Q_CHECK_PTR(newbits);
+ if (!newbits) // no memory
+ return;
+ uchar* p = newbits;
+ memcpy(p, cimage.bits(), nbytes); // copy image data into newbits
+
+ /*
+ * The code below picks the most important colors. It is based on the
+ * diversity algorithm, implemented in XV 3.10. XV is (C) by John Bradley.
+ */
+
+ struct PIX { // pixel sort element
+ uchar r,g,b,n; // color + pad
+ int use; // popularity
+ int index; // index in colormap
+ int mindist;
+ };
+ int ncols = 0;
+ for (int i=0; i< cimage.colorCount(); i++) { // compute number of colors
+ if (pop[i] > 0)
+ ncols++;
+ }
+ for (int i = cimage.colorCount(); i < 256; i++) // ignore out-of-range pixels
+ pop[i] = 0;
+
+ // works since we make sure above to have at least
+ // one color in the image
+ if (ncols == 0)
+ ncols = 1;
+
+ PIX pixarr[256]; // pixel array
+ PIX pixarr_sorted[256]; // pixel array (sorted)
+ memset(pixarr, 0, ncols*sizeof(PIX));
+ PIX *px = &pixarr[0];
+ int maxpop = 0;
+ int maxpix = 0;
+ uint j = 0;
+ QVector<QRgb> ctable = cimage.colorTable();
+ for (int i = 0; i < 256; i++) { // init pixel array
+ if (pop[i] > 0) {
+ px->r = qRed (ctable[i]);
+ px->g = qGreen(ctable[i]);
+ px->b = qBlue (ctable[i]);
+ px->n = 0;
+ px->use = pop[i];
+ if (pop[i] > maxpop) { // select most popular entry
+ maxpop = pop[i];
+ maxpix = j;
+ }
+ px->index = i;
+ px->mindist = 1000000;
+ px++;
+ j++;
+ }
+ }
+ pixarr_sorted[0] = pixarr[maxpix];
+ pixarr[maxpix].use = 0;
+
+ for (int i = 1; i < ncols; i++) { // sort pixels
+ int minpix = -1, mindist = -1;
+ px = &pixarr_sorted[i-1];
+ int r = px->r;
+ int g = px->g;
+ int b = px->b;
+ int dist;
+ if ((i & 1) || i<10) { // sort on max distance
+ for (int j=0; j<ncols; j++) {
+ px = &pixarr[j];
+ if (px->use) {
+ dist = (px->r - r)*(px->r - r) +
+ (px->g - g)*(px->g - g) +
+ (px->b - b)*(px->b - b);
+ if (px->mindist > dist)
+ px->mindist = dist;
+ if (px->mindist > mindist) {
+ mindist = px->mindist;
+ minpix = j;
+ }
+ }
+ }
+ } else { // sort on max popularity
+ for (int j=0; j<ncols; j++) {
+ px = &pixarr[j];
+ if (px->use) {
+ dist = (px->r - r)*(px->r - r) +
+ (px->g - g)*(px->g - g) +
+ (px->b - b)*(px->b - b);
+ if (px->mindist > dist)
+ px->mindist = dist;
+ if (px->use > mindist) {
+ mindist = px->use;
+ minpix = j;
+ }
+ }
+ }
+ }
+ pixarr_sorted[i] = pixarr[minpix];
+ pixarr[minpix].use = 0;
+ }
+
+ QXcbColormap cmap = QXcbColormap::instance(xinfo.screen());
+ uint pix[256]; // pixel translation table
+ px = &pixarr_sorted[0];
+ for (int i = 0; i < ncols; i++) { // allocate colors
+ QColor c(px->r, px->g, px->b);
+ pix[px->index] = cmap.pixel(c);
+ px++;
+ }
+
+ p = newbits;
+ for (size_t i = 0; i < nbytes; i++) { // translate pixels
+ *p = pix[*p];
+ p++;
+ }
+ }
+
+ if (!xi) { // X image not created
+ xi = XCreateImage(dpy, visual, dd, ZPixmap, 0, 0, w, h, 32, 0);
+ if (xi->bits_per_pixel == 16) { // convert 8 bpp ==> 16 bpp
+ ushort *p2;
+ int p2inc = xi->bytes_per_line/sizeof(ushort);
+ ushort *newerbits = (ushort *)malloc(xi->bytes_per_line * h);
+ Q_CHECK_PTR(newerbits);
+ if (!newerbits) // no memory
+ return;
+ uchar* p = newbits;
+ for (int y = 0; y < h; y++) { // OOPS: Do right byte order!!
+ p2 = newerbits + p2inc*y;
+ for (int x = 0; x < w; x++)
+ *p2++ = *p++;
+ }
+ free(newbits);
+ newbits = (uchar *)newerbits;
+ } else if (xi->bits_per_pixel != 8) {
+ qWarning("QPixmap::fromImage: Display not supported "
+ "(bpp=%d)", xi->bits_per_pixel);
+ }
+ xi->data = (char *)newbits;
+ }
+
+ hd = XCreatePixmap(dpy,
+ RootWindow(dpy, xinfo.screen()),
+ w, h, dd);
+
+ GC gc = XCreateGC(dpy, hd, 0, 0);
+ XPutImage(dpy, hd, gc, xi, 0, 0, 0, 0, w, h);
+ XFreeGC(dpy, gc);
+
+ qSafeXDestroyImage(xi);
+ d = dd;
+
+#if QT_CONFIG(xrender)
+ if (X11->use_xrender) {
+ XRenderPictFormat *format = d == 1
+ ? XRenderFindStandardFormat(dpy, PictStandardA1)
+ : XRenderFindVisualFormat(dpy, (Visual *)xinfo.visual());
+ picture = XRenderCreatePicture(dpy, hd, format, 0, 0);
+ }
+#endif
+
+ if (alphaCheck.hasAlpha()) {
+ QBitmap m = QBitmap::fromImage(image.createAlphaMask(flags));
+ setMask(m);
+ }
+}
+
+void QX11PlatformPixmap::copy(const QPlatformPixmap *data, const QRect &rect)
+{
+ if (data->pixelType() == BitmapType) {
+ fromImage(data->toImage().copy(rect), Qt::AutoColor);
+ return;
+ }
+
+ const QX11PlatformPixmap *x11Data = static_cast<const QX11PlatformPixmap*>(data);
+
+ setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1));
+
+ flags &= ~Uninitialized;
+ xinfo = x11Data->xinfo;
+ d = x11Data->d;
+ w = rect.width();
+ h = rect.height();
+ is_null = (w <= 0 || h <= 0);
+ hd = XCreatePixmap(xinfo.display(),
+ RootWindow(xinfo.display(), x11Data->xinfo.screen()),
+ w, h, d);
+#if QT_CONFIG(xrender)
+ if (X11->use_xrender) {
+ XRenderPictFormat *format = d == 32
+ ? XRenderFindStandardFormat(xinfo.display(), PictStandardARGB32)
+ : XRenderFindVisualFormat(xinfo.display(), (Visual *)xinfo.visual());
+ picture = XRenderCreatePicture(xinfo.display(), hd, format, 0, 0);
+ }
+#endif // QT_CONFIG(xrender)
+ if (x11Data->x11_mask) {
+ x11_mask = XCreatePixmap(xinfo.display(), hd, w, h, 1);
+#if QT_CONFIG(xrender)
+ if (X11->use_xrender) {
+ mask_picture = XRenderCreatePicture(xinfo.display(), x11_mask,
+ XRenderFindStandardFormat(xinfo.display(), PictStandardA1), 0, 0);
+ XRenderPictureAttributes attrs;
+ attrs.alpha_map = x11Data->mask_picture;
+ XRenderChangePicture(xinfo.display(), x11Data->picture, CPAlphaMap, &attrs);
+ }
+#endif
+ }
+
+#if QT_CONFIG(xrender)
+ if (x11Data->picture && x11Data->d == 32) {
+ XRenderComposite(xinfo.display(), PictOpSrc,
+ x11Data->picture, 0, picture,
+ rect.x(), rect.y(), 0, 0, 0, 0, w, h);
+ } else
+#endif
+ {
+ GC gc = XCreateGC(xinfo.display(), hd, 0, 0);
+ XCopyArea(xinfo.display(), x11Data->hd, hd, gc,
+ rect.x(), rect.y(), w, h, 0, 0);
+ if (x11Data->x11_mask) {
+ GC monogc = XCreateGC(xinfo.display(), x11_mask, 0, 0);
+ XCopyArea(xinfo.display(), x11Data->x11_mask, x11_mask, monogc,
+ rect.x(), rect.y(), w, h, 0, 0);
+ XFreeGC(xinfo.display(), monogc);
+ }
+ XFreeGC(xinfo.display(), gc);
+ }
+}
+
+bool QX11PlatformPixmap::scroll(int dx, int dy, const QRect &rect)
+{
+ GC gc = XCreateGC(xinfo.display(), hd, 0, 0);
+ XCopyArea(xinfo.display(), hd, hd, gc,
+ rect.left(), rect.top(), rect.width(), rect.height(),
+ rect.left() + dx, rect.top() + dy);
+ XFreeGC(xinfo.display(), gc);
+ return true;
+}
+
+int QX11PlatformPixmap::metric(QPaintDevice::PaintDeviceMetric metric) const
+{
+ switch (metric) {
+ case QPaintDevice::PdmDevicePixelRatio:
+ return devicePixelRatio();
+ break;
+ case QPaintDevice::PdmDevicePixelRatioScaled:
+ return devicePixelRatio() * QPaintDevice::devicePixelRatioFScale();
+ break;
+ case QPaintDevice::PdmWidth:
+ return w;
+ case QPaintDevice::PdmHeight:
+ return h;
+ case QPaintDevice::PdmNumColors:
+ return 1 << d;
+ case QPaintDevice::PdmDepth:
+ return d;
+ case QPaintDevice::PdmWidthMM: {
+ const int screen = xinfo.screen();
+ const int mm = DisplayWidthMM(xinfo.display(), screen) * w
+ / DisplayWidth(xinfo.display(), screen);
+ return mm;
+ }
+ case QPaintDevice::PdmHeightMM: {
+ const int screen = xinfo.screen();
+ const int mm = (DisplayHeightMM(xinfo.display(), screen) * h)
+ / DisplayHeight(xinfo.display(), screen);
+ return mm;
+ }
+ case QPaintDevice::PdmDpiX:
+ case QPaintDevice::PdmPhysicalDpiX:
+ return QXcbX11Info::appDpiX(xinfo.screen());
+ case QPaintDevice::PdmDpiY:
+ case QPaintDevice::PdmPhysicalDpiY:
+ return QXcbX11Info::appDpiY(xinfo.screen());
+ default:
+ qWarning("QX11PlatformPixmap::metric(): Invalid metric");
+ return 0;
+ }
+}
+
+void QX11PlatformPixmap::fill(const QColor &fillColor)
+{
+ if (fillColor.alpha() != 255) {
+#if QT_CONFIG(xrender)
+ if (X11->use_xrender) {
+ if (!picture || d != 32)
+ convertToARGB32(/*preserveContents = */false);
+
+ ::Picture src = X11->getSolidFill(xinfo.screen(), fillColor);
+ XRenderComposite(xinfo.display(), PictOpSrc, src, 0, picture,
+ 0, 0, width(), height(),
+ 0, 0, width(), height());
+ } else
+#endif
+ {
+ QImage im(width(), height(), QImage::Format_ARGB32_Premultiplied);
+ im.fill(PREMUL(fillColor.rgba()));
+ release();
+ fromImage(im, Qt::AutoColor | Qt::OrderedAlphaDither);
+ }
+ return;
+ }
+
+ GC gc = XCreateGC(xinfo.display(), hd, 0, 0);
+ if (depth() == 1) {
+ XSetForeground(xinfo.display(), gc, qGray(fillColor.rgb()) > 127 ? 0 : 1);
+ } else if (X11->use_xrender && d >= 24) {
+ XSetForeground(xinfo.display(), gc, fillColor.rgba());
+ } else {
+ XSetForeground(xinfo.display(), gc,
+ QXcbColormap::instance(xinfo.screen()).pixel(fillColor));
+ }
+ XFillRectangle(xinfo.display(), hd, gc, 0, 0, width(), height());
+ XFreeGC(xinfo.display(), gc);
+}
+
+QBitmap QX11PlatformPixmap::mask() const
+{
+ QBitmap mask;
+#if QT_CONFIG(xrender)
+ if (picture && d == 32) {
+ // #### slow - there must be a better way..
+ mask = QBitmap::fromImage(toImage().createAlphaMask());
+ } else
+#endif
+ if (d == 1) {
+ QX11PlatformPixmap *that = const_cast<QX11PlatformPixmap*>(this);
+ mask = QPixmap(that);
+ } else {
+ mask = mask_to_bitmap(xinfo.screen());
+ }
+ return mask;
+}
+
+void QX11PlatformPixmap::setMask(const QBitmap &newmask)
+{
+ if (newmask.isNull()) { // clear mask
+#if QT_CONFIG(xrender)
+ if (picture && d == 32) {
+ QX11PlatformPixmap newData(pixelType());
+ newData.resize(w, h);
+ newData.fill(Qt::black);
+ XRenderComposite(xinfo.display(), PictOpOver,
+ picture, 0, newData.picture,
+ 0, 0, 0, 0, 0, 0, w, h);
+ release();
+ *this = newData;
+ // the new QX11PlatformPixmap object isn't referenced yet, so
+ // ref it
+ ref.ref();
+
+ // the below is to make sure the QX11PlatformPixmap destructor
+ // doesn't delete our newly created render picture
+ newData.hd = 0;
+ newData.x11_mask = 0;
+ newData.picture = 0;
+ newData.mask_picture = 0;
+ newData.hd2 = 0;
+ } else
+#endif
+ if (x11_mask) {
+#if QT_CONFIG(xrender)
+ if (picture) {
+ XRenderPictureAttributes attrs;
+ attrs.alpha_map = 0;
+ XRenderChangePicture(xinfo.display(), picture, CPAlphaMap,
+ &attrs);
+ }
+ if (mask_picture)
+ XRenderFreePicture(xinfo.display(), mask_picture);
+ mask_picture = 0;
+#endif
+ XFreePixmap(xinfo.display(), x11_mask);
+ x11_mask = 0;
+ }
+ return;
+ }
+
+#if QT_CONFIG(xrender)
+ if (picture && d == 32) {
+ XRenderComposite(xinfo.display(), PictOpSrc,
+ picture, qt_x11Pixmap(newmask)->x11PictureHandle(),
+ picture, 0, 0, 0, 0, 0, 0, w, h);
+ } else
+#endif
+ if (depth() == 1) {
+ XGCValues vals;
+ vals.function = GXand;
+ GC gc = XCreateGC(xinfo.display(), hd, GCFunction, &vals);
+ XCopyArea(xinfo.display(), qt_x11Pixmap(newmask)->handle(), hd, gc, 0, 0,
+ width(), height(), 0, 0);
+ XFreeGC(xinfo.display(), gc);
+ } else {
+ // ##### should or the masks together
+ if (x11_mask) {
+ XFreePixmap(xinfo.display(), x11_mask);
+#if QT_CONFIG(xrender)
+ if (mask_picture)
+ XRenderFreePicture(xinfo.display(), mask_picture);
+#endif
+ }
+ x11_mask = QX11PlatformPixmap::bitmap_to_mask(newmask, xinfo.screen());
+#if QT_CONFIG(xrender)
+ if (picture) {
+ mask_picture = XRenderCreatePicture(xinfo.display(), x11_mask,
+ XRenderFindStandardFormat(xinfo.display(), PictStandardA1), 0, 0);
+ XRenderPictureAttributes attrs;
+ attrs.alpha_map = mask_picture;
+ XRenderChangePicture(xinfo.display(), picture, CPAlphaMap, &attrs);
+ }
+#endif
+ }
+}
+
+bool QX11PlatformPixmap::hasAlphaChannel() const
+{
+ if (picture && d == 32)
+ return true;
+
+ if (x11_mask && d == 1)
+ return true;
+
+ return false;
+}
+
+QPixmap QX11PlatformPixmap::transformed(const QTransform &transform, Qt::TransformationMode mode) const
+{
+ if (mode == Qt::SmoothTransformation || transform.type() >= QTransform::TxProject) {
+ QImage image = toImage();
+ return QPixmap::fromImage(image.transformed(transform, mode));
+ }
+
+ uint w = 0;
+ uint h = 0; // size of target pixmap
+ uint ws, hs; // size of source pixmap
+ uchar *dptr; // data in target pixmap
+ uint dbpl, dbytes; // bytes per line/bytes total
+ uchar *sptr; // data in original pixmap
+ int sbpl; // bytes per line in original
+ int bpp; // bits per pixel
+ bool depth1 = depth() == 1;
+ Display *dpy = xinfo.display();
+
+ ws = width();
+ hs = height();
+
+ QTransform mat(transform.m11(), transform.m12(), transform.m13(),
+ transform.m21(), transform.m22(), transform.m23(),
+ 0., 0., 1);
+ bool complex_xform = false;
+ qreal scaledWidth;
+ qreal scaledHeight;
+
+ if (mat.type() <= QTransform::TxScale) {
+ scaledHeight = qAbs(mat.m22()) * hs + 0.9999;
+ scaledWidth = qAbs(mat.m11()) * ws + 0.9999;
+ h = qAbs(int(scaledHeight));
+ w = qAbs(int(scaledWidth));
+ } else { // rotation or shearing
+ QPolygonF a(QRectF(0, 0, ws, hs));
+ a = mat.map(a);
+ QRect r = a.boundingRect().toAlignedRect();
+ w = r.width();
+ h = r.height();
+ scaledWidth = w;
+ scaledHeight = h;
+ complex_xform = true;
+ }
+ mat = QPixmap::trueMatrix(mat, ws, hs); // true matrix
+
+ bool invertible;
+ mat = mat.inverted(&invertible); // invert matrix
+
+ if (h == 0 || w == 0 || !invertible
+ || qAbs(scaledWidth) >= 32768 || qAbs(scaledHeight) >= 32768 )
+ // error, return null pixmap
+ return QPixmap();
+
+ XImage *xi = XGetImage(xinfo.display(), handle(), 0, 0, ws, hs, AllPlanes,
+ depth1 ? XYPixmap : ZPixmap);
+
+ if (!xi)
+ return QPixmap();
+
+ sbpl = xi->bytes_per_line;
+ sptr = (uchar *)xi->data;
+ bpp = xi->bits_per_pixel;
+
+ if (depth1)
+ dbpl = (w+7)/8;
+ else
+ dbpl = ((w*bpp+31)/32)*4;
+ dbytes = dbpl*h;
+
+ dptr = (uchar *)malloc(dbytes); // create buffer for bits
+ Q_CHECK_PTR(dptr);
+ if (depth1) // fill with zeros
+ memset(dptr, 0, dbytes);
+ else if (bpp == 8) // fill with background color
+ memset(dptr, WhitePixel(xinfo.display(), xinfo.screen()), dbytes);
+ else
+ memset(dptr, 0, dbytes);
+
+ // #define QT_DEBUG_XIMAGE
+#if defined(QT_DEBUG_XIMAGE)
+ qDebug("----IMAGE--INFO--------------");
+ qDebug("width............. %d", xi->width);
+ qDebug("height............ %d", xi->height);
+ qDebug("xoffset........... %d", xi->xoffset);
+ qDebug("format............ %d", xi->format);
+ qDebug("byte order........ %d", xi->byte_order);
+ qDebug("bitmap unit....... %d", xi->bitmap_unit);
+ qDebug("bitmap bit order.. %d", xi->bitmap_bit_order);
+ qDebug("depth............. %d", xi->depth);
+ qDebug("bytes per line.... %d", xi->bytes_per_line);
+ qDebug("bits per pixel.... %d", xi->bits_per_pixel);
+#endif
+
+ int type;
+ if (xi->bitmap_bit_order == MSBFirst)
+ type = QT_XFORM_TYPE_MSBFIRST;
+ else
+ type = QT_XFORM_TYPE_LSBFIRST;
+ int xbpl, p_inc;
+ if (depth1) {
+ xbpl = (w+7)/8;
+ p_inc = dbpl - xbpl;
+ } else {
+ xbpl = (w*bpp)/8;
+ p_inc = dbpl - xbpl;
+ }
+
+ if (!qt_xForm_helper(mat, xi->xoffset, type, bpp, dptr, xbpl, p_inc, h, sptr, sbpl, ws, hs)){
+ qWarning("QPixmap::transform: display not supported (bpp=%d)",bpp);
+ QPixmap pm;
+ free(dptr);
+ return pm;
+ }
+
+ qSafeXDestroyImage(xi);
+
+ if (depth1) { // mono bitmap
+ QBitmap bm = QBitmap::fromData(QSize(w, h), dptr,
+ BitmapBitOrder(xinfo.display()) == MSBFirst
+ ? QImage::Format_Mono
+ : QImage::Format_MonoLSB);
+ free(dptr);
+ return bm;
+ } else { // color pixmap
+ QX11PlatformPixmap *x11Data = new QX11PlatformPixmap(QPlatformPixmap::PixmapType);
+ QPixmap pm(x11Data);
+ x11Data->flags &= ~QX11PlatformPixmap::Uninitialized;
+ x11Data->xinfo = xinfo;
+ x11Data->d = d;
+ x11Data->w = w;
+ x11Data->h = h;
+ x11Data->is_null = (w <= 0 || h <= 0);
+ x11Data->hd = XCreatePixmap(xinfo.display(),
+ RootWindow(xinfo.display(), xinfo.screen()),
+ w, h, d);
+ x11Data->setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1));
+
+#if QT_CONFIG(xrender)
+ if (X11->use_xrender) {
+ XRenderPictFormat *format = x11Data->d == 32
+ ? XRenderFindStandardFormat(xinfo.display(), PictStandardARGB32)
+ : XRenderFindVisualFormat(xinfo.display(), (Visual *) x11Data->xinfo.visual());
+ x11Data->picture = XRenderCreatePicture(xinfo.display(), x11Data->hd, format, 0, 0);
+ }
+#endif // QT_CONFIG(xrender)
+
+ GC gc = XCreateGC(xinfo.display(), x11Data->hd, 0, 0);
+ xi = XCreateImage(dpy, (Visual*)x11Data->xinfo.visual(),
+ x11Data->d,
+ ZPixmap, 0, (char *)dptr, w, h, 32, 0);
+ XPutImage(dpy, qt_x11Pixmap(pm)->handle(), gc, xi, 0, 0, 0, 0, w, h);
+ qSafeXDestroyImage(xi);
+ XFreeGC(xinfo.display(), gc);
+
+ if (x11_mask) { // xform mask, too
+ pm.setMask(mask_to_bitmap(xinfo.screen()).transformed(transform));
+ } else if (d != 32 && complex_xform) { // need a mask!
+ QBitmap mask(ws, hs);
+ mask.fill(Qt::color1);
+ pm.setMask(mask.transformed(transform));
+ }
+ return pm;
+ }
+}
+
+QImage QX11PlatformPixmap::toImage() const
+{
+ return toImage(QRect(0, 0, w, h));
+}
+
+QImage QX11PlatformPixmap::toImage(const QRect &rect) const
+{
+ Window root_return;
+ int x_return;
+ int y_return;
+ unsigned int width_return;
+ unsigned int height_return;
+ unsigned int border_width_return;
+ unsigned int depth_return;
+
+ XGetGeometry(xinfo.display(), hd, &root_return, &x_return, &y_return, &width_return, &height_return, &border_width_return, &depth_return);
+
+ QXImageWrapper xiWrapper;
+ xiWrapper.xi = XGetImage(xinfo.display(), hd, rect.x(), rect.y(), rect.width(), rect.height(),
+ AllPlanes, (depth() == 1) ? XYPixmap : ZPixmap);
+
+ Q_CHECK_PTR(xiWrapper.xi);
+ if (!xiWrapper.xi)
+ return QImage();
+
+ if (!x11_mask && canTakeQImageFromXImage(xiWrapper))
+ return takeQImageFromXImage(xiWrapper);
+
+ QImage image = toImage(xiWrapper, rect);
+ qSafeXDestroyImage(xiWrapper.xi);
+ return image;
+}
+
+#if QT_CONFIG(xrender)
+static XRenderPictFormat *qt_renderformat_for_depth(const QXcbX11Info &xinfo, int depth)
+{
+ if (depth == 1)
+ return XRenderFindStandardFormat(xinfo.display(), PictStandardA1);
+ else if (depth == 32)
+ return XRenderFindStandardFormat(xinfo.display(), PictStandardARGB32);
+ else
+ return XRenderFindVisualFormat(xinfo.display(), (Visual *)xinfo.visual());
+}
+#endif
+
+Q_GLOBAL_STATIC(QX11PaintEngine, qt_x11_paintengine)
+
+QPaintEngine *QX11PlatformPixmap::paintEngine() const
+{
+ QX11PlatformPixmap *that = const_cast<QX11PlatformPixmap*>(this);
+
+ if ((flags & Readonly)/* && share_mode == QPixmap::ImplicitlyShared*/) {
+ // if someone wants to draw onto us, copy the shared contents
+ // and turn it into a fully fledged QPixmap
+ ::Pixmap hd_copy = XCreatePixmap(xinfo.display(), RootWindow(xinfo.display(), xinfo.screen()),
+ w, h, d);
+#if QT_CONFIG(xrender)
+ if (picture && d == 32) {
+ XRenderPictFormat *format = qt_renderformat_for_depth(xinfo, d);
+ ::Picture picture_copy = XRenderCreatePicture(xinfo.display(),
+ hd_copy, format,
+ 0, 0);
+
+ XRenderComposite(xinfo.display(), PictOpSrc, picture, 0, picture_copy,
+ 0, 0, 0, 0, 0, 0, w, h);
+ XRenderFreePicture(xinfo.display(), picture);
+ that->picture = picture_copy;
+ } else
+#endif
+ {
+ GC gc = XCreateGC(xinfo.display(), hd_copy, 0, 0);
+ XCopyArea(xinfo.display(), hd, hd_copy, gc, 0, 0, w, h, 0, 0);
+ XFreeGC(xinfo.display(), gc);
+ }
+ that->hd = hd_copy;
+ that->flags &= ~QX11PlatformPixmap::Readonly;
+ }
+
+ if (qt_x11_paintengine->isActive()) {
+ if (!that->pengine)
+ that->pengine = new QX11PaintEngine;
+
+ return that->pengine;
+ }
+
+ return qt_x11_paintengine();
+}
+
+qreal QX11PlatformPixmap::devicePixelRatio() const
+{
+ return dpr;
+}
+
+void QX11PlatformPixmap::setDevicePixelRatio(qreal scaleFactor)
+{
+ dpr = scaleFactor;
+}
+
+Pixmap QX11PlatformPixmap::x11ConvertToDefaultDepth()
+{
+#if QT_CONFIG(xrender)
+ if (d == xinfo.appDepth() || !X11->use_xrender)
+ return hd;
+ if (!hd2) {
+ hd2 = XCreatePixmap(xinfo.display(), hd, w, h, xinfo.appDepth());
+ XRenderPictFormat *format = XRenderFindVisualFormat(xinfo.display(),
+ (Visual*) xinfo.visual());
+ Picture pic = XRenderCreatePicture(xinfo.display(), hd2, format, 0, 0);
+ XRenderComposite(xinfo.display(), PictOpSrc, picture,
+ XNone, pic, 0, 0, 0, 0, 0, 0, w, h);
+ XRenderFreePicture(xinfo.display(), pic);
+ }
+ return hd2;
+#else
+ return hd;
+#endif
+}
+
+XID QX11PlatformPixmap::createBitmapFromImage(const QImage &image)
+{
+ QImage img = image.convertToFormat(QImage::Format_MonoLSB);
+ const QRgb c0 = QColor(Qt::black).rgb();
+ const QRgb c1 = QColor(Qt::white).rgb();
+ if (img.color(0) == c0 && img.color(1) == c1) {
+ img.invertPixels();
+ img.setColor(0, c1);
+ img.setColor(1, c0);
+ }
+
+ char *bits;
+ uchar *tmp_bits;
+ int w = img.width();
+ int h = img.height();
+ int bpl = (w + 7) / 8;
+ int ibpl = img.bytesPerLine();
+ if (bpl != ibpl) {
+ tmp_bits = new uchar[bpl*h];
+ bits = (char *)tmp_bits;
+ uchar *p, *b;
+ int y;
+ b = tmp_bits;
+ p = img.scanLine(0);
+ for (y = 0; y < h; y++) {
+ memcpy(b, p, bpl);
+ b += bpl;
+ p += ibpl;
+ }
+ } else {
+ bits = (char *)img.bits();
+ tmp_bits = 0;
+ }
+ XID hd = XCreateBitmapFromData(QXcbX11Info::display(),
+ QXcbX11Info::appRootWindow(),
+ bits, w, h);
+ if (tmp_bits) // Avoid purify complaint
+ delete [] tmp_bits;
+ return hd;
+}
+
+#if QT_CONFIG(xrender)
+void QX11PlatformPixmap::convertToARGB32(bool preserveContents)
+{
+ if (!X11->use_xrender)
+ return;
+
+ // Q_ASSERT(count == 1);
+ if ((flags & Readonly)/* && share_mode == QPixmap::ExplicitlyShared*/)
+ return;
+
+ Pixmap pm = XCreatePixmap(xinfo.display(), RootWindow(xinfo.display(), xinfo.screen()),
+ w, h, 32);
+ Picture p = XRenderCreatePicture(xinfo.display(), pm,
+ XRenderFindStandardFormat(xinfo.display(), PictStandardARGB32), 0, 0);
+ if (picture) {
+ if (preserveContents)
+ XRenderComposite(xinfo.display(), PictOpSrc, picture, 0, p, 0, 0, 0, 0, 0, 0, w, h);
+ if (!(flags & Readonly))
+ XRenderFreePicture(xinfo.display(), picture);
+ }
+ if (hd && !(flags & Readonly))
+ XFreePixmap(xinfo.display(), hd);
+ if (x11_mask) {
+ XFreePixmap(xinfo.display(), x11_mask);
+ if (mask_picture)
+ XRenderFreePicture(xinfo.display(), mask_picture);
+ x11_mask = 0;
+ mask_picture = 0;
+ }
+ hd = pm;
+ picture = p;
+
+ d = 32;
+ xinfo.setDepth(32);
+
+ XVisualInfo visinfo;
+ if (XMatchVisualInfo(xinfo.display(), xinfo.screen(), 32, TrueColor, &visinfo))
+ xinfo.setVisual(visinfo.visual);
+}
+#endif
+
+void QX11PlatformPixmap::release()
+{
+ delete pengine;
+ pengine = 0;
+
+ if (/*!X11*/ QCoreApplication::closingDown()) {
+ // At this point, the X server will already have freed our resources,
+ // so there is nothing to do.
+ return;
+ }
+
+ if (x11_mask) {
+#if QT_CONFIG(xrender)
+ if (mask_picture)
+ XRenderFreePicture(xinfo.display(), mask_picture);
+ mask_picture = 0;
+#endif
+ XFreePixmap(xinfo.display(), x11_mask);
+ x11_mask = 0;
+ }
+
+ if (hd) {
+#if QT_CONFIG(xrender)
+ if (picture) {
+ XRenderFreePicture(xinfo.display(), picture);
+ picture = 0;
+ }
+#endif // QT_CONFIG(xrender)
+
+ if (hd2) {
+ XFreePixmap(xinfo.display(), hd2);
+ hd2 = 0;
+ }
+ if (!(flags & Readonly))
+ XFreePixmap(xinfo.display(), hd);
+ hd = 0;
+ }
+}
+
+QImage QX11PlatformPixmap::toImage(const QXImageWrapper &xiWrapper, const QRect &rect) const
+{
+ XImage *xi = xiWrapper.xi;
+
+ int d = depth();
+ Visual *visual = (Visual *)xinfo.visual();
+ bool trucol = (visual->c_class >= TrueColor) && d > 1;
+
+ QImage::Format format = QImage::Format_Mono;
+ if (d > 1 && d <= 8) {
+ d = 8;
+ format = QImage::Format_Indexed8;
+ }
+ // we could run into the situation where d == 8 AND trucol is true, which can
+ // cause problems when converting to and from images. in this case, always treat
+ // the depth as 32...
+ if (d > 8 || trucol) {
+ d = 32;
+ format = QImage::Format_RGB32;
+ }
+
+ if (d == 1 && xi->bitmap_bit_order == LSBFirst)
+ format = QImage::Format_MonoLSB;
+ if (x11_mask && format == QImage::Format_RGB32)
+ format = QImage::Format_ARGB32;
+
+ QImage image(xi->width, xi->height, format);
+ image.setDevicePixelRatio(devicePixelRatio());
+ if (image.isNull()) // could not create image
+ return image;
+
+ QImage alpha;
+ if (x11_mask) {
+ if (rect.contains(QRect(0, 0, w, h)))
+ alpha = mask().toImage();
+ else
+ alpha = mask().toImage().copy(rect);
+ }
+ bool ale = alpha.format() == QImage::Format_MonoLSB;
+
+ if (trucol) { // truecolor
+ const uint red_mask = (uint)visual->red_mask;
+ const uint green_mask = (uint)visual->green_mask;
+ const uint blue_mask = (uint)visual->blue_mask;
+ const int red_shift = highest_bit(red_mask) - 7;
+ const int green_shift = highest_bit(green_mask) - 7;
+ const int blue_shift = highest_bit(blue_mask) - 7;
+
+ const uint red_bits = n_bits(red_mask);
+ const uint green_bits = n_bits(green_mask);
+ const uint blue_bits = n_bits(blue_mask);
+
+ static uint red_table_bits = 0;
+ static uint green_table_bits = 0;
+ static uint blue_table_bits = 0;
+
+ if (red_bits < 8 && red_table_bits != red_bits) {
+ build_scale_table(&red_scale_table, red_bits);
+ red_table_bits = red_bits;
+ }
+ if (blue_bits < 8 && blue_table_bits != blue_bits) {
+ build_scale_table(&blue_scale_table, blue_bits);
+ blue_table_bits = blue_bits;
+ }
+ if (green_bits < 8 && green_table_bits != green_bits) {
+ build_scale_table(&green_scale_table, green_bits);
+ green_table_bits = green_bits;
+ }
+
+ int r, g, b;
+
+ QRgb *dst;
+ uchar *src;
+ uint pixel;
+ int bppc = xi->bits_per_pixel;
+
+ if (bppc > 8 && xi->byte_order == LSBFirst)
+ bppc++;
+
+ for (int y = 0; y < xi->height; ++y) {
+ uchar* asrc = x11_mask ? alpha.scanLine(y) : 0;
+ dst = (QRgb *)image.scanLine(y);
+ src = (uchar *)xi->data + xi->bytes_per_line*y;
+ for (int x = 0; x < xi->width; x++) {
+ switch (bppc) {
+ case 8:
+ pixel = *src++;
+ break;
+ case 16: // 16 bit MSB
+ pixel = src[1] | (uint)src[0] << 8;
+ src += 2;
+ break;
+ case 17: // 16 bit LSB
+ pixel = src[0] | (uint)src[1] << 8;
+ src += 2;
+ break;
+ case 24: // 24 bit MSB
+ pixel = src[2] | (uint)src[1] << 8 | (uint)src[0] << 16;
+ src += 3;
+ break;
+ case 25: // 24 bit LSB
+ pixel = src[0] | (uint)src[1] << 8 | (uint)src[2] << 16;
+ src += 3;
+ break;
+ case 32: // 32 bit MSB
+ pixel = src[3] | (uint)src[2] << 8 | (uint)src[1] << 16 | (uint)src[0] << 24;
+ src += 4;
+ break;
+ case 33: // 32 bit LSB
+ pixel = src[0] | (uint)src[1] << 8 | (uint)src[2] << 16 | (uint)src[3] << 24;
+ src += 4;
+ break;
+ default: // should not really happen
+ x = xi->width; // leave loop
+ y = xi->height;
+ pixel = 0; // eliminate compiler warning
+ qWarning("QPixmap::convertToImage: Invalid depth %d", bppc);
+ }
+ if (red_shift > 0)
+ r = (pixel & red_mask) >> red_shift;
+ else
+ r = (pixel & red_mask) << -red_shift;
+ if (green_shift > 0)
+ g = (pixel & green_mask) >> green_shift;
+ else
+ g = (pixel & green_mask) << -green_shift;
+ if (blue_shift > 0)
+ b = (pixel & blue_mask) >> blue_shift;
+ else
+ b = (pixel & blue_mask) << -blue_shift;
+
+ if (red_bits < 8)
+ r = red_scale_table[r];
+ if (green_bits < 8)
+ g = green_scale_table[g];
+ if (blue_bits < 8)
+ b = blue_scale_table[b];
+
+ if (x11_mask) {
+ if (ale) {
+ *dst++ = (asrc[x >> 3] & (1 << (x & 7))) ? qRgba(r, g, b, 0xff) : 0;
+ } else {
+ *dst++ = (asrc[x >> 3] & (0x80 >> (x & 7))) ? qRgba(r, g, b, 0xff) : 0;
+ }
+ } else {
+ *dst++ = qRgb(r, g, b);
+ }
+ }
+ }
+ } else if (xi->bits_per_pixel == d) { // compatible depth
+ char *xidata = xi->data; // copy each scanline
+ int bpl = qMin(image.bytesPerLine(),xi->bytes_per_line);
+ for (int y=0; y<xi->height; y++) {
+ memcpy(image.scanLine(y), xidata, bpl);
+ xidata += xi->bytes_per_line;
+ }
+ } else {
+ /* Typically 2 or 4 bits display depth */
+ qWarning("QPixmap::convertToImage: Display not supported (bpp=%d)",
+ xi->bits_per_pixel);
+ return QImage();
+ }
+
+ if (d == 1) { // bitmap
+ image.setColorCount(2);
+ image.setColor(0, qRgb(255,255,255));
+ image.setColor(1, qRgb(0,0,0));
+ } else if (!trucol) { // pixmap with colormap
+ uchar *p;
+ uchar *end;
+ uchar use[256]; // pixel-in-use table
+ uchar pix[256]; // pixel translation table
+ int ncols, bpl;
+ memset(use, 0, 256);
+ memset(pix, 0, 256);
+ bpl = image.bytesPerLine();
+
+ if (x11_mask) { // which pixels are used?
+ for (int i = 0; i < xi->height; i++) {
+ uchar* asrc = alpha.scanLine(i);
+ p = image.scanLine(i);
+ if (ale) {
+ for (int x = 0; x < xi->width; x++) {
+ if (asrc[x >> 3] & (1 << (x & 7)))
+ use[*p] = 1;
+ ++p;
+ }
+ } else {
+ for (int x = 0; x < xi->width; x++) {
+ if (asrc[x >> 3] & (0x80 >> (x & 7)))
+ use[*p] = 1;
+ ++p;
+ }
+ }
+ }
+ } else {
+ for (int i = 0; i < xi->height; i++) {
+ p = image.scanLine(i);
+ end = p + bpl;
+ while (p < end)
+ use[*p++] = 1;
+ }
+ }
+ ncols = 0;
+ for (int i = 0; i < 256; i++) { // build translation table
+ if (use[i])
+ pix[i] = ncols++;
+ }
+ for (int i = 0; i < xi->height; i++) { // translate pixels
+ p = image.scanLine(i);
+ end = p + bpl;
+ while (p < end) {
+ *p = pix[*p];
+ p++;
+ }
+ }
+ if (x11_mask) {
+ int trans;
+ if (ncols < 256) {
+ trans = ncols++;
+ image.setColorCount(ncols); // create color table
+ image.setColor(trans, 0x00000000);
+ } else {
+ image.setColorCount(ncols); // create color table
+ // oh dear... no spare "transparent" pixel.
+ // use first pixel in image (as good as any).
+ trans = image.scanLine(0)[0];
+ }
+ for (int i = 0; i < xi->height; i++) {
+ uchar* asrc = alpha.scanLine(i);
+ p = image.scanLine(i);
+ if (ale) {
+ for (int x = 0; x < xi->width; x++) {
+ if (!(asrc[x >> 3] & (1 << (x & 7))))
+ *p = trans;
+ ++p;
+ }
+ } else {
+ for (int x = 0; x < xi->width; x++) {
+ if (!(asrc[x >> 3] & (1 << (7 -(x & 7)))))
+ *p = trans;
+ ++p;
+ }
+ }
+ }
+ } else {
+ image.setColorCount(ncols); // create color table
+ }
+ QVector<QColor> colors = QXcbColormap::instance(xinfo.screen()).colormap();
+ int j = 0;
+ for (int i=0; i<colors.size(); i++) { // translate pixels
+ if (use[i])
+ image.setColor(j++, 0xff000000 | colors.at(i).rgb());
+ }
+ }
+
+ return image;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/nativepainting/qpixmap_x11_p.h b/src/plugins/platforms/xcb/nativepainting/qpixmap_x11_p.h
new file mode 100644
index 0000000000..7392cbfccf
--- /dev/null
+++ b/src/plugins/platforms/xcb/nativepainting/qpixmap_x11_p.h
@@ -0,0 +1,166 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QX11PLATFORMPIXMAP_H
+#define QX11PLATFORMPIXMAP_H
+
+#include <QBitmap>
+#include <QPixmap>
+
+#include <qpa/qplatformpixmap.h>
+#include "qxcbnativepainting.h"
+
+typedef unsigned long XID;
+typedef XID Drawable;
+typedef XID Picture;
+typedef XID Pixmap;
+
+QT_BEGIN_NAMESPACE
+
+class QX11PaintEngine;
+struct QXImageWrapper;
+
+class QX11PlatformPixmap : public QPlatformPixmap
+{
+public:
+ QX11PlatformPixmap(PixelType pixelType);
+ ~QX11PlatformPixmap();
+
+ QPlatformPixmap *createCompatiblePlatformPixmap() const override;
+ void resize(int width, int height) override;
+ void fromImage(const QImage &img, Qt::ImageConversionFlags flags) override;
+ void copy(const QPlatformPixmap *data, const QRect &rect) override;
+ bool scroll(int dx, int dy, const QRect &rect) override;
+ int metric(QPaintDevice::PaintDeviceMetric metric) const override;
+ void fill(const QColor &fillColor) override;
+ QBitmap mask() const override;
+ void setMask(const QBitmap &mask) override;
+ bool hasAlphaChannel() const override;
+ QPixmap transformed(const QTransform &matrix, Qt::TransformationMode mode) const override;
+ QImage toImage() const override;
+ QImage toImage(const QRect &rect) const override;
+ QPaintEngine *paintEngine() const override;
+ qreal devicePixelRatio() const override;
+ void setDevicePixelRatio(qreal scaleFactor) override;
+
+ inline Drawable handle() const { return hd; }
+ inline Picture x11PictureHandle() const { return picture; }
+ inline const QXcbX11Info *x11_info() const { return &xinfo; }
+
+ Pixmap x11ConvertToDefaultDepth();
+ static XID createBitmapFromImage(const QImage &image);
+
+#if QT_CONFIG(xrender)
+ void convertToARGB32(bool preserveContents = true);
+#endif
+
+private:
+ friend class QX11PaintEngine;
+ friend const QXcbX11Info &qt_x11Info(const QPixmap &pixmap);
+ friend void qt_x11SetScreen(QPixmap &pixmap, int screen);
+
+ void release();
+ QImage toImage(const QXImageWrapper &xi, const QRect &rect) const;
+ QBitmap mask_to_bitmap(int screen) const;
+ static Pixmap bitmap_to_mask(const QBitmap &, int screen);
+ void bitmapFromImage(const QImage &image);
+ bool canTakeQImageFromXImage(const QXImageWrapper &xi) const;
+ QImage takeQImageFromXImage(const QXImageWrapper &xi) const;
+
+ Pixmap hd = 0;
+
+ enum Flag {
+ NoFlags = 0x0,
+ Uninitialized = 0x1,
+ Readonly = 0x2,
+ InvertedWhenBoundToTexture = 0x4,
+ GlSurfaceCreatedWithAlpha = 0x8
+ };
+ uint flags;
+
+ QXcbX11Info xinfo;
+ Pixmap x11_mask;
+ Picture picture;
+ Picture mask_picture;
+ Pixmap hd2; // sorted in the default display depth
+ //QPixmap::ShareMode share_mode;
+ qreal dpr;
+
+ QX11PaintEngine *pengine;
+};
+
+inline QX11PlatformPixmap *qt_x11Pixmap(const QPixmap &pixmap)
+{
+ return (pixmap.handle() && pixmap.handle()->classId() == QPlatformPixmap::X11Class)
+ ? static_cast<QX11PlatformPixmap *>(pixmap.handle())
+ : nullptr;
+}
+
+inline Picture qt_x11PictureHandle(const QPixmap &pixmap)
+{
+ if (QX11PlatformPixmap *pm = qt_x11Pixmap(pixmap))
+ return pm->x11PictureHandle();
+
+ return 0;
+}
+
+inline Pixmap qt_x11PixmapHandle(const QPixmap &pixmap)
+{
+ if (QX11PlatformPixmap *pm = qt_x11Pixmap(pixmap))
+ return pm->handle();
+
+ return 0;
+}
+
+inline const QXcbX11Info &qt_x11Info(const QPixmap &pixmap)
+{
+ if (QX11PlatformPixmap *pm = qt_x11Pixmap(pixmap)) {
+ return pm->xinfo;
+ } else {
+ static QXcbX11Info nullX11Info;
+ return nullX11Info;
+ }
+}
+
+int qt_x11SetDefaultScreen(int screen);
+void qt_x11SetScreen(QPixmap &pixmap, int screen);
+
+QT_END_NAMESPACE
+
+#endif // QX11PLATFORMPIXMAP_H
diff --git a/src/plugins/platforms/xcb/nativepainting/qt_x11_p.h b/src/plugins/platforms/xcb/nativepainting/qt_x11_p.h
new file mode 100644
index 0000000000..aa8dfa5af0
--- /dev/null
+++ b/src/plugins/platforms/xcb/nativepainting/qt_x11_p.h
@@ -0,0 +1,199 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT_X11_P_H
+#define QT_X11_P_H
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+
+#if QT_CONFIG(xrender)
+# include "qtessellator_p.h"
+# include <X11/extensions/Xrender.h>
+#endif
+
+#if QT_CONFIG(fontconfig)
+#include <fontconfig/fontconfig.h>
+#endif
+
+#if defined(FT_LCD_FILTER_H)
+#include FT_LCD_FILTER_H
+#endif
+
+#if defined(FC_LCD_FILTER)
+
+#ifndef FC_LCD_FILTER_NONE
+#define FC_LCD_FILTER_NONE FC_LCD_NONE
+#endif
+
+#ifndef FC_LCD_FILTER_DEFAULT
+#define FC_LCD_FILTER_DEFAULT FC_LCD_DEFAULT
+#endif
+
+#ifndef FC_LCD_FILTER_LIGHT
+#define FC_LCD_FILTER_LIGHT FC_LCD_LIGHT
+#endif
+
+#ifndef FC_LCD_FILTER_LEGACY
+#define FC_LCD_FILTER_LEGACY FC_LCD_LEGACY
+#endif
+
+#endif
+
+QT_BEGIN_NAMESPACE
+
+// rename a couple of X defines to get rid of name clashes
+// resolve the conflict between X11's FocusIn and QEvent::FocusIn
+enum {
+ XFocusOut = FocusOut,
+ XFocusIn = FocusIn,
+ XKeyPress = KeyPress,
+ XKeyRelease = KeyRelease,
+ XNone = None,
+ XRevertToParent = RevertToParent,
+ XGrayScale = GrayScale,
+ XCursorShape = CursorShape,
+};
+#undef FocusOut
+#undef FocusIn
+#undef KeyPress
+#undef KeyRelease
+#undef None
+#undef RevertToParent
+#undef GrayScale
+#undef CursorShape
+
+#ifdef FontChange
+#undef FontChange
+#endif
+
+Q_DECLARE_TYPEINFO(XPoint, Q_PRIMITIVE_TYPE);
+Q_DECLARE_TYPEINFO(XRectangle, Q_PRIMITIVE_TYPE);
+Q_DECLARE_TYPEINFO(XChar2b, Q_PRIMITIVE_TYPE);
+#if QT_CONFIG(xrender)
+Q_DECLARE_TYPEINFO(XGlyphElt32, Q_PRIMITIVE_TYPE);
+#endif
+
+struct QX11InfoData;
+
+enum DesktopEnvironment {
+ DE_UNKNOWN,
+ DE_KDE,
+ DE_GNOME,
+ DE_CDE,
+ DE_MEEGO_COMPOSITOR,
+ DE_4DWM
+};
+
+struct QXcbX11Data {
+ Display *display = nullptr;
+
+ // true if Qt is compiled w/ RENDER support and RENDER is supported on the connected Display
+ bool use_xrender = false;
+ int xrender_major = 0;
+ int xrender_version = 0;
+
+ QX11InfoData *screens = nullptr;
+ Visual **argbVisuals = nullptr;
+ Colormap *argbColormaps = nullptr;
+ int screenCount = 0;
+ int defaultScreen = 0;
+
+ // options
+ int visual_class = 0;
+ int visual_id = 0;
+ int color_count = 0;
+ bool custom_cmap = false;
+
+ // outside visual/colormap
+ Visual *visual = nullptr;
+ Colormap colormap = 0;
+
+#if QT_CONFIG(xrender)
+ enum { solid_fill_count = 16 };
+ struct SolidFills {
+ XRenderColor color;
+ int screen;
+ Picture picture;
+ } solid_fills[solid_fill_count];
+ enum { pattern_fill_count = 16 };
+ struct PatternFills {
+ XRenderColor color;
+ XRenderColor bg_color;
+ int screen;
+ int style;
+ bool opaque;
+ Picture picture;
+ } pattern_fills[pattern_fill_count];
+ Picture getSolidFill(int screen, const QColor &c);
+ XRenderColor preMultiply(const QColor &c);
+#endif
+
+ bool fc_antialias = true;
+ int fc_hint_style = 0;
+
+ DesktopEnvironment desktopEnvironment = DE_GNOME;
+};
+
+extern QXcbX11Data *qt_x11Data;
+#define X11 qt_x11Data
+
+struct QX11InfoData {
+ int screen;
+ int dpiX;
+ int dpiY;
+ int depth;
+ int cells;
+ Colormap colormap;
+ Visual *visual;
+ bool defaultColormap;
+ bool defaultVisual;
+ int subpixel = 0;
+};
+
+template <class T>
+Q_DECL_RELAXED_CONSTEXPR inline int lowest_bit(T v) Q_DECL_NOTHROW
+{
+ int result = qCountTrailingZeroBits(v);
+ return ((result >> 3) == sizeof(T)) ? -1 : result;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_X11_P_H
diff --git a/src/plugins/platforms/xcb/nativepainting/qtessellator.cpp b/src/plugins/platforms/xcb/nativepainting/qtessellator.cpp
new file mode 100644
index 0000000000..1afa00cfc9
--- /dev/null
+++ b/src/plugins/platforms/xcb/nativepainting/qtessellator.cpp
@@ -0,0 +1,1500 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtessellator_p.h"
+
+#include <QRect>
+#include <QList>
+#include <QDebug>
+
+#include <qmath.h>
+#include <limits.h>
+#include <algorithm>
+
+QT_BEGIN_NAMESPACE
+
+//#define DEBUG
+#ifdef DEBUG
+#define QDEBUG qDebug
+#else
+#define QDEBUG if (1){} else qDebug
+#endif
+
+static const bool emit_clever = true;
+static const bool mark_clever = false;
+
+enum VertexFlags {
+ LineBeforeStarts = 0x1,
+ LineBeforeEnds = 0x2,
+ LineBeforeHorizontal = 0x4,
+ LineAfterStarts = 0x8,
+ LineAfterEnds = 0x10,
+ LineAfterHorizontal = 0x20
+};
+
+
+
+class QTessellatorPrivate {
+public:
+ struct Vertices;
+
+ QTessellatorPrivate() {}
+
+ QRectF collectAndSortVertices(const QPointF *points, int *maxActiveEdges);
+ void cancelCoincidingEdges();
+
+ void emitEdges(QTessellator *tessellator);
+ void processIntersections();
+ void removeEdges();
+ void addEdges();
+ void addIntersections();
+
+ struct Vertex : public QTessellator::Vertex
+ {
+ int flags;
+ };
+
+ struct Intersection
+ {
+ Q27Dot5 y;
+ int edge;
+ bool operator <(const Intersection &other) const {
+ if (y != other.y)
+ return y < other.y;
+ return edge < other.edge;
+ }
+ };
+ struct IntersectionLink
+ {
+ int next;
+ int prev;
+ };
+ typedef QMap<Intersection, IntersectionLink> Intersections;
+
+ struct Edge {
+ Edge(const Vertices &v, int _edge);
+ int edge;
+ const Vertex *v0;
+ const Vertex *v1;
+ Q27Dot5 y_left;
+ Q27Dot5 y_right;
+ signed int winding : 8;
+ bool mark;
+ bool free;
+ bool intersect_left;
+ bool intersect_right;
+ bool isLeftOf(const Edge &other, Q27Dot5 y) const;
+ Q27Dot5 positionAt(Q27Dot5 y) const;
+ bool intersect(const Edge &other, Q27Dot5 *y, bool *det_positive) const;
+
+ };
+
+ class EdgeSorter
+ {
+ public:
+ EdgeSorter(int _y) : y(_y) {}
+ bool operator() (const Edge *e1, const Edge *e2);
+ int y;
+ };
+
+ class Scanline {
+ public:
+ Scanline();
+ ~Scanline();
+
+ void init(int maxActiveEdges);
+ void done();
+
+ int findEdgePosition(Q27Dot5 x, Q27Dot5 y) const;
+ int findEdgePosition(const Edge &e) const;
+ int findEdge(int edge) const;
+ void clearMarks();
+
+ void swap(int p1, int p2) {
+ Edge *tmp = edges[p1];
+ edges[p1] = edges[p2];
+ edges[p2] = tmp;
+ }
+ void insert(int pos, const Edge &e);
+ void removeAt(int pos);
+ void markEdges(int pos1, int pos2);
+
+ void prepareLine();
+ void lineDone();
+
+ Edge **old;
+ int old_size;
+
+ Edge **edges;
+ int size;
+
+ private:
+ Edge *edge_table;
+ int first_unused;
+ int max_edges;
+ enum { default_alloc = 32 };
+ };
+
+ struct Vertices {
+ enum { default_alloc = 128 };
+ Vertices();
+ ~Vertices();
+ void init(int maxVertices);
+ void done();
+ Vertex *storage;
+ Vertex **sorted;
+
+ Vertex *operator[] (int i) { return storage + i; }
+ const Vertex *operator[] (int i) const { return storage + i; }
+ int position(const Vertex *v) const {
+ return v - storage;
+ }
+ Vertex *next(Vertex *v) {
+ ++v;
+ if (v == storage + nPoints)
+ v = storage;
+ return v;
+ }
+ const Vertex *next(const Vertex *v) const {
+ ++v;
+ if (v == storage + nPoints)
+ v = storage;
+ return v;
+ }
+ int nextPos(const Vertex *v) const {
+ ++v;
+ if (v == storage + nPoints)
+ return 0;
+ return v - storage;
+ }
+ Vertex *prev(Vertex *v) {
+ if (v == storage)
+ v = storage + nPoints;
+ --v;
+ return v;
+ }
+ const Vertex *prev(const Vertex *v) const {
+ if (v == storage)
+ v = storage + nPoints;
+ --v;
+ return v;
+ }
+ int prevPos(const Vertex *v) const {
+ if (v == storage)
+ v = storage + nPoints;
+ --v;
+ return v - storage;
+ }
+ int nPoints;
+ int allocated;
+ };
+ Vertices vertices;
+ Intersections intersections;
+ Scanline scanline;
+ bool winding;
+ Q27Dot5 y;
+ int currentVertex;
+
+private:
+ void addIntersection(const Edge *e1, const Edge *e2);
+ bool edgeInChain(Intersection i, int edge);
+};
+
+
+QTessellatorPrivate::Edge::Edge(const QTessellatorPrivate::Vertices &vertices, int edge)
+{
+ this->edge = edge;
+ intersect_left = intersect_right = true;
+ mark = false;
+ free = false;
+
+ v0 = vertices[edge];
+ v1 = vertices.next(v0);
+
+ Q_ASSERT(v0->y != v1->y);
+
+ if (v0->y > v1->y) {
+ qSwap(v0, v1);
+ winding = -1;
+ } else {
+ winding = 1;
+ }
+ y_left = y_right = v0->y;
+}
+
+// This is basically the algorithm from graphics gems. The algorithm
+// is cubic in the coordinates at one place. Since we use 64bit
+// integers, this implies, that the allowed range for our coordinates
+// is limited to 21 bits. With 5 bits behind the decimal, this
+// implies that differences in coordaintes can range from 2*SHORT_MIN
+// to 2*SHORT_MAX, giving us efficiently a coordinate system from
+// SHORT_MIN to SHORT_MAX.
+//
+
+// WARNING: It's absolutely critical that the intersect() and isLeftOf() methods use
+// exactly the same algorithm to calulate yi. It's also important to be sure the algorithms
+// are transitive (ie. the conditions below are true for all input data):
+//
+// a.intersect(b) == b.intersect(a)
+// a.isLeftOf(b) != b.isLeftOf(a)
+//
+// This is tricky to get right, so be very careful when changing anything in here!
+
+static inline bool sameSign(qint64 a, qint64 b) {
+ return (((qint64) ((quint64) a ^ (quint64) b)) >= 0 );
+}
+
+bool QTessellatorPrivate::Edge::intersect(const Edge &other, Q27Dot5 *y, bool *det_positive) const
+{
+ qint64 a1 = v1->y - v0->y;
+ qint64 b1 = v0->x - v1->x;
+
+ qint64 a2 = other.v1->y - other.v0->y;
+ qint64 b2 = other.v0->x - other.v1->x;
+
+ qint64 det = a1 * b2 - a2 * b1;
+ if (det == 0)
+ return false;
+
+ qint64 c1 = qint64(v1->x) * v0->y - qint64(v0->x) * v1->y;
+
+ qint64 r3 = a1 * other.v0->x + b1 * other.v0->y + c1;
+ qint64 r4 = a1 * other.v1->x + b1 * other.v1->y + c1;
+
+ // Check signs of r3 and r4. If both point 3 and point 4 lie on
+ // same side of line 1, the line segments do not intersect.
+ QDEBUG() << " " << r3 << r4;
+ if (r3 != 0 && r4 != 0 && sameSign( r3, r4 ))
+ return false;
+
+ qint64 c2 = qint64(other.v1->x) * other.v0->y - qint64(other.v0->x) * other.v1->y;
+
+ qint64 r1 = a2 * v0->x + b2 * v0->y + c2;
+ qint64 r2 = a2 * v1->x + b2 * v1->y + c2;
+
+ // Check signs of r1 and r2. If both point 1 and point 2 lie
+ // on same side of second line segment, the line segments do not intersect.
+ QDEBUG() << " " << r1 << r2;
+ if (r1 != 0 && r2 != 0 && sameSign( r1, r2 ))
+ return false;
+
+ // The det/2 is to get rounding instead of truncating. It
+ // is added or subtracted to the numerator, depending upon the
+ // sign of the numerator.
+ qint64 offset = det < 0 ? -det : det;
+ offset >>= 1;
+
+ qint64 num = a2 * c1 - a1 * c2;
+ *y = ( num < 0 ? num - offset : num + offset ) / det;
+
+ *det_positive = (det > 0);
+
+ return true;
+}
+
+#undef SAME_SIGNS
+
+bool QTessellatorPrivate::Edge::isLeftOf(const Edge &other, Q27Dot5 y) const
+{
+// QDEBUG() << "isLeftOf" << edge << other.edge << y;
+ qint64 a1 = v1->y - v0->y;
+ qint64 b1 = v0->x - v1->x;
+ qint64 a2 = other.v1->y - other.v0->y;
+ qint64 b2 = other.v0->x - other.v1->x;
+
+ qint64 c2 = qint64(other.v1->x) * other.v0->y - qint64(other.v0->x) * other.v1->y;
+
+ qint64 det = a1 * b2 - a2 * b1;
+ if (det == 0) {
+ // lines are parallel. Only need to check side of one point
+ // fixed ordering for coincident edges
+ qint64 r1 = a2 * v0->x + b2 * v0->y + c2;
+// QDEBUG() << "det = 0" << r1;
+ if (r1 == 0)
+ return edge < other.edge;
+ return (r1 < 0);
+ }
+
+ // not parallel, need to find the y coordinate of the intersection point
+ qint64 c1 = qint64(v1->x) * v0->y - qint64(v0->x) * v1->y;
+
+ qint64 offset = det < 0 ? -det : det;
+ offset >>= 1;
+
+ qint64 num = a2 * c1 - a1 * c2;
+ qint64 yi = ( num < 0 ? num - offset : num + offset ) / det;
+// QDEBUG() << " num=" << num << "offset=" << offset << "det=" << det;
+
+ return ((yi > y) ^ (det < 0));
+}
+
+static inline bool compareVertex(const QTessellatorPrivate::Vertex *p1,
+ const QTessellatorPrivate::Vertex *p2)
+{
+ if (p1->y == p2->y) {
+ if (p1->x == p2->x)
+ return p1 < p2;
+ return p1->x < p2->x;
+ }
+ return p1->y < p2->y;
+}
+
+Q27Dot5 QTessellatorPrivate::Edge::positionAt(Q27Dot5 y) const
+{
+ if (y == v0->y)
+ return v0->x;
+ else if (y == v1->y)
+ return v1->x;
+
+ qint64 d = v1->x - v0->x;
+ return (v0->x + d*(y - v0->y)/(v1->y-v0->y));
+}
+
+bool QTessellatorPrivate::EdgeSorter::operator() (const Edge *e1, const Edge *e2)
+{
+ return e1->isLeftOf(*e2, y);
+}
+
+
+QTessellatorPrivate::Scanline::Scanline()
+{
+ edges = 0;
+ edge_table = 0;
+ old = 0;
+}
+
+void QTessellatorPrivate::Scanline::init(int maxActiveEdges)
+{
+ maxActiveEdges *= 2;
+ if (!edges || maxActiveEdges > default_alloc) {
+ max_edges = maxActiveEdges;
+ int s = qMax(maxActiveEdges + 1, default_alloc + 1);
+ edges = q_check_ptr((Edge **)realloc(edges, s*sizeof(Edge *)));
+ edge_table = q_check_ptr((Edge *)realloc(edge_table, s*sizeof(Edge)));
+ old = q_check_ptr((Edge **)realloc(old, s*sizeof(Edge *)));
+ }
+ size = 0;
+ old_size = 0;
+ first_unused = 0;
+ for (int i = 0; i < maxActiveEdges; ++i)
+ edge_table[i].edge = i+1;
+ edge_table[maxActiveEdges].edge = -1;
+}
+
+void QTessellatorPrivate::Scanline::done()
+{
+ if (max_edges > default_alloc) {
+ free(edges);
+ free(old);
+ free(edge_table);
+ edges = 0;
+ old = 0;
+ edge_table = 0;
+ }
+}
+
+QTessellatorPrivate::Scanline::~Scanline()
+{
+ free(edges);
+ free(old);
+ free(edge_table);
+}
+
+int QTessellatorPrivate::Scanline::findEdgePosition(Q27Dot5 x, Q27Dot5 y) const
+{
+ int min = 0;
+ int max = size - 1;
+ while (min < max) {
+ int pos = min + ((max - min + 1) >> 1);
+ Q27Dot5 ax = edges[pos]->positionAt(y);
+ if (ax > x) {
+ max = pos - 1;
+ } else {
+ min = pos;
+ }
+ }
+ return min;
+}
+
+int QTessellatorPrivate::Scanline::findEdgePosition(const Edge &e) const
+{
+// qDebug() << ">> findEdgePosition";
+ int min = 0;
+ int max = size;
+ while (min < max) {
+ int pos = min + ((max - min) >> 1);
+// qDebug() << " " << min << max << pos << edges[pos]->isLeftOf(e, e.y0);
+ if (edges[pos]->isLeftOf(e, e.v0->y)) {
+ min = pos + 1;
+ } else {
+ max = pos;
+ }
+ }
+// qDebug() << "<< findEdgePosition got" << min;
+ return min;
+}
+
+int QTessellatorPrivate::Scanline::findEdge(int edge) const
+{
+ for (int i = 0; i < size; ++i) {
+ int item_edge = edges[i]->edge;
+ if (item_edge == edge)
+ return i;
+ }
+ //Q_ASSERT(false);
+ return -1;
+}
+
+void QTessellatorPrivate::Scanline::clearMarks()
+{
+ for (int i = 0; i < size; ++i) {
+ edges[i]->mark = false;
+ edges[i]->intersect_left = false;
+ edges[i]->intersect_right = false;
+ }
+}
+
+void QTessellatorPrivate::Scanline::prepareLine()
+{
+ Edge **end = edges + size;
+ Edge **e = edges;
+ Edge **o = old;
+ while (e < end) {
+ *o = *e;
+ ++o;
+ ++e;
+ }
+ old_size = size;
+}
+
+void QTessellatorPrivate::Scanline::lineDone()
+{
+ Edge **end = old + old_size;
+ Edge **e = old;
+ while (e < end) {
+ if ((*e)->free) {
+ (*e)->edge = first_unused;
+ first_unused = (*e - edge_table);
+ }
+ ++e;
+ }
+}
+
+void QTessellatorPrivate::Scanline::insert(int pos, const Edge &e)
+{
+ Edge *edge = edge_table + first_unused;
+ first_unused = edge->edge;
+ Q_ASSERT(first_unused != -1);
+ *edge = e;
+ memmove(edges + pos + 1, edges + pos, (size - pos)*sizeof(Edge *));
+ edges[pos] = edge;
+ ++size;
+}
+
+void QTessellatorPrivate::Scanline::removeAt(int pos)
+{
+ Edge *e = edges[pos];
+ e->free = true;
+ --size;
+ memmove(edges + pos, edges + pos + 1, (size - pos)*sizeof(Edge *));
+}
+
+void QTessellatorPrivate::Scanline::markEdges(int pos1, int pos2)
+{
+ if (pos2 < pos1)
+ return;
+
+ for (int i = pos1; i <= pos2; ++i)
+ edges[i]->mark = true;
+}
+
+
+QTessellatorPrivate::Vertices::Vertices()
+{
+ storage = 0;
+ sorted = 0;
+ allocated = 0;
+ nPoints = 0;
+}
+
+QTessellatorPrivate::Vertices::~Vertices()
+{
+ if (storage) {
+ free(storage);
+ free(sorted);
+ }
+}
+
+void QTessellatorPrivate::Vertices::init(int maxVertices)
+{
+ if (!storage || maxVertices > allocated) {
+ int size = qMax((int)default_alloc, maxVertices);
+ storage = q_check_ptr((Vertex *)realloc(storage, size*sizeof(Vertex)));
+ sorted = q_check_ptr((Vertex **)realloc(sorted, size*sizeof(Vertex *)));
+ allocated = maxVertices;
+ }
+}
+
+void QTessellatorPrivate::Vertices::done()
+{
+ if (allocated > default_alloc) {
+ free(storage);
+ free(sorted);
+ storage = 0;
+ sorted = 0;
+ allocated = 0;
+ }
+}
+
+
+
+static inline void fillTrapezoid(Q27Dot5 y1, Q27Dot5 y2, int left, int right,
+ const QTessellatorPrivate::Vertices &vertices,
+ QTessellator::Trapezoid *trap)
+{
+ trap->top = y1;
+ trap->bottom = y2;
+ const QTessellatorPrivate::Vertex *v = vertices[left];
+ trap->topLeft = v;
+ trap->bottomLeft = vertices.next(v);
+ if (trap->topLeft->y > trap->bottomLeft->y)
+ qSwap(trap->topLeft,trap->bottomLeft);
+ v = vertices[right];
+ trap->topRight = v;
+ trap->bottomRight = vertices.next(v);
+ if (trap->topRight->y > trap->bottomRight->y)
+ qSwap(trap->topRight, trap->bottomRight);
+}
+
+QRectF QTessellatorPrivate::collectAndSortVertices(const QPointF *points, int *maxActiveEdges)
+{
+ *maxActiveEdges = 0;
+ Vertex *v = vertices.storage;
+ Vertex **vv = vertices.sorted;
+
+ qreal xmin(points[0].x());
+ qreal xmax(points[0].x());
+ qreal ymin(points[0].y());
+ qreal ymax(points[0].y());
+
+ // collect vertex data
+ Q27Dot5 y_prev = FloatToQ27Dot5(points[vertices.nPoints-1].y());
+ Q27Dot5 x_next = FloatToQ27Dot5(points[0].x());
+ Q27Dot5 y_next = FloatToQ27Dot5(points[0].y());
+ int j = 0;
+ int i = 0;
+ while (i < vertices.nPoints) {
+ Q27Dot5 y_curr = y_next;
+
+ *vv = v;
+
+ v->x = x_next;
+ v->y = y_next;
+ v->flags = 0;
+
+ next_point:
+
+ xmin = qMin(xmin, points[i+1].x());
+ xmax = qMax(xmax, points[i+1].x());
+ ymin = qMin(ymin, points[i+1].y());
+ ymax = qMax(ymax, points[i+1].y());
+
+ y_next = FloatToQ27Dot5(points[i+1].y());
+ x_next = FloatToQ27Dot5(points[i+1].x());
+
+ // skip vertices on top of each other
+ if (v->x == x_next && v->y == y_next) {
+ ++i;
+ if (i < vertices.nPoints)
+ goto next_point;
+ Vertex *v0 = vertices.storage;
+ v0->flags &= ~(LineBeforeStarts|LineBeforeEnds|LineBeforeHorizontal);
+ if (y_prev < y_curr)
+ v0->flags |= LineBeforeEnds;
+ else if (y_prev > y_curr)
+ v0->flags |= LineBeforeStarts;
+ else
+ v0->flags |= LineBeforeHorizontal;
+ if ((v0->flags & (LineBeforeStarts|LineAfterStarts))
+ && !(v0->flags & (LineAfterEnds|LineBeforeEnds)))
+ *maxActiveEdges += 2;
+ break;
+ }
+
+ if (y_prev < y_curr)
+ v->flags |= LineBeforeEnds;
+ else if (y_prev > y_curr)
+ v->flags |= LineBeforeStarts;
+ else
+ v->flags |= LineBeforeHorizontal;
+
+
+ if (y_curr < y_next)
+ v->flags |= LineAfterStarts;
+ else if (y_curr > y_next)
+ v->flags |= LineAfterEnds;
+ else
+ v->flags |= LineAfterHorizontal;
+ // ### could probably get better limit by looping over sorted list and counting down on ending edges
+ if ((v->flags & (LineBeforeStarts|LineAfterStarts))
+ && !(v->flags & (LineAfterEnds|LineBeforeEnds)))
+ *maxActiveEdges += 2;
+ y_prev = y_curr;
+ ++v;
+ ++vv;
+ ++j;
+ ++i;
+ }
+ vertices.nPoints = j;
+
+ QDEBUG() << "maxActiveEdges=" << *maxActiveEdges;
+ vv = vertices.sorted;
+ std::sort(vv, vv + vertices.nPoints, compareVertex);
+
+ return QRectF(xmin, ymin, xmax-xmin, ymax-ymin);
+}
+
+struct QCoincidingEdge {
+ QTessellatorPrivate::Vertex *start;
+ QTessellatorPrivate::Vertex *end;
+ bool used;
+ bool before;
+
+ inline bool operator<(const QCoincidingEdge &e2) const
+ {
+ return end->y == e2.end->y ? end->x < e2.end->x : end->y < e2.end->y;
+ }
+};
+
+static void cancelEdges(QCoincidingEdge &e1, QCoincidingEdge &e2)
+{
+ if (e1.before) {
+ e1.start->flags &= ~(LineBeforeStarts|LineBeforeHorizontal);
+ e1.end->flags &= ~(LineAfterEnds|LineAfterHorizontal);
+ } else {
+ e1.start->flags &= ~(LineAfterStarts|LineAfterHorizontal);
+ e1.end->flags &= ~(LineBeforeEnds|LineBeforeHorizontal);
+ }
+ if (e2.before) {
+ e2.start->flags &= ~(LineBeforeStarts|LineBeforeHorizontal);
+ e2.end->flags &= ~(LineAfterEnds|LineAfterHorizontal);
+ } else {
+ e2.start->flags &= ~(LineAfterStarts|LineAfterHorizontal);
+ e2.end->flags &= ~(LineBeforeEnds|LineBeforeHorizontal);
+ }
+ e1.used = e2.used = true;
+}
+
+void QTessellatorPrivate::cancelCoincidingEdges()
+{
+ Vertex **vv = vertices.sorted;
+
+ QCoincidingEdge *tl = 0;
+ int tlSize = 0;
+
+ for (int i = 0; i < vertices.nPoints - 1; ++i) {
+ Vertex *v = vv[i];
+ int testListSize = 0;
+ while (i < vertices.nPoints - 1) {
+ Vertex *n = vv[i];
+ if (v->x != n->x || v->y != n->y)
+ break;
+
+ if (testListSize > tlSize - 2) {
+ tlSize = qMax(tlSize*2, 16);
+ tl = q_check_ptr((QCoincidingEdge *)realloc(tl, tlSize*sizeof(QCoincidingEdge)));
+ }
+ if (n->flags & (LineBeforeStarts|LineBeforeHorizontal)) {
+ tl[testListSize].start = n;
+ tl[testListSize].end = vertices.prev(n);
+ tl[testListSize].used = false;
+ tl[testListSize].before = true;
+ ++testListSize;
+ }
+ if (n->flags & (LineAfterStarts|LineAfterHorizontal)) {
+ tl[testListSize].start = n;
+ tl[testListSize].end = vertices.next(n);
+ tl[testListSize].used = false;
+ tl[testListSize].before = false;
+ ++testListSize;
+ }
+ ++i;
+ }
+ if (!testListSize)
+ continue;
+
+ std::sort(tl, tl + testListSize);
+
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_CLANG("-Wfor-loop-analysis")
+ for (int j = 0; j < testListSize; ++j) {
+ if (tl[j].used)
+ continue;
+
+ for (int k = j + 1; k < testListSize; ++k) {
+ if (tl[j].end->x != tl[k].end->x
+ || tl[j].end->y != tl[k].end->y
+ || tl[k].used)
+ break;
+
+ if (!winding || tl[j].before != tl[k].before) {
+ cancelEdges(tl[j], tl[k]);
+ break;
+ }
+ ++k;
+ }
+ ++j;
+ }
+QT_WARNING_POP
+ }
+ free(tl);
+}
+
+
+void QTessellatorPrivate::emitEdges(QTessellator *tessellator)
+{
+ //QDEBUG() << "TRAPS:";
+ if (!scanline.old_size)
+ return;
+
+ // emit edges
+ if (winding) {
+ // winding fill rule
+ int w = 0;
+
+ scanline.old[0]->y_left = y;
+
+ for (int i = 0; i < scanline.old_size - 1; ++i) {
+ Edge *left = scanline.old[i];
+ Edge *right = scanline.old[i+1];
+ w += left->winding;
+// qDebug() << "i=" << i << "edge->winding=" << left->winding << "winding=" << winding;
+ if (w == 0) {
+ left->y_right = y;
+ right->y_left = y;
+ } else if (!emit_clever || left->mark || right->mark) {
+ Q27Dot5 top = qMax(left->y_right, right->y_left);
+ if (top != y) {
+ QTessellator::Trapezoid trap;
+ fillTrapezoid(top, y, left->edge, right->edge, vertices, &trap);
+ tessellator->addTrap(trap);
+// QDEBUG() << " top=" << Q27Dot5ToDouble(top) << "left=" << left->edge << "right=" << right->edge;
+ }
+ right->y_left = y;
+ left->y_right = y;
+ }
+ left->mark = false;
+ }
+ if (scanline.old[scanline.old_size - 1]->mark) {
+ scanline.old[scanline.old_size - 1]->y_right = y;
+ scanline.old[scanline.old_size - 1]->mark = false;
+ }
+ } else {
+ // odd-even fill rule
+ for (int i = 0; i < scanline.old_size; i += 2) {
+ Edge *left = scanline.old[i];
+ Edge *right = scanline.old[i+1];
+ if (!emit_clever || left->mark || right->mark) {
+ Q27Dot5 top = qMax(left->y_right, right->y_left);
+ if (top != y) {
+ QTessellator::Trapezoid trap;
+ fillTrapezoid(top, y, left->edge, right->edge, vertices, &trap);
+ tessellator->addTrap(trap);
+ }
+// QDEBUG() << " top=" << Q27Dot5ToDouble(top) << "left=" << left->edge << "right=" << right->edge;
+ left->y_left = y;
+ left->y_right = y;
+ right->y_left = y;
+ right->y_right = y;
+ left->mark = right->mark = false;
+ }
+ }
+ }
+}
+
+
+void QTessellatorPrivate::processIntersections()
+{
+ QDEBUG() << "PROCESS INTERSECTIONS";
+ // process intersections
+ while (!intersections.isEmpty()) {
+ Intersections::iterator it = intersections.begin();
+ if (it.key().y != y)
+ break;
+
+ // swap edges
+ QDEBUG() << " swapping intersecting edges ";
+ int min = scanline.size;
+ int max = 0;
+ Q27Dot5 xmin = INT_MAX;
+ Q27Dot5 xmax = INT_MIN;
+ int num = 0;
+ while (1) {
+ const Intersection i = it.key();
+ int next = it->next;
+
+ int edgePos = scanline.findEdge(i.edge);
+ if (edgePos >= 0) {
+ ++num;
+ min = qMin(edgePos, min);
+ max = qMax(edgePos, max);
+ Edge *edge = scanline.edges[edgePos];
+ xmin = qMin(xmin, edge->positionAt(y));
+ xmax = qMax(xmax, edge->positionAt(y));
+ }
+ Intersection key;
+ key.y = y;
+ key.edge = next;
+ it = intersections.find(key);
+ intersections.remove(i);
+ if (it == intersections.end())
+ break;
+ }
+ if (num < 2)
+ continue;
+
+ Q_ASSERT(min != max);
+ QDEBUG() << "sorting between" << min << "and" << max << "xpos=" << xmin << xmax;
+ while (min > 0 && scanline.edges[min - 1]->positionAt(y) >= xmin) {
+ QDEBUG() << " adding edge on left";
+ --min;
+ }
+ while (max < scanline.size - 1 && scanline.edges[max + 1]->positionAt(y) <= xmax) {
+ QDEBUG() << " adding edge on right";
+ ++max;
+ }
+
+ std::sort(scanline.edges + min, scanline.edges + max + 1, EdgeSorter(y));
+#ifdef DEBUG
+ for (int i = min; i <= max; ++i)
+ QDEBUG() << " " << scanline.edges[i]->edge << "at pos" << i;
+#endif
+ for (int i = min; i <= max; ++i) {
+ Edge *edge = scanline.edges[i];
+ edge->intersect_left = true;
+ edge->intersect_right = true;
+ edge->mark = true;
+ }
+ }
+}
+
+void QTessellatorPrivate::removeEdges()
+{
+ int cv = currentVertex;
+ while (cv < vertices.nPoints) {
+ const Vertex *v = vertices.sorted[cv];
+ if (v->y > y)
+ break;
+ if (v->flags & LineBeforeEnds) {
+ QDEBUG() << " removing edge" << vertices.prevPos(v);
+ int pos = scanline.findEdge(vertices.prevPos(v));
+ if (pos == -1)
+ continue;
+ scanline.edges[pos]->mark = true;
+ if (pos > 0)
+ scanline.edges[pos - 1]->intersect_right = true;
+ if (pos < scanline.size - 1)
+ scanline.edges[pos + 1]->intersect_left = true;
+ scanline.removeAt(pos);
+ }
+ if (v->flags & LineAfterEnds) {
+ QDEBUG() << " removing edge" << vertices.position(v);
+ int pos = scanline.findEdge(vertices.position(v));
+ if (pos == -1)
+ continue;
+ scanline.edges[pos]->mark = true;
+ if (pos > 0)
+ scanline.edges[pos - 1]->intersect_right = true;
+ if (pos < scanline.size - 1)
+ scanline.edges[pos + 1]->intersect_left = true;
+ scanline.removeAt(pos);
+ }
+ ++cv;
+ }
+}
+
+void QTessellatorPrivate::addEdges()
+{
+ while (currentVertex < vertices.nPoints) {
+ const Vertex *v = vertices.sorted[currentVertex];
+ if (v->y > y)
+ break;
+ if (v->flags & LineBeforeStarts) {
+ // add new edge
+ int start = vertices.prevPos(v);
+ Edge e(vertices, start);
+ int pos = scanline.findEdgePosition(e);
+ QDEBUG() << " adding edge" << start << "at position" << pos;
+ scanline.insert(pos, e);
+ if (!mark_clever || !(v->flags & LineAfterEnds)) {
+ if (pos > 0)
+ scanline.edges[pos - 1]->mark = true;
+ if (pos < scanline.size - 1)
+ scanline.edges[pos + 1]->mark = true;
+ }
+ }
+ if (v->flags & LineAfterStarts) {
+ Edge e(vertices, vertices.position(v));
+ int pos = scanline.findEdgePosition(e);
+ QDEBUG() << " adding edge" << vertices.position(v) << "at position" << pos;
+ scanline.insert(pos, e);
+ if (!mark_clever || !(v->flags & LineBeforeEnds)) {
+ if (pos > 0)
+ scanline.edges[pos - 1]->mark = true;
+ if (pos < scanline.size - 1)
+ scanline.edges[pos + 1]->mark = true;
+ }
+ }
+ if (v->flags & LineAfterHorizontal) {
+ int pos1 = scanline.findEdgePosition(v->x, v->y);
+ const Vertex *next = vertices.next(v);
+ Q_ASSERT(v->y == next->y);
+ int pos2 = scanline.findEdgePosition(next->x, next->y);
+ if (pos2 < pos1)
+ qSwap(pos1, pos2);
+ if (pos1 > 0)
+ --pos1;
+ if (pos2 == scanline.size)
+ --pos2;
+ //QDEBUG() << "marking horizontal edge from " << pos1 << "to" << pos2;
+ scanline.markEdges(pos1, pos2);
+ }
+ ++currentVertex;
+ }
+}
+
+#ifdef DEBUG
+static void checkLinkChain(const QTessellatorPrivate::Intersections &intersections,
+ QTessellatorPrivate::Intersection i)
+{
+// qDebug() << " Link chain: ";
+ int end = i.edge;
+ while (1) {
+ QTessellatorPrivate::IntersectionLink l = intersections.value(i);
+// qDebug() << " " << i.edge << "next=" << l.next << "prev=" << l.prev;
+ if (l.next == end)
+ break;
+ Q_ASSERT(l.next != -1);
+ Q_ASSERT(l.prev != -1);
+
+ QTessellatorPrivate::Intersection i2 = i;
+ i2.edge = l.next;
+ QTessellatorPrivate::IntersectionLink l2 = intersections.value(i2);
+
+ Q_ASSERT(l2.next != -1);
+ Q_ASSERT(l2.prev != -1);
+ Q_ASSERT(l.next == i2.edge);
+ Q_ASSERT(l2.prev == i.edge);
+ i = i2;
+ }
+}
+#endif
+
+bool QTessellatorPrivate::edgeInChain(Intersection i, int edge)
+{
+ int end = i.edge;
+ while (1) {
+ if (i.edge == edge)
+ return true;
+ IntersectionLink l = intersections.value(i);
+ if (l.next == end)
+ break;
+ Q_ASSERT(l.next != -1);
+ Q_ASSERT(l.prev != -1);
+
+ Intersection i2 = i;
+ i2.edge = l.next;
+
+#ifndef QT_NO_DEBUG
+ IntersectionLink l2 = intersections.value(i2);
+ Q_ASSERT(l2.next != -1);
+ Q_ASSERT(l2.prev != -1);
+ Q_ASSERT(l.next == i2.edge);
+ Q_ASSERT(l2.prev == i.edge);
+#endif
+ i = i2;
+ }
+ return false;
+}
+
+
+void QTessellatorPrivate::addIntersection(const Edge *e1, const Edge *e2)
+{
+ const IntersectionLink emptyLink = {-1, -1};
+
+ int next = vertices.nextPos(vertices[e1->edge]);
+ if (e2->edge == next)
+ return;
+ int prev = vertices.prevPos(vertices[e1->edge]);
+ if (e2->edge == prev)
+ return;
+
+ Q27Dot5 yi;
+ bool det_positive;
+ bool isect = e1->intersect(*e2, &yi, &det_positive);
+ QDEBUG("checking edges %d and %d", e1->edge, e2->edge);
+ if (!isect) {
+ QDEBUG() << " no intersection";
+ return;
+ }
+
+ // don't emit an intersection if it's at the start of a line segment or above us
+ if (yi <= y) {
+ if (!det_positive)
+ return;
+ QDEBUG() << " ----->>>>>> WRONG ORDER!";
+ yi = y;
+ }
+ QDEBUG() << " between edges " << e1->edge << "and" << e2->edge << "at point ("
+ << Q27Dot5ToDouble(yi) << ')';
+
+ Intersection i1;
+ i1.y = yi;
+ i1.edge = e1->edge;
+ IntersectionLink link1 = intersections.value(i1, emptyLink);
+ Intersection i2;
+ i2.y = yi;
+ i2.edge = e2->edge;
+ IntersectionLink link2 = intersections.value(i2, emptyLink);
+
+ // new pair of edges
+ if (link1.next == -1 && link2.next == -1) {
+ link1.next = link1.prev = i2.edge;
+ link2.next = link2.prev = i1.edge;
+ } else if (link1.next == i2.edge || link1.prev == i2.edge
+ || link2.next == i1.edge || link2.prev == i1.edge) {
+#ifdef DEBUG
+ checkLinkChain(intersections, i1);
+ checkLinkChain(intersections, i2);
+ Q_ASSERT(edgeInChain(i1, i2.edge));
+#endif
+ return;
+ } else if (link1.next == -1 || link2.next == -1) {
+ if (link2.next == -1) {
+ qSwap(i1, i2);
+ qSwap(link1, link2);
+ }
+ Q_ASSERT(link1.next == -1);
+#ifdef DEBUG
+ checkLinkChain(intersections, i2);
+#endif
+ // only i2 in list
+ link1.next = i2.edge;
+ link1.prev = link2.prev;
+ link2.prev = i1.edge;
+ Intersection other;
+ other.y = yi;
+ other.edge = link1.prev;
+ IntersectionLink link = intersections.value(other, emptyLink);
+ Q_ASSERT(link.next == i2.edge);
+ Q_ASSERT(link.prev != -1);
+ link.next = i1.edge;
+ intersections.insert(other, link);
+ } else {
+ bool connected = edgeInChain(i1, i2.edge);
+ if (connected)
+ return;
+#ifdef DEBUG
+ checkLinkChain(intersections, i1);
+ checkLinkChain(intersections, i2);
+#endif
+ // both already in some list. Have to make sure they are connected
+ // this can be done by cutting open the ring(s) after the two eges and
+ // connecting them again
+ Intersection other1;
+ other1.y = yi;
+ other1.edge = link1.next;
+ IntersectionLink linko1 = intersections.value(other1, emptyLink);
+ Intersection other2;
+ other2.y = yi;
+ other2.edge = link2.next;
+ IntersectionLink linko2 = intersections.value(other2, emptyLink);
+
+ linko1.prev = i2.edge;
+ link2.next = other1.edge;
+
+ linko2.prev = i1.edge;
+ link1.next = other2.edge;
+ intersections.insert(other1, linko1);
+ intersections.insert(other2, linko2);
+ }
+ intersections.insert(i1, link1);
+ intersections.insert(i2, link2);
+#ifdef DEBUG
+ checkLinkChain(intersections, i1);
+ checkLinkChain(intersections, i2);
+ Q_ASSERT(edgeInChain(i1, i2.edge));
+#endif
+ return;
+
+}
+
+
+void QTessellatorPrivate::addIntersections()
+{
+ if (scanline.size) {
+ QDEBUG() << "INTERSECTIONS";
+ // check marked edges for intersections
+#ifdef DEBUG
+ for (int i = 0; i < scanline.size; ++i) {
+ Edge *e = scanline.edges[i];
+ QDEBUG() << " " << i << e->edge << "isect=(" << e->intersect_left << e->intersect_right
+ << ')';
+ }
+#endif
+
+ for (int i = 0; i < scanline.size - 1; ++i) {
+ Edge *e1 = scanline.edges[i];
+ Edge *e2 = scanline.edges[i + 1];
+ // check for intersection
+ if (e1->intersect_right || e2->intersect_left)
+ addIntersection(e1, e2);
+ }
+ }
+#if 0
+ if (intersections.constBegin().key().y == y) {
+ QDEBUG() << "----------------> intersection on same line";
+ scanline.clearMarks();
+ scanline.processIntersections(y, &intersections);
+ goto redo;
+ }
+#endif
+}
+
+
+QTessellator::QTessellator()
+{
+ d = new QTessellatorPrivate;
+}
+
+QTessellator::~QTessellator()
+{
+ delete d;
+}
+
+void QTessellator::setWinding(bool w)
+{
+ d->winding = w;
+}
+
+
+QRectF QTessellator::tessellate(const QPointF *points, int nPoints)
+{
+ Q_ASSERT(points[0] == points[nPoints-1]);
+ --nPoints;
+
+#ifdef DEBUG
+ QDEBUG()<< "POINTS:";
+ for (int i = 0; i < nPoints; ++i) {
+ QDEBUG() << points[i];
+ }
+#endif
+
+ // collect edges and calculate bounds
+ d->vertices.nPoints = nPoints;
+ d->vertices.init(nPoints);
+
+ int maxActiveEdges = 0;
+ QRectF br = d->collectAndSortVertices(points, &maxActiveEdges);
+ d->cancelCoincidingEdges();
+
+#ifdef DEBUG
+ QDEBUG() << "nPoints = " << nPoints << "using " << d->vertices.nPoints;
+ QDEBUG()<< "VERTICES:";
+ for (int i = 0; i < d->vertices.nPoints; ++i) {
+ QDEBUG() << " " << i << ": "
+ << "point=" << d->vertices.position(d->vertices.sorted[i])
+ << "flags=" << d->vertices.sorted[i]->flags
+ << "pos=(" << Q27Dot5ToDouble(d->vertices.sorted[i]->x) << '/'
+ << Q27Dot5ToDouble(d->vertices.sorted[i]->y) << ')';
+ }
+#endif
+
+ d->scanline.init(maxActiveEdges);
+ d->y = INT_MIN/256;
+ d->currentVertex = 0;
+
+ while (d->currentVertex < d->vertices.nPoints) {
+ d->scanline.clearMarks();
+
+ d->y = d->vertices.sorted[d->currentVertex]->y;
+ if (!d->intersections.isEmpty())
+ d->y = qMin(d->y, d->intersections.constBegin().key().y);
+
+ QDEBUG()<< "===== SCANLINE: y =" << Q27Dot5ToDouble(d->y) << " =====";
+
+ d->scanline.prepareLine();
+ d->processIntersections();
+ d->removeEdges();
+ d->addEdges();
+ d->addIntersections();
+ d->emitEdges(this);
+ d->scanline.lineDone();
+
+#ifdef DEBUG
+ QDEBUG()<< "===== edges:";
+ for (int i = 0; i < d->scanline.size; ++i) {
+ QDEBUG() << " " << d->scanline.edges[i]->edge
+ << "p0= (" << Q27Dot5ToDouble(d->scanline.edges[i]->v0->x)
+ << '/' << Q27Dot5ToDouble(d->scanline.edges[i]->v0->y)
+ << ") p1= (" << Q27Dot5ToDouble(d->scanline.edges[i]->v1->x)
+ << '/' << Q27Dot5ToDouble(d->scanline.edges[i]->v1->y) << ')'
+ << "x=" << Q27Dot5ToDouble(d->scanline.edges[i]->positionAt(d->y))
+ << "isLeftOfNext="
+ << ((i < d->scanline.size - 1)
+ ? d->scanline.edges[i]->isLeftOf(*d->scanline.edges[i+1], d->y)
+ : true);
+ }
+#endif
+}
+
+ d->scanline.done();
+ d->intersections.clear();
+ return br;
+}
+
+// tessellates the given convex polygon
+void QTessellator::tessellateConvex(const QPointF *points, int nPoints)
+{
+ Q_ASSERT(points[0] == points[nPoints-1]);
+ --nPoints;
+
+ d->vertices.nPoints = nPoints;
+ d->vertices.init(nPoints);
+
+ for (int i = 0; i < nPoints; ++i) {
+ d->vertices[i]->x = FloatToQ27Dot5(points[i].x());
+ d->vertices[i]->y = FloatToQ27Dot5(points[i].y());
+ }
+
+ int left = 0, right = 0;
+
+ int top = 0;
+ for (int i = 1; i < nPoints; ++i) {
+ if (d->vertices[i]->y < d->vertices[top]->y)
+ top = i;
+ }
+
+ left = (top + nPoints - 1) % nPoints;
+ right = (top + 1) % nPoints;
+
+ while (d->vertices[left]->x == d->vertices[top]->x && d->vertices[left]->y == d->vertices[top]->y && left != right)
+ left = (left + nPoints - 1) % nPoints;
+
+ while (d->vertices[right]->x == d->vertices[top]->x && d->vertices[right]->y == d->vertices[top]->y && left != right)
+ right = (right + 1) % nPoints;
+
+ if (left == right)
+ return;
+
+ int dir = 1;
+
+ Vertex dLeft = { d->vertices[top]->x - d->vertices[left]->x,
+ d->vertices[top]->y - d->vertices[left]->y };
+
+ Vertex dRight = { d->vertices[right]->x - d->vertices[top]->x,
+ d->vertices[right]->y - d->vertices[top]->y };
+
+ Q27Dot5 cross = dLeft.x * dRight.y - dLeft.y * dRight.x;
+
+ // flip direction if polygon is clockwise
+ if (cross < 0 || (cross == 0 && dLeft.x > 0)) {
+ qSwap(left, right);
+ dir = -1;
+ }
+
+ Vertex *lastLeft = d->vertices[top];
+ Vertex *lastRight = d->vertices[top];
+
+ QTessellator::Trapezoid trap;
+
+ while (lastLeft->y == d->vertices[left]->y && left != right) {
+ lastLeft = d->vertices[left];
+ left = (left + nPoints - dir) % nPoints;
+ }
+
+ while (lastRight->y == d->vertices[right]->y && left != right) {
+ lastRight = d->vertices[right];
+ right = (right + nPoints + dir) % nPoints;
+ }
+
+ while (true) {
+ trap.top = qMax(lastRight->y, lastLeft->y);
+ trap.bottom = qMin(d->vertices[left]->y, d->vertices[right]->y);
+ trap.topLeft = lastLeft;
+ trap.topRight = lastRight;
+ trap.bottomLeft = d->vertices[left];
+ trap.bottomRight = d->vertices[right];
+
+ if (trap.bottom > trap.top)
+ addTrap(trap);
+
+ if (left == right)
+ break;
+
+ if (d->vertices[right]->y < d->vertices[left]->y) {
+ do {
+ lastRight = d->vertices[right];
+ right = (right + nPoints + dir) % nPoints;
+ }
+ while (lastRight->y == d->vertices[right]->y && left != right);
+ } else {
+ do {
+ lastLeft = d->vertices[left];
+ left = (left + nPoints - dir) % nPoints;
+ }
+ while (lastLeft->y == d->vertices[left]->y && left != right);
+ }
+ }
+}
+
+// tessellates the stroke of the line from a_ to b_ with the given width and a flat cap
+void QTessellator::tessellateRect(const QPointF &a_, const QPointF &b_, qreal width)
+{
+ Vertex a = { FloatToQ27Dot5(a_.x()), FloatToQ27Dot5(a_.y()) };
+ Vertex b = { FloatToQ27Dot5(b_.x()), FloatToQ27Dot5(b_.y()) };
+
+ QPointF pa = a_, pb = b_;
+
+ if (a.y > b.y) {
+ qSwap(a, b);
+ qSwap(pa, pb);
+ }
+
+ Vertex delta = { b.x - a.x, b.y - a.y };
+
+ if (delta.x == 0 && delta.y == 0)
+ return;
+
+ qreal hw = 0.5 * width;
+
+ if (delta.x == 0) {
+ Q27Dot5 halfWidth = FloatToQ27Dot5(hw);
+
+ if (halfWidth == 0)
+ return;
+
+ Vertex topLeft = { a.x - halfWidth, a.y };
+ Vertex topRight = { a.x + halfWidth, a.y };
+ Vertex bottomLeft = { a.x - halfWidth, b.y };
+ Vertex bottomRight = { a.x + halfWidth, b.y };
+
+ QTessellator::Trapezoid trap = { topLeft.y, bottomLeft.y, &topLeft, &bottomLeft, &topRight, &bottomRight };
+ addTrap(trap);
+ } else if (delta.y == 0) {
+ Q27Dot5 halfWidth = FloatToQ27Dot5(hw);
+
+ if (halfWidth == 0)
+ return;
+
+ if (a.x > b.x)
+ qSwap(a.x, b.x);
+
+ Vertex topLeft = { a.x, a.y - halfWidth };
+ Vertex topRight = { b.x, a.y - halfWidth };
+ Vertex bottomLeft = { a.x, a.y + halfWidth };
+ Vertex bottomRight = { b.x, a.y + halfWidth };
+
+ QTessellator::Trapezoid trap = { topLeft.y, bottomLeft.y, &topLeft, &bottomLeft, &topRight, &bottomRight };
+ addTrap(trap);
+ } else {
+ QPointF perp(pb.y() - pa.y(), pa.x() - pb.x());
+ qreal length = qSqrt(perp.x() * perp.x() + perp.y() * perp.y());
+
+ if (qFuzzyIsNull(length))
+ return;
+
+ // need the half of the width
+ perp *= hw / length;
+
+ QPointF pta = pa + perp;
+ QPointF ptb = pa - perp;
+ QPointF ptc = pb - perp;
+ QPointF ptd = pb + perp;
+
+ Vertex ta = { FloatToQ27Dot5(pta.x()), FloatToQ27Dot5(pta.y()) };
+ Vertex tb = { FloatToQ27Dot5(ptb.x()), FloatToQ27Dot5(ptb.y()) };
+ Vertex tc = { FloatToQ27Dot5(ptc.x()), FloatToQ27Dot5(ptc.y()) };
+ Vertex td = { FloatToQ27Dot5(ptd.x()), FloatToQ27Dot5(ptd.y()) };
+
+ if (ta.y < tb.y) {
+ if (tb.y < td.y) {
+ QTessellator::Trapezoid top = { ta.y, tb.y, &ta, &tb, &ta, &td };
+ QTessellator::Trapezoid bottom = { td.y, tc.y, &tb, &tc, &td, &tc };
+ addTrap(top);
+ addTrap(bottom);
+
+ QTessellator::Trapezoid middle = { tb.y, td.y, &tb, &tc, &ta, &td };
+ addTrap(middle);
+ } else {
+ QTessellator::Trapezoid top = { ta.y, td.y, &ta, &tb, &ta, &td };
+ QTessellator::Trapezoid bottom = { tb.y, tc.y, &tb, &tc, &td, &tc };
+ addTrap(top);
+ addTrap(bottom);
+
+ if (tb.y != td.y) {
+ QTessellator::Trapezoid middle = { td.y, tb.y, &ta, &tb, &td, &tc };
+ addTrap(middle);
+ }
+ }
+ } else {
+ if (ta.y < tc.y) {
+ QTessellator::Trapezoid top = { tb.y, ta.y, &tb, &tc, &tb, &ta };
+ QTessellator::Trapezoid bottom = { tc.y, td.y, &tc, &td, &ta, &td };
+ addTrap(top);
+ addTrap(bottom);
+
+ QTessellator::Trapezoid middle = { ta.y, tc.y, &tb, &tc, &ta, &td };
+ addTrap(middle);
+ } else {
+ QTessellator::Trapezoid top = { tb.y, tc.y, &tb, &tc, &tb, &ta };
+ QTessellator::Trapezoid bottom = { ta.y, td.y, &tc, &td, &ta, &td };
+ addTrap(top);
+ addTrap(bottom);
+
+ if (ta.y != tc.y) {
+ QTessellator::Trapezoid middle = { tc.y, ta.y, &tc, &td, &tb, &ta };
+ addTrap(middle);
+ }
+ }
+ }
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/nativepainting/qtessellator_p.h b/src/plugins/platforms/xcb/nativepainting/qtessellator_p.h
new file mode 100644
index 0000000000..7181a1ecc9
--- /dev/null
+++ b/src/plugins/platforms/xcb/nativepainting/qtessellator_p.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTESSELATOR_P_H
+#define QTESSELATOR_P_H
+
+#include <QPoint>
+#include <QRect>
+
+QT_BEGIN_NAMESPACE
+
+class QTessellatorPrivate;
+
+typedef int Q27Dot5;
+#define Q27Dot5ToDouble(i) ((i)/32.)
+#define FloatToQ27Dot5(i) (int)((i) * 32)
+#define IntToQ27Dot5(i) ((i) << 5)
+#define Q27Dot5ToXFixed(i) ((i) << 11)
+#define Q27Dot5Factor 32
+
+class QTessellator {
+public:
+ QTessellator();
+ virtual ~QTessellator();
+
+ QRectF tessellate(const QPointF *points, int nPoints);
+ void tessellateConvex(const QPointF *points, int nPoints);
+ void tessellateRect(const QPointF &a, const QPointF &b, qreal width);
+
+ void setWinding(bool w);
+
+ struct Vertex {
+ Q27Dot5 x;
+ Q27Dot5 y;
+ };
+ struct Trapezoid {
+ Q27Dot5 top;
+ Q27Dot5 bottom;
+ const Vertex *topLeft;
+ const Vertex *bottomLeft;
+ const Vertex *topRight;
+ const Vertex *bottomRight;
+ };
+ virtual void addTrap(const Trapezoid &trap) = 0;
+
+private:
+ friend class QTessellatorPrivate;
+ QTessellatorPrivate *d;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/platforms/xcb/nativepainting/qxcbnativepainting.cpp b/src/plugins/platforms/xcb/nativepainting/qxcbnativepainting.cpp
new file mode 100644
index 0000000000..57b1882e4b
--- /dev/null
+++ b/src/plugins/platforms/xcb/nativepainting/qxcbnativepainting.cpp
@@ -0,0 +1,321 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qxcbconnection.h"
+#include "qcolormap_x11_p.h"
+#include "qxcbnativepainting.h"
+#include "qt_x11_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QXcbX11Data *qt_x11Data = nullptr;
+
+void qt_xcb_native_x11_info_init(QXcbConnection *conn)
+{
+ qt_x11Data = new QXcbX11Data;
+ X11->display = static_cast<Display *>(conn->xlib_display());
+ X11->defaultScreen = DefaultScreen(X11->display);
+ X11->screenCount = ScreenCount(X11->display);
+
+ X11->screens = new QX11InfoData[X11->screenCount];
+ X11->argbVisuals = new Visual *[X11->screenCount];
+ X11->argbColormaps = new Colormap[X11->screenCount];
+
+ for (int s = 0; s < X11->screenCount; s++) {
+ QX11InfoData *screen = X11->screens + s;
+ //screen->ref = 1; // ensures it doesn't get deleted
+ screen->screen = s;
+
+ int widthMM = DisplayWidthMM(X11->display, s);
+ if (widthMM != 0) {
+ screen->dpiX = (DisplayWidth(X11->display, s) * 254 + widthMM * 5) / (widthMM * 10);
+ } else {
+ screen->dpiX = 72;
+ }
+
+ int heightMM = DisplayHeightMM(X11->display, s);
+ if (heightMM != 0) {
+ screen->dpiY = (DisplayHeight(X11->display, s) * 254 + heightMM * 5) / (heightMM * 10);
+ } else {
+ screen->dpiY = 72;
+ }
+
+ X11->argbVisuals[s] = 0;
+ X11->argbColormaps[s] = 0;
+ }
+
+ X11->use_xrender = conn->hasXRender() && !qEnvironmentVariableIsSet("QT_XCB_NATIVE_PAINTING_NO_XRENDER");
+
+#if QT_CONFIG(xrender)
+ memset(X11->solid_fills, 0, sizeof(X11->solid_fills));
+ for (int i = 0; i < X11->solid_fill_count; ++i)
+ X11->solid_fills[i].screen = -1;
+ memset(X11->pattern_fills, 0, sizeof(X11->pattern_fills));
+ for (int i = 0; i < X11->pattern_fill_count; ++i)
+ X11->pattern_fills[i].screen = -1;
+#endif
+
+ QXcbColormap::initialize();
+
+#if QT_CONFIG(xrender)
+ if (X11->use_xrender) {
+ // XRender is supported, let's see if we have a PictFormat for the
+ // default visual
+ XRenderPictFormat *format =
+ XRenderFindVisualFormat(X11->display,
+ (Visual *) QXcbX11Info::appVisual(X11->defaultScreen));
+
+ if (!format) {
+ X11->use_xrender = false;
+ }
+ }
+#endif // QT_CONFIG(xrender)
+}
+
+QVector<XRectangle> qt_region_to_xrectangles(const QRegion &r)
+{
+ const int numRects = r.rectCount();
+ const auto input = r.begin();
+ QVector<XRectangle> output(numRects);
+ for (int i = 0; i < numRects; ++i) {
+ const QRect &in = input[i];
+ XRectangle &out = output[i];
+ out.x = qMax(SHRT_MIN, in.x());
+ out.y = qMax(SHRT_MIN, in.y());
+ out.width = qMin((int)USHRT_MAX, in.width());
+ out.height = qMin((int)USHRT_MAX, in.height());
+ }
+ return output;
+}
+
+class QXcbX11InfoData : public QSharedData, public QX11InfoData
+{};
+
+QXcbX11Info::QXcbX11Info()
+ : d(nullptr)
+{}
+
+QXcbX11Info::~QXcbX11Info()
+{}
+
+QXcbX11Info::QXcbX11Info(const QXcbX11Info &other)
+ : d(other.d)
+{}
+
+QXcbX11Info &QXcbX11Info::operator=(const QXcbX11Info &other)
+{
+ d = other.d;
+ return *this;
+}
+
+QXcbX11Info QXcbX11Info::fromScreen(int screen)
+{
+ QXcbX11InfoData *xd = new QXcbX11InfoData;
+ xd->screen = screen;
+ xd->depth = QXcbX11Info::appDepth(screen);
+ xd->cells = QXcbX11Info::appCells(screen);
+ xd->colormap = QXcbX11Info::appColormap(screen);
+ xd->defaultColormap = QXcbX11Info::appDefaultColormap(screen);
+ xd->visual = (Visual *)QXcbX11Info::appVisual(screen);
+ xd->defaultVisual = QXcbX11Info::appDefaultVisual(screen);
+
+ QXcbX11Info info;
+ info.d = xd;
+ return info;
+}
+
+void QXcbX11Info::setDepth(int depth)
+{
+ if (!d)
+ *this = fromScreen(appScreen());
+
+ d->depth = depth;
+}
+
+Display *QXcbX11Info::display()
+{
+ return X11 ? X11->display : 0;
+}
+
+int QXcbX11Info::screen() const
+{
+ return d ? d->screen : QXcbX11Info::appScreen();
+}
+
+int QXcbX11Info::depth() const
+{
+ return d ? d->depth : QXcbX11Info::appDepth();
+}
+
+Colormap QXcbX11Info::colormap() const
+{
+ return d ? d->colormap : QXcbX11Info::appColormap();
+}
+
+void *QXcbX11Info::visual() const
+{
+ return d ? d->visual : QXcbX11Info::appVisual();
+}
+
+void QXcbX11Info::setVisual(void *visual)
+{
+ if (!d)
+ *this = fromScreen(appScreen());
+
+ d->visual = (Visual *) visual;
+}
+
+int QXcbX11Info::appScreen()
+{
+ return X11 ? X11->defaultScreen : 0;
+}
+
+int QXcbX11Info::appDepth(int screen)
+{
+ return X11 ? X11->screens[screen == -1 ? X11->defaultScreen : screen].depth : 32;
+}
+
+int QXcbX11Info::appCells(int screen)
+{
+ return X11 ? X11->screens[screen == -1 ? X11->defaultScreen : screen].cells : 0;
+}
+
+Colormap QXcbX11Info::appColormap(int screen)
+{
+ return X11 ? X11->screens[screen == -1 ? X11->defaultScreen : screen].colormap : 0;
+}
+
+void *QXcbX11Info::appVisual(int screen)
+{
+ return X11 ? X11->screens[screen == -1 ? X11->defaultScreen : screen].visual : 0;
+}
+
+Window QXcbX11Info::appRootWindow(int screen)
+{
+ return X11 ? RootWindow(X11->display, screen == -1 ? X11->defaultScreen : screen) : 0;
+}
+
+bool QXcbX11Info::appDefaultColormap(int screen)
+{
+ return X11 ? X11->screens[screen == -1 ? X11->defaultScreen : screen].defaultColormap : true;
+}
+
+bool QXcbX11Info::appDefaultVisual(int screen)
+{
+ return X11 ? X11->screens[screen == -1 ? X11->defaultScreen : screen].defaultVisual : true;
+}
+
+int QXcbX11Info::appDpiX(int screen)
+{
+ if (!X11)
+ return 75;
+ if (screen < 0)
+ screen = X11->defaultScreen;
+ if (screen > X11->screenCount)
+ return 0;
+ return X11->screens[screen].dpiX;
+}
+
+int QXcbX11Info::appDpiY(int screen)
+{
+ if (!X11)
+ return 75;
+ if (screen < 0)
+ screen = X11->defaultScreen;
+ if (screen > X11->screenCount)
+ return 0;
+ return X11->screens[screen].dpiY;
+}
+
+#if QT_CONFIG(xrender)
+Picture QXcbX11Data::getSolidFill(int screen, const QColor &c)
+{
+ if (!X11->use_xrender)
+ return XNone;
+
+ XRenderColor color = preMultiply(c);
+ for (int i = 0; i < X11->solid_fill_count; ++i) {
+ if (X11->solid_fills[i].screen == screen
+ && X11->solid_fills[i].color.alpha == color.alpha
+ && X11->solid_fills[i].color.red == color.red
+ && X11->solid_fills[i].color.green == color.green
+ && X11->solid_fills[i].color.blue == color.blue)
+ return X11->solid_fills[i].picture;
+ }
+ // none found, replace one
+ int i = qrand() % 16;
+
+ if (X11->solid_fills[i].screen != screen && X11->solid_fills[i].picture) {
+ XRenderFreePicture (X11->display, X11->solid_fills[i].picture);
+ X11->solid_fills[i].picture = 0;
+ }
+
+ if (!X11->solid_fills[i].picture) {
+ Pixmap pixmap = XCreatePixmap (X11->display, RootWindow (X11->display, screen), 1, 1, 32);
+ XRenderPictureAttributes attrs;
+ attrs.repeat = True;
+ X11->solid_fills[i].picture = XRenderCreatePicture (X11->display, pixmap,
+ XRenderFindStandardFormat(X11->display, PictStandardARGB32),
+ CPRepeat, &attrs);
+ XFreePixmap (X11->display, pixmap);
+ }
+
+ X11->solid_fills[i].color = color;
+ X11->solid_fills[i].screen = screen;
+ XRenderFillRectangle (X11->display, PictOpSrc, X11->solid_fills[i].picture, &color, 0, 0, 1, 1);
+ return X11->solid_fills[i].picture;
+}
+
+XRenderColor QXcbX11Data::preMultiply(const QColor &c)
+{
+ XRenderColor color;
+ const uint A = c.alpha(),
+ R = c.red(),
+ G = c.green(),
+ B = c.blue();
+ color.alpha = (A | A << 8);
+ color.red = (R | R << 8) * color.alpha / 0x10000;
+ color.green = (G | G << 8) * color.alpha / 0x10000;
+ color.blue = (B | B << 8) * color.alpha / 0x10000;
+ return color;
+}
+#endif // QT_CONFIG(xrender)
+
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/nativepainting/qxcbnativepainting.h b/src/plugins/platforms/xcb/nativepainting/qxcbnativepainting.h
new file mode 100644
index 0000000000..b00ccfcdff
--- /dev/null
+++ b/src/plugins/platforms/xcb/nativepainting/qxcbnativepainting.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QXCBNATIVEPAINTING_H
+#define QXCBNATIVEPAINTING_H
+
+#include <QSharedDataPointer>
+#include "qt_x11_p.h"
+
+typedef struct _FcPattern FcPattern;
+typedef unsigned long XID;
+typedef XID Colormap;
+typedef XID Window;
+typedef struct _XDisplay Display;
+
+QT_BEGIN_NAMESPACE
+
+class QXcbConnection;
+class QPixmap;
+
+void qt_xcb_native_x11_info_init(QXcbConnection *conn);
+QVector<XRectangle> qt_region_to_xrectangles(const QRegion &r);
+
+class QXcbX11InfoData;
+class QXcbX11Info
+{
+public:
+ QXcbX11Info();
+ ~QXcbX11Info();
+ QXcbX11Info(const QXcbX11Info &other);
+ QXcbX11Info &operator=(const QXcbX11Info &other);
+
+ static QXcbX11Info fromScreen(int screen);
+ static Display *display();
+
+ int depth() const;
+ void setDepth(int depth);
+
+ int screen() const;
+ Colormap colormap() const;
+
+ void *visual() const;
+ void setVisual(void *visual);
+
+ static int appScreen();
+ static int appDepth(int screen = -1);
+ static int appCells(int screen = -1);
+ static Colormap appColormap(int screen = -1);
+ static void *appVisual(int screen = -1);
+ static Window appRootWindow(int screen = -1);
+ static bool appDefaultColormap(int screen = -1);
+ static bool appDefaultVisual(int screen = -1);
+ static int appDpiX(int screen = -1);
+ static int appDpiY(int screen = -1);
+
+private:
+ QSharedDataPointer<QXcbX11InfoData> d;
+
+ friend class QX11PaintEngine;
+ friend class QX11PlatformPixmap;
+ friend void qt_x11SetScreen(QPixmap &pixmap, int screen);
+};
+
+QT_END_NAMESPACE
+
+#endif // QXCBNATIVEPAINTING_H
diff --git a/src/plugins/platforms/xcb/qxcbbackingstore.cpp b/src/plugins/platforms/xcb/qxcbbackingstore.cpp
index a419caf0fc..1cf45c96d1 100644
--- a/src/plugins/platforms/xcb/qxcbbackingstore.cpp
+++ b/src/plugins/platforms/xcb/qxcbbackingstore.cpp
@@ -69,6 +69,8 @@ public:
QXcbShmImage(QXcbScreen *connection, const QSize &size, uint depth, QImage::Format format);
~QXcbShmImage() { destroy(); }
+ void flushScrolledRegion(bool clientSideScroll);
+
bool scroll(const QRegion &area, int dx, int dy);
QImage *image() { return &m_qimage; }
@@ -86,7 +88,8 @@ private:
void destroy();
void ensureGC(xcb_drawable_t dst);
- void flushPixmap(const QRegion &region);
+ void shmPutImage(xcb_drawable_t drawable, const QRegion &region, const QPoint &offset = QPoint());
+ void flushPixmap(const QRegion &region, bool fullRegion = false);
void setClip(const QRegion &region);
xcb_shm_segment_info_t m_shm_info;
@@ -99,18 +102,26 @@ private:
xcb_gcontext_t m_gc;
xcb_drawable_t m_gc_drawable;
- // When using shared memory this is the region currently shared with the server
- QRegion m_dirtyShm;
-
+ // When using shared memory these variables are used only for server-side scrolling.
// When not using shared memory, we maintain a server-side pixmap with the backing
// store as well as repainted content not yet flushed to the pixmap. We only flush
// the regions we need and only when these are marked dirty. This way we can just
// do a server-side copy on expose instead of sending the pixels every time
xcb_pixmap_t m_xcb_pixmap;
QRegion m_pendingFlush;
+
+ // This is the scrolled region which is stored in server-side pixmap
+ QRegion m_scrolledRegion;
+
+ // When using shared memory this is the region currently shared with the server
+ QRegion m_dirtyShm;
+
+ // When not using shared memory this is a temporary buffer which is uploaded
+ // as a pixmap region to server
QByteArray m_flushBuffer;
bool m_hasAlpha;
+ bool m_clientSideScroll;
};
class QXcbShmGraphicsBuffer : public QPlatformGraphicsBuffer
@@ -145,13 +156,12 @@ private:
QXcbShmImage::QXcbShmImage(QXcbScreen *screen, const QSize &size, uint depth, QImage::Format format)
: QXcbObject(screen->connection())
- , m_graphics_buffer(Q_NULLPTR)
+ , m_graphics_buffer(nullptr)
, m_gc(0)
, m_gc_drawable(0)
, m_xcb_pixmap(0)
+ , m_clientSideScroll(false)
{
- Q_XCB_NOOP(connection());
-
const xcb_format_t *fmt = connection()->formatForDepth(depth);
Q_ASSERT(fmt);
@@ -204,13 +214,59 @@ QXcbShmImage::QXcbShmImage(QXcbScreen *screen, const QSize &size, uint depth, QI
m_qimage = QImage( (uchar*) m_xcb_image->data, m_xcb_image->width, m_xcb_image->height, m_xcb_image->stride, format);
m_graphics_buffer = new QXcbShmGraphicsBuffer(&m_qimage);
- if (!hasShm()) {
- m_xcb_pixmap = xcb_generate_id(xcb_connection());
- Q_XCB_CALL(xcb_create_pixmap(xcb_connection(),
- m_xcb_image->depth,
- m_xcb_pixmap,
- screen->screen()->root,
- m_xcb_image->width, m_xcb_image->height));
+ m_xcb_pixmap = xcb_generate_id(xcb_connection());
+ xcb_create_pixmap(xcb_connection(),
+ m_xcb_image->depth,
+ m_xcb_pixmap,
+ screen->screen()->root,
+ m_xcb_image->width, m_xcb_image->height);
+}
+
+void QXcbShmImage::flushScrolledRegion(bool clientSideScroll)
+{
+ if (m_clientSideScroll == clientSideScroll)
+ return;
+
+ m_clientSideScroll = clientSideScroll;
+
+ if (m_scrolledRegion.isNull())
+ return;
+
+ if (hasShm() && m_dirtyShm.intersects(m_scrolledRegion)) {
+ connection()->sync();
+ m_dirtyShm = QRegion();
+ }
+
+ if (m_clientSideScroll) {
+ // Copy scrolled image region from server-side pixmap to client-side memory
+ for (const QRect &rect : m_scrolledRegion) {
+ const int w = rect.width();
+ const int h = rect.height();
+
+ auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_image,
+ xcb_connection(),
+ m_xcb_image->format,
+ m_xcb_pixmap,
+ rect.x(), rect.y(),
+ w, h,
+ ~0u);
+
+ if (reply && reply->depth == m_xcb_image->depth) {
+ const QImage img(xcb_get_image_data(reply.get()), w, h, m_qimage.format());
+
+ QPainter p(&m_qimage);
+ p.setCompositionMode(QPainter::CompositionMode_Source);
+ p.drawImage(rect.topLeft(), img);
+ }
+ }
+ m_scrolledRegion = QRegion();
+ } else {
+ // Copy scrolled image region from client-side memory to server-side pixmap
+ ensureGC(m_xcb_pixmap);
+ if (hasShm())
+ shmPutImage(m_xcb_pixmap, m_scrolledRegion);
+ else
+ flushPixmap(m_scrolledRegion, true);
}
}
@@ -218,32 +274,45 @@ extern void qt_scrollRectInImage(QImage &img, const QRect &rect, const QPoint &o
bool QXcbShmImage::scroll(const QRegion &area, int dx, int dy)
{
- if (image()->isNull())
- return false;
+ const QRect bounds(QPoint(), size());
+ const QRegion scrollArea(area & bounds);
+ const QPoint delta(dx, dy);
- if (hasShm())
- preparePaint(area);
+ if (m_clientSideScroll) {
+ if (m_qimage.isNull())
+ return false;
- const QPoint delta(dx, dy);
- for (const QRect &rect : area)
- qt_scrollRectInImage(*image(), rect, delta);
+ if (hasShm())
+ preparePaint(scrollArea);
+
+ for (const QRect &rect : scrollArea)
+ qt_scrollRectInImage(m_qimage, rect, delta);
+ } else {
+ if (hasShm())
+ shmPutImage(m_xcb_pixmap, m_pendingFlush.intersected(scrollArea));
+ else
+ flushPixmap(scrollArea);
- if (m_xcb_pixmap) {
- flushPixmap(area);
ensureGC(m_xcb_pixmap);
- const QRect bounds(QPoint(0, 0), size());
- for (const QRect &src : area) {
+
+ for (const QRect &src : scrollArea) {
const QRect dst = src.translated(delta).intersected(bounds);
- Q_XCB_CALL(xcb_copy_area(xcb_connection(),
- m_xcb_pixmap,
- m_xcb_pixmap,
- m_gc,
- src.x(), src.y(),
- dst.x(), dst.y(),
- dst.width(), dst.height()));
+ xcb_copy_area(xcb_connection(),
+ m_xcb_pixmap,
+ m_xcb_pixmap,
+ m_gc,
+ src.x(), src.y(),
+ dst.x(), dst.y(),
+ dst.width(), dst.height());
}
}
+ m_scrolledRegion |= scrollArea.translated(delta).intersected(bounds);
+ if (hasShm()) {
+ m_pendingFlush -= scrollArea;
+ m_pendingFlush -= m_scrolledRegion;
+ }
+
return true;
}
@@ -251,7 +320,7 @@ void QXcbShmImage::destroy()
{
const int segmentSize = m_xcb_image ? (m_xcb_image->stride * m_xcb_image->height) : 0;
if (segmentSize && m_shm_info.shmaddr)
- Q_XCB_CALL(xcb_shm_detach(xcb_connection(), m_shm_info.shmseg));
+ xcb_shm_detach(xcb_connection(), m_shm_info.shmseg);
if (segmentSize) {
if (m_shm_info.shmaddr) {
@@ -265,27 +334,25 @@ void QXcbShmImage::destroy()
xcb_image_destroy(m_xcb_image);
if (m_gc)
- Q_XCB_CALL(xcb_free_gc(xcb_connection(), m_gc));
+ xcb_free_gc(xcb_connection(), m_gc);
delete m_graphics_buffer;
- m_graphics_buffer = Q_NULLPTR;
+ m_graphics_buffer = nullptr;
- if (m_xcb_pixmap) {
- Q_XCB_CALL(xcb_free_pixmap(xcb_connection(), m_xcb_pixmap));
- m_xcb_pixmap = 0;
- }
+ xcb_free_pixmap(xcb_connection(), m_xcb_pixmap);
+ m_xcb_pixmap = 0;
}
void QXcbShmImage::ensureGC(xcb_drawable_t dst)
{
if (m_gc_drawable != dst) {
if (m_gc)
- Q_XCB_CALL(xcb_free_gc(xcb_connection(), m_gc));
+ xcb_free_gc(xcb_connection(), m_gc);
static const uint32_t mask = XCB_GC_GRAPHICS_EXPOSURES;
static const uint32_t values[] = { 0 };
m_gc = xcb_generate_id(xcb_connection());
- Q_XCB_CALL(xcb_create_gc(xcb_connection(), m_gc, dst, mask, values));
+ xcb_create_gc(xcb_connection(), m_gc, dst, mask, values);
m_gc_drawable = dst;
}
@@ -355,10 +422,35 @@ static inline quint32 round_up_scanline(quint32 base, quint32 pad)
return (base + pad - 1) & -pad;
}
-void QXcbShmImage::flushPixmap(const QRegion &region)
+void QXcbShmImage::shmPutImage(xcb_drawable_t drawable, const QRegion &region, const QPoint &offset)
{
- const QVector<QRect> rects = m_pendingFlush.intersected(region).rects();
- m_pendingFlush -= region;
+ for (const QRect &rect : region) {
+ const QPoint source = rect.translated(offset).topLeft();
+ xcb_shm_put_image(xcb_connection(),
+ drawable,
+ m_gc,
+ m_xcb_image->width,
+ m_xcb_image->height,
+ source.x(), source.y(),
+ rect.width(), rect.height(),
+ rect.x(), rect.y(),
+ m_xcb_image->depth,
+ m_xcb_image->format,
+ 0, // send event?
+ m_shm_info.shmseg,
+ m_xcb_image->data - m_shm_info.shmaddr);
+ }
+ m_dirtyShm |= region.translated(offset);
+}
+
+void QXcbShmImage::flushPixmap(const QRegion &region, bool fullRegion)
+{
+ if (!fullRegion) {
+ auto actualRegion = m_pendingFlush.intersected(region);
+ m_pendingFlush -= region;
+ flushPixmap(actualRegion, true);
+ return;
+ }
xcb_image_t xcb_subimage;
memset(&xcb_subimage, 0, sizeof(xcb_image_t));
@@ -374,7 +466,7 @@ void QXcbShmImage::flushPixmap(const QRegion &region)
const bool needsByteSwap = xcb_subimage.byte_order != m_xcb_image->byte_order;
- for (const QRect &rect : rects) {
+ for (const QRect &rect : region) {
// We must make sure that each request is not larger than max_req_size.
// Each request takes req_size + m_xcb_image->stride * height bytes.
static const uint32_t req_size = sizeof(xcb_put_image_request_t);
@@ -425,68 +517,56 @@ void QXcbShmImage::setClip(const QRegion &region)
if (region.isEmpty()) {
static const uint32_t mask = XCB_GC_CLIP_MASK;
static const uint32_t values[] = { XCB_NONE };
- Q_XCB_CALL(xcb_change_gc(xcb_connection(),
- m_gc,
- mask,
- values));
+ xcb_change_gc(xcb_connection(), m_gc, mask, values);
} else {
- const QVector<QRect> qrects = region.rects();
- QVector<xcb_rectangle_t> xcb_rects(qrects.size());
-
- for (int i = 0; i < qrects.size(); i++) {
- xcb_rects[i].x = qrects[i].x();
- xcb_rects[i].y = qrects[i].y();
- xcb_rects[i].width = qrects[i].width();
- xcb_rects[i].height = qrects[i].height();
- }
-
- Q_XCB_CALL(xcb_set_clip_rectangles(xcb_connection(),
- XCB_CLIP_ORDERING_YX_BANDED,
- m_gc,
- 0, 0,
- xcb_rects.size(), xcb_rects.constData()));
+ const auto xcb_rects = qRegionToXcbRectangleList(region);
+ xcb_set_clip_rectangles(xcb_connection(),
+ XCB_CLIP_ORDERING_YX_BANDED,
+ m_gc,
+ 0, 0,
+ xcb_rects.size(), xcb_rects.constData());
}
}
void QXcbShmImage::put(xcb_drawable_t dst, const QRegion &region, const QPoint &offset)
{
- Q_XCB_NOOP(connection());
+ Q_ASSERT(!m_clientSideScroll);
ensureGC(dst);
setClip(region);
- const QRect bounds = region.boundingRect();
- const QPoint target = bounds.topLeft();
- const QRect source = bounds.translated(offset);
-
if (hasShm()) {
- Q_XCB_CALL(xcb_shm_put_image(xcb_connection(),
- dst,
- m_gc,
- m_xcb_image->width,
- m_xcb_image->height,
- source.x(), source.y(),
- source.width(), source.height(),
- target.x(), target.y(),
- m_xcb_image->depth,
- m_xcb_image->format,
- 0, // send event?
- m_shm_info.shmseg,
- m_xcb_image->data - m_shm_info.shmaddr));
- m_dirtyShm |= region.translated(offset);
+ // Copy scrolled area on server-side from pixmap to window
+ const QRegion scrolledRegion = m_scrolledRegion.translated(-offset);
+ for (const QRect &rect : scrolledRegion) {
+ const QPoint source = rect.translated(offset).topLeft();
+ xcb_copy_area(xcb_connection(),
+ m_xcb_pixmap,
+ dst,
+ m_gc,
+ source.x(), source.y(),
+ rect.x(), rect.y(),
+ rect.width(), rect.height());
+ }
+
+ // Copy non-scrolled image from client-side memory to server-side window
+ const QRegion notScrolledArea = region - scrolledRegion;
+ shmPutImage(dst, notScrolledArea, offset);
} else {
+ const QRect bounds = region.boundingRect();
+ const QPoint target = bounds.topLeft();
+ const QRect source = bounds.translated(offset);
flushPixmap(region);
- Q_XCB_CALL(xcb_copy_area(xcb_connection(),
- m_xcb_pixmap,
- dst,
- m_gc,
- source.x(), source.y(),
- target.x(), target.y(),
- source.width(), source.height()));
+ xcb_copy_area(xcb_connection(),
+ m_xcb_pixmap,
+ dst,
+ m_gc,
+ source.x(), source.y(),
+ target.x(), target.y(),
+ source.width(), source.height());
}
setClip(QRegion());
- Q_XCB_NOOP(connection());
}
void QXcbShmImage::preparePaint(const QRegion &region)
@@ -497,9 +577,9 @@ void QXcbShmImage::preparePaint(const QRegion &region)
connection()->sync();
m_dirtyShm = QRegion();
}
- } else {
- m_pendingFlush |= region;
}
+ m_scrolledRegion -= region;
+ m_pendingFlush |= region;
}
QXcbBackingStore::QXcbBackingStore(QWindow *window)
@@ -573,7 +653,7 @@ QImage QXcbBackingStore::toImage() const
QPlatformGraphicsBuffer *QXcbBackingStore::graphicsBuffer() const
{
- return m_image ? m_image->graphicsBuffer() : Q_NULLPTR;
+ return m_image ? m_image->graphicsBuffer() : nullptr;
}
void QXcbBackingStore::flush(QWindow *window, const QRegion &region, const QPoint &offset)
@@ -581,6 +661,8 @@ void QXcbBackingStore::flush(QWindow *window, const QRegion &region, const QPoin
if (!m_image || m_image->size().isEmpty())
return;
+ m_image->flushScrolledRegion(false);
+
QSize imageSize = m_image->size();
QRegion clipped = region;
@@ -592,8 +674,6 @@ void QXcbBackingStore::flush(QWindow *window, const QRegion &region, const QPoin
if (bounds.isNull())
return;
- Q_XCB_NOOP(connection());
-
QXcbWindow *platformWindow = static_cast<QXcbWindow *>(window->handle());
if (!platformWindow) {
qWarning("QXcbBackingStore::flush: QWindow has no platform window (QTBUG-32681)");
@@ -602,8 +682,6 @@ void QXcbBackingStore::flush(QWindow *window, const QRegion &region, const QPoin
m_image->put(platformWindow->xcb_window(), clipped, offset);
- Q_XCB_NOOP(connection());
-
if (platformWindow->needsSync())
platformWindow->updateSyncRequestCounter();
else
@@ -612,12 +690,15 @@ void QXcbBackingStore::flush(QWindow *window, const QRegion &region, const QPoin
#ifndef QT_NO_OPENGL
void QXcbBackingStore::composeAndFlush(QWindow *window, const QRegion &region, const QPoint &offset,
- QPlatformTextureList *textures, QOpenGLContext *context,
+ QPlatformTextureList *textures,
bool translucentBackground)
{
- QPlatformBackingStore::composeAndFlush(window, region, offset, textures, context, translucentBackground);
+ if (!m_image || m_image->size().isEmpty())
+ return;
+
+ m_image->flushScrolledRegion(true);
- Q_XCB_NOOP(connection());
+ QPlatformBackingStore::composeAndFlush(window, region, offset, textures, translucentBackground);
QXcbWindow *platformWindow = static_cast<QXcbWindow *>(window->handle());
if (platformWindow->needsSync()) {
@@ -632,7 +713,6 @@ void QXcbBackingStore::resize(const QSize &size, const QRegion &)
{
if (m_image && size == m_image->size())
return;
- Q_XCB_NOOP(connection());
QXcbScreen *screen = static_cast<QXcbScreen *>(window()->screen()->handle());
QPlatformWindow *pw = window()->handle();
@@ -649,7 +729,6 @@ void QXcbBackingStore::resize(const QSize &size, const QRegion &)
if (win->imageNeedsRgbSwap()) {
m_rgbImage = QImage(size, win->imageFormat());
}
- Q_XCB_NOOP(connection());
}
bool QXcbBackingStore::scroll(const QRegion &area, int dx, int dy)
diff --git a/src/plugins/platforms/xcb/qxcbbackingstore.h b/src/plugins/platforms/xcb/qxcbbackingstore.h
index 94b5994004..2e8fbfb7fa 100644
--- a/src/plugins/platforms/xcb/qxcbbackingstore.h
+++ b/src/plugins/platforms/xcb/qxcbbackingstore.h
@@ -61,7 +61,7 @@ public:
void flush(QWindow *window, const QRegion &region, const QPoint &offset) override;
#ifndef QT_NO_OPENGL
void composeAndFlush(QWindow *window, const QRegion &region, const QPoint &offset,
- QPlatformTextureList *textures, QOpenGLContext *context,
+ QPlatformTextureList *textures,
bool translucentBackground) override;
#endif
QImage toImage() const override;
diff --git a/src/plugins/platforms/xcb/qxcbclipboard.cpp b/src/plugins/platforms/xcb/qxcbclipboard.cpp
index 01b3bca0d2..b091928e8c 100644
--- a/src/plugins/platforms/xcb/qxcbclipboard.cpp
+++ b/src/plugins/platforms/xcb/qxcbclipboard.cpp
@@ -278,22 +278,22 @@ QXcbClipboard::QXcbClipboard(QXcbConnection *c)
#ifndef QT_NO_DEBUG
QByteArray ba("Qt clipboard window");
- Q_XCB_CALL(xcb_change_property(xcb_connection(),
- XCB_PROP_MODE_REPLACE,
- m_owner,
- atom(QXcbAtom::_NET_WM_NAME),
- atom(QXcbAtom::UTF8_STRING),
- 8,
- ba.length(),
- ba.constData()));
+ xcb_change_property(xcb_connection(),
+ XCB_PROP_MODE_REPLACE,
+ m_owner,
+ atom(QXcbAtom::_NET_WM_NAME),
+ atom(QXcbAtom::UTF8_STRING),
+ 8,
+ ba.length(),
+ ba.constData());
#endif
if (connection()->hasXFixes()) {
const uint32_t mask = XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER |
XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY |
XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE;
- Q_XCB_CALL(xcb_xfixes_select_selection_input_checked(xcb_connection(), m_owner, XCB_ATOM_PRIMARY, mask));
- Q_XCB_CALL(xcb_xfixes_select_selection_input_checked(xcb_connection(), m_owner, atom(QXcbAtom::CLIPBOARD), mask));
+ xcb_xfixes_select_selection_input_checked(xcb_connection(), m_owner, XCB_ATOM_PRIMARY, mask);
+ xcb_xfixes_select_selection_input_checked(xcb_connection(), m_owner, atom(QXcbAtom::CLIPBOARD), mask);
}
}
@@ -305,8 +305,7 @@ QXcbClipboard::~QXcbClipboard()
m_timestamp[QClipboard::Selection] != XCB_CURRENT_TIME) {
// First we check if there is a clipboard manager.
- xcb_get_selection_owner_cookie_t cookie = xcb_get_selection_owner(xcb_connection(), atom(QXcbAtom::CLIPBOARD_MANAGER));
- xcb_get_selection_owner_reply_t *reply = xcb_get_selection_owner_reply(xcb_connection(), cookie, 0);
+ auto reply = Q_XCB_REPLY(xcb_get_selection_owner, xcb_connection(), atom(QXcbAtom::CLIPBOARD_MANAGER));
if (reply && reply->owner != XCB_NONE) {
// we delete the property so the manager saves all TARGETS.
xcb_delete_property(xcb_connection(), m_owner, atom(QXcbAtom::_QT_SELECTION));
@@ -320,7 +319,6 @@ QXcbClipboard::~QXcbClipboard()
"clipboard manager in a reasonable time");
}
}
- free(reply);
}
if (m_clientClipboard[QClipboard::Clipboard] != m_clientClipboard[QClipboard::Selection])
@@ -459,26 +457,26 @@ xcb_window_t QXcbClipboard::requestor() const
QXcbClipboard *that = const_cast<QXcbClipboard *>(this);
xcb_window_t window = xcb_generate_id(xcb_connection());
- Q_XCB_CALL(xcb_create_window(xcb_connection(),
- XCB_COPY_FROM_PARENT, // depth -- same as root
- window, // window id
- platformScreen->screen()->root, // parent window id
- x, y, w, h,
- 0, // border width
- XCB_WINDOW_CLASS_INPUT_OUTPUT, // window class
- platformScreen->screen()->root_visual, // visual
- 0, // value mask
- 0)); // value list
+ xcb_create_window(xcb_connection(),
+ XCB_COPY_FROM_PARENT, // depth -- same as root
+ window, // window id
+ platformScreen->screen()->root, // parent window id
+ x, y, w, h,
+ 0, // border width
+ XCB_WINDOW_CLASS_INPUT_OUTPUT, // window class
+ platformScreen->screen()->root_visual, // visual
+ 0, // value mask
+ 0); // value list
#ifndef QT_NO_DEBUG
QByteArray ba("Qt clipboard requestor window");
- Q_XCB_CALL(xcb_change_property(xcb_connection(),
- XCB_PROP_MODE_REPLACE,
- window,
- atom(QXcbAtom::_NET_WM_NAME),
- atom(QXcbAtom::UTF8_STRING),
- 8,
- ba.length(),
- ba.constData()));
+ xcb_change_property(xcb_connection(),
+ XCB_PROP_MODE_REPLACE,
+ window,
+ atom(QXcbAtom::_NET_WM_NAME),
+ atom(QXcbAtom::UTF8_STRING),
+ 8,
+ ba.length(),
+ ba.constData());
#endif
uint32_t mask = XCB_EVENT_MASK_PROPERTY_CHANGE;
@@ -759,17 +757,14 @@ bool QXcbClipboard::clipboardReadProperty(xcb_window_t win, xcb_atom_t property,
format = &dummy_format;
// Don't read anything, just get the size of the property data
- xcb_get_property_cookie_t cookie = Q_XCB_CALL(xcb_get_property(xcb_connection(), false, win, property, XCB_GET_PROPERTY_TYPE_ANY, 0, 0));
- xcb_get_property_reply_t *reply = xcb_get_property_reply(xcb_connection(), cookie, 0);
+ auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(), false, win, property, XCB_GET_PROPERTY_TYPE_ANY, 0, 0);
if (!reply || reply->type == XCB_NONE) {
- free(reply);
buffer->resize(0);
return false;
}
*type = reply->type;
*format = reply->format;
bytes_left = reply->bytes_after;
- free(reply);
int offset = 0, buffer_offset = 0;
@@ -784,17 +779,15 @@ bool QXcbClipboard::clipboardReadProperty(xcb_window_t win, xcb_atom_t property,
while (bytes_left) {
// more to read...
- xcb_get_property_cookie_t cookie = Q_XCB_CALL(xcb_get_property(xcb_connection(), false, win, property, XCB_GET_PROPERTY_TYPE_ANY, offset, maxsize/4));
- reply = xcb_get_property_reply(xcb_connection(), cookie, 0);
- if (!reply || reply->type == XCB_NONE) {
- free(reply);
+ reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(), false, win, property, XCB_GET_PROPERTY_TYPE_ANY, offset, maxsize/4);
+ if (!reply || reply->type == XCB_NONE)
break;
- }
+
*type = reply->type;
*format = reply->format;
bytes_left = reply->bytes_after;
- char *data = (char *)xcb_get_property_value(reply);
- int length = xcb_get_property_value_length(reply);
+ char *data = (char *)xcb_get_property_value(reply.get());
+ int length = xcb_get_property_value_length(reply.get());
// Here we check if we get a buffer overflow and tries to
// recover -- this shouldn't normally happen, but it doesn't
@@ -814,7 +807,6 @@ bool QXcbClipboard::clipboardReadProperty(xcb_window_t win, xcb_atom_t property,
// offset is specified in 32-bit multiples
offset += length / 4;
}
- free(reply);
}
}
@@ -891,13 +883,9 @@ xcb_generic_event_t *QXcbClipboard::waitForClipboardEvent(xcb_window_t win, int
return e;
if (checkManager) {
- xcb_get_selection_owner_cookie_t cookie = xcb_get_selection_owner(xcb_connection(), atom(QXcbAtom::CLIPBOARD_MANAGER));
- xcb_get_selection_owner_reply_t *reply = xcb_get_selection_owner_reply(xcb_connection(), cookie, 0);
- if (!reply || reply->owner == XCB_NONE) {
- free(reply);
+ auto reply = Q_XCB_REPLY(xcb_get_selection_owner, xcb_connection(), atom(QXcbAtom::CLIPBOARD_MANAGER));
+ if (!reply || reply->owner == XCB_NONE)
return 0;
- }
- free(reply);
}
// process other clipboard events, since someone is probably requesting data from us
@@ -911,10 +899,7 @@ xcb_generic_event_t *QXcbClipboard::waitForClipboardEvent(xcb_window_t win, int
connection()->flush();
// sleep 50 ms, so we don't use up CPU cycles all the time.
- struct timeval usleep_tv;
- usleep_tv.tv_sec = 0;
- usleep_tv.tv_usec = 50000;
- select(0, 0, 0, 0, &usleep_tv);
+ QThread::msleep(50);
} while (timer.elapsed() < timeout);
return 0;
diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp
index d36a14b920..c5eae20266 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection.cpp
@@ -109,6 +109,9 @@ Q_LOGGING_CATEGORY(lcQpaXInput, "qt.qpa.input")
Q_LOGGING_CATEGORY(lcQpaXInputDevices, "qt.qpa.input.devices")
Q_LOGGING_CATEGORY(lcQpaXInputEvents, "qt.qpa.input.events")
Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen")
+Q_LOGGING_CATEGORY(lcQpaEvents, "qt.qpa.events")
+Q_LOGGING_CATEGORY(lcQpaXcb, "qt.qpa.xcb") // for general (uncategorized) XCB logging
+Q_LOGGING_CATEGORY(lcQpaPeeker, "qt.qpa.peeker")
// this event type was added in libxcb 1.10,
// but we support also older version
@@ -148,8 +151,18 @@ static const char * const xcbConnectionErrors[] = {
"Error during FD passing" /* XCB_CONN_CLOSED_FDPASSING_FAILED */
};
-static int nullErrorHandler(Display *, XErrorEvent *)
+static int nullErrorHandler(Display *dpy, XErrorEvent *err)
{
+#ifndef Q_XCB_DEBUG
+ Q_UNUSED(dpy);
+ Q_UNUSED(err);
+#else
+ const int buflen = 1024;
+ char buf[buflen];
+
+ XGetErrorText(dpy, err->error_code, buf, buflen);
+ fprintf(stderr, "X Error: serial %lu error %d %s\n", err->serial, (int) err->error_code, buf);
+#endif
return 0;
}
@@ -243,11 +256,8 @@ void QXcbConnection::updateScreens(const xcb_randr_notify_event_t *event)
} else if (!screen && output.connection == XCB_RANDR_CONNECTION_CONNECTED) {
// New XRandR output is available and it's enabled
if (output.crtc != XCB_NONE && output.mode != XCB_NONE) {
- xcb_randr_get_output_info_cookie_t outputInfoCookie =
- xcb_randr_get_output_info(xcb_connection(), output.output, output.config_timestamp);
- QScopedPointer<xcb_randr_get_output_info_reply_t, QScopedPointerPodDeleter> outputInfo(
- xcb_randr_get_output_info_reply(xcb_connection(), outputInfoCookie, NULL));
-
+ auto outputInfo = Q_XCB_REPLY(xcb_randr_get_output_info, xcb_connection(),
+ output.output, output.config_timestamp);
// Find a fake screen
const auto scrs = virtualDesktop->screens();
for (QPlatformScreen *scr : scrs) {
@@ -261,12 +271,12 @@ void QXcbConnection::updateScreens(const xcb_randr_notify_event_t *event)
if (screen) {
QString nameWas = screen->name();
// Transform the fake screen into a physical screen
- screen->setOutput(output.output, outputInfo.data());
+ screen->setOutput(output.output, outputInfo.get());
updateScreen(screen, output);
qCDebug(lcQpaScreen) << "output" << screen->name()
<< "is connected and enabled; was fake:" << nameWas;
} else {
- screen = createScreen(virtualDesktop, output, outputInfo.data());
+ screen = createScreen(virtualDesktop, output, outputInfo.get());
qCDebug(lcQpaScreen) << "output" << screen->name() << "is connected and enabled";
}
QHighDpiScaling::updateHighDpiScaling();
@@ -274,10 +284,8 @@ void QXcbConnection::updateScreens(const xcb_randr_notify_event_t *event)
} else if (screen) {
if (output.crtc == XCB_NONE && output.mode == XCB_NONE) {
// Screen has been disabled
- xcb_randr_get_output_info_cookie_t outputInfoCookie =
- xcb_randr_get_output_info(xcb_connection(), output.output, output.config_timestamp);
- QScopedPointer<xcb_randr_get_output_info_reply_t, QScopedPointerPodDeleter> outputInfo(
- xcb_randr_get_output_info_reply(xcb_connection(), outputInfoCookie, NULL));
+ auto outputInfo = Q_XCB_REPLY(xcb_randr_get_output_info, xcb_connection(),
+ output.output, output.config_timestamp);
if (outputInfo->crtc == XCB_NONE) {
qCDebug(lcQpaScreen) << "output" << screen->name() << "has been disabled";
destroyScreen(screen);
@@ -299,16 +307,10 @@ void QXcbConnection::updateScreens(const xcb_randr_notify_event_t *event)
bool QXcbConnection::checkOutputIsPrimary(xcb_window_t rootWindow, xcb_randr_output_t output)
{
- xcb_generic_error_t *error = 0;
- xcb_randr_get_output_primary_cookie_t primaryCookie =
- xcb_randr_get_output_primary(xcb_connection(), rootWindow);
- QScopedPointer<xcb_randr_get_output_primary_reply_t, QScopedPointerPodDeleter> primary (
- xcb_randr_get_output_primary_reply(xcb_connection(), primaryCookie, &error));
- if (!primary || error) {
+ auto primary = Q_XCB_REPLY(xcb_randr_get_output_primary, xcb_connection(), rootWindow);
+ if (!primary)
qWarning("failed to get the primary output of the screen");
- free(error);
- error = NULL;
- }
+
const bool isPrimary = primary ? (primary->output == output) : false;
return isPrimary;
@@ -367,7 +369,7 @@ void QXcbConnection::destroyScreen(QXcbScreen *screen)
// If there are no other screens on the same virtual desktop,
// then transform the physical screen into a fake screen.
const QString nameWas = screen->name();
- screen->setOutput(XCB_NONE, Q_NULLPTR);
+ screen->setOutput(XCB_NONE, nullptr);
qCDebug(lcQpaScreen) << "transformed" << nameWas << "to fake" << screen;
} else {
// There is more than one screen on the same virtual desktop, remove the screen
@@ -393,7 +395,7 @@ void QXcbConnection::initializeScreens()
{
xcb_screen_iterator_t it = xcb_setup_roots_iterator(m_setup);
int xcbScreenNumber = 0; // screen number in the xcb sense
- QXcbScreen *primaryScreen = Q_NULLPTR;
+ QXcbScreen *primaryScreen = nullptr;
while (it.rem) {
// Each "screen" in xcb terminology is a virtual desktop,
// potentially a collection of separate juxtaposed monitors.
@@ -404,72 +406,59 @@ void QXcbConnection::initializeScreens()
m_virtualDesktops.append(virtualDesktop);
QList<QPlatformScreen *> siblings;
if (has_randr_extension) {
- xcb_generic_error_t *error = NULL;
// RRGetScreenResourcesCurrent is fast but it may return nothing if the
// configuration is not initialized wrt to the hardware. We should call
// RRGetScreenResources in this case.
- QScopedPointer<xcb_randr_get_screen_resources_reply_t, QScopedPointerPodDeleter> resources;
- xcb_randr_get_screen_resources_current_cookie_t resourcesCookie =
- xcb_randr_get_screen_resources_current(xcb_connection(), xcbScreen->root);
- QScopedPointer<xcb_randr_get_screen_resources_current_reply_t, QScopedPointerPodDeleter> resources_current(
- xcb_randr_get_screen_resources_current_reply(xcb_connection(), resourcesCookie, &error));
- if (!resources_current || error) {
+ auto resources_current = Q_XCB_REPLY(xcb_randr_get_screen_resources_current,
+ xcb_connection(), xcbScreen->root);
+ if (!resources_current) {
qWarning("failed to get the current screen resources");
- free(error);
} else {
xcb_timestamp_t timestamp = 0;
- xcb_randr_output_t *outputs = Q_NULLPTR;
- int outputCount = xcb_randr_get_screen_resources_current_outputs_length(resources_current.data());
+ xcb_randr_output_t *outputs = nullptr;
+ int outputCount = xcb_randr_get_screen_resources_current_outputs_length(resources_current.get());
if (outputCount) {
timestamp = resources_current->config_timestamp;
- outputs = xcb_randr_get_screen_resources_current_outputs(resources_current.data());
+ outputs = xcb_randr_get_screen_resources_current_outputs(resources_current.get());
} else {
- xcb_randr_get_screen_resources_cookie_t resourcesCookie =
- xcb_randr_get_screen_resources(xcb_connection(), xcbScreen->root);
- resources.reset(xcb_randr_get_screen_resources_reply(xcb_connection(), resourcesCookie, &error));
- if (!resources || error) {
+ auto resources = Q_XCB_REPLY(xcb_randr_get_screen_resources,
+ xcb_connection(), xcbScreen->root);
+ if (!resources) {
qWarning("failed to get the screen resources");
- free(error);
} else {
timestamp = resources->config_timestamp;
- outputCount = xcb_randr_get_screen_resources_outputs_length(resources.data());
- outputs = xcb_randr_get_screen_resources_outputs(resources.data());
+ outputCount = xcb_randr_get_screen_resources_outputs_length(resources.get());
+ outputs = xcb_randr_get_screen_resources_outputs(resources.get());
}
}
if (outputCount) {
- xcb_randr_get_output_primary_cookie_t primaryCookie =
- xcb_randr_get_output_primary(xcb_connection(), xcbScreen->root);
- QScopedPointer<xcb_randr_get_output_primary_reply_t, QScopedPointerPodDeleter> primary(
- xcb_randr_get_output_primary_reply(xcb_connection(), primaryCookie, &error));
- if (!primary || error) {
+ auto primary = Q_XCB_REPLY(xcb_randr_get_output_primary, xcb_connection(), xcbScreen->root);
+ if (!primary) {
qWarning("failed to get the primary output of the screen");
- free(error);
} else {
for (int i = 0; i < outputCount; i++) {
- QScopedPointer<xcb_randr_get_output_info_reply_t, QScopedPointerPodDeleter> output(
- xcb_randr_get_output_info_reply(xcb_connection(),
- xcb_randr_get_output_info_unchecked(xcb_connection(), outputs[i], timestamp), NULL));
-
+ auto output = Q_XCB_REPLY_UNCHECKED(xcb_randr_get_output_info,
+ xcb_connection(), outputs[i], timestamp);
// Invalid, disconnected or disabled output
- if (output == NULL)
+ if (!output)
continue;
if (output->connection != XCB_RANDR_CONNECTION_CONNECTED) {
qCDebug(lcQpaScreen, "Output %s is not connected", qPrintable(
- QString::fromUtf8((const char*)xcb_randr_get_output_info_name(output.data()),
- xcb_randr_get_output_info_name_length(output.data()))));
+ QString::fromUtf8((const char*)xcb_randr_get_output_info_name(output.get()),
+ xcb_randr_get_output_info_name_length(output.get()))));
continue;
}
if (output->crtc == XCB_NONE) {
qCDebug(lcQpaScreen, "Output %s is not enabled", qPrintable(
- QString::fromUtf8((const char*)xcb_randr_get_output_info_name(output.data()),
- xcb_randr_get_output_info_name_length(output.data()))));
+ QString::fromUtf8((const char*)xcb_randr_get_output_info_name(output.get()),
+ xcb_randr_get_output_info_name_length(output.get()))));
continue;
}
- QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, outputs[i], output.data());
+ QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, outputs[i], output.get());
siblings << screen;
m_screens << screen;
@@ -492,28 +481,24 @@ void QXcbConnection::initializeScreens()
}
} else if (has_xinerama_extension) {
// Xinerama is available
- xcb_xinerama_query_screens_cookie_t cookie = xcb_xinerama_query_screens(m_connection);
- xcb_xinerama_query_screens_reply_t *screens = xcb_xinerama_query_screens_reply(m_connection,
- cookie,
- Q_NULLPTR);
+ auto screens = Q_XCB_REPLY(xcb_xinerama_query_screens, m_connection);
if (screens) {
- xcb_xinerama_screen_info_iterator_t it = xcb_xinerama_query_screens_screen_info_iterator(screens);
+ xcb_xinerama_screen_info_iterator_t it = xcb_xinerama_query_screens_screen_info_iterator(screens.get());
while (it.rem) {
xcb_xinerama_screen_info_t *screen_info = it.data;
QXcbScreen *screen = new QXcbScreen(this, virtualDesktop,
- XCB_NONE, Q_NULLPTR,
+ XCB_NONE, nullptr,
screen_info, it.index);
siblings << screen;
m_screens << screen;
xcb_xinerama_screen_info_next(&it);
}
- free(screens);
}
}
if (siblings.isEmpty()) {
// If there are no XRandR outputs or XRandR extension is missing,
// then create a fake/legacy screen.
- QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, XCB_NONE, Q_NULLPTR);
+ QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, XCB_NONE, nullptr);
qCDebug(lcQpaScreen) << "created fake screen" << screen;
m_screens << screen;
if (m_primaryScreenNumber == xcbScreenNumber) {
@@ -646,7 +631,7 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra
if (m_glIntegration && !m_glIntegration->initialize(this)) {
qCDebug(lcQpaGl) << "Failed to initialize xcb gl-integration" << glIntegrationNames.at(i);
delete m_glIntegration;
- m_glIntegration = Q_NULLPTR;
+ m_glIntegration = nullptr;
}
}
if (!m_glIntegration)
@@ -664,11 +649,6 @@ QXcbConnection::~QXcbConnection()
#ifndef QT_NO_DRAGANDDROP
delete m_drag;
#endif
-
-#if QT_CONFIG(xinput2)
- finalizeXInput2();
-#endif
-
if (m_reader && m_reader->isRunning()) {
sendConnectionEvent(QXcbAtom::_QT_CLOSE_CONNECTION);
m_reader->wait();
@@ -709,7 +689,7 @@ QXcbScreen *QXcbConnection::primaryScreen() const
return m_screens.first();
}
- return Q_NULLPTR;
+ return nullptr;
}
void QXcbConnection::addWindowEventListener(xcb_window_t id, QXcbWindowEventListener *eventListener)
@@ -757,58 +737,73 @@ break;
} \
break;
-//#define XCB_EVENT_DEBUG
-
-void printXcbEvent(const char *message, xcb_generic_event_t *event)
-{
-#ifdef XCB_EVENT_DEBUG
-#define PRINT_XCB_EVENT(ev) \
- case ev: \
- qDebug("QXcbConnection: %s: %d - %s - sequence: %d", message, int(ev), #ev, event->sequence); \
- break;
-
- switch (event->response_type & ~0x80) {
- PRINT_XCB_EVENT(XCB_KEY_PRESS);
- PRINT_XCB_EVENT(XCB_KEY_RELEASE);
- PRINT_XCB_EVENT(XCB_BUTTON_PRESS);
- PRINT_XCB_EVENT(XCB_BUTTON_RELEASE);
- PRINT_XCB_EVENT(XCB_MOTION_NOTIFY);
- PRINT_XCB_EVENT(XCB_ENTER_NOTIFY);
- PRINT_XCB_EVENT(XCB_LEAVE_NOTIFY);
- PRINT_XCB_EVENT(XCB_FOCUS_IN);
- PRINT_XCB_EVENT(XCB_FOCUS_OUT);
- PRINT_XCB_EVENT(XCB_KEYMAP_NOTIFY);
- PRINT_XCB_EVENT(XCB_EXPOSE);
- PRINT_XCB_EVENT(XCB_GRAPHICS_EXPOSURE);
- PRINT_XCB_EVENT(XCB_NO_EXPOSURE);
- PRINT_XCB_EVENT(XCB_VISIBILITY_NOTIFY);
- PRINT_XCB_EVENT(XCB_CREATE_NOTIFY);
- PRINT_XCB_EVENT(XCB_DESTROY_NOTIFY);
- PRINT_XCB_EVENT(XCB_UNMAP_NOTIFY);
- PRINT_XCB_EVENT(XCB_MAP_NOTIFY);
- PRINT_XCB_EVENT(XCB_MAP_REQUEST);
- PRINT_XCB_EVENT(XCB_REPARENT_NOTIFY);
- PRINT_XCB_EVENT(XCB_CONFIGURE_NOTIFY);
- PRINT_XCB_EVENT(XCB_CONFIGURE_REQUEST);
- PRINT_XCB_EVENT(XCB_GRAVITY_NOTIFY);
- PRINT_XCB_EVENT(XCB_RESIZE_REQUEST);
- PRINT_XCB_EVENT(XCB_CIRCULATE_NOTIFY);
- PRINT_XCB_EVENT(XCB_CIRCULATE_REQUEST);
- PRINT_XCB_EVENT(XCB_PROPERTY_NOTIFY);
- PRINT_XCB_EVENT(XCB_SELECTION_CLEAR);
- PRINT_XCB_EVENT(XCB_SELECTION_REQUEST);
- PRINT_XCB_EVENT(XCB_SELECTION_NOTIFY);
- PRINT_XCB_EVENT(XCB_COLORMAP_NOTIFY);
- PRINT_XCB_EVENT(XCB_CLIENT_MESSAGE);
- PRINT_XCB_EVENT(XCB_MAPPING_NOTIFY);
- PRINT_XCB_EVENT(XCB_GE_GENERIC);
- default:
- qDebug("QXcbConnection: %s: unknown event - response_type: %d - sequence: %d", message, int(event->response_type & ~0x80), int(event->sequence));
+void QXcbConnection::printXcbEvent(const QLoggingCategory &log, const char *message,
+ xcb_generic_event_t *event) const
+{
+ quint8 response_type = event->response_type & ~0x80;
+ quint16 sequence = event->sequence;
+
+#define PRINT_AND_RETURN(name) { \
+ qCDebug(log, "%s | %s(%d) | sequence: %d", message, name, response_type, sequence); \
+ return; \
+}
+#define CASE_PRINT_AND_RETURN(name) case name : PRINT_AND_RETURN(#name);
+
+ switch (response_type) {
+ CASE_PRINT_AND_RETURN( XCB_KEY_PRESS );
+ CASE_PRINT_AND_RETURN( XCB_KEY_RELEASE );
+ CASE_PRINT_AND_RETURN( XCB_BUTTON_PRESS );
+ CASE_PRINT_AND_RETURN( XCB_BUTTON_RELEASE );
+ CASE_PRINT_AND_RETURN( XCB_MOTION_NOTIFY );
+ CASE_PRINT_AND_RETURN( XCB_ENTER_NOTIFY );
+ CASE_PRINT_AND_RETURN( XCB_LEAVE_NOTIFY );
+ CASE_PRINT_AND_RETURN( XCB_FOCUS_IN );
+ CASE_PRINT_AND_RETURN( XCB_FOCUS_OUT );
+ CASE_PRINT_AND_RETURN( XCB_KEYMAP_NOTIFY );
+ CASE_PRINT_AND_RETURN( XCB_EXPOSE );
+ CASE_PRINT_AND_RETURN( XCB_GRAPHICS_EXPOSURE );
+ CASE_PRINT_AND_RETURN( XCB_NO_EXPOSURE );
+ CASE_PRINT_AND_RETURN( XCB_VISIBILITY_NOTIFY );
+ CASE_PRINT_AND_RETURN( XCB_CREATE_NOTIFY );
+ CASE_PRINT_AND_RETURN( XCB_DESTROY_NOTIFY );
+ CASE_PRINT_AND_RETURN( XCB_UNMAP_NOTIFY );
+ CASE_PRINT_AND_RETURN( XCB_MAP_NOTIFY );
+ CASE_PRINT_AND_RETURN( XCB_MAP_REQUEST );
+ CASE_PRINT_AND_RETURN( XCB_REPARENT_NOTIFY );
+ CASE_PRINT_AND_RETURN( XCB_CONFIGURE_NOTIFY );
+ CASE_PRINT_AND_RETURN( XCB_CONFIGURE_REQUEST );
+ CASE_PRINT_AND_RETURN( XCB_GRAVITY_NOTIFY );
+ CASE_PRINT_AND_RETURN( XCB_RESIZE_REQUEST );
+ CASE_PRINT_AND_RETURN( XCB_CIRCULATE_NOTIFY );
+ CASE_PRINT_AND_RETURN( XCB_CIRCULATE_REQUEST );
+ CASE_PRINT_AND_RETURN( XCB_PROPERTY_NOTIFY );
+ CASE_PRINT_AND_RETURN( XCB_SELECTION_CLEAR );
+ CASE_PRINT_AND_RETURN( XCB_SELECTION_REQUEST );
+ CASE_PRINT_AND_RETURN( XCB_SELECTION_NOTIFY );
+ CASE_PRINT_AND_RETURN( XCB_COLORMAP_NOTIFY );
+ CASE_PRINT_AND_RETURN( XCB_CLIENT_MESSAGE );
+ CASE_PRINT_AND_RETURN( XCB_MAPPING_NOTIFY );
+ CASE_PRINT_AND_RETURN( XCB_GE_GENERIC );
}
-#else
- Q_UNUSED(message);
- Q_UNUSED(event);
-#endif
+ // XFixes
+ if (has_xfixes && response_type == xfixes_first_event + XCB_XFIXES_SELECTION_NOTIFY)
+ PRINT_AND_RETURN("XCB_XFIXES_SELECTION_NOTIFY");
+ // XRandR
+ if (has_randr_extension) {
+ if (response_type == xrandr_first_event + XCB_RANDR_NOTIFY)
+ PRINT_AND_RETURN("XCB_RANDR_NOTIFY");
+ if (response_type == xrandr_first_event + XCB_RANDR_SCREEN_CHANGE_NOTIFY)
+ PRINT_AND_RETURN("XCB_RANDR_SCREEN_CHANGE_NOTIFY");
+ }
+ // XKB
+ if (response_type == xkb_first_event)
+ PRINT_AND_RETURN("XCB_XKB_* event");
+
+ // UNKNOWN
+ qCDebug(log, "%s | unknown(%d) | sequence: %d", message, response_type, sequence);
+
+#undef PRINT_AND_RETURN
+#undef CASE_PRINT_AND_RETURN
}
const char *xcb_errors[] =
@@ -959,18 +954,6 @@ const char *xcb_protocol_request_codes[] =
"Unknown"
};
-#ifdef Q_XCB_DEBUG
-void QXcbConnection::log(const char *file, int line, int sequence)
-{
- QMutexLocker locker(&m_callLogMutex);
- CallInfo info;
- info.sequence = sequence;
- info.file = file;
- info.line = line;
- m_callLog << info;
-}
-#endif
-
void QXcbConnection::handleXcbError(xcb_generic_error_t *error)
{
long result = 0;
@@ -986,26 +969,6 @@ void QXcbConnection::handleXcbError(xcb_generic_error_t *error)
int(error->sequence), int(error->resource_id),
int(error->major_code), xcb_protocol_request_codes[clamped_major_code],
int(error->minor_code));
-#ifdef Q_XCB_DEBUG
- QMutexLocker locker(&m_callLogMutex);
- int i = 0;
- for (; i < m_callLog.size(); ++i) {
- if (m_callLog.at(i).sequence == error->sequence) {
- qDebug("Caused by: %s:%d", m_callLog.at(i).file.constData(), m_callLog.at(i).line);
- break;
- } else if (m_callLog.at(i).sequence > error->sequence) {
- qDebug("Caused some time before: %s:%d", m_callLog.at(i).file.constData(),
- m_callLog.at(i).line);
- if (i > 0)
- qDebug("and after: %s:%d", m_callLog.at(i-1).file.constData(),
- m_callLog.at(i-1).line);
- break;
- }
- }
- if (i == m_callLog.size() && !m_callLog.isEmpty())
- qDebug("Caused some time after: %s:%d", qAsConst(m_callLog).first().file.constData(),
- qAsConst(m_callLog).first().line);
-#endif
}
static Qt::MouseButtons translateMouseButtons(int s)
@@ -1020,6 +983,12 @@ static Qt::MouseButtons translateMouseButtons(int s)
return ret;
}
+void QXcbConnection::setButtonState(Qt::MouseButton button, bool down)
+{
+ m_buttonState.setFlag(button, down);
+ m_button = button;
+}
+
Qt::MouseButton QXcbConnection::translateMouseButton(xcb_button_t s)
{
switch (s) {
@@ -1075,17 +1044,6 @@ namespace {
void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
{
-#ifdef Q_XCB_DEBUG
- {
- QMutexLocker locker(&m_callLogMutex);
- int i = 0;
- for (; i < m_callLog.size(); ++i)
- if (m_callLog.at(i).sequence >= event->sequence)
- break;
- m_callLog.remove(0, i);
- }
-#endif
-
long result = 0;
QAbstractEventDispatcher* dispatcher = QAbstractEventDispatcher::instance();
bool handled = dispatcher && dispatcher->filterNativeEvent(m_nativeInterface->genericEventFilterType(), event, &result);
@@ -1097,34 +1055,33 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
case XCB_EXPOSE:
HANDLE_PLATFORM_WINDOW_EVENT(xcb_expose_event_t, window, handleExposeEvent);
- // press/release/motion is only delivered here when XI 2.2+ is _not_ in use
case XCB_BUTTON_PRESS: {
xcb_button_press_event_t *ev = (xcb_button_press_event_t *)event;
m_keyboard->updateXKBStateFromCore(ev->state);
// the event explicitly contains the state of the three first buttons,
// the rest we need to manage ourselves
- m_buttons = (m_buttons & ~0x7) | translateMouseButtons(ev->state);
- m_buttons |= translateMouseButton(ev->detail);
+ m_buttonState = (m_buttonState & ~0x7) | translateMouseButtons(ev->state);
+ setButtonState(translateMouseButton(ev->detail), true);
if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
- qCDebug(lcQpaXInputEvents, "legacy mouse press, button %d state %X", ev->detail, static_cast<unsigned int>(m_buttons));
+ qCDebug(lcQpaXInputEvents, "legacy mouse press, button %d state %X", ev->detail, static_cast<unsigned int>(m_buttonState));
HANDLE_PLATFORM_WINDOW_EVENT(xcb_button_press_event_t, event, handleButtonPressEvent);
}
case XCB_BUTTON_RELEASE: {
xcb_button_release_event_t *ev = (xcb_button_release_event_t *)event;
m_keyboard->updateXKBStateFromCore(ev->state);
- m_buttons = (m_buttons & ~0x7) | translateMouseButtons(ev->state);
- m_buttons &= ~translateMouseButton(ev->detail);
+ m_buttonState = (m_buttonState & ~0x7) | translateMouseButtons(ev->state);
+ setButtonState(translateMouseButton(ev->detail), false);
if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
- qCDebug(lcQpaXInputEvents, "legacy mouse release, button %d state %X", ev->detail, static_cast<unsigned int>(m_buttons));
+ qCDebug(lcQpaXInputEvents, "legacy mouse release, button %d state %X", ev->detail, static_cast<unsigned int>(m_buttonState));
HANDLE_PLATFORM_WINDOW_EVENT(xcb_button_release_event_t, event, handleButtonReleaseEvent);
}
case XCB_MOTION_NOTIFY: {
xcb_motion_notify_event_t *ev = (xcb_motion_notify_event_t *)event;
m_keyboard->updateXKBStateFromCore(ev->state);
- m_buttons = (m_buttons & ~0x7) | translateMouseButtons(ev->state);
+ m_buttonState = (m_buttonState & ~0x7) | translateMouseButtons(ev->state);
if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
qCDebug(lcQpaXInputEvents, "legacy mouse move %d,%d button %d state %X", ev->event_x, ev->event_y,
- ev->detail, static_cast<unsigned int>(m_buttons));
+ ev->detail, static_cast<unsigned int>(m_buttonState));
HANDLE_PLATFORM_WINDOW_EVENT(xcb_motion_notify_event_t, event, handleMotionNotifyEvent);
}
@@ -1140,14 +1097,14 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
handleClientMessageEvent((xcb_client_message_event_t *)event);
break;
case XCB_ENTER_NOTIFY:
-#ifdef XCB_USE_XINPUT22
- if (isAtLeastXI22() && xi2MouseEvents())
+#if QT_CONFIG(xinput2)
+ if (hasXInput2() && !xi2MouseEventsDisabled())
break;
#endif
HANDLE_PLATFORM_WINDOW_EVENT(xcb_enter_notify_event_t, event, handleEnterNotifyEvent);
case XCB_LEAVE_NOTIFY:
-#ifdef XCB_USE_XINPUT22
- if (isAtLeastXI22() && xi2MouseEvents())
+#if QT_CONFIG(xinput2)
+ if (hasXInput2() && !xi2MouseEventsDisabled())
break;
#endif
m_keyboard->updateXKBStateFromCore(((xcb_leave_notify_event_t *)event)->state);
@@ -1212,7 +1169,7 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
#if QT_CONFIG(xinput2)
case XCB_GE_GENERIC:
// Here the windowEventListener is invoked from xi2HandleEvent()
- if (m_xi2Enabled && isXIEvent(event, m_xiOpCode))
+ if (hasXInput2() && isXIEvent(event, m_xiOpCode))
xi2HandleEvent(reinterpret_cast<xcb_ge_event_t *>(event));
break;
#endif
@@ -1223,7 +1180,7 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
}
if (!handled) {
- if (response_type == xfixes_first_event + XCB_XFIXES_SELECTION_NOTIFY) {
+ if (has_xfixes && response_type == xfixes_first_event + XCB_XFIXES_SELECTION_NOTIFY) {
xcb_xfixes_selection_notify_event_t *notify_event = reinterpret_cast<xcb_xfixes_selection_notify_event_t *>(event);
setTime(notify_event->timestamp);
#ifndef QT_NO_CLIPBOARD
@@ -1275,10 +1232,10 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
if (!handled && m_glIntegration)
handled = m_glIntegration->handleXcbEvent(event, response_type);
- if (handled)
- printXcbEvent("Handled XCB event", event);
- else
- printXcbEvent("Unhandled XCB event", event);
+#if 0
+ if (Q_UNLIKELY(lcQpaEvents().isDebugEnabled()))
+ printXcbEvent(lcQpaEvents(), handled ? "Handled" : "Unhandled", event);
+#endif
}
void QXcbConnection::addPeekFunc(PeekFunc f)
@@ -1286,6 +1243,95 @@ void QXcbConnection::addPeekFunc(PeekFunc f)
m_peekFuncs.append(f);
}
+qint32 QXcbConnection::generatePeekerId()
+{
+ qint32 peekerId = m_peekerIdSource++;
+ m_peekerToCachedIndex.insert(peekerId, 0);
+ return peekerId;
+}
+
+bool QXcbConnection::removePeekerId(qint32 peekerId)
+{
+ if (!m_peekerToCachedIndex.contains(peekerId)) {
+ qCWarning(lcQpaXcb, "failed to remove unknown peeker id: %d", peekerId);
+ return false;
+ }
+ m_peekerToCachedIndex.remove(peekerId);
+ if (m_peekerToCachedIndex.isEmpty()) {
+ m_peekerIdSource = 0; // Once the hash becomes empty, we can start reusing IDs
+ m_peekerIndexCacheDirty = false;
+ }
+ return true;
+}
+
+bool QXcbConnection::peekEventQueue(PeekerCallback peeker, void *peekerData,
+ PeekOptions option, qint32 peekerId)
+{
+ bool peekerIdProvided = peekerId != -1;
+ if (peekerIdProvided && !m_peekerToCachedIndex.contains(peekerId)) {
+ qCWarning(lcQpaXcb, "failed to find index for unknown peeker id: %d", peekerId);
+ return false;
+ }
+
+ bool peekFromCachedIndex = option.testFlag(PeekOption::PeekFromCachedIndex);
+ if (peekFromCachedIndex && !peekerIdProvided) {
+ qCWarning(lcQpaXcb, "PeekOption::PeekFromCachedIndex requires peeker id");
+ return false;
+ }
+
+ if (peekerIdProvided && m_peekerIndexCacheDirty) {
+ // When the main event loop has flushed the buffered XCB events into the window
+ // system event queue, the cached indices are not valid anymore and need reset.
+ auto it = m_peekerToCachedIndex.begin();
+ while (it != m_peekerToCachedIndex.constEnd()) {
+ (*it) = 0;
+ ++it;
+ }
+ m_peekerIndexCacheDirty = false;
+ }
+
+ qint32 peekerIndex = peekFromCachedIndex ? m_peekerToCachedIndex.value(peekerId) : 0;
+ qint32 startingIndex = peekerIndex;
+ bool result = false;
+ m_mainEventLoopFlushedQueue = false;
+
+ QXcbEventArray *eventqueue = m_reader->lock();
+
+ if (Q_UNLIKELY(lcQpaPeeker().isDebugEnabled())) {
+ qCDebug(lcQpaPeeker, "[%d] peeker index: %d | mode: %s | queue size: %d", peekerId,
+ peekerIndex, peekFromCachedIndex ? "cache" : "start", eventqueue->size());
+ }
+ while (peekerIndex < eventqueue->size() && !result && !m_mainEventLoopFlushedQueue) {
+ xcb_generic_event_t *event = eventqueue->at(peekerIndex++);
+ if (!event)
+ continue;
+ if (Q_UNLIKELY(lcQpaPeeker().isDebugEnabled())) {
+ QString debug = QString((QLatin1String("[%1] peeking at index: %2")))
+ .arg(peekerId).arg(peekerIndex - 1);
+ printXcbEvent(lcQpaPeeker(), debug.toLatin1(), event);
+ }
+ // A peeker may call QCoreApplication::processEvents(), which has two implications:
+ // 1) We need to make the lock available for QXcbConnection::processXcbEvents(),
+ // otherwise we will deadlock;
+ // 2) QXcbConnection::processXcbEvents() will flush the queue we are currently
+ // looping through;
+ m_reader->unlock();
+ result = peeker(event, peekerData);
+ m_reader->lock();
+ }
+
+ m_reader->unlock();
+
+ if (peekerIdProvided && peekerIndex != startingIndex && !m_mainEventLoopFlushedQueue) {
+ auto it = m_peekerToCachedIndex.find(peekerId);
+ // Make sure that a peeker callback did not remove the peeker id
+ if (it != m_peekerToCachedIndex.constEnd())
+ (*it) = peekerIndex;
+ }
+
+ return result;
+}
+
QXcbEventReader::QXcbEventReader(QXcbConnection *connection)
: m_connection(connection)
{
@@ -1373,7 +1419,7 @@ void QXcbConnection::setFocusWindow(QWindow *w)
void QXcbConnection::setMouseGrabber(QXcbWindow *w)
{
m_mouseGrabber = w;
- m_mousePressWindow = Q_NULLPTR;
+ m_mousePressWindow = nullptr;
}
void QXcbConnection::setMousePressWindow(QXcbWindow *w)
{
@@ -1400,10 +1446,10 @@ void QXcbConnection::sendConnectionEvent(QXcbAtom::Atom a, uint id)
const xcb_window_t eventListener = xcb_generate_id(m_connection);
xcb_screen_iterator_t it = xcb_setup_roots_iterator(m_setup);
xcb_screen_t *screen = it.data;
- Q_XCB_CALL(xcb_create_window(m_connection, XCB_COPY_FROM_PARENT,
- eventListener, screen->root,
- 0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_ONLY,
- screen->root_visual, 0, 0));
+ xcb_create_window(m_connection, XCB_COPY_FROM_PARENT,
+ eventListener, screen->root,
+ 0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_ONLY,
+ screen->root_visual, 0, 0);
event.response_type = XCB_CLIENT_MESSAGE;
event.format = 32;
@@ -1412,8 +1458,8 @@ void QXcbConnection::sendConnectionEvent(QXcbAtom::Atom a, uint id)
event.type = atom(a);
event.data.data32[0] = id;
- Q_XCB_CALL(xcb_send_event(xcb_connection(), false, eventListener, XCB_EVENT_MASK_NO_EVENT, reinterpret_cast<const char *>(&event)));
- Q_XCB_CALL(xcb_destroy_window(m_connection, eventListener));
+ xcb_send_event(xcb_connection(), false, eventListener, XCB_EVENT_MASK_NO_EVENT, reinterpret_cast<const char *>(&event));
+ xcb_destroy_window(m_connection, eventListener);
xcb_flush(xcb_connection());
}
@@ -1471,13 +1517,7 @@ xcb_timestamp_t QXcbConnection::getTimestamp()
xcb_window_t QXcbConnection::getSelectionOwner(xcb_atom_t atom) const
{
- xcb_connection_t *c = xcb_connection();
- xcb_get_selection_owner_cookie_t cookie = xcb_get_selection_owner(c, atom);
- xcb_get_selection_owner_reply_t *reply;
- reply = xcb_get_selection_owner_reply(c, cookie, 0);
- xcb_window_t win = reply->owner;
- free(reply);
- return win;
+ return Q_XCB_REPLY(xcb_get_selection_owner, xcb_connection(), atom)->owner;
}
xcb_window_t QXcbConnection::getQtSelectionOwner()
@@ -1487,16 +1527,16 @@ xcb_window_t QXcbConnection::getQtSelectionOwner()
int16_t x = 0, y = 0;
uint16_t w = 3, h = 3;
m_qtSelectionOwner = xcb_generate_id(xcb_connection());
- Q_XCB_CALL(xcb_create_window(xcb_connection(),
- XCB_COPY_FROM_PARENT, // depth -- same as root
- m_qtSelectionOwner, // window id
- xcbScreen->root, // parent window id
- x, y, w, h,
- 0, // border width
- XCB_WINDOW_CLASS_INPUT_OUTPUT, // window class
- xcbScreen->root_visual, // visual
- 0, // value mask
- 0)); // value list
+ xcb_create_window(xcb_connection(),
+ XCB_COPY_FROM_PARENT, // depth -- same as root
+ m_qtSelectionOwner, // window id
+ xcbScreen->root, // parent window id
+ x, y, w, h,
+ 0, // border width
+ XCB_WINDOW_CLASS_INPUT_OUTPUT, // window class
+ xcbScreen->root_visual, // visual
+ 0, // value mask
+ 0); // value list
}
return m_qtSelectionOwner;
}
@@ -1512,47 +1552,47 @@ xcb_window_t QXcbConnection::clientLeader()
if (m_clientLeader == 0) {
m_clientLeader = xcb_generate_id(xcb_connection());
QXcbScreen *screen = primaryScreen();
- Q_XCB_CALL(xcb_create_window(xcb_connection(),
- XCB_COPY_FROM_PARENT,
- m_clientLeader,
- screen->root(),
- 0, 0, 1, 1,
- 0,
- XCB_WINDOW_CLASS_INPUT_OUTPUT,
- screen->screen()->root_visual,
- 0, 0));
+ xcb_create_window(xcb_connection(),
+ XCB_COPY_FROM_PARENT,
+ m_clientLeader,
+ screen->root(),
+ 0, 0, 1, 1,
+ 0,
+ XCB_WINDOW_CLASS_INPUT_OUTPUT,
+ screen->screen()->root_visual,
+ 0, 0);
#ifndef QT_NO_DEBUG
QByteArray ba("Qt client leader window");
- Q_XCB_CALL(xcb_change_property(xcb_connection(),
- XCB_PROP_MODE_REPLACE,
- m_clientLeader,
- atom(QXcbAtom::_NET_WM_NAME),
- atom(QXcbAtom::UTF8_STRING),
- 8,
- ba.length(),
- ba.constData()));
+ xcb_change_property(xcb_connection(),
+ XCB_PROP_MODE_REPLACE,
+ m_clientLeader,
+ atom(QXcbAtom::_NET_WM_NAME),
+ atom(QXcbAtom::UTF8_STRING),
+ 8,
+ ba.length(),
+ ba.constData());
#endif
- Q_XCB_CALL(xcb_change_property(xcb_connection(),
- XCB_PROP_MODE_REPLACE,
- m_clientLeader,
- atom(QXcbAtom::WM_CLIENT_LEADER),
- XCB_ATOM_WINDOW,
- 32,
- 1,
- &m_clientLeader));
+ xcb_change_property(xcb_connection(),
+ XCB_PROP_MODE_REPLACE,
+ m_clientLeader,
+ atom(QXcbAtom::WM_CLIENT_LEADER),
+ XCB_ATOM_WINDOW,
+ 32,
+ 1,
+ &m_clientLeader);
#if QT_CONFIG(xcb_sm)
// If we are session managed, inform the window manager about it
QByteArray session = qGuiApp->sessionId().toLatin1();
if (!session.isEmpty()) {
- Q_XCB_CALL(xcb_change_property(xcb_connection(),
- XCB_PROP_MODE_REPLACE,
- m_clientLeader,
- atom(QXcbAtom::SM_CLIENT_ID),
- XCB_ATOM_STRING,
- 8,
- session.length(),
- session.constData()));
+ xcb_change_property(xcb_connection(),
+ XCB_PROP_MODE_REPLACE,
+ m_clientLeader,
+ atom(QXcbAtom::SM_CLIENT_ID),
+ XCB_ATOM_STRING,
+ 8,
+ session.length(),
+ session.constData());
}
#endif
}
@@ -1574,7 +1614,8 @@ void *QXcbConnection::createVisualInfoForDefaultVisualId() const
info.visualid = m_defaultVisualId;
int count = 0;
- XVisualInfo *retVisual = XGetVisualInfo(DISPLAY_FROM_XCB(this), VisualIDMask, &info, &count);
+ Display *dpy = static_cast<Display *>(connection()->xlib_display());
+ XVisualInfo *retVisual = XGetVisualInfo(dpy, VisualIDMask, &info, &count);
Q_ASSERT(count < 2);
return retVisual;
}
@@ -1629,14 +1670,15 @@ bool QXcbConnection::compressEvent(xcb_generic_event_t *event, int currentIndex,
#if QT_CONFIG(xinput2)
// compress XI_* events
if (responseType == XCB_GE_GENERIC) {
- if (!m_xi2Enabled)
+ if (!hasXInput2())
return false;
// compress XI_Motion, but not from tablet devices
if (isXIType(event, m_xiOpCode, XI_Motion)) {
#if QT_CONFIG(tabletevent)
xXIDeviceEvent *xdev = reinterpret_cast<xXIDeviceEvent *>(event);
- if (const_cast<QXcbConnection *>(this)->tabletDataForDevice(xdev->sourceid))
+ if (!QCoreApplication::testAttribute(Qt::AA_CompressTabletEvents) &&
+ const_cast<QXcbConnection *>(this)->tabletDataForDevice(xdev->sourceid))
return false;
#endif // QT_CONFIG(tabletevent)
for (int j = nextIndex; j < eventqueue->size(); ++j) {
@@ -1736,6 +1778,8 @@ void QXcbConnection::processXcbEvents()
m_reader->unlock();
+ m_peekerIndexCacheDirty = m_mainEventLoopFlushedQueue = true;
+
// Indicate with a null event that the event the callbacks are waiting for
// is not in the queue currently.
for (PeekFunc f : qAsConst(m_peekFuncs))
@@ -2003,11 +2047,7 @@ xcb_atom_t QXcbConnection::internAtom(const char *name)
if (!name || *name == 0)
return XCB_NONE;
- xcb_intern_atom_cookie_t cookie = xcb_intern_atom(xcb_connection(), false, strlen(name), name);
- xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(xcb_connection(), cookie, 0);
- int atom = reply->atom;
- free(reply);
- return atom;
+ return Q_XCB_REPLY(xcb_intern_atom, xcb_connection(), false, strlen(name), name)->atom;
}
QByteArray QXcbConnection::atomName(xcb_atom_t atom)
@@ -2015,18 +2055,12 @@ QByteArray QXcbConnection::atomName(xcb_atom_t atom)
if (!atom)
return QByteArray();
- xcb_generic_error_t *error = 0;
- xcb_get_atom_name_cookie_t cookie = Q_XCB_CALL(xcb_get_atom_name(xcb_connection(), atom));
- xcb_get_atom_name_reply_t *reply = xcb_get_atom_name_reply(xcb_connection(), cookie, &error);
- if (error) {
+ auto reply = Q_XCB_REPLY(xcb_get_atom_name, xcb_connection(), atom);
+ if (!reply)
qWarning() << "QXcbConnection::atomName: bad Atom" << atom;
- free(error);
- }
- if (reply) {
- QByteArray result(xcb_get_atom_name_name(reply), xcb_get_atom_name_name_length(reply));
- free(reply);
- return result;
- }
+ else
+ return QByteArray(xcb_get_atom_name_name(reply.get()), xcb_get_atom_name_name_length(reply.get()));
+
return QByteArray();
}
@@ -2042,35 +2076,32 @@ const xcb_format_t *QXcbConnection::formatForDepth(uint8_t depth) const
xcb_format_next(&iterator);
}
- return 0;
+ qWarning() << "XCB failed to find an xcb_format_t for depth:" << depth;
+ return nullptr;
}
void QXcbConnection::sync()
{
// from xcb_aux_sync
- xcb_get_input_focus_cookie_t cookie = Q_XCB_CALL(xcb_get_input_focus(xcb_connection()));
+ xcb_get_input_focus_cookie_t cookie = xcb_get_input_focus(xcb_connection());
free(xcb_get_input_focus_reply(xcb_connection(), cookie, 0));
}
void QXcbConnection::initializeXFixes()
{
- xcb_generic_error_t *error = 0;
const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_connection, &xcb_xfixes_id);
if (!reply || !reply->present)
return;
- xfixes_first_event = reply->first_event;
- xcb_xfixes_query_version_cookie_t xfixes_query_cookie = xcb_xfixes_query_version(m_connection,
- XCB_XFIXES_MAJOR_VERSION,
- XCB_XFIXES_MINOR_VERSION);
- xcb_xfixes_query_version_reply_t *xfixes_query = xcb_xfixes_query_version_reply (m_connection,
- xfixes_query_cookie, &error);
- if (!xfixes_query || error || xfixes_query->major_version < 2) {
+ auto xfixes_query = Q_XCB_REPLY(xcb_xfixes_query_version, m_connection,
+ XCB_XFIXES_MAJOR_VERSION,
+ XCB_XFIXES_MINOR_VERSION);
+ if (!xfixes_query || xfixes_query->major_version < 2) {
qWarning("QXcbConnection: Failed to initialize XFixes");
- free(error);
- xfixes_first_event = 0;
+ return;
}
- free(xfixes_query);
+ xfixes_first_event = reply->first_event;
+ has_xfixes = true;
}
void QXcbConnection::initializeXRender()
@@ -2080,17 +2111,14 @@ void QXcbConnection::initializeXRender()
if (!reply || !reply->present)
return;
- xcb_generic_error_t *error = 0;
- xcb_render_query_version_cookie_t xrender_query_cookie = xcb_render_query_version(m_connection,
- XCB_RENDER_MAJOR_VERSION,
- XCB_RENDER_MINOR_VERSION);
- xcb_render_query_version_reply_t *xrender_query = xcb_render_query_version_reply(m_connection,
- xrender_query_cookie, &error);
- if (!xrender_query || error || (xrender_query->major_version == 0 && xrender_query->minor_version < 5)) {
+ auto xrender_query = Q_XCB_REPLY(xcb_render_query_version, m_connection,
+ XCB_RENDER_MAJOR_VERSION,
+ XCB_RENDER_MINOR_VERSION);
+ if (!xrender_query || (xrender_query->major_version == 0 && xrender_query->minor_version < 5)) {
qWarning("QXcbConnection: Failed to initialize XRender");
- free(error);
+ return;
}
- free(xrender_query);
+ has_render_extension = true;
#endif
}
@@ -2102,21 +2130,16 @@ void QXcbConnection::initializeXRandr()
xrandr_first_event = reply->first_event;
- xcb_generic_error_t *error = 0;
- xcb_randr_query_version_cookie_t xrandr_query_cookie = xcb_randr_query_version(m_connection,
- XCB_RANDR_MAJOR_VERSION,
- XCB_RANDR_MINOR_VERSION);
+ auto xrandr_query = Q_XCB_REPLY(xcb_randr_query_version, m_connection,
+ XCB_RANDR_MAJOR_VERSION,
+ XCB_RANDR_MINOR_VERSION);
has_randr_extension = true;
- xcb_randr_query_version_reply_t *xrandr_query = xcb_randr_query_version_reply(m_connection,
- xrandr_query_cookie, &error);
- if (!xrandr_query || error || (xrandr_query->major_version < 1 || (xrandr_query->major_version == 1 && xrandr_query->minor_version < 2))) {
+ if (!xrandr_query || (xrandr_query->major_version < 1 || (xrandr_query->major_version == 1 && xrandr_query->minor_version < 2))) {
qWarning("QXcbConnection: Failed to initialize XRandr");
- free(error);
has_randr_extension = false;
}
- free(xrandr_query);
xcb_screen_iterator_t rootIter = xcb_setup_roots_iterator(m_setup);
for (; rootIter.rem; xcb_screen_next(&rootIter)) {
@@ -2136,14 +2159,8 @@ void QXcbConnection::initializeXinerama()
if (!reply || !reply->present)
return;
- xcb_generic_error_t *error = Q_NULLPTR;
- xcb_xinerama_is_active_cookie_t xinerama_query_cookie = xcb_xinerama_is_active(m_connection);
- xcb_xinerama_is_active_reply_t *xinerama_is_active = xcb_xinerama_is_active_reply(m_connection,
- xinerama_query_cookie,
- &error);
- has_xinerama_extension = xinerama_is_active && !error && xinerama_is_active->state;
- free(error);
- free(xinerama_is_active);
+ auto xinerama_is_active = Q_XCB_REPLY(xcb_xinerama_is_active, m_connection);
+ has_xinerama_extension = xinerama_is_active && xinerama_is_active->state;
}
void QXcbConnection::initializeXShape()
@@ -2153,16 +2170,13 @@ void QXcbConnection::initializeXShape()
return;
has_shape_extension = true;
- xcb_shape_query_version_cookie_t cookie = xcb_shape_query_version(m_connection);
- xcb_shape_query_version_reply_t *shape_query = xcb_shape_query_version_reply(m_connection,
- cookie, NULL);
+ auto shape_query = Q_XCB_REPLY(xcb_shape_query_version, m_connection);
if (!shape_query) {
qWarning("QXcbConnection: Failed to initialize SHAPE extension");
} else if (shape_query->major_version > 1 || (shape_query->major_version == 1 && shape_query->minor_version >= 1)) {
// The input shape is the only thing added in SHAPE 1.1
has_input_shape = true;
}
- free(shape_query);
}
void QXcbConnection::initializeXKB()
@@ -2177,11 +2191,10 @@ void QXcbConnection::initializeXKB()
xkb_first_event = reply->first_event;
xcb_connection_t *c = connection()->xcb_connection();
- xcb_xkb_use_extension_cookie_t xkb_query_cookie;
- xcb_xkb_use_extension_reply_t *xkb_query;
- xkb_query_cookie = xcb_xkb_use_extension(c, XKB_X11_MIN_MAJOR_XKB_VERSION, XKB_X11_MIN_MINOR_XKB_VERSION);
- xkb_query = xcb_xkb_use_extension_reply(c, xkb_query_cookie, 0);
+ auto xkb_query = Q_XCB_REPLY(xcb_xkb_use_extension, c,
+ XKB_X11_MIN_MAJOR_XKB_VERSION,
+ XKB_X11_MIN_MINOR_XKB_VERSION);
if (!xkb_query) {
qWarning("Qt: Failed to initialize XKB extension");
@@ -2190,12 +2203,10 @@ void QXcbConnection::initializeXKB()
qWarning("Qt: Unsupported XKB version (We want %d %d, but X server has %d %d)",
XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION,
xkb_query->serverMajor, xkb_query->serverMinor);
- free(xkb_query);
return;
}
has_xkb = true;
- free(xkb_query);
const uint16_t required_map_parts = (XCB_XKB_MAP_PART_KEY_TYPES |
XCB_XKB_MAP_PART_KEY_SYMS |
@@ -2230,62 +2241,6 @@ void QXcbConnection::initializeXKB()
#endif
}
-#if defined(XCB_USE_XINPUT22)
-bool QXcbConnection::xi2MouseEvents() const
-{
- static bool mouseViaXI2 = !qEnvironmentVariableIsSet("QT_XCB_NO_XI2_MOUSE");
- // FIXME: Don't use XInput2 mouse events when Xinerama extension
- // is enabled, because it causes problems with multi-monitor setup.
- return mouseViaXI2 && !has_xinerama_extension;
-}
-#endif
-
-#if QT_CONFIG(xinput2)
-static int xi2ValuatorOffset(const unsigned char *maskPtr, int maskLen, int number)
-{
- int offset = 0;
- for (int i = 0; i < maskLen; i++) {
- if (number < 8) {
- if ((maskPtr[i] & (1 << number)) == 0)
- return -1;
- }
- for (int j = 0; j < 8; j++) {
- if (j == number)
- return offset;
- if (maskPtr[i] & (1 << j))
- offset++;
- }
- number -= 8;
- }
- return -1;
-}
-
-bool QXcbConnection::xi2GetValuatorValueIfSet(const void *event, int valuatorNum, double *value)
-{
- const xXIDeviceEvent *xideviceevent = static_cast<const xXIDeviceEvent *>(event);
- const unsigned char *buttonsMaskAddr = (const unsigned char*)&xideviceevent[1];
- const unsigned char *valuatorsMaskAddr = buttonsMaskAddr + xideviceevent->buttons_len * 4;
- FP3232 *valuatorsValuesAddr = (FP3232*)(valuatorsMaskAddr + xideviceevent->valuators_len * 4);
-
- int valuatorOffset = xi2ValuatorOffset(valuatorsMaskAddr, xideviceevent->valuators_len, valuatorNum);
- if (valuatorOffset < 0)
- return false;
-
- *value = valuatorsValuesAddr[valuatorOffset].integral;
- *value += ((double)valuatorsValuesAddr[valuatorOffset].frac / (1 << 16) / (1 << 16));
- return true;
-}
-
-void QXcbConnection::xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *event)
-{
- // xcb event structs contain stuff that wasn't on the wire, the full_sequence field
- // adds an extra 4 bytes and generic events cookie data is on the wire right after the standard 32 bytes.
- // Move this data back to have the same layout in memory as it was on the wire
- // and allow casting, overwriting the full_sequence field.
- memmove((char*) event + 32, (char*) event + 36, event->length * 4);
-}
-#endif // QT_CONFIG(xinput2)
-
QXcbSystemTrayTracker *QXcbConnection::systemTrayTracker() const
{
if (!m_systemTrayTracker) {
diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h
index edbc8d846e..07df963ec5 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.h
+++ b/src/plugins/platforms/xcb/qxcbconnection.h
@@ -56,6 +56,9 @@
#include <QtCore/QLoggingCategory>
#include <QtCore/private/qglobal_p.h>
+#include <cstdlib>
+#include <memory>
+
// This is needed to make Qt compile together with XKB. xkb.h is using a variable
// which is called 'explicit', this is a reserved keyword in c++
#if QT_CONFIG(xkb)
@@ -76,19 +79,19 @@
#define XCB_USE_XINPUT22 // XI 2.2 adds multi-point touch support
#endif
#endif
-struct XInput2TouchDeviceData;
#endif // QT_CONFIG(xinput2)
struct xcb_randr_get_output_info_reply_t;
-//#define Q_XCB_DEBUG
-
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcQpaXInput)
Q_DECLARE_LOGGING_CATEGORY(lcQpaXInputDevices)
Q_DECLARE_LOGGING_CATEGORY(lcQpaXInputEvents)
Q_DECLARE_LOGGING_CATEGORY(lcQpaScreen)
+Q_DECLARE_LOGGING_CATEGORY(lcQpaEvents)
+Q_DECLARE_LOGGING_CATEGORY(lcQpaXcb)
+Q_DECLARE_LOGGING_CATEGORY(lcQpaPeeker)
class QXcbVirtualDesktop;
class QXcbScreen;
@@ -358,7 +361,7 @@ public:
virtual void handleFocusInEvent(const xcb_focus_in_event_t *) {}
virtual void handleFocusOutEvent(const xcb_focus_out_event_t *) {}
virtual void handlePropertyNotifyEvent(const xcb_property_notify_event_t *) {}
-#ifdef XCB_USE_XINPUT22
+#if QT_CONFIG(xinput2)
virtual void handleXIMouseEvent(xcb_ge_event_t *, Qt::MouseEventSource = Qt::MouseEventNotSynthesized) {}
virtual void handleXIEnterLeave(xcb_ge_event_t *) {}
#endif
@@ -406,6 +409,15 @@ public:
const xcb_setup_t *setup() const { return m_setup; }
const xcb_format_t *formatForDepth(uint8_t depth) const;
+ bool imageNeedsEndianSwap() const
+ {
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+ return m_setup->image_byte_order != XCB_IMAGE_ORDER_MSB_FIRST;
+#else
+ return m_setup->image_byte_order != XCB_IMAGE_ORDER_LSB_FIRST;
+#endif
+ }
+
QXcbKeyboard *keyboard() const { return m_keyboard; }
#ifndef QT_NO_CLIPBOARD
@@ -426,26 +438,12 @@ public:
void *xlib_display() const;
void *createVisualInfoForDefaultVisualId() const;
#endif
-
-#if QT_CONFIG(xinput2)
- void xi2Select(xcb_window_t window);
- void xi2SelectStateEvents();
-#endif
-#ifdef XCB_USE_XINPUT21
- bool isAtLeastXI21() const { return m_xi2Enabled && m_xi2Minor >= 1; }
-#else
- bool isAtLeastXI21() const { return false; }
-#endif
-#ifdef XCB_USE_XINPUT22
- bool isAtLeastXI22() const { return m_xi2Enabled && m_xi2Minor >= 2; }
-#else
- bool isAtLeastXI22() const { return false; }
-#endif
-
void sync();
void handleXcbError(xcb_generic_error_t *error);
void handleXcbEvent(xcb_generic_event_t *event);
+ void printXcbEvent(const QLoggingCategory &log, const char *message,
+ xcb_generic_event_t *event) const;
void addWindowEventListener(xcb_window_t id, QXcbWindowEventListener *eventListener);
void removeWindowEventListener(xcb_window_t id);
@@ -458,27 +456,38 @@ public:
typedef bool (*PeekFunc)(QXcbConnection *, xcb_generic_event_t *);
void addPeekFunc(PeekFunc f);
+ // Peek at all queued events
+ qint32 generatePeekerId();
+ bool removePeekerId(qint32 peekerId);
+ enum PeekOption { PeekDefault = 0, PeekFromCachedIndex = 1 }; // see qx11info_x11.h
+ Q_DECLARE_FLAGS(PeekOptions, PeekOption)
+ typedef bool (*PeekerCallback)(xcb_generic_event_t *event, void *peekerData);
+ bool peekEventQueue(PeekerCallback peeker, void *peekerData = nullptr,
+ PeekOptions option = PeekDefault, qint32 peekerId = -1);
+
inline xcb_timestamp_t time() const { return m_time; }
inline void setTime(xcb_timestamp_t t) { if (t > m_time) m_time = t; }
inline xcb_timestamp_t netWmUserTime() const { return m_netWmUserTime; }
inline void setNetWmUserTime(xcb_timestamp_t t) { if (t > m_netWmUserTime) m_netWmUserTime = t; }
- bool hasXFixes() const { return xfixes_first_event > 0; }
+ bool hasXFixes() const { return has_xfixes; }
bool hasXShape() const { return has_shape_extension; }
bool hasXRandr() const { return has_randr_extension; }
bool hasInputShape() const { return has_input_shape; }
bool hasXKB() const { return has_xkb; }
+ bool hasXRender() const { return has_render_extension; }
+ bool hasXInput2() const { return m_xi2Enabled; }
- bool supportsThreadedRendering() const { return m_reader->isRunning(); }
bool threadedEventHandling() const { return m_reader->isRunning(); }
xcb_timestamp_t getTimestamp();
xcb_window_t getSelectionOwner(xcb_atom_t atom) const;
xcb_window_t getQtSelectionOwner();
- void setButton(Qt::MouseButton button, bool down) { m_buttons.setFlag(button, down); }
- Qt::MouseButtons buttons() const { return m_buttons; }
+ void setButtonState(Qt::MouseButton button, bool down);
+ Qt::MouseButtons buttonState() const { return m_buttonState; }
+ Qt::MouseButton button() const { return m_button; }
Qt::MouseButton translateMouseButton(xcb_button_t s);
QXcbWindow *focusWindow() const { return m_focusWindow; }
@@ -501,27 +510,29 @@ public:
static bool xEmbedSystemTrayAvailable();
static bool xEmbedSystemTrayVisualHasAlphaChannel();
+#if QT_CONFIG(xinput2)
+ void xi2SelectStateEvents();
+ void xi2SelectDeviceEvents(xcb_window_t window);
+ void xi2SelectDeviceEventsCompatibility(xcb_window_t window);
+ bool xi2SetMouseGrabEnabled(xcb_window_t w, bool grab);
+ bool xi2MouseEventsDisabled() const;
+ bool isAtLeastXI21() const { return m_xi2Enabled && m_xi2Minor >= 1; }
+ bool isAtLeastXI22() const { return m_xi2Enabled && m_xi2Minor >= 2; }
+ Qt::MouseButton xiToQtMouseButton(uint32_t b);
#ifdef XCB_USE_XINPUT21
- void handleEnterEvent();
+ void xi2UpdateScrollingDevices();
#endif
-
#ifdef XCB_USE_XINPUT22
- bool startSystemResizeForTouchBegin(xcb_window_t window, const QPoint &point, Qt::Corner corner);
- bool xi2SetMouseGrabEnabled(xcb_window_t w, bool grab);
+ bool startSystemMoveResizeForTouchBegin(xcb_window_t window, const QPoint &point, int corner);
+ bool isTouchScreen(int id);
+#endif
#endif
- Qt::MouseButton xiToQtMouseButton(uint32_t b);
-
QXcbEventReader *eventReader() const { return m_reader; }
bool canGrab() const { return m_canGrabServer; }
QXcbGlIntegration *glIntegration() const { return m_glIntegration; }
-#ifdef XCB_USE_XINPUT22
- bool xi2MouseEvents() const;
- bool isTouchScreen(int id) const;
-#endif
-
protected:
bool event(QEvent *e) override;
@@ -553,15 +564,35 @@ private:
void destroyScreen(QXcbScreen *screen);
void initializeScreens();
bool compressEvent(xcb_generic_event_t *event, int currentIndex, QXcbEventArray *eventqueue) const;
-#if QT_CONFIG(xinput2)
+
bool m_xi2Enabled = false;
- int m_xi2Minor = 2;
+#if QT_CONFIG(xinput2)
+ int m_xi2Minor = -1;
void initializeXInput2();
- void finalizeXInput2();
+ void xi2SetupDevice(void *info, bool removeExisting = true);
void xi2SetupDevices();
- XInput2TouchDeviceData *touchDeviceForId(int id);
+ struct TouchDeviceData {
+ QTouchDevice *qtTouchDevice = nullptr;
+ QHash<int, QWindowSystemInterface::TouchPoint> touchPoints;
+ QHash<int, QPointF> pointPressedPosition; // in screen coordinates where each point was pressed
+ struct ValuatorClassInfo {
+ double min = 0;
+ double max = 0;
+ int number = -1;
+ QXcbAtom::Atom label;
+ };
+ QVector<ValuatorClassInfo> valuatorInfo;
+
+ // Stuff that is relevant only for touchpads
+ QPointF firstPressedPosition; // in screen coordinates where the first point was pressed
+ QPointF firstPressedNormalPosition; // device coordinates (0 to 1, 0 to 1) where the first point was pressed
+ QSizeF size; // device size in mm
+ bool providesTouchOrientation = false;
+ };
+ TouchDeviceData *populateTouchDevices(void *info);
+ TouchDeviceData *touchDeviceForId(int id);
void xi2HandleEvent(xcb_ge_event_t *event);
- void xi2HandleHierachyEvent(void *event);
+ void xi2HandleHierarchyEvent(void *event);
void xi2HandleDeviceChangedEvent(void *event);
int m_xiOpCode, m_xiEventBase, m_xiErrorBase;
#ifdef XCB_USE_XINPUT22
@@ -600,9 +631,12 @@ private:
Qt::Orientations legacyOrientations = 0;
QPointF lastScrollPosition;
};
- void updateScrollingDevice(ScrollingDevice& scrollingDevice, int num_classes, void *classes);
- void xi2HandleScrollEvent(void *event, ScrollingDevice &scrollingDevice);
QHash<int, ScrollingDevice> m_scrollingDevices;
+#ifdef XCB_USE_XINPUT21
+ void xi2HandleScrollEvent(void *event, ScrollingDevice &scrollingDevice);
+ void xi2UpdateScrollingDevice(ScrollingDevice &scrollingDevice);
+ ScrollingDevice *scrollingDeviceForId(int id);
+#endif
static bool xi2GetValuatorValueIfSet(const void *event, int valuatorNum, double *value);
static void xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *event);
@@ -638,34 +672,18 @@ private:
void *m_xlib_display = nullptr;
#endif
QXcbEventReader *m_reader = nullptr;
+
#if QT_CONFIG(xinput2)
- QHash<int, XInput2TouchDeviceData*> m_touchDevices;
+ QHash<int, TouchDeviceData> m_touchDevices;
#ifdef XCB_USE_XINPUT22
- struct StartSystemResizeInfo {
- xcb_window_t window;
+ struct StartSystemMoveResizeInfo {
+ xcb_window_t window = XCB_NONE;
uint16_t deviceid;
uint32_t pointid;
- Qt::Corner corner;
- } m_startSystemResizeInfo;
+ int corner;
+ } m_startSystemMoveResizeInfo;
#endif
#endif
-#ifdef Q_XCB_DEBUG
- struct CallInfo {
- int sequence;
- QByteArray file;
- int line;
- };
- QVector<CallInfo> m_callLog;
- QMutex m_callLogMutex;
- void log(const char *file, int line, int sequence);
- template <typename cookie_t>
- friend cookie_t q_xcb_call_template(const cookie_t &cookie, QXcbConnection *connection,
- const char *file, int line);
- template <typename reply_t>
- friend reply_t *q_xcb_call_template(reply_t *reply, QXcbConnection *connection,
- const char *file, int line);
-#endif
-
WindowMapper m_mapper;
QVector<PeekFunc> m_peekFuncs;
@@ -674,13 +692,16 @@ private:
uint32_t xrandr_first_event = 0;
uint32_t xkb_first_event = 0;
+ bool has_xfixes = false;
bool has_xinerama_extension = false;
bool has_shape_extension = false;
bool has_randr_extension = false;
bool has_input_shape;
bool has_xkb = false;
+ bool has_render_extension = false;
- Qt::MouseButtons m_buttons = 0;
+ Qt::MouseButtons m_buttonState = 0;
+ Qt::MouseButton m_button = Qt::NoButton;
QXcbWindow *m_focusWindow = nullptr;
QXcbWindow *m_mouseGrabber = nullptr;
@@ -691,9 +712,14 @@ private:
QXcbSystemTrayTracker *m_systemTrayTracker = nullptr;
QXcbGlIntegration *m_glIntegration = nullptr;
bool m_xiGrab = false;
+ QVector<int> m_xiMasterPointerIds;
xcb_window_t m_qtSelectionOwner = 0;
+ bool m_mainEventLoopFlushedQueue = false;
+ qint32 m_peekerIdSource = 0;
+ bool m_peekerIndexCacheDirty = false;
+ QHash<qint32, qint32> m_peekerToCachedIndex;
friend class QXcbEventReader;
};
#if QT_CONFIG(xinput2)
@@ -703,9 +729,6 @@ Q_DECLARE_TYPEINFO(QXcbConnection::TabletData, Q_MOVABLE_TYPE);
#endif
#endif
-#define DISPLAY_FROM_XCB(object) (reinterpret_cast<Display *>(object->connection()->xlib_display()))
-#define CREATE_VISUALINFO_FROM_DEFAULT_VISUALID(object) ((XVisualInfo *)(object->connection()->createVisualInfoForDefaultVisualId()))
-
template<typename T>
xcb_generic_event_t *QXcbConnection::checkEvent(T &checker)
{
@@ -733,6 +756,22 @@ private:
QXcbConnection *m_connection;
};
+#define Q_XCB_REPLY_CONNECTION_ARG(connection, ...) connection
+
+struct QStdFreeDeleter {
+ void operator()(void *p) const Q_DECL_NOTHROW { return std::free(p); }
+};
+
+#define Q_XCB_REPLY(call, ...) \
+ std::unique_ptr<call##_reply_t, QStdFreeDeleter>( \
+ call##_reply(Q_XCB_REPLY_CONNECTION_ARG(__VA_ARGS__), call(__VA_ARGS__), nullptr) \
+ )
+
+#define Q_XCB_REPLY_UNCHECKED(call, ...) \
+ std::unique_ptr<call##_reply_t, QStdFreeDeleter>( \
+ call##_reply(Q_XCB_REPLY_CONNECTION_ARG(__VA_ARGS__), call##_unchecked(__VA_ARGS__), nullptr) \
+ )
+
template <typename T>
union q_padded_xcb_event {
T event;
@@ -746,30 +785,6 @@ union q_padded_xcb_event {
q_padded_xcb_event<event_type> store = {}; \
auto &event_var = store.event;
-#ifdef Q_XCB_DEBUG
-template <typename cookie_t>
-cookie_t q_xcb_call_template(const cookie_t &cookie, QXcbConnection *connection, const char *file,
- int line)
-{
- connection->log(file, line, cookie.sequence);
- return cookie;
-}
-
-template <typename reply_t>
-reply_t *q_xcb_call_template(reply_t *reply, QXcbConnection *connection, const char *file, int line)
-{
- connection->log(file, line, reply->sequence);
- return reply;
-}
-#define Q_XCB_CALL(x) q_xcb_call_template(x, connection(), __FILE__, __LINE__)
-#define Q_XCB_CALL2(x, connection) q_xcb_call_template(x, connection, __FILE__, __LINE__)
-#define Q_XCB_NOOP(c) q_xcb_call_template(xcb_no_operation(c->xcb_connection()), c, __FILE__, __LINE__);
-#else
-#define Q_XCB_CALL(x) x
-#define Q_XCB_CALL2(x, connection) x
-#define Q_XCB_NOOP(c) (void)c;
-#endif
-
QT_END_NAMESPACE
#endif
diff --git a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
index 94f543fd39..39d2857212 100644
--- a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
@@ -50,50 +50,38 @@
#include <X11/extensions/XInput2.h>
#include <X11/extensions/XI2proto.h>
-struct XInput2TouchDeviceData {
- XIDeviceInfo *xiDeviceInfo = nullptr;
- QTouchDevice *qtTouchDevice = nullptr;
- QHash<int, QWindowSystemInterface::TouchPoint> touchPoints;
- QHash<int, QPointF> pointPressedPosition; // in screen coordinates where each point was pressed
-
- // Stuff that is relevant only for touchpads
- QPointF firstPressedPosition; // in screen coordinates where the first point was pressed
- QPointF firstPressedNormalPosition; // device coordinates (0 to 1, 0 to 1) where the first point was pressed
- QSizeF size; // device size in mm
- bool providesTouchOrientation = false;
-};
-
void QXcbConnection::initializeXInput2()
{
- // TODO Qt 6 (or perhaps earlier): remove these redundant env variables
- if (qEnvironmentVariableIsSet("QT_XCB_DEBUG_XINPUT"))
- const_cast<QLoggingCategory&>(lcQpaXInput()).setEnabled(QtDebugMsg, true);
- if (qEnvironmentVariableIsSet("QT_XCB_DEBUG_XINPUT_DEVICES"))
- const_cast<QLoggingCategory&>(lcQpaXInputDevices()).setEnabled(QtDebugMsg, true);
Display *xDisplay = static_cast<Display *>(m_xlib_display);
if (XQueryExtension(xDisplay, "XInputExtension", &m_xiOpCode, &m_xiEventBase, &m_xiErrorBase)) {
int xiMajor = 2;
- // try 2.2 first, needed for TouchBegin/Update/End
- if (XIQueryVersion(xDisplay, &xiMajor, &m_xi2Minor) == BadRequest) {
- m_xi2Minor = 1; // for smooth scrolling 2.1 is enough
- if (XIQueryVersion(xDisplay, &xiMajor, &m_xi2Minor) == BadRequest) {
- m_xi2Minor = 0; // for tablet support 2.0 is enough
- m_xi2Enabled = XIQueryVersion(xDisplay, &xiMajor, &m_xi2Minor) != BadRequest;
- } else
- m_xi2Enabled = true;
- } else
- m_xi2Enabled = true;
- if (m_xi2Enabled) {
-#ifdef XCB_USE_XINPUT22
- qCDebug(lcQpaXInputDevices, "XInput version %d.%d is available and Qt supports 2.2 or greater", xiMajor, m_xi2Minor);
- m_startSystemResizeInfo.window = XCB_NONE;
+#if defined(XCB_USE_XINPUT22)
+ m_xi2Minor = 2; // for touch support 2.2 is enough
+#elif defined(XCB_USE_XINPUT21)
+ m_xi2Minor = 1; // for smooth scrolling 2.1 is enough
#else
- qCDebug(lcQpaXInputDevices, "XInput version %d.%d is available and Qt supports 2.0", xiMajor, m_xi2Minor);
+ m_xi2Minor = 0; // for tablet support 2.0 is enough
#endif
+ qCDebug(lcQpaXInput, "Plugin build with support for XInput 2 version up "
+ "to %d.%d", xiMajor, m_xi2Minor);
+
+ switch (XIQueryVersion(xDisplay, &xiMajor, &m_xi2Minor)) {
+ case Success:
+ // Server's supported version can be lower than the version we have
+ // announced to support. In this case Qt client will be limited by
+ // X server's supported version.
+ qCDebug(lcQpaXInput, "Using XInput version %d.%d", xiMajor, m_xi2Minor);
+ m_xi2Enabled = true;
+ xi2SetupDevices();
xi2SelectStateEvents();
+ break;
+ case BadRequest: // Must be an X server with XInput 1
+ qCDebug(lcQpaXInput, "X server does not support XInput 2");
+ break;
+ default: // BadValue
+ qCDebug(lcQpaXInput, "Internal error");
+ break;
}
-
- xi2SetupDevices();
}
}
@@ -106,6 +94,7 @@ void QXcbConnection::xi2SelectStateEvents()
XIEventMask xiEventMask;
bitMask = XI_HierarchyChangedMask;
bitMask |= XI_DeviceChangedMask;
+ bitMask |= XI_PropertyEventMask;
xiEventMask.deviceid = XIAllDevices;
xiEventMask.mask_len = sizeof(bitMask);
xiEventMask.mask = xiBitMask;
@@ -113,377 +102,445 @@ void QXcbConnection::xi2SelectStateEvents()
XISelectEvents(dpy, DefaultRootWindow(dpy), &xiEventMask, 1);
}
-void QXcbConnection::xi2SetupDevices()
+void QXcbConnection::xi2SelectDeviceEvents(xcb_window_t window)
{
-#if QT_CONFIG(tabletevent)
- m_tabletData.clear();
+ if (window == rootWindow())
+ return;
+
+ unsigned int bitMask = 0;
+ unsigned char *xiBitMask = reinterpret_cast<unsigned char *>(&bitMask);
+ bitMask |= XI_ButtonPressMask;
+ bitMask |= XI_ButtonReleaseMask;
+ bitMask |= XI_MotionMask;
+ // There is a check for enter/leave events in plain xcb enter/leave event handler,
+ // core enter/leave events will be ignored in this case.
+ bitMask |= XI_EnterMask;
+ bitMask |= XI_LeaveMask;
+#ifdef XCB_USE_XINPUT22
+ if (isAtLeastXI22()) {
+ bitMask |= XI_TouchBeginMask;
+ bitMask |= XI_TouchUpdateMask;
+ bitMask |= XI_TouchEndMask;
+ }
#endif
- m_scrollingDevices.clear();
- if (!m_xi2Enabled)
- return;
+ XIEventMask mask;
+ mask.mask_len = sizeof(bitMask);
+ mask.mask = xiBitMask;
+ mask.deviceid = XIAllMasterDevices;
+ Display *dpy = static_cast<Display *>(m_xlib_display);
+ Status result = XISelectEvents(dpy, window, &mask, 1);
+ if (result == Success)
+ QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(false);
+ else
+ qCDebug(lcQpaXInput, "failed to select events, window %x, result %d", window, result);
+}
- Display *xDisplay = static_cast<Display *>(m_xlib_display);
- int deviceCount = 0;
- XIDeviceInfo *devices = XIQueryDevice(xDisplay, XIAllDevices, &deviceCount);
- for (int i = 0; i < deviceCount; ++i) {
- // Only non-master pointing devices are relevant here.
- if (devices[i].use != XISlavePointer)
- continue;
- qCDebug(lcQpaXInputDevices) << "input device " << devices[i].name << "ID" << devices[i].deviceid;
+void QXcbConnection::xi2SetupDevice(void *info, bool removeExisting)
+{
+ XIDeviceInfo *deviceInfo = reinterpret_cast<XIDeviceInfo *>(info);
+ if (removeExisting) {
#if QT_CONFIG(tabletevent)
- TabletData tabletData;
+ for (int i = 0; i < m_tabletData.count(); ++i) {
+ if (m_tabletData.at(i).deviceId == deviceInfo->deviceid) {
+ m_tabletData.remove(i);
+ break;
+ }
+ }
#endif
- ScrollingDevice scrollingDevice;
- for (int c = 0; c < devices[i].num_classes; ++c) {
- switch (devices[i].classes[c]->type) {
- case XIValuatorClass: {
- XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(devices[i].classes[c]);
- const int valuatorAtom = qatom(vci->label);
- qCDebug(lcQpaXInputDevices) << " has valuator" << atomName(vci->label) << "recognized?" << (valuatorAtom < QXcbAtom::NAtoms);
+ m_scrollingDevices.remove(deviceInfo->deviceid);
+ m_touchDevices.remove(deviceInfo->deviceid);
+ }
+
+ qCDebug(lcQpaXInputDevices) << "input device " << deviceInfo->name << "ID" << deviceInfo->deviceid;
#if QT_CONFIG(tabletevent)
- if (valuatorAtom < QXcbAtom::NAtoms) {
- TabletData::ValuatorClassInfo info;
- info.minVal = vci->min;
- info.maxVal = vci->max;
- info.number = vci->number;
- tabletData.valuatorInfo[valuatorAtom] = info;
- }
-#endif // QT_CONFIG(tabletevent)
- if (valuatorAtom == QXcbAtom::RelHorizScroll || valuatorAtom == QXcbAtom::RelHorizWheel)
- scrollingDevice.lastScrollPosition.setX(vci->value);
- else if (valuatorAtom == QXcbAtom::RelVertScroll || valuatorAtom == QXcbAtom::RelVertWheel)
- scrollingDevice.lastScrollPosition.setY(vci->value);
- break;
+ TabletData tabletData;
+#endif
+ ScrollingDevice scrollingDevice;
+ for (int c = 0; c < deviceInfo->num_classes; ++c) {
+ XIAnyClassInfo *classinfo = deviceInfo->classes[c];
+ switch (classinfo->type) {
+ case XIValuatorClass: {
+ XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(classinfo);
+ const int valuatorAtom = qatom(vci->label);
+ qCDebug(lcQpaXInputDevices) << " has valuator" << atomName(vci->label) << "recognized?" << (valuatorAtom < QXcbAtom::NAtoms);
+#if QT_CONFIG(tabletevent)
+ if (valuatorAtom < QXcbAtom::NAtoms) {
+ TabletData::ValuatorClassInfo info;
+ info.minVal = vci->min;
+ info.maxVal = vci->max;
+ info.number = vci->number;
+ tabletData.valuatorInfo[valuatorAtom] = info;
}
+#endif // QT_CONFIG(tabletevent)
+ if (valuatorAtom == QXcbAtom::RelHorizScroll || valuatorAtom == QXcbAtom::RelHorizWheel)
+ scrollingDevice.lastScrollPosition.setX(vci->value);
+ else if (valuatorAtom == QXcbAtom::RelVertScroll || valuatorAtom == QXcbAtom::RelVertWheel)
+ scrollingDevice.lastScrollPosition.setY(vci->value);
+ break;
+ }
#ifdef XCB_USE_XINPUT21
- case XIScrollClass: {
- XIScrollClassInfo *sci = reinterpret_cast<XIScrollClassInfo *>(devices[i].classes[c]);
- if (sci->scroll_type == XIScrollTypeVertical) {
- scrollingDevice.orientations |= Qt::Vertical;
- scrollingDevice.verticalIndex = sci->number;
- scrollingDevice.verticalIncrement = sci->increment;
- }
- else if (sci->scroll_type == XIScrollTypeHorizontal) {
- scrollingDevice.orientations |= Qt::Horizontal;
- scrollingDevice.horizontalIndex = sci->number;
- scrollingDevice.horizontalIncrement = sci->increment;
- }
- break;
+ case XIScrollClass: {
+ XIScrollClassInfo *sci = reinterpret_cast<XIScrollClassInfo *>(classinfo);
+ if (sci->scroll_type == XIScrollTypeVertical) {
+ scrollingDevice.orientations |= Qt::Vertical;
+ scrollingDevice.verticalIndex = sci->number;
+ scrollingDevice.verticalIncrement = sci->increment;
}
- case XIButtonClass: {
- XIButtonClassInfo *bci = reinterpret_cast<XIButtonClassInfo *>(devices[i].classes[c]);
- if (bci->num_buttons >= 5) {
- Atom label4 = bci->labels[3];
- Atom label5 = bci->labels[4];
- // Some drivers have no labels on the wheel buttons, some have no label on just one and some have no label on
- // button 4 and the wrong one on button 5. So we just check that they are not labelled with unrelated buttons.
- if ((!label4 || qatom(label4) == QXcbAtom::ButtonWheelUp || qatom(label4) == QXcbAtom::ButtonWheelDown) &&
- (!label5 || qatom(label5) == QXcbAtom::ButtonWheelUp || qatom(label5) == QXcbAtom::ButtonWheelDown))
- scrollingDevice.legacyOrientations |= Qt::Vertical;
- }
- if (bci->num_buttons >= 7) {
- Atom label6 = bci->labels[5];
- Atom label7 = bci->labels[6];
- if ((!label6 || qatom(label6) == QXcbAtom::ButtonHorizWheelLeft) && (!label7 || qatom(label7) == QXcbAtom::ButtonHorizWheelRight))
- scrollingDevice.legacyOrientations |= Qt::Horizontal;
- }
- qCDebug(lcQpaXInputDevices, " has %d buttons", bci->num_buttons);
- break;
+ else if (sci->scroll_type == XIScrollTypeHorizontal) {
+ scrollingDevice.orientations |= Qt::Horizontal;
+ scrollingDevice.horizontalIndex = sci->number;
+ scrollingDevice.horizontalIncrement = sci->increment;
+ }
+ break;
+ }
+ case XIButtonClass: {
+ XIButtonClassInfo *bci = reinterpret_cast<XIButtonClassInfo *>(classinfo);
+ if (bci->num_buttons >= 5) {
+ Atom label4 = bci->labels[3];
+ Atom label5 = bci->labels[4];
+ // Some drivers have no labels on the wheel buttons, some have no label on just one and some have no label on
+ // button 4 and the wrong one on button 5. So we just check that they are not labelled with unrelated buttons.
+ if ((!label4 || qatom(label4) == QXcbAtom::ButtonWheelUp || qatom(label4) == QXcbAtom::ButtonWheelDown) &&
+ (!label5 || qatom(label5) == QXcbAtom::ButtonWheelUp || qatom(label5) == QXcbAtom::ButtonWheelDown))
+ scrollingDevice.legacyOrientations |= Qt::Vertical;
}
+ if (bci->num_buttons >= 7) {
+ Atom label6 = bci->labels[5];
+ Atom label7 = bci->labels[6];
+ if ((!label6 || qatom(label6) == QXcbAtom::ButtonHorizWheelLeft) && (!label7 || qatom(label7) == QXcbAtom::ButtonHorizWheelRight))
+ scrollingDevice.legacyOrientations |= Qt::Horizontal;
+ }
+ qCDebug(lcQpaXInputDevices, " has %d buttons", bci->num_buttons);
+ break;
+ }
#endif
- case XIKeyClass:
- qCDebug(lcQpaXInputDevices) << " it's a keyboard";
- break;
+ case XIKeyClass:
+ qCDebug(lcQpaXInputDevices) << " it's a keyboard";
+ break;
#ifdef XCB_USE_XINPUT22
- case XITouchClass:
- // will be handled in deviceForId()
- break;
+ case XITouchClass:
+ // will be handled in populateTouchDevices()
+ break;
#endif
- default:
- qCDebug(lcQpaXInputDevices) << " has class" << devices[i].classes[c]->type;
- break;
- }
+ default:
+ qCDebug(lcQpaXInputDevices) << " has class" << classinfo->type;
+ break;
}
- bool isTablet = false;
+ }
+ bool isTablet = false;
#if QT_CONFIG(tabletevent)
- // If we have found the valuators which we expect a tablet to have, it might be a tablet.
- if (tabletData.valuatorInfo.contains(QXcbAtom::AbsX) &&
- tabletData.valuatorInfo.contains(QXcbAtom::AbsY) &&
- tabletData.valuatorInfo.contains(QXcbAtom::AbsPressure))
- isTablet = true;
-
- // But we need to be careful not to take the touch and tablet-button devices as tablets.
- QByteArray name = QByteArray(devices[i].name).toLower();
- QString dbgType = QLatin1String("UNKNOWN");
- if (name.contains("eraser")) {
- isTablet = true;
- tabletData.pointerType = QTabletEvent::Eraser;
- dbgType = QLatin1String("eraser");
- } else if (name.contains("cursor") && !(name.contains("cursor controls") && name.contains("trackball"))) {
- isTablet = true;
- tabletData.pointerType = QTabletEvent::Cursor;
- dbgType = QLatin1String("cursor");
- } else if (name.contains("wacom") && name.contains("finger touch")) {
- isTablet = false;
- } else if ((name.contains("pen") || name.contains("stylus")) && isTablet) {
- tabletData.pointerType = QTabletEvent::Pen;
- dbgType = QLatin1String("pen");
- } else if (name.contains("wacom") && isTablet && !name.contains("touch")) {
- // combined device (evdev) rather than separate pen/eraser (wacom driver)
- tabletData.pointerType = QTabletEvent::Pen;
- dbgType = QLatin1String("pen");
- } else if (name.contains("aiptek") /* && device == QXcbAtom::KEYBOARD */) {
- // some "Genius" tablets
- isTablet = true;
- tabletData.pointerType = QTabletEvent::Pen;
- dbgType = QLatin1String("pen");
- } else if (name.contains("waltop") && name.contains("tablet")) {
- // other "Genius" tablets
- // WALTOP International Corp. Slim Tablet
- isTablet = true;
- tabletData.pointerType = QTabletEvent::Pen;
- dbgType = QLatin1String("pen");
- } else if (name.contains("uc-logic") && isTablet) {
- tabletData.pointerType = QTabletEvent::Pen;
- dbgType = QLatin1String("pen");
- } else {
- isTablet = false;
- }
+ // If we have found the valuators which we expect a tablet to have, it might be a tablet.
+ if (tabletData.valuatorInfo.contains(QXcbAtom::AbsX) &&
+ tabletData.valuatorInfo.contains(QXcbAtom::AbsY) &&
+ tabletData.valuatorInfo.contains(QXcbAtom::AbsPressure))
+ isTablet = true;
+
+ // But we need to be careful not to take the touch and tablet-button devices as tablets.
+ QByteArray name = QByteArray(deviceInfo->name).toLower();
+ QString dbgType = QLatin1String("UNKNOWN");
+ if (name.contains("eraser")) {
+ isTablet = true;
+ tabletData.pointerType = QTabletEvent::Eraser;
+ dbgType = QLatin1String("eraser");
+ } else if (name.contains("cursor") && !(name.contains("cursor controls") && name.contains("trackball"))) {
+ isTablet = true;
+ tabletData.pointerType = QTabletEvent::Cursor;
+ dbgType = QLatin1String("cursor");
+ } else if (name.contains("wacom") && name.contains("finger touch")) {
+ isTablet = false;
+ } else if ((name.contains("pen") || name.contains("stylus")) && isTablet) {
+ tabletData.pointerType = QTabletEvent::Pen;
+ dbgType = QLatin1String("pen");
+ } else if (name.contains("wacom") && isTablet && !name.contains("touch")) {
+ // combined device (evdev) rather than separate pen/eraser (wacom driver)
+ tabletData.pointerType = QTabletEvent::Pen;
+ dbgType = QLatin1String("pen");
+ } else if (name.contains("aiptek") /* && device == QXcbAtom::KEYBOARD */) {
+ // some "Genius" tablets
+ isTablet = true;
+ tabletData.pointerType = QTabletEvent::Pen;
+ dbgType = QLatin1String("pen");
+ } else if (name.contains("waltop") && name.contains("tablet")) {
+ // other "Genius" tablets
+ // WALTOP International Corp. Slim Tablet
+ isTablet = true;
+ tabletData.pointerType = QTabletEvent::Pen;
+ dbgType = QLatin1String("pen");
+ } else if (name.contains("uc-logic") && isTablet) {
+ tabletData.pointerType = QTabletEvent::Pen;
+ dbgType = QLatin1String("pen");
+ } else {
+ isTablet = false;
+ }
- if (isTablet) {
- tabletData.deviceId = devices[i].deviceid;
- m_tabletData.append(tabletData);
- qCDebug(lcQpaXInputDevices) << " it's a tablet with pointer type" << dbgType;
- }
+ if (isTablet) {
+ tabletData.deviceId = deviceInfo->deviceid;
+ m_tabletData.append(tabletData);
+ qCDebug(lcQpaXInputDevices) << " it's a tablet with pointer type" << dbgType;
+ }
#endif // QT_CONFIG(tabletevent)
#ifdef XCB_USE_XINPUT21
- if (scrollingDevice.orientations || scrollingDevice.legacyOrientations) {
- scrollingDevice.deviceId = devices[i].deviceid;
- // Only use legacy wheel button events when we don't have real scroll valuators.
- scrollingDevice.legacyOrientations &= ~scrollingDevice.orientations;
- m_scrollingDevices.insert(scrollingDevice.deviceId, scrollingDevice);
- qCDebug(lcQpaXInputDevices) << " it's a scrolling device";
- }
+ if (scrollingDevice.orientations || scrollingDevice.legacyOrientations) {
+ scrollingDevice.deviceId = deviceInfo->deviceid;
+ // Only use legacy wheel button events when we don't have real scroll valuators.
+ scrollingDevice.legacyOrientations &= ~scrollingDevice.orientations;
+ m_scrollingDevices.insert(scrollingDevice.deviceId, scrollingDevice);
+ qCDebug(lcQpaXInputDevices) << " it's a scrolling device";
+ }
#endif
- if (!isTablet) {
- // touchDeviceForId populates XInput2DeviceData the first time it is called
- // with a new deviceId. On subsequent calls it will return the cached object.
- XInput2TouchDeviceData *dev = touchDeviceForId(devices[i].deviceid);
- if (dev && lcQpaXInputDevices().isDebugEnabled()) {
- if (dev->qtTouchDevice->type() == QTouchDevice::TouchScreen)
- qCDebug(lcQpaXInputDevices, " it's a touchscreen with type %d capabilities 0x%X max touch points %d",
- dev->qtTouchDevice->type(), (unsigned int)dev->qtTouchDevice->capabilities(),
- dev->qtTouchDevice->maximumTouchPoints());
- else if (dev->qtTouchDevice->type() == QTouchDevice::TouchPad)
- qCDebug(lcQpaXInputDevices, " it's a touchpad with type %d capabilities 0x%X max touch points %d size %f x %f",
- dev->qtTouchDevice->type(), (unsigned int)dev->qtTouchDevice->capabilities(),
- dev->qtTouchDevice->maximumTouchPoints(),
- dev->size.width(), dev->size.height());
- }
+ if (!isTablet) {
+ TouchDeviceData *dev = populateTouchDevices(deviceInfo);
+ if (dev && lcQpaXInputDevices().isDebugEnabled()) {
+ if (dev->qtTouchDevice->type() == QTouchDevice::TouchScreen)
+ qCDebug(lcQpaXInputDevices, " it's a touchscreen with type %d capabilities 0x%X max touch points %d",
+ dev->qtTouchDevice->type(), (unsigned int)dev->qtTouchDevice->capabilities(),
+ dev->qtTouchDevice->maximumTouchPoints());
+ else if (dev->qtTouchDevice->type() == QTouchDevice::TouchPad)
+ qCDebug(lcQpaXInputDevices, " it's a touchpad with type %d capabilities 0x%X max touch points %d size %f x %f",
+ dev->qtTouchDevice->type(), (unsigned int)dev->qtTouchDevice->capabilities(),
+ dev->qtTouchDevice->maximumTouchPoints(),
+ dev->size.width(), dev->size.height());
}
}
- XIFreeDeviceInfo(devices);
+
}
-void QXcbConnection::finalizeXInput2()
+void QXcbConnection::xi2SetupDevices()
{
- for (XInput2TouchDeviceData *dev : qAsConst(m_touchDevices)) {
- if (dev->xiDeviceInfo)
- XIFreeDeviceInfo(dev->xiDeviceInfo);
- delete dev;
+#if QT_CONFIG(tabletevent)
+ m_tabletData.clear();
+#endif
+ m_scrollingDevices.clear();
+ m_touchDevices.clear();
+
+ Display *xDisplay = static_cast<Display *>(m_xlib_display);
+ int deviceCount = 0;
+ XIDeviceInfo *devices = XIQueryDevice(xDisplay, XIAllDevices, &deviceCount);
+ m_xiMasterPointerIds.clear();
+ for (int i = 0; i < deviceCount; ++i) {
+ XIDeviceInfo deviceInfo = devices[i];
+ if (deviceInfo.use == XIMasterPointer) {
+ m_xiMasterPointerIds.append(deviceInfo.deviceid);
+ continue;
+ }
+ if (deviceInfo.use == XISlavePointer) // only slave pointer devices are relevant here
+ xi2SetupDevice(&deviceInfo, false);
}
+ if (m_xiMasterPointerIds.size() > 1)
+ qCDebug(lcQpaXInputDevices) << "multi-pointer X detected";
+ XIFreeDeviceInfo(devices);
}
-void QXcbConnection::xi2Select(xcb_window_t window)
+/*! \internal
+
+ Notes on QT_XCB_NO_XI2_MOUSE Handling:
+
+ Here we don't select pointer button press/release and motion events on master devices, instead
+ we select these events directly on slave devices. This means that a master device will fallback
+ to sending core events for every XI_* event that is sent directly by a slave device. For more
+ details see "Event processing for attached slave devices" in XInput2 specification. To prevent
+ handling of the same event twice, we have checks for xi2MouseEventsDisabled() in XI2 event
+ handlers (but this is somewhat inconsistent in some situations). If the purpose for
+ QT_XCB_NO_XI2_MOUSE was so that an application using QAbstractNativeEventFilter would see core
+ mouse events before they are handled by Qt then QT_XCB_NO_XI2_MOUSE won't always work as
+ expected (e.g. we handle scroll event directly from a slave device event, before an application
+ has seen the fallback core event from a master device).
+
+ The commit introducing QT_XCB_NO_XI2_MOUSE also states that setting this envvar "restores the
+ old behavior with broken grabbing". It did not elaborate why grabbing was not fixed for this
+ code path. The issue that this envvar tries to solve seem to be less important than broken
+ grabbing (broken apparently only for touch events). Thus, if you really want core mouse events
+ in your application and do not care about broken touch, then use QT_XCB_NO_XI2 (more on this
+ below) to disable the extension all together. The reason why grabbing might have not been fixed
+ is that calling XIGrabDevice with this code path for some reason always returns AlreadyGrabbed
+ (by debugging X server's code it appears that when we call XIGrabDevice, an X server first grabs
+ pointer via core pointer and then fails to do XI2 grab with AlreadyGrabbed; disclaimer - I did
+ not debug this in great detail). When we try supporting odd setups like QT_XCB_NO_XI2_MOUSE, we
+ are asking for trouble anyways.
+
+ In conclusion, introduction of QT_XCB_NO_XI2_MOUSE causes more issues than solves - the above
+ mentioned inconsistencies, maintenance of this code path and that QT_XCB_NO_XI2_MOUSE replaces
+ less important issue with somewhat more important issue. It also makes us to use less optimal
+ code paths in certain situations (see xi2HandleHierarchyEvent). Using of QT_XCB_NO_XI2 has its
+ drawbacks too - no tablet and touch events. So the only real fix in this case is at an
+ application side (teach the application about xcb_ge_event_t events). Based on this,
+ QT_XCB_NO_XI2_MOUSE will be removed in ### Qt 6. It should not have existed in the first place,
+ native events seen by QAbstractNativeEventFilter is not really a public API, applications should
+ expect changes at this level and do ifdefs if something changes between Qt version.
+*/
+void QXcbConnection::xi2SelectDeviceEventsCompatibility(xcb_window_t window)
{
- if (!m_xi2Enabled || window == rootWindow())
+ if (window == rootWindow())
return;
- Display *xDisplay = static_cast<Display *>(m_xlib_display);
- unsigned int bitMask = 0;
- unsigned char *xiBitMask = reinterpret_cast<unsigned char *>(&bitMask);
+ unsigned int mask = 0;
+ unsigned char *bitMask = reinterpret_cast<unsigned char *>(&mask);
+ Display *dpy = static_cast<Display *>(m_xlib_display);
#ifdef XCB_USE_XINPUT22
if (isAtLeastXI22()) {
- bitMask |= XI_TouchBeginMask;
- bitMask |= XI_TouchUpdateMask;
- bitMask |= XI_TouchEndMask;
- bitMask |= XI_PropertyEventMask; // for tablets
- if (xi2MouseEvents()) {
- // We want both mouse and touch through XI2 if touch is supported (>= 2.2).
- // The plain xcb press and motion events will not be delivered after this.
- bitMask |= XI_ButtonPressMask;
- bitMask |= XI_ButtonReleaseMask;
- bitMask |= XI_MotionMask;
-
- // There is a check for enter/leave events in plain xcb enter/leave event handler
- bitMask |= XI_EnterMask;
- bitMask |= XI_LeaveMask;
-
- qCDebug(lcQpaXInput, "XInput 2.2: Selecting press/release/motion events in addition to touch");
- }
- XIEventMask mask;
- mask.mask_len = sizeof(bitMask);
- mask.mask = xiBitMask;
- // When xi2MouseEvents() is true (the default), pointer emulation for touch and tablet
- // events will get disabled. This is preferable, as Qt Quick handles touch events
- // directly, while for other applications QtGui synthesizes mouse events.
- mask.deviceid = XIAllMasterDevices;
- Status result = XISelectEvents(xDisplay, window, &mask, 1);
+ mask |= XI_TouchBeginMask;
+ mask |= XI_TouchUpdateMask;
+ mask |= XI_TouchEndMask;
+
+ XIEventMask xiMask;
+ xiMask.mask_len = sizeof(mask);
+ xiMask.mask = bitMask;
+ xiMask.deviceid = XIAllMasterDevices;
+ Status result = XISelectEvents(dpy, window, &xiMask, 1);
if (result == Success)
QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(false);
else
- qCDebug(lcQpaXInput, "XInput 2.2: failed to select pointer/touch events, window %x, result %d", window, result);
+ qCDebug(lcQpaXInput, "failed to select events, window %x, result %d", window, result);
}
+#endif
- const bool pointerSelected = isAtLeastXI22() && xi2MouseEvents();
-#else
- const bool pointerSelected = false;
-#endif // XCB_USE_XINPUT22
+ mask = XI_ButtonPressMask;
+ mask |= XI_ButtonReleaseMask;
+ mask |= XI_MotionMask;
- QSet<int> tabletDevices;
#if QT_CONFIG(tabletevent)
+ QSet<int> tabletDevices;
if (!m_tabletData.isEmpty()) {
- unsigned int tabletBitMask;
- unsigned char *xiTabletBitMask = reinterpret_cast<unsigned char *>(&tabletBitMask);
- QVector<XIEventMask> xiEventMask(m_tabletData.count());
- tabletBitMask = XI_PropertyEventMask;
- if (!pointerSelected)
- tabletBitMask |= XI_ButtonPressMask | XI_ButtonReleaseMask | XI_MotionMask;
- for (int i = 0; i < m_tabletData.count(); ++i) {
+ const int nrTablets = m_tabletData.count();
+ QVector<XIEventMask> xiEventMask(nrTablets);
+ for (int i = 0; i < nrTablets; ++i) {
int deviceId = m_tabletData.at(i).deviceId;
tabletDevices.insert(deviceId);
xiEventMask[i].deviceid = deviceId;
- xiEventMask[i].mask_len = sizeof(tabletBitMask);
- xiEventMask[i].mask = xiTabletBitMask;
+ xiEventMask[i].mask_len = sizeof(mask);
+ xiEventMask[i].mask = bitMask;
}
- XISelectEvents(xDisplay, window, xiEventMask.data(), m_tabletData.count());
+ XISelectEvents(dpy, window, xiEventMask.data(), nrTablets);
}
-#endif // QT_CONFIG(tabletevent)
+#endif
#ifdef XCB_USE_XINPUT21
- // Enable each scroll device
- if (!m_scrollingDevices.isEmpty() && !pointerSelected) {
- // Only when XI2 mouse events are not enabled, otherwise motion and release are selected already.
+ if (!m_scrollingDevices.isEmpty()) {
QVector<XIEventMask> xiEventMask(m_scrollingDevices.size());
- unsigned int scrollBitMask;
- unsigned char *xiScrollBitMask = reinterpret_cast<unsigned char *>(&scrollBitMask);
-
- scrollBitMask = XI_MotionMask;
- scrollBitMask |= XI_ButtonReleaseMask;
- int i=0;
+ int i = 0;
for (const ScrollingDevice& scrollingDevice : qAsConst(m_scrollingDevices)) {
+#if QT_CONFIG(tabletevent)
if (tabletDevices.contains(scrollingDevice.deviceId))
continue; // All necessary events are already captured.
+#endif
xiEventMask[i].deviceid = scrollingDevice.deviceId;
- xiEventMask[i].mask_len = sizeof(scrollBitMask);
- xiEventMask[i].mask = xiScrollBitMask;
+ xiEventMask[i].mask_len = sizeof(mask);
+ xiEventMask[i].mask = bitMask;
i++;
}
- XISelectEvents(xDisplay, window, xiEventMask.data(), i);
+ XISelectEvents(dpy, window, xiEventMask.data(), i);
}
-#else
- Q_UNUSED(xiBitMask);
+#endif
+
+#if !QT_CONFIG(tabletevent) && !defined(XCB_USE_XINPUT21)
+ Q_UNUSED(bitMask);
+ Q_UNUSED(dpy);
#endif
}
-XInput2TouchDeviceData *QXcbConnection::touchDeviceForId(int id)
+QXcbConnection::TouchDeviceData *QXcbConnection::touchDeviceForId(int id)
{
- XInput2TouchDeviceData *dev = Q_NULLPTR;
- QHash<int, XInput2TouchDeviceData*>::const_iterator devIt = m_touchDevices.constFind(id);
- if (devIt != m_touchDevices.cend()) {
- dev = devIt.value();
- } else {
- int nrDevices = 0;
- QTouchDevice::Capabilities caps = 0;
- dev = new XInput2TouchDeviceData;
- dev->xiDeviceInfo = XIQueryDevice(static_cast<Display *>(m_xlib_display), id, &nrDevices);
- if (nrDevices <= 0) {
- delete dev;
- return 0;
- }
- int type = -1;
- int maxTouchPoints = 1;
- bool hasRelativeCoords = false;
- for (int i = 0; i < dev->xiDeviceInfo->num_classes; ++i) {
- XIAnyClassInfo *classinfo = dev->xiDeviceInfo->classes[i];
- switch (classinfo->type) {
+ TouchDeviceData *dev = nullptr;
+ if (m_touchDevices.contains(id))
+ dev = &m_touchDevices[id];
+ return dev;
+}
+
+QXcbConnection::TouchDeviceData *QXcbConnection::populateTouchDevices(void *info)
+{
+ XIDeviceInfo *deviceinfo = reinterpret_cast<XIDeviceInfo *>(info);
+ QTouchDevice::Capabilities caps = 0;
+ int type = -1;
+ int maxTouchPoints = 1;
+ bool isTouchDevice = false;
+ bool hasRelativeCoords = false;
+ TouchDeviceData dev;
+ for (int i = 0; i < deviceinfo->num_classes; ++i) {
+ XIAnyClassInfo *classinfo = deviceinfo->classes[i];
+ switch (classinfo->type) {
#ifdef XCB_USE_XINPUT22
- case XITouchClass: {
- XITouchClassInfo *tci = reinterpret_cast<XITouchClassInfo *>(classinfo);
- maxTouchPoints = tci->num_touches;
- qCDebug(lcQpaXInputDevices, " has touch class with mode %d", tci->mode);
- switch (tci->mode) {
- case XIDependentTouch:
- type = QTouchDevice::TouchPad;
- break;
- case XIDirectTouch:
- type = QTouchDevice::TouchScreen;
- break;
- }
+ case XITouchClass: {
+ XITouchClassInfo *tci = reinterpret_cast<XITouchClassInfo *>(classinfo);
+ maxTouchPoints = tci->num_touches;
+ qCDebug(lcQpaXInputDevices, " has touch class with mode %d", tci->mode);
+ switch (tci->mode) {
+ case XIDependentTouch:
+ type = QTouchDevice::TouchPad;
+ break;
+ case XIDirectTouch:
+ type = QTouchDevice::TouchScreen;
break;
}
+ break;
+ }
#endif // XCB_USE_XINPUT22
- case XIValuatorClass: {
- XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(classinfo);
- // Some devices (mice) report a resolution of 0; they will be excluded later,
- // for now just prevent a division by zero
- const int vciResolution = vci->resolution ? vci->resolution : 1;
- if (vci->label == atom(QXcbAtom::AbsMTPositionX))
- caps |= QTouchDevice::Position | QTouchDevice::NormalizedPosition;
- else if (vci->label == atom(QXcbAtom::AbsMTTouchMajor))
- caps |= QTouchDevice::Area;
- else if (vci->label == atom(QXcbAtom::AbsMTOrientation))
- dev->providesTouchOrientation = true;
- else if (vci->label == atom(QXcbAtom::AbsMTPressure) || vci->label == atom(QXcbAtom::AbsPressure))
- caps |= QTouchDevice::Pressure;
- else if (vci->label == atom(QXcbAtom::RelX)) {
- hasRelativeCoords = true;
- dev->size.setWidth((vci->max - vci->min) * 1000.0 / vciResolution);
- } else if (vci->label == atom(QXcbAtom::RelY)) {
- hasRelativeCoords = true;
- dev->size.setHeight((vci->max - vci->min) * 1000.0 / vciResolution);
- } else if (vci->label == atom(QXcbAtom::AbsX)) {
- caps |= QTouchDevice::Position;
- dev->size.setWidth((vci->max - vci->min) * 1000.0 / vciResolution);
- } else if (vci->label == atom(QXcbAtom::AbsY)) {
- caps |= QTouchDevice::Position;
- dev->size.setHeight((vci->max - vci->min) * 1000.0 / vciResolution);
- }
- break;
+ case XIValuatorClass: {
+ XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(classinfo);
+ const QXcbAtom::Atom valuatorAtom = qatom(vci->label);
+ if (valuatorAtom < QXcbAtom::NAtoms) {
+ TouchDeviceData::ValuatorClassInfo info;
+ info.min = vci->min;
+ info.max = vci->max;
+ info.number = vci->number;
+ info.label = valuatorAtom;
+ dev.valuatorInfo.append(info);
}
- default:
- break;
+ // Some devices (mice) report a resolution of 0; they will be excluded later,
+ // for now just prevent a division by zero
+ const int vciResolution = vci->resolution ? vci->resolution : 1;
+ if (valuatorAtom == QXcbAtom::AbsMTPositionX)
+ caps |= QTouchDevice::Position | QTouchDevice::NormalizedPosition;
+ else if (valuatorAtom == QXcbAtom::AbsMTTouchMajor)
+ caps |= QTouchDevice::Area;
+ else if (valuatorAtom == QXcbAtom::AbsMTOrientation)
+ dev.providesTouchOrientation = true;
+ else if (valuatorAtom == QXcbAtom::AbsMTPressure || valuatorAtom == QXcbAtom::AbsPressure)
+ caps |= QTouchDevice::Pressure;
+ else if (valuatorAtom == QXcbAtom::RelX) {
+ hasRelativeCoords = true;
+ dev.size.setWidth((vci->max - vci->min) * 1000.0 / vciResolution);
+ } else if (valuatorAtom == QXcbAtom::RelY) {
+ hasRelativeCoords = true;
+ dev.size.setHeight((vci->max - vci->min) * 1000.0 / vciResolution);
+ } else if (valuatorAtom == QXcbAtom::AbsX) {
+ caps |= QTouchDevice::Position;
+ dev.size.setWidth((vci->max - vci->min) * 1000.0 / vciResolution);
+ } else if (valuatorAtom == QXcbAtom::AbsY) {
+ caps |= QTouchDevice::Position;
+ dev.size.setHeight((vci->max - vci->min) * 1000.0 / vciResolution);
}
+ break;
}
- if (type < 0 && caps && hasRelativeCoords) {
- type = QTouchDevice::TouchPad;
- if (dev->size.width() < 10 || dev->size.height() < 10 ||
- dev->size.width() > 10000 || dev->size.height() > 10000)
- dev->size = QSizeF(130, 110);
- }
- if (!isAtLeastXI22() || type == QTouchDevice::TouchPad)
- caps |= QTouchDevice::MouseEmulation;
-
- if (type >= QTouchDevice::TouchScreen && type <= QTouchDevice::TouchPad) {
- dev->qtTouchDevice = new QTouchDevice;
- dev->qtTouchDevice->setName(QString::fromUtf8(dev->xiDeviceInfo->name));
- dev->qtTouchDevice->setType((QTouchDevice::DeviceType)type);
- dev->qtTouchDevice->setCapabilities(caps);
- dev->qtTouchDevice->setMaximumTouchPoints(maxTouchPoints);
- if (caps != 0)
- QWindowSystemInterface::registerTouchDevice(dev->qtTouchDevice);
- m_touchDevices[id] = dev;
- } else {
- XIFreeDeviceInfo(dev->xiDeviceInfo);
- delete dev;
- dev = 0;
+ default:
+ break;
}
}
- return dev;
+ if (type < 0 && caps && hasRelativeCoords) {
+ type = QTouchDevice::TouchPad;
+ if (dev.size.width() < 10 || dev.size.height() < 10 ||
+ dev.size.width() > 10000 || dev.size.height() > 10000)
+ dev.size = QSizeF(130, 110);
+ }
+ if (!isAtLeastXI22() || type == QTouchDevice::TouchPad)
+ caps |= QTouchDevice::MouseEmulation;
+
+ if (type >= QTouchDevice::TouchScreen && type <= QTouchDevice::TouchPad) {
+ dev.qtTouchDevice = new QTouchDevice;
+ dev.qtTouchDevice->setName(QString::fromUtf8(deviceinfo->name));
+ dev.qtTouchDevice->setType((QTouchDevice::DeviceType)type);
+ dev.qtTouchDevice->setCapabilities(caps);
+ dev.qtTouchDevice->setMaximumTouchPoints(maxTouchPoints);
+ if (caps != 0)
+ QWindowSystemInterface::registerTouchDevice(dev.qtTouchDevice);
+ m_touchDevices[deviceinfo->deviceid] = dev;
+ isTouchDevice = true;
+ }
+
+ return isTouchDevice ? &m_touchDevices[deviceinfo->deviceid] : nullptr;
}
#if defined(XCB_USE_XINPUT21) || QT_CONFIG(tabletevent)
@@ -525,7 +582,7 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
break;
}
case XI_HierarchyChanged:
- xi2HandleHierachyEvent(xiEvent);
+ xi2HandleHierarchyEvent(xiEvent);
return;
case XI_DeviceChanged:
xi2HandleDeviceChangedEvent(xiEvent);
@@ -549,9 +606,8 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
#endif // QT_CONFIG(tabletevent)
#ifdef XCB_USE_XINPUT21
- QHash<int, ScrollingDevice>::iterator device = m_scrollingDevices.find(sourceDeviceId);
- if (device != m_scrollingDevices.end())
- xi2HandleScrollEvent(xiEvent, device.value());
+ if (ScrollingDevice *device = scrollingDeviceForId(sourceDeviceId))
+ xi2HandleScrollEvent(xiEvent, *device);
#endif // XCB_USE_XINPUT21
#ifdef XCB_USE_XINPUT22
@@ -560,7 +616,7 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
case XI_ButtonPress:
case XI_ButtonRelease:
case XI_Motion:
- if (xi2MouseEvents() && eventListener && !(xiDeviceEvent->flags & XIPointerEmulated))
+ if (!xi2MouseEventsDisabled() && eventListener && !(xiDeviceEvent->flags & XIPointerEmulated))
eventListener->handleXIMouseEvent(event);
break;
@@ -576,7 +632,7 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
xi2ProcessTouch(xiDeviceEvent, platformWindow);
break;
}
- } else if (xiEnterEvent && xi2MouseEvents() && eventListener) {
+ } else if (xiEnterEvent && !xi2MouseEventsDisabled() && eventListener) {
switch (xiEnterEvent->evtype) {
case XI_Enter:
case XI_Leave:
@@ -587,20 +643,25 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
#endif // XCB_USE_XINPUT22
}
+bool QXcbConnection::xi2MouseEventsDisabled() const
+{
+ static bool xi2MouseDisabled = qEnvironmentVariableIsSet("QT_XCB_NO_XI2_MOUSE");
+ // FIXME: Don't use XInput2 mouse events when Xinerama extension
+ // is enabled, because it causes problems with multi-monitor setup.
+ return xi2MouseDisabled || has_xinerama_extension;
+}
+
#ifdef XCB_USE_XINPUT22
-static qreal valuatorNormalized(double value, XIValuatorClassInfo *vci)
+bool QXcbConnection::isTouchScreen(int id)
{
- if (value > vci->max)
- value = vci->max;
- if (value < vci->min)
- value = vci->min;
- return (value - vci->min) / (vci->max - vci->min);
+ auto device = touchDeviceForId(id);
+ return device && device->qtTouchDevice->type() == QTouchDevice::TouchScreen;
}
void QXcbConnection::xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindow)
{
xXIDeviceEvent *xiDeviceEvent = static_cast<xXIDeviceEvent *>(xiDevEvent);
- XInput2TouchDeviceData *dev = touchDeviceForId(xiDeviceEvent->sourceid);
+ TouchDeviceData *dev = touchDeviceForId(xiDeviceEvent->sourceid);
Q_ASSERT(dev);
const bool firstTouch = dev->touchPoints.isEmpty();
if (xiDeviceEvent->evtype == XI_TouchBegin) {
@@ -617,53 +678,53 @@ void QXcbConnection::xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindo
qreal nx = -1.0, ny = -1.0;
qreal w = 0.0, h = 0.0;
bool majorAxisIsY = touchPoint.area.height() > touchPoint.area.width();
- for (int i = 0; i < dev->xiDeviceInfo->num_classes; ++i) {
- XIAnyClassInfo *classinfo = dev->xiDeviceInfo->classes[i];
- if (classinfo->type == XIValuatorClass) {
- XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(classinfo);
- int n = vci->number;
- double value;
- if (!xi2GetValuatorValueIfSet(xiDeviceEvent, n, &value))
- continue;
- if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
- qCDebug(lcQpaXInputEvents, " valuator %20s value %lf from range %lf -> %lf",
- atomName(vci->label).constData(), value, vci->min, vci->max );
- if (vci->label == atom(QXcbAtom::RelX)) {
- nx = valuatorNormalized(value, vci);
- } else if (vci->label == atom(QXcbAtom::RelY)) {
- ny = valuatorNormalized(value, vci);
- } else if (vci->label == atom(QXcbAtom::AbsX)) {
- nx = valuatorNormalized(value, vci);
- } else if (vci->label == atom(QXcbAtom::AbsY)) {
- ny = valuatorNormalized(value, vci);
- } else if (vci->label == atom(QXcbAtom::AbsMTPositionX)) {
- nx = valuatorNormalized(value, vci);
- } else if (vci->label == atom(QXcbAtom::AbsMTPositionY)) {
- ny = valuatorNormalized(value, vci);
- } else if (vci->label == atom(QXcbAtom::AbsMTTouchMajor)) {
- const qreal sw = screen->geometry().width();
- const qreal sh = screen->geometry().height();
- w = valuatorNormalized(value, vci) * std::sqrt(sw * sw + sh * sh);
- } else if (vci->label == atom(QXcbAtom::AbsMTTouchMinor)) {
- const qreal sw = screen->geometry().width();
- const qreal sh = screen->geometry().height();
- h = valuatorNormalized(value, vci) * std::sqrt(sw * sw + sh * sh);
- } else if (vci->label == atom(QXcbAtom::AbsMTOrientation)) {
- // Find the closest axis.
- // 0 corresponds to the Y axis, vci->max to the X axis.
- // Flipping over the Y axis and rotating by 180 degrees
- // don't change the result, so normalize value to range
- // [0, vci->max] first.
- value = qAbs(value);
- while (value > vci->max)
- value -= 2 * vci->max;
- value = qAbs(value);
- majorAxisIsY = value < vci->max - value;
- } else if (vci->label == atom(QXcbAtom::AbsMTPressure) ||
- vci->label == atom(QXcbAtom::AbsPressure)) {
- touchPoint.pressure = valuatorNormalized(value, vci);
- }
+ for (const TouchDeviceData::ValuatorClassInfo vci : dev->valuatorInfo) {
+ double value;
+ if (!xi2GetValuatorValueIfSet(xiDeviceEvent, vci.number, &value))
+ continue;
+ if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
+ qCDebug(lcQpaXInputEvents, " valuator %20s value %lf from range %lf -> %lf",
+ atomName(vci.label).constData(), value, vci.min, vci.max);
+ if (value > vci.max)
+ value = vci.max;
+ if (value < vci.min)
+ value = vci.min;
+ qreal valuatorNormalized = (value - vci.min) / (vci.max - vci.min);
+ if (vci.label == QXcbAtom::RelX) {
+ nx = valuatorNormalized;
+ } else if (vci.label == QXcbAtom::RelY) {
+ ny = valuatorNormalized;
+ } else if (vci.label == QXcbAtom::AbsX) {
+ nx = valuatorNormalized;
+ } else if (vci.label == QXcbAtom::AbsY) {
+ ny = valuatorNormalized;
+ } else if (vci.label == QXcbAtom::AbsMTPositionX) {
+ nx = valuatorNormalized;
+ } else if (vci.label == QXcbAtom::AbsMTPositionY) {
+ ny = valuatorNormalized;
+ } else if (vci.label == QXcbAtom::AbsMTTouchMajor) {
+ const qreal sw = screen->geometry().width();
+ const qreal sh = screen->geometry().height();
+ w = valuatorNormalized * std::sqrt(sw * sw + sh * sh);
+ } else if (vci.label == QXcbAtom::AbsMTTouchMinor) {
+ const qreal sw = screen->geometry().width();
+ const qreal sh = screen->geometry().height();
+ h = valuatorNormalized * std::sqrt(sw * sw + sh * sh);
+ } else if (vci.label == QXcbAtom::AbsMTOrientation) {
+ // Find the closest axis.
+ // 0 corresponds to the Y axis, vci.max to the X axis.
+ // Flipping over the Y axis and rotating by 180 degrees
+ // don't change the result, so normalize value to range
+ // [0, vci.max] first.
+ value = qAbs(value);
+ while (value > vci.max)
+ value -= 2 * vci.max;
+ value = qAbs(value);
+ majorAxisIsY = value < vci.max - value;
+ } else if (vci.label == QXcbAtom::AbsMTPressure || vci.label == QXcbAtom::AbsPressure) {
+ touchPoint.pressure = valuatorNormalized;
}
+
}
// If any value was not updated, use the last-known value.
if (nx == -1.0) {
@@ -723,15 +784,15 @@ void QXcbConnection::xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindo
}
if (dev->qtTouchDevice->type() == QTouchDevice::TouchScreen &&
- xiDeviceEvent->event == m_startSystemResizeInfo.window &&
- xiDeviceEvent->sourceid == m_startSystemResizeInfo.deviceid &&
- xiDeviceEvent->detail == m_startSystemResizeInfo.pointid) {
- QXcbWindow *window = platformWindowFromId(m_startSystemResizeInfo.window);
+ xiDeviceEvent->event == m_startSystemMoveResizeInfo.window &&
+ xiDeviceEvent->sourceid == m_startSystemMoveResizeInfo.deviceid &&
+ xiDeviceEvent->detail == m_startSystemMoveResizeInfo.pointid) {
+ QXcbWindow *window = platformWindowFromId(m_startSystemMoveResizeInfo.window);
if (window) {
XIAllowTouchEvents(static_cast<Display *>(m_xlib_display), xiDeviceEvent->deviceid,
xiDeviceEvent->detail, xiDeviceEvent->event, XIRejectTouch);
- window->doStartSystemResize(QPoint(x, y), m_startSystemResizeInfo.corner);
- m_startSystemResizeInfo.window = XCB_NONE;
+ window->doStartSystemMoveResize(QPoint(x, y), m_startSystemMoveResizeInfo.corner);
+ m_startSystemMoveResizeInfo.window = XCB_NONE;
}
}
break;
@@ -764,19 +825,19 @@ void QXcbConnection::xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindo
touchPoint.state = Qt::TouchPointStationary;
}
-bool QXcbConnection::startSystemResizeForTouchBegin(xcb_window_t window, const QPoint &point, Qt::Corner corner)
+bool QXcbConnection::startSystemMoveResizeForTouchBegin(xcb_window_t window, const QPoint &point, int corner)
{
- QHash<int, XInput2TouchDeviceData*>::const_iterator devIt = m_touchDevices.constBegin();
+ QHash<int, TouchDeviceData>::const_iterator devIt = m_touchDevices.constBegin();
for (; devIt != m_touchDevices.constEnd(); ++devIt) {
- XInput2TouchDeviceData *deviceData = devIt.value();
- if (deviceData->qtTouchDevice->type() == QTouchDevice::TouchScreen) {
- QHash<int, QPointF>::const_iterator pointIt = deviceData->pointPressedPosition.constBegin();
- for (; pointIt != deviceData->pointPressedPosition.constEnd(); ++pointIt) {
+ TouchDeviceData deviceData = devIt.value();
+ if (deviceData.qtTouchDevice->type() == QTouchDevice::TouchScreen) {
+ QHash<int, QPointF>::const_iterator pointIt = deviceData.pointPressedPosition.constBegin();
+ for (; pointIt != deviceData.pointPressedPosition.constEnd(); ++pointIt) {
if (pointIt.value().toPoint() == point) {
- m_startSystemResizeInfo.window = window;
- m_startSystemResizeInfo.deviceid = devIt.key();
- m_startSystemResizeInfo.pointid = pointIt.key();
- m_startSystemResizeInfo.corner = corner;
+ m_startSystemMoveResizeInfo.window = window;
+ m_startSystemMoveResizeInfo.deviceid = devIt.key();
+ m_startSystemMoveResizeInfo.pointid = pointIt.key();
+ m_startSystemMoveResizeInfo.corner = corner;
return true;
}
}
@@ -784,119 +845,123 @@ bool QXcbConnection::startSystemResizeForTouchBegin(xcb_window_t window, const Q
}
return false;
}
+#endif // XCB_USE_XINPUT22
bool QXcbConnection::xi2SetMouseGrabEnabled(xcb_window_t w, bool grab)
{
- if (grab && !canGrab())
- return false;
-
- int num_devices = 0;
Display *xDisplay = static_cast<Display *>(xlib_display());
- XIDeviceInfo *info = XIQueryDevice(xDisplay, XIAllMasterDevices, &num_devices);
- if (!info)
- return false;
-
- XIEventMask evmask;
- unsigned char mask[XIMaskLen(XI_LASTEVENT)];
- evmask.mask = mask;
- evmask.mask_len = sizeof(mask);
- memset(mask, 0, sizeof(mask));
- evmask.deviceid = XIAllMasterDevices;
-
- XISetMask(mask, XI_ButtonPress);
- XISetMask(mask, XI_ButtonRelease);
- XISetMask(mask, XI_Motion);
- XISetMask(mask, XI_Enter);
- XISetMask(mask, XI_Leave);
- XISetMask(mask, XI_TouchBegin);
- XISetMask(mask, XI_TouchUpdate);
- XISetMask(mask, XI_TouchEnd);
-
- bool grabbed = true;
- for (int i = 0; i < num_devices; i++) {
- int id = info[i].deviceid, n = 0;
- XIDeviceInfo *deviceInfo = XIQueryDevice(xDisplay, id, &n);
- if (deviceInfo) {
- const bool grabbable = deviceInfo->use != XIMasterKeyboard;
- XIFreeDeviceInfo(deviceInfo);
- if (!grabbable)
- continue;
- }
- if (!grab) {
- Status result = XIUngrabDevice(xDisplay, id, CurrentTime);
+ bool ok = false;
+
+ if (grab) { // grab
+ XIEventMask evmask;
+ unsigned char mask[XIMaskLen(XI_LASTEVENT)];
+ evmask.mask = mask;
+ evmask.mask_len = sizeof(mask);
+ memset(mask, 0, sizeof(mask));
+ XISetMask(mask, XI_ButtonPress);
+ XISetMask(mask, XI_ButtonRelease);
+ XISetMask(mask, XI_Motion);
+ XISetMask(mask, XI_Enter);
+ XISetMask(mask, XI_Leave);
+ XISetMask(mask, XI_TouchBegin);
+ XISetMask(mask, XI_TouchUpdate);
+ XISetMask(mask, XI_TouchEnd);
+
+ for (int id : m_xiMasterPointerIds) {
+ evmask.deviceid = id;
+ Status result = XIGrabDevice(xDisplay, id, w, CurrentTime, None,
+ XIGrabModeAsync, XIGrabModeAsync, False, &evmask);
if (result != Success) {
- grabbed = false;
- qCDebug(lcQpaXInput, "XInput 2.2: failed to ungrab events for device %d (result %d)", id, result);
- }
- } else {
- Status result = XIGrabDevice(xDisplay, id, w, CurrentTime, None, XIGrabModeAsync,
- XIGrabModeAsync, False, &evmask);
- if (result != Success) {
- grabbed = false;
- qCDebug(lcQpaXInput, "XInput 2.2: failed to grab events for device %d on window %x (result %d)", id, w, result);
+ qCDebug(lcQpaXInput, "failed to grab events for device %d on window %x"
+ "(result %d)", id, w, result);
+ } else {
+ // Managed to grab at least one of master pointers, that should be enough
+ // to properly dismiss windows that rely on mouse grabbing.
+ ok = true;
}
}
+ } else { // ungrab
+ for (int id : m_xiMasterPointerIds) {
+ Status result = XIUngrabDevice(xDisplay, id, CurrentTime);
+ if (result != Success)
+ qCDebug(lcQpaXInput, "XIUngrabDevice failed - id: %d (result %d)", id, result);
+ }
+ // XIUngrabDevice does not seem to wait for a reply from X server (similar to
+ // xcb_ungrab_pointer). Ungrabbing won't fail, unless NoSuchExtension error
+ // has occurred due to a programming error somewhere else in the stack. That
+ // would mean that things will crash soon anyway.
+ ok = true;
}
- XIFreeDeviceInfo(info);
+ if (ok)
+ m_xiGrab = grab;
- m_xiGrab = grabbed;
-
- return grabbed;
+ return ok;
}
-#endif // XCB_USE_XINPUT22
-void QXcbConnection::xi2HandleHierachyEvent(void *event)
+void QXcbConnection::xi2HandleHierarchyEvent(void *event)
{
xXIHierarchyEvent *xiEvent = reinterpret_cast<xXIHierarchyEvent *>(event);
// We only care about hotplugged devices
if (!(xiEvent->flags & (XISlaveRemoved | XISlaveAdded)))
return;
+
xi2SetupDevices();
- // Reselect events for all event-listening windows.
- for (auto it = m_mapper.cbegin(), end = m_mapper.cend(); it != end; ++it)
- xi2Select(it.key());
+
+ if (xi2MouseEventsDisabled()) {
+ // In compatibility mode (a.k.a xi2MouseEventsDisabled() mode) we select events for
+ // each device separately. When a new device appears, we have to select events from
+ // this device on all event-listening windows. This is not needed when events are
+ // selected via XIAllDevices/XIAllMasterDevices (as in xi2SelectDeviceEvents()).
+ for (auto it = m_mapper.cbegin(), end = m_mapper.cend(); it != end; ++it)
+ xi2SelectDeviceEventsCompatibility(it.key());
+ }
}
void QXcbConnection::xi2HandleDeviceChangedEvent(void *event)
{
xXIDeviceChangedEvent *xiEvent = reinterpret_cast<xXIDeviceChangedEvent *>(event);
-
- // ### If a slave device changes (XIDeviceChange), we should probably run setup on it again.
- if (xiEvent->reason != XISlaveSwitch)
- return;
-
+ switch (xiEvent->reason) {
+ case XIDeviceChange: {
+ int nrDevices = 0;
+ Display *dpy = static_cast<Display *>(m_xlib_display);
+ XIDeviceInfo* deviceInfo = XIQueryDevice(dpy, xiEvent->sourceid, &nrDevices);
+ if (nrDevices <= 0)
+ return;
+ xi2SetupDevice(deviceInfo);
+ XIFreeDeviceInfo(deviceInfo);
+ break;
+ }
+ case XISlaveSwitch: {
#ifdef XCB_USE_XINPUT21
- // This code handles broken scrolling device drivers that reset absolute positions
- // when they are made active. Whenever a new slave device is made active the
- // primary pointer sends a DeviceChanged event with XISlaveSwitch, and the new
- // active slave in sourceid.
-
- QHash<int, ScrollingDevice>::iterator device = m_scrollingDevices.find(xiEvent->sourceid);
- if (device == m_scrollingDevices.end())
- return;
+ if (ScrollingDevice *scrollingDevice = scrollingDeviceForId(xiEvent->sourceid))
+ xi2UpdateScrollingDevice(*scrollingDevice);
+#endif
+ break;
+ }
+ default:
+ qCDebug(lcQpaXInputEvents, "unknown device-changed-event (device %d)", xiEvent->sourceid);
+ break;
+ }
+}
+#ifdef XCB_USE_XINPUT21
+void QXcbConnection::xi2UpdateScrollingDevice(ScrollingDevice &scrollingDevice)
+{
int nrDevices = 0;
- XIDeviceInfo* xiDeviceInfo = XIQueryDevice(static_cast<Display *>(m_xlib_display), xiEvent->sourceid, &nrDevices);
+ Display *dpy = static_cast<Display *>(m_xlib_display);
+ XIDeviceInfo* deviceInfo = XIQueryDevice(dpy, scrollingDevice.deviceId, &nrDevices);
if (nrDevices <= 0) {
- qCDebug(lcQpaXInputDevices, "scrolling device %d no longer present", xiEvent->sourceid);
+ qCDebug(lcQpaXInputDevices, "scrolling device %d no longer present", scrollingDevice.deviceId);
return;
}
- updateScrollingDevice(*device, xiDeviceInfo->num_classes, xiDeviceInfo->classes);
- XIFreeDeviceInfo(xiDeviceInfo);
-#endif
-}
-
-void QXcbConnection::updateScrollingDevice(ScrollingDevice &scrollingDevice, int num_classes, void *classInfo)
-{
-#ifdef XCB_USE_XINPUT21
- XIAnyClassInfo **classes = reinterpret_cast<XIAnyClassInfo**>(classInfo);
QPointF lastScrollPosition;
if (lcQpaXInput().isDebugEnabled())
lastScrollPosition = scrollingDevice.lastScrollPosition;
- for (int c = 0; c < num_classes; ++c) {
- if (classes[c]->type == XIValuatorClass) {
- XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(classes[c]);
+ for (int c = 0; c < deviceInfo->num_classes; ++c) {
+ XIAnyClassInfo *classInfo = deviceInfo->classes[c];
+ if (classInfo->type == XIValuatorClass) {
+ XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(classInfo);
const int valuatorAtom = qatom(vci->label);
if (valuatorAtom == QXcbAtom::RelHorizScroll || valuatorAtom == QXcbAtom::RelHorizWheel)
scrollingDevice.lastScrollPosition.setX(vci->value);
@@ -909,37 +974,30 @@ void QXcbConnection::updateScrollingDevice(ScrollingDevice &scrollingDevice, int
lastScrollPosition.x(), lastScrollPosition.y(),
scrollingDevice.lastScrollPosition.x(),
scrollingDevice.lastScrollPosition.y());
-#else
- Q_UNUSED(scrollingDevice);
- Q_UNUSED(num_classes);
- Q_UNUSED(classInfo);
-#endif
+
+ XIFreeDeviceInfo(deviceInfo);
}
-#ifdef XCB_USE_XINPUT21
-void QXcbConnection::handleEnterEvent()
+void QXcbConnection::xi2UpdateScrollingDevices()
{
QHash<int, ScrollingDevice>::iterator it = m_scrollingDevices.begin();
const QHash<int, ScrollingDevice>::iterator end = m_scrollingDevices.end();
while (it != end) {
- ScrollingDevice& scrollingDevice = it.value();
- int nrDevices = 0;
- XIDeviceInfo* xiDeviceInfo = XIQueryDevice(static_cast<Display *>(m_xlib_display), scrollingDevice.deviceId, &nrDevices);
- if (nrDevices <= 0) {
- qCDebug(lcQpaXInputDevices, "scrolling device %d no longer present", scrollingDevice.deviceId);
- it = m_scrollingDevices.erase(it);
- continue;
- }
- updateScrollingDevice(scrollingDevice, xiDeviceInfo->num_classes, xiDeviceInfo->classes);
- XIFreeDeviceInfo(xiDeviceInfo);
+ xi2UpdateScrollingDevice(it.value());
++it;
}
}
-#endif
+
+QXcbConnection::ScrollingDevice *QXcbConnection::scrollingDeviceForId(int id)
+{
+ ScrollingDevice *dev = nullptr;
+ if (m_scrollingDevices.contains(id))
+ dev = &m_scrollingDevices[id];
+ return dev;
+}
void QXcbConnection::xi2HandleScrollEvent(void *event, ScrollingDevice &scrollingDevice)
{
-#ifdef XCB_USE_XINPUT21
xXIGenericDeviceEvent *xiEvent = reinterpret_cast<xXIGenericDeviceEvent *>(event);
if (xiEvent->evtype == XI_Motion && scrollingDevice.orientations) {
@@ -1011,10 +1069,51 @@ void QXcbConnection::xi2HandleScrollEvent(void *event, ScrollingDevice &scrollin
}
}
}
-#else
- Q_UNUSED(event);
- Q_UNUSED(scrollingDevice);
+}
#endif // XCB_USE_XINPUT21
+
+static int xi2ValuatorOffset(const unsigned char *maskPtr, int maskLen, int number)
+{
+ int offset = 0;
+ for (int i = 0; i < maskLen; i++) {
+ if (number < 8) {
+ if ((maskPtr[i] & (1 << number)) == 0)
+ return -1;
+ }
+ for (int j = 0; j < 8; j++) {
+ if (j == number)
+ return offset;
+ if (maskPtr[i] & (1 << j))
+ offset++;
+ }
+ number -= 8;
+ }
+ return -1;
+}
+
+bool QXcbConnection::xi2GetValuatorValueIfSet(const void *event, int valuatorNum, double *value)
+{
+ const xXIDeviceEvent *xideviceevent = static_cast<const xXIDeviceEvent *>(event);
+ const unsigned char *buttonsMaskAddr = (const unsigned char*)&xideviceevent[1];
+ const unsigned char *valuatorsMaskAddr = buttonsMaskAddr + xideviceevent->buttons_len * 4;
+ FP3232 *valuatorsValuesAddr = (FP3232*)(valuatorsMaskAddr + xideviceevent->valuators_len * 4);
+
+ int valuatorOffset = xi2ValuatorOffset(valuatorsMaskAddr, xideviceevent->valuators_len, valuatorNum);
+ if (valuatorOffset < 0)
+ return false;
+
+ *value = valuatorsValuesAddr[valuatorOffset].integral;
+ *value += ((double)valuatorsValuesAddr[valuatorOffset].frac / (1 << 16) / (1 << 16));
+ return true;
+}
+
+void QXcbConnection::xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *event)
+{
+ // xcb event structs contain stuff that wasn't on the wire, the full_sequence field
+ // adds an extra 4 bytes and generic events cookie data is on the wire right after the standard 32 bytes.
+ // Move this data back to have the same layout in memory as it was on the wire
+ // and allow casting, overwriting the full_sequence field.
+ memmove((char*) event + 32, (char*) event + 36, event->length * 4);
}
Qt::MouseButton QXcbConnection::xiToQtMouseButton(uint32_t b)
@@ -1031,15 +1130,6 @@ Qt::MouseButton QXcbConnection::xiToQtMouseButton(uint32_t b)
return Qt::NoButton;
}
-#ifdef XCB_USE_XINPUT22
-bool QXcbConnection::isTouchScreen(int id) const
-{
- auto device = m_touchDevices.value(id);
- return device && device->qtTouchDevice
- && device->qtTouchDevice->type() == QTouchDevice::TouchScreen;
-}
-#endif
-
#if QT_CONFIG(tabletevent)
static QTabletEvent::TabletDevice toolIdToTabletDevice(quint32 toolId) {
// keep in sync with wacom_intuos_inout() in Linux kernel driver wacom_wac.c
@@ -1244,7 +1334,7 @@ QXcbConnection::TabletData *QXcbConnection::tabletDataForDevice(int id)
if (m_tabletData.at(i).deviceId == id)
return &m_tabletData[i];
}
- return Q_NULLPTR;
+ return nullptr;
}
#endif // QT_CONFIG(tabletevent)
diff --git a/src/plugins/platforms/xcb/qxcbcursor.cpp b/src/plugins/platforms/xcb/qxcbcursor.cpp
index 7c62c2e2b3..34b7d0d236 100644
--- a/src/plugins/platforms/xcb/qxcbcursor.cpp
+++ b/src/plugins/platforms/xcb/qxcbcursor.cpp
@@ -255,29 +255,29 @@ static const uint8_t * const cursor_bits20[] = {
forbidden_bits, forbiddenm_bits
};
-static const char * const cursorNames[] = {
- "left_ptr",
- "up_arrow",
- "cross",
- "wait",
- "ibeam",
- "size_ver",
- "size_hor",
- "size_bdiag",
- "size_fdiag",
- "size_all",
- "blank",
- "split_v",
- "split_h",
- "pointing_hand",
- "forbidden",
- "whats_this",
- "left_ptr_watch",
- "openhand",
- "closedhand",
- "copy",
- "move",
- "link"
+static const std::vector<const char *> cursorNames[] = {
+ { "left_ptr", "default", "top_left_arrow", "left_arrow" },
+ { "up_arrow" },
+ { "cross" },
+ { "wait", "watch", "0426c94ea35c87780ff01dc239897213" },
+ { "ibeam", "text", "xterm" },
+ { "size_ver", "ns-resize", "v_double_arrow", "00008160000006810000408080010102" },
+ { "size_hor", "ew-resize", "h_double_arrow", "028006030e0e7ebffc7f7070c0600140" },
+ { "size_bdiag", "nesw-resize", "50585d75b494802d0151028115016902", "fcf1c3c7cd4491d801f1e1c78f100000" },
+ { "size_fdiag", "nwse-resize", "38c5dff7c7b8962045400281044508d2", "c7088f0f3e6c8088236ef8e1e3e70000" },
+ { "size_all" },
+ { "blank" },
+ { "split_v", "row-resize", "sb_v_double_arrow", "2870a09082c103050810ffdffffe0204", "c07385c7190e701020ff7ffffd08103c" },
+ { "split_h", "col-resize", "sb_h_double_arrow", "043a9f68147c53184671403ffa811cc5", "14fef782d02440884392942c11205230" },
+ { "pointing_hand", "pointer", "hand1", "e29285e634086352946a0e7090d73106" },
+ { "forbidden", "not-allowed", "crossed_circle", "circle", "03b6e0fcb3499374a867c041f52298f0" },
+ { "whats_this", "help", "question_arrow", "5c6cd98b3f3ebcb1f9c7f1c204630408", "d9ce0ab605698f320427677b458ad60b" },
+ { "left_ptr_watch", "half-busy", "progress", "00000000000000020006000e7e9ffc3f", "08e8e1c95fe2fc01f976f1e063a24ccd" },
+ { "openhand", "fleur", "5aca4d189052212118709018842178c0", "9d800788f1b08800ae810202380a0822" },
+ { "closedhand", "grabbing", "208530c400c041818281048008011002" },
+ { "dnd-copy", "copy" },
+ { "dnd-move", "move" },
+ { "dnd-link", "link" }
};
QXcbCursorCacheKey::QXcbCursorCacheKey(const QCursor &c)
@@ -535,22 +535,13 @@ static xcb_cursor_t loadCursor(void *dpy, int cshape)
xcb_cursor_t cursor = XCB_NONE;
if (!ptrXcursorLibraryLoadCursor || !dpy)
return cursor;
- switch (cshape) {
- case Qt::DragCopyCursor:
- cursor = ptrXcursorLibraryLoadCursor(dpy, "dnd-copy");
- break;
- case Qt::DragMoveCursor:
- cursor = ptrXcursorLibraryLoadCursor(dpy, "dnd-move");
- break;
- case Qt::DragLinkCursor:
- cursor = ptrXcursorLibraryLoadCursor(dpy, "dnd-link");
- break;
- default:
- break;
- }
- if (!cursor) {
- cursor = ptrXcursorLibraryLoadCursor(dpy, cursorNames[cshape]);
+
+ for (const char *cursorName: cursorNames[cshape]) {
+ cursor = ptrXcursorLibraryLoadCursor(dpy, cursorName);
+ if (cursor != XCB_NONE)
+ break;
}
+
return cursor;
}
#endif // QT_CONFIG(xcb_xlib) / QT_CONFIG(library)
@@ -565,7 +556,6 @@ xcb_cursor_t QXcbCursor::createFontCursor(int cshape)
#if QT_CONFIG(xcb_xlib) && QT_CONFIG(library)
if (cshape >= 0 && cshape <= Qt::LastCursor) {
void *dpy = connection()->xlib_display();
- // special case for non-standard dnd-* cursors
cursor = loadCursor(dpy, cshape);
if (!cursor && !m_gtkCursorThemeInitialized && m_screen->xSettings()->initialized()) {
QByteArray gtkCursorTheme = m_screen->xSettings()->setting("Gtk/CursorThemeName").toByteArray();
@@ -579,7 +569,7 @@ xcb_cursor_t QXcbCursor::createFontCursor(int cshape)
if (cursor)
return cursor;
if (!cursor && cursorId) {
- cursor = XCreateFontCursor(DISPLAY_FROM_XCB(this), cursorId);
+ cursor = XCreateFontCursor(static_cast<Display *>(connection()->xlib_display()), cursorId);
if (cursor)
return cursor;
}
@@ -598,7 +588,7 @@ xcb_cursor_t QXcbCursor::createFontCursor(int cshape)
}
if (cursor && cshape >= 0 && cshape < Qt::LastCursor && connection()->hasXFixes()) {
- const char *name = cursorNames[cshape];
+ const char *name = cursorNames[cshape].front();
xcb_xfixes_set_cursor_name(conn, cursor, strlen(name), name);
}
@@ -631,10 +621,9 @@ void QXcbCursor::queryPointer(QXcbConnection *c, QXcbVirtualDesktop **virtualDes
*pos = QPoint();
xcb_window_t root = c->primaryVirtualDesktop()->root();
- xcb_query_pointer_cookie_t cookie = xcb_query_pointer(c->xcb_connection(), root);
- xcb_generic_error_t *err = 0;
- xcb_query_pointer_reply_t *reply = xcb_query_pointer_reply(c->xcb_connection(), cookie, &err);
- if (!err && reply) {
+
+ auto reply = Q_XCB_REPLY(xcb_query_pointer, c->xcb_connection(), root);
+ if (reply) {
if (virtualDesktop) {
const auto virtualDesktops = c->virtualDesktops();
for (QXcbVirtualDesktop *vd : virtualDesktops) {
@@ -648,11 +637,8 @@ void QXcbCursor::queryPointer(QXcbConnection *c, QXcbVirtualDesktop **virtualDes
*pos = QPoint(reply->root_x, reply->root_y);
if (keybMask)
*keybMask = reply->mask;
- free(reply);
return;
}
- free(err);
- free(reply);
}
QPoint QXcbCursor::pos() const
@@ -664,7 +650,7 @@ QPoint QXcbCursor::pos() const
void QXcbCursor::setPos(const QPoint &pos)
{
- QXcbVirtualDesktop *virtualDesktop = Q_NULLPTR;
+ QXcbVirtualDesktop *virtualDesktop = nullptr;
queryPointer(connection(), &virtualDesktop, 0);
xcb_warp_pointer(xcb_connection(), XCB_NONE, virtualDesktop->root(), 0, 0, 0, 0, pos.x(), pos.y());
xcb_flush(xcb_connection());
diff --git a/src/plugins/platforms/xcb/qxcbdrag.cpp b/src/plugins/platforms/xcb/qxcbdrag.cpp
index 51d09d8927..8ea0ebf966 100644
--- a/src/plugins/platforms/xcb/qxcbdrag.cpp
+++ b/src/plugins/platforms/xcb/qxcbdrag.cpp
@@ -94,32 +94,27 @@ static xcb_window_t xdndProxy(QXcbConnection *c, xcb_window_t w)
{
xcb_window_t proxy = XCB_NONE;
- xcb_get_property_cookie_t cookie = Q_XCB_CALL2(xcb_get_property(c->xcb_connection(), false, w, c->atom(QXcbAtom::XdndProxy),
- XCB_ATOM_WINDOW, 0, 1), c);
- xcb_get_property_reply_t *reply = xcb_get_property_reply(c->xcb_connection(), cookie, 0);
+ auto reply = Q_XCB_REPLY(xcb_get_property, c->xcb_connection(),
+ false, w, c->atom(QXcbAtom::XdndProxy), XCB_ATOM_WINDOW, 0, 1);
if (reply && reply->type == XCB_ATOM_WINDOW)
- proxy = *((xcb_window_t *)xcb_get_property_value(reply));
- free(reply);
+ proxy = *((xcb_window_t *)xcb_get_property_value(reply.get()));
if (proxy == XCB_NONE)
return proxy;
// exists and is real?
- cookie = Q_XCB_CALL2(xcb_get_property(c->xcb_connection(), false, proxy, c->atom(QXcbAtom::XdndProxy),
- XCB_ATOM_WINDOW, 0, 1), c);
- reply = xcb_get_property_reply(c->xcb_connection(), cookie, 0);
+ reply = Q_XCB_REPLY(xcb_get_property, c->xcb_connection(),
+ false, proxy, c->atom(QXcbAtom::XdndProxy), XCB_ATOM_WINDOW, 0, 1);
if (reply && reply->type == XCB_ATOM_WINDOW) {
- xcb_window_t p = *((xcb_window_t *)xcb_get_property_value(reply));
+ xcb_window_t p = *((xcb_window_t *)xcb_get_property_value(reply.get()));
if (proxy != p)
proxy = 0;
} else {
proxy = 0;
}
- free(reply);
-
return proxy;
}
@@ -142,7 +137,7 @@ protected:
QXcbDrag::QXcbDrag(QXcbConnection *c) : QXcbObject(c)
{
- dropData = new QXcbDropData(this);
+ m_dropData = new QXcbDropData(this);
init();
cleanup_timer = -1;
@@ -150,7 +145,7 @@ QXcbDrag::QXcbDrag(QXcbConnection *c) : QXcbObject(c)
QXcbDrag::~QXcbDrag()
{
- delete dropData;
+ delete m_dropData;
}
void QXcbDrag::init()
@@ -175,11 +170,6 @@ void QXcbDrag::init()
canceled = false;
}
-QMimeData *QXcbDrag::platformDropData()
-{
- return dropData;
-}
-
bool QXcbDrag::eventFilter(QObject *o, QEvent *e)
{
/* We are setting a mouse grab on the QShapedPixmapWindow in order not to
@@ -221,7 +211,7 @@ void QXcbDrag::startDrag()
setScreen(current_virtual_desktop->screens().constFirst()->screen());
initiatorWindow = QGuiApplicationPrivate::currentMouseWindow;
QBasicDrag::startDrag();
- if (connection()->mouseGrabber() == Q_NULLPTR)
+ if (connection()->mouseGrabber() == nullptr)
shapedPixmapWindow()->setMouseGrabEnabled(true);
}
@@ -235,28 +225,19 @@ void QXcbDrag::endDrag()
initiatorWindow.clear();
}
-static xcb_translate_coordinates_reply_t *
-translateCoordinates(QXcbConnection *c, xcb_window_t from, xcb_window_t to, int x, int y)
-{
- xcb_translate_coordinates_cookie_t cookie =
- xcb_translate_coordinates(c->xcb_connection(), from, to, x, y);
- return xcb_translate_coordinates_reply(c->xcb_connection(), cookie, 0);
-}
-
static
bool windowInteractsWithPosition(xcb_connection_t *connection, const QPoint & pos, xcb_window_t w, xcb_shape_sk_t shapeType)
{
bool interacts = false;
- xcb_shape_get_rectangles_reply_t *reply = xcb_shape_get_rectangles_reply(connection, xcb_shape_get_rectangles(connection, w, shapeType), NULL);
+ auto reply = Q_XCB_REPLY(xcb_shape_get_rectangles, connection, w, shapeType);
if (reply) {
- xcb_rectangle_t *rectangles = xcb_shape_get_rectangles_rectangles(reply);
+ xcb_rectangle_t *rectangles = xcb_shape_get_rectangles_rectangles(reply.get());
if (rectangles) {
- const int nRectangles = xcb_shape_get_rectangles_rectangles_length(reply);
+ const int nRectangles = xcb_shape_get_rectangles_rectangles_length(reply.get());
for (int i = 0; !interacts && i < nRectangles; ++i) {
interacts = QRect(rectangles[i].x, rectangles[i].y, rectangles[i].width, rectangles[i].height).contains(pos);
}
}
- free(reply);
}
return interacts;
@@ -268,33 +249,25 @@ xcb_window_t QXcbDrag::findRealWindow(const QPoint & pos, xcb_window_t w, int md
return 0;
if (md) {
- xcb_get_window_attributes_cookie_t cookie = xcb_get_window_attributes(xcb_connection(), w);
- xcb_get_window_attributes_reply_t *reply = xcb_get_window_attributes_reply(xcb_connection(), cookie, 0);
+ auto reply = Q_XCB_REPLY(xcb_get_window_attributes, xcb_connection(), w);
if (!reply)
return 0;
if (reply->map_state != XCB_MAP_STATE_VIEWABLE)
return 0;
- free(reply);
-
- xcb_get_geometry_cookie_t gcookie = xcb_get_geometry(xcb_connection(), w);
- xcb_get_geometry_reply_t *greply = xcb_get_geometry_reply(xcb_connection(), gcookie, 0);
+ auto greply = Q_XCB_REPLY(xcb_get_geometry, xcb_connection(), w);
if (!greply)
return 0;
QRect windowRect(greply->x, greply->y, greply->width, greply->height);
- free(greply);
if (windowRect.contains(pos)) {
bool windowContainsMouse = !ignoreNonXdndAwareWindows;
{
- xcb_get_property_cookie_t cookie =
- Q_XCB_CALL(xcb_get_property(xcb_connection(), false, w, connection()->atom(QXcbAtom::XdndAware),
- XCB_GET_PROPERTY_TYPE_ANY, 0, 0));
- xcb_get_property_reply_t *reply = xcb_get_property_reply(xcb_connection(), cookie, 0);
-
+ auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(),
+ false, w, connection()->atom(QXcbAtom::XdndAware),
+ XCB_GET_PROPERTY_TYPE_ANY, 0, 0);
bool isAware = reply && reply->type != XCB_NONE;
- free(reply);
if (isAware) {
const QPoint relPos = pos - windowRect.topLeft();
// When ShapeInput and ShapeBounding are not set they return a single rectangle with the geometry of the window, this is why we
@@ -310,19 +283,16 @@ xcb_window_t QXcbDrag::findRealWindow(const QPoint & pos, xcb_window_t w, int md
}
}
- xcb_query_tree_cookie_t cookie = xcb_query_tree (xcb_connection(), w);
- xcb_query_tree_reply_t *reply = xcb_query_tree_reply(xcb_connection(), cookie, 0);
-
+ auto reply = Q_XCB_REPLY(xcb_query_tree, xcb_connection(), w);
if (!reply)
return 0;
- int nc = xcb_query_tree_children_length(reply);
- xcb_window_t *c = xcb_query_tree_children(reply);
+ int nc = xcb_query_tree_children_length(reply.get());
+ xcb_window_t *c = xcb_query_tree_children(reply.get());
xcb_window_t r = 0;
for (uint i = nc; !r && i--;)
r = findRealWindow(pos - windowRect.topLeft(), c[i], md-1, ignoreNonXdndAwareWindows);
- free(reply);
if (r)
return r;
@@ -345,7 +315,7 @@ void QXcbDrag::move(const QPoint &globalPos)
if (source_sameanswer.contains(globalPos) && source_sameanswer.isValid())
return;
- QXcbVirtualDesktop *virtualDesktop = Q_NULLPTR;
+ QXcbVirtualDesktop *virtualDesktop = nullptr;
QPoint cursorPos;
QXcbCursor::queryPointer(connection(), &virtualDesktop, &cursorPos);
QXcbScreen *screen = virtualDesktop->screenAt(cursorPos);
@@ -354,7 +324,7 @@ void QXcbDrag::move(const QPoint &globalPos)
if (virtualDesktop != current_virtual_desktop) {
setUseCompositing(virtualDesktop->compositingActive());
recreateShapedPixmapWindow(static_cast<QPlatformScreen*>(screen)->screen(), deviceIndependentPos);
- if (connection()->mouseGrabber() == Q_NULLPTR)
+ if (connection()->mouseGrabber() == nullptr)
shapedPixmapWindow()->setMouseGrabEnabled(true);
current_virtual_desktop = virtualDesktop;
@@ -363,15 +333,14 @@ void QXcbDrag::move(const QPoint &globalPos)
}
xcb_window_t rootwin = current_virtual_desktop->root();
- xcb_translate_coordinates_reply_t *translate =
- ::translateCoordinates(connection(), rootwin, rootwin, globalPos.x(), globalPos.y());
+ auto translate = Q_XCB_REPLY(xcb_translate_coordinates, connection()->xcb_connection(),
+ rootwin, rootwin, globalPos.x(), globalPos.y());
if (!translate)
return;
xcb_window_t target = translate->child;
int lx = translate->dst_x;
int ly = translate->dst_y;
- free (translate);
if (target && target != rootwin) {
xcb_window_t src = rootwin;
@@ -379,7 +348,8 @@ void QXcbDrag::move(const QPoint &globalPos)
DNDDEBUG << "checking target for XdndAware" << target << lx << ly;
// translate coordinates
- translate = ::translateCoordinates(connection(), src, target, lx, ly);
+ auto translate = Q_XCB_REPLY(xcb_translate_coordinates, connection()->xcb_connection(),
+ src, target, lx, ly);
if (!translate) {
target = 0;
break;
@@ -388,14 +358,11 @@ void QXcbDrag::move(const QPoint &globalPos)
ly = translate->dst_y;
src = target;
xcb_window_t child = translate->child;
- free(translate);
// check if it has XdndAware
- xcb_get_property_cookie_t cookie = Q_XCB_CALL(xcb_get_property(xcb_connection(), false, target,
- atom(QXcbAtom::XdndAware), XCB_GET_PROPERTY_TYPE_ANY, 0, 0));
- xcb_get_property_reply_t *reply = xcb_get_property_reply(xcb_connection(), cookie, 0);
+ auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(), false, target,
+ atom(QXcbAtom::XdndAware), XCB_GET_PROPERTY_TYPE_ANY, 0, 0);
bool aware = reply && reply->type != XCB_NONE;
- free(reply);
if (aware) {
DNDDEBUG << "Found XdndAware on " << target;
break;
@@ -429,16 +396,14 @@ void QXcbDrag::move(const QPoint &globalPos)
int target_version = 1;
if (proxy_target) {
- xcb_get_property_cookie_t cookie = xcb_get_property(xcb_connection(), false, proxy_target,
- atom(QXcbAtom::XdndAware), XCB_GET_PROPERTY_TYPE_ANY, 0, 1);
- xcb_get_property_reply_t *reply = xcb_get_property_reply(xcb_connection(), cookie, 0);
+ auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(),
+ false, proxy_target,
+ atom(QXcbAtom::XdndAware), XCB_GET_PROPERTY_TYPE_ANY, 0, 1);
if (!reply || reply->type == XCB_NONE)
target = 0;
- target_version = *(uint32_t *)xcb_get_property_value(reply);
+ target_version = *(uint32_t *)xcb_get_property_value(reply.get());
target_version = qMin(xdnd_version, target_version ? target_version : 1);
-
- free(reply);
}
if (target != current_target) {
@@ -721,21 +686,19 @@ void QXcbDrag::handleEnter(QPlatformWindow *window, const xcb_client_message_eve
if (event->data.data32[1] & 1) {
// get the types from XdndTypeList
- xcb_get_property_cookie_t cookie = xcb_get_property(xcb_connection(), false, xdnd_dragsource,
- atom(QXcbAtom::XdndTypelist), XCB_ATOM_ATOM,
- 0, xdnd_max_type);
- xcb_get_property_reply_t *reply = xcb_get_property_reply(xcb_connection(), cookie, 0);
+ auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(), false, xdnd_dragsource,
+ atom(QXcbAtom::XdndTypelist), XCB_ATOM_ATOM,
+ 0, xdnd_max_type);
if (reply && reply->type != XCB_NONE && reply->format == 32) {
- int length = xcb_get_property_value_length(reply) / 4;
+ int length = xcb_get_property_value_length(reply.get()) / 4;
if (length > xdnd_max_type)
length = xdnd_max_type;
- xcb_atom_t *atoms = (xcb_atom_t *)xcb_get_property_value(reply);
+ xcb_atom_t *atoms = (xcb_atom_t *)xcb_get_property_value(reply.get());
xdnd_types.reserve(length);
for (int i = 0; i < length; ++i)
xdnd_types.append(atoms[i]);
}
- free(reply);
} else {
// get the types from the message
for(int i = 2; i < 5; i++) {
@@ -776,7 +739,7 @@ void QXcbDrag::handle_xdnd_position(QPlatformWindow *w, const xcb_client_message
dropData = currentDrag()->mimeData();
supported_actions = currentDrag()->supportedActions();
} else {
- dropData = platformDropData();
+ dropData = m_dropData;
supported_actions = Qt::DropActions(toDropAction(e->data.data32[4]));
}
@@ -819,8 +782,8 @@ void QXcbDrag::handle_xdnd_position(QPlatformWindow *w, const xcb_client_message
handle_xdnd_status(&response);
else
#endif
- Q_XCB_CALL(xcb_send_event(xcb_connection(), false, current_proxy_target,
- XCB_EVENT_MASK_NO_EVENT, (const char *)&response));
+ xcb_send_event(xcb_connection(), false, current_proxy_target,
+ XCB_EVENT_MASK_NO_EVENT, (const char *)&response);
}
namespace
@@ -997,7 +960,7 @@ void QXcbDrag::handleDrop(QPlatformWindow *, const xcb_client_message_event_t *e
dropData = currentDrag()->mimeData();
supported_drop_actions = Qt::DropActions(l[4]);
} else {
- dropData = platformDropData();
+ dropData = m_dropData;
supported_drop_actions = accepted_drop_action;
// Drop coming from another app? Update keyboard modifiers.
@@ -1024,8 +987,8 @@ void QXcbDrag::handleDrop(QPlatformWindow *, const xcb_client_message_event_t *e
finished.data.data32[0] = currentWindow ? xcb_window(currentWindow.data()) : XCB_NONE;
finished.data.data32[1] = response.isAccepted(); // flags
finished.data.data32[2] = toXdndAction(response.acceptedAction());
- Q_XCB_CALL(xcb_send_event(xcb_connection(), false, current_proxy_target,
- XCB_EVENT_MASK_NO_EVENT, (char *)&finished));
+ xcb_send_event(xcb_connection(), false, current_proxy_target,
+ XCB_EVENT_MASK_NO_EVENT, (char *)&finished);
xdnd_dragsource = 0;
currentWindow.clear();
@@ -1143,28 +1106,20 @@ static xcb_window_t findXdndAwareParent(QXcbConnection *c, xcb_window_t window)
xcb_window_t target = 0;
forever {
// check if window has XdndAware
- xcb_get_property_cookie_t gpCookie = Q_XCB_CALL(
- xcb_get_property(c->xcb_connection(), false, window,
- c->atom(QXcbAtom::XdndAware), XCB_GET_PROPERTY_TYPE_ANY, 0, 0));
- xcb_get_property_reply_t *gpReply = xcb_get_property_reply(
- c->xcb_connection(), gpCookie, 0);
+ auto gpReply = Q_XCB_REPLY(xcb_get_property, c->xcb_connection(), false, window,
+ c->atom(QXcbAtom::XdndAware), XCB_GET_PROPERTY_TYPE_ANY, 0, 0);
bool aware = gpReply && gpReply->type != XCB_NONE;
- free(gpReply);
if (aware) {
target = window;
break;
}
// try window's parent
- xcb_query_tree_cookie_t qtCookie = Q_XCB_CALL(
- xcb_query_tree_unchecked(c->xcb_connection(), window));
- xcb_query_tree_reply_t *qtReply = xcb_query_tree_reply(
- c->xcb_connection(), qtCookie, NULL);
+ auto qtReply = Q_XCB_REPLY_UNCHECKED(xcb_query_tree, c->xcb_connection(), window);
if (!qtReply)
break;
xcb_window_t root = qtReply->root;
xcb_window_t parent = qtReply->parent;
- free(qtReply);
if (window == root)
break;
window = parent;
diff --git a/src/plugins/platforms/xcb/qxcbdrag.h b/src/plugins/platforms/xcb/qxcbdrag.h
index 9e1ee46593..31f1c47d83 100644
--- a/src/plugins/platforms/xcb/qxcbdrag.h
+++ b/src/plugins/platforms/xcb/qxcbdrag.h
@@ -74,7 +74,6 @@ public:
QXcbDrag(QXcbConnection *c);
~QXcbDrag();
- QMimeData *platformDropData() override;
bool eventFilter(QObject *o, QEvent *e) override;
void startDrag() override;
@@ -117,7 +116,7 @@ private:
QPointer<QWindow> currentWindow;
QPoint currentPosition;
- QXcbDropData *dropData;
+ QXcbDropData *m_dropData;
Qt::DropAction accepted_drop_action;
QWindow *desktop_proxy;
diff --git a/src/plugins/platforms/xcb/qxcbimage.cpp b/src/plugins/platforms/xcb/qxcbimage.cpp
index c419bd913d..e18a08755b 100644
--- a/src/plugins/platforms/xcb/qxcbimage.cpp
+++ b/src/plugins/platforms/xcb/qxcbimage.cpp
@@ -38,6 +38,7 @@
****************************************************************************/
#include "qxcbimage.h"
+#include <QtCore/QtEndian>
#include <QtGui/QColor>
#include <QtGui/private/qimage_p.h>
#include <QtGui/private/qdrawhelper_p.h>
@@ -52,47 +53,108 @@ extern "C" {
#undef template
#endif
+#include "qxcbconnection.h"
+#include "qxcbintegration.h"
+
+namespace {
+
+QImage::Format imageFormatForMasks(int depth, int bits_per_pixel, int red_mask, int blue_mask)
+{
+ if (bits_per_pixel == 32) {
+ switch (depth) {
+ case 32:
+ if (red_mask == 0xff0000 && blue_mask == 0xff)
+ return QImage::Format_ARGB32_Premultiplied;
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+ if (red_mask == 0xff && blue_mask == 0xff0000)
+ return QImage::Format_RGBA8888_Premultiplied;
+#else
+ if (red_mask == 0xff000000 && blue_mask == 0xff00)
+ return QImage::Format_RGBA8888_Premultiplied;
+#endif
+ if (red_mask == 0x3ff && blue_mask == 0x3ff00000)
+ return QImage::Format_A2BGR30_Premultiplied;
+ if (red_mask == 0x3ff00000 && blue_mask == 0x3ff)
+ return QImage::Format_A2RGB30_Premultiplied;
+ break;
+ case 30:
+ if (red_mask == 0x3ff && blue_mask == 0x3ff00000)
+ return QImage::Format_BGR30;
+ if (blue_mask == 0x3ff && red_mask == 0x3ff00000)
+ return QImage::Format_RGB30;
+ break;
+ case 24:
+ if (red_mask == 0xff0000 && blue_mask == 0xff)
+ return QImage::Format_RGB32;
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+ if (red_mask == 0xff && blue_mask == 0xff0000)
+ return QImage::Format_RGBX8888;
+#else
+ if (red_mask == 0xff000000 && blue_mask == 0xff00)
+ return QImage::Format_RGBX8888;
+#endif
+ break;
+ }
+ } else if (bits_per_pixel == 16) {
+ if (depth == 16 && red_mask == 0xf800 && blue_mask == 0x1f)
+ return QImage::Format_RGB16;
+ if (depth == 15 && red_mask == 0x7c00 && blue_mask == 0x1f)
+ return QImage::Format_RGB555;
+ }
+ return QImage::Format_Invalid;
+}
+
+} // namespace
+
QT_BEGIN_NAMESPACE
-QImage::Format qt_xcb_imageFormatForVisual(QXcbConnection *connection, uint8_t depth,
- const xcb_visualtype_t *visual)
+bool qt_xcb_imageFormatForVisual(QXcbConnection *connection, uint8_t depth, const xcb_visualtype_t *visual,
+ QImage::Format *imageFormat, bool *needsRgbSwap)
{
- const xcb_format_t *format = connection->formatForDepth(depth);
+ Q_ASSERT(connection && visual && imageFormat);
- if (!visual || !format)
- return QImage::Format_Invalid;
-
- if (depth == 32 && format->bits_per_pixel == 32 && visual->red_mask == 0xff0000
- && visual->green_mask == 0xff00 && visual->blue_mask == 0xff)
- return QImage::Format_ARGB32_Premultiplied;
-
- if (depth == 30 && format->bits_per_pixel == 32 && visual->red_mask == 0x3ff
- && visual->green_mask == 0x0ffc00 && visual->blue_mask == 0x3ff00000)
- return QImage::Format_BGR30;
-
- if (depth == 30 && format->bits_per_pixel == 32 && visual->blue_mask == 0x3ff
- && visual->green_mask == 0x0ffc00 && visual->red_mask == 0x3ff00000)
- return QImage::Format_RGB30;
-
- if (depth == 24 && format->bits_per_pixel == 32 && visual->red_mask == 0xff0000
- && visual->green_mask == 0xff00 && visual->blue_mask == 0xff)
- return QImage::Format_RGB32;
-
- if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
- if (depth == 24 && format->bits_per_pixel == 32 && visual->blue_mask == 0xff0000
- && visual->green_mask == 0xff00 && visual->red_mask == 0xff)
- return QImage::Format_RGBX8888;
- } else {
- if (depth == 24 && format->bits_per_pixel == 32 && visual->blue_mask == 0xff00
- && visual->green_mask == 0xff0000 && visual->red_mask == 0xff000000)
- return QImage::Format_RGBX8888;
+ if (needsRgbSwap)
+ *needsRgbSwap = false;
+ *imageFormat = QImage::Format_Invalid;
+
+ if (depth == 8) {
+ if (visual->_class == XCB_VISUAL_CLASS_GRAY_SCALE) {
+ *imageFormat = QImage::Format_Grayscale8;
+ return true;
+ }
+#if QT_CONFIG(xcb_native_painting)
+ if (QXcbIntegration::instance() && QXcbIntegration::instance()->nativePaintingEnabled()) {
+ *imageFormat = QImage::Format_Indexed8;
+ return true;
+ }
+#endif
+ return false;
+ }
+
+ const xcb_format_t *format = connection->formatForDepth(depth);
+ if (!format)
+ return false;
+
+ const bool connectionEndianSwap = connection->imageNeedsEndianSwap();
+ // We swap the masks and see if we can recognize it as a host format
+ const quint32 red_mask = connectionEndianSwap ? qbswap(visual->red_mask) : visual->red_mask;
+ const quint32 blue_mask = connectionEndianSwap ? qbswap(visual->blue_mask) : visual->blue_mask;
+
+ *imageFormat = imageFormatForMasks(depth, format->bits_per_pixel, red_mask, blue_mask);
+ if (*imageFormat != QImage::Format_Invalid)
+ return true;
+
+ if (needsRgbSwap) {
+ *imageFormat = imageFormatForMasks(depth, format->bits_per_pixel, blue_mask, red_mask);
+ if (*imageFormat != QImage::Format_Invalid) {
+ *needsRgbSwap = true;
+ return true;
+ }
}
- if (depth == 16 && format->bits_per_pixel == 16 && visual->red_mask == 0xf800
- && visual->green_mask == 0x7e0 && visual->blue_mask == 0x1f)
- return QImage::Format_RGB16;
+ qWarning("Unsupported screen format: depth: %d, bits_per_pixel: %d, red_mask: %x, blue_mask: %x", depth, format->bits_per_pixel, red_mask, blue_mask);
- return QImage::Format_Invalid;
+ return false;
}
QPixmap qt_xcb_pixmapFromXPixmap(QXcbConnection *connection, xcb_pixmap_t pixmap,
@@ -101,60 +163,25 @@ QPixmap qt_xcb_pixmapFromXPixmap(QXcbConnection *connection, xcb_pixmap_t pixmap
{
xcb_connection_t *conn = connection->xcb_connection();
- xcb_get_image_cookie_t get_image_cookie =
- xcb_get_image_unchecked(conn, XCB_IMAGE_FORMAT_Z_PIXMAP, pixmap,
- 0, 0, width, height, 0xffffffff);
-
- xcb_get_image_reply_t *image_reply =
- xcb_get_image_reply(conn, get_image_cookie, NULL);
-
+ auto image_reply = Q_XCB_REPLY_UNCHECKED(xcb_get_image, conn, XCB_IMAGE_FORMAT_Z_PIXMAP, pixmap,
+ 0, 0, width, height, 0xffffffff);
if (!image_reply) {
return QPixmap();
}
- uint8_t *data = xcb_get_image_data(image_reply);
- uint32_t length = xcb_get_image_data_length(image_reply);
+ uint8_t *data = xcb_get_image_data(image_reply.get());
+ uint32_t length = xcb_get_image_data_length(image_reply.get());
QPixmap result;
- QImage::Format format = qt_xcb_imageFormatForVisual(connection, depth, visual);
- if (format != QImage::Format_Invalid) {
+ QImage::Format format;
+ bool needsRgbSwap;
+ if (qt_xcb_imageFormatForVisual(connection, depth, visual, &format, &needsRgbSwap)) {
uint32_t bytes_per_line = length / height;
QImage image(const_cast<uint8_t *>(data), width, height, bytes_per_line, format);
- uint8_t image_byte_order = connection->setup()->image_byte_order;
-
- // we may have to swap the byte order
- if ((QSysInfo::ByteOrder == QSysInfo::LittleEndian && image_byte_order == XCB_IMAGE_ORDER_MSB_FIRST)
- || (QSysInfo::ByteOrder == QSysInfo::BigEndian && image_byte_order == XCB_IMAGE_ORDER_LSB_FIRST))
- {
- for (int i=0; i < image.height(); i++) {
- switch (format) {
- case QImage::Format_RGB16: {
- ushort *p = (ushort*)image.scanLine(i);
- ushort *end = p + image.width();
- while (p < end) {
- *p = ((*p << 8) & 0xff00) | ((*p >> 8) & 0x00ff);
- p++;
- }
- break;
- }
- case QImage::Format_RGB32:
- case QImage::Format_ARGB32_Premultiplied:
- case QImage::Format_RGBX8888: {
- uint *p = (uint*)image.scanLine(i);
- uint *end = p + image.width();
- while (p < end) {
- *p = ((*p << 24) & 0xff000000) | ((*p << 8) & 0x00ff0000)
- | ((*p >> 8) & 0x0000ff00) | ((*p >> 24) & 0x000000ff);
- p++;
- }
- break;
- }
- default:
- Q_ASSERT(false);
- }
- }
- }
+
+ if (needsRgbSwap)
+ image = std::move(image).rgbSwapped();
// fix-up alpha channel
if (format == QImage::Format_RGB32 || format == QImage::Format_RGBX8888) {
@@ -176,7 +203,6 @@ QPixmap qt_xcb_pixmapFromXPixmap(QXcbConnection *connection, xcb_pixmap_t pixmap
result = QPixmap::fromImage(image.copy());
}
- free(image_reply);
return result;
}
@@ -214,22 +240,15 @@ xcb_cursor_t qt_xcb_createCursorXRender(QXcbScreen *screen, const QImage &image,
xcb_connection_t *conn = screen->xcb_connection();
const int w = image.width();
const int h = image.height();
- xcb_generic_error_t *error = 0;
- xcb_render_query_pict_formats_cookie_t formatsCookie = xcb_render_query_pict_formats(conn);
- xcb_render_query_pict_formats_reply_t *formatsReply = xcb_render_query_pict_formats_reply(conn,
- formatsCookie,
- &error);
- if (!formatsReply || error) {
+ auto formats = Q_XCB_REPLY(xcb_render_query_pict_formats, conn);
+ if (!formats) {
qWarning("qt_xcb_createCursorXRender: query_pict_formats failed");
- free(formatsReply);
- free(error);
return XCB_NONE;
}
- xcb_render_pictforminfo_t *fmt = xcb_render_util_find_standard_format(formatsReply,
+ xcb_render_pictforminfo_t *fmt = xcb_render_util_find_standard_format(formats.get(),
XCB_PICT_STANDARD_ARGB_32);
if (!fmt) {
qWarning("qt_xcb_createCursorXRender: Failed to find format PICT_STANDARD_ARGB_32");
- free(formatsReply);
return XCB_NONE;
}
@@ -241,17 +260,15 @@ xcb_cursor_t qt_xcb_createCursorXRender(QXcbScreen *screen, const QImage &image,
0, 0, 0);
if (!xi) {
qWarning("qt_xcb_createCursorXRender: xcb_image_create failed");
- free(formatsReply);
return XCB_NONE;
}
xi->data = (uint8_t *) malloc(xi->stride * h);
if (!xi->data) {
qWarning("qt_xcb_createCursorXRender: Failed to malloc() image data");
xcb_image_destroy(xi);
- free(formatsReply);
return XCB_NONE;
}
- memcpy(xi->data, img.constBits(), img.byteCount());
+ memcpy(xi->data, img.constBits(), img.sizeInBytes());
xcb_pixmap_t pix = xcb_generate_id(conn);
xcb_create_pixmap(conn, 32, pix, screen->root(), w, h);
@@ -271,7 +288,6 @@ xcb_cursor_t qt_xcb_createCursorXRender(QXcbScreen *screen, const QImage &image,
xcb_image_destroy(xi);
xcb_render_free_picture(conn, pic);
xcb_free_pixmap(conn, pix);
- free(formatsReply);
return cursor;
#else
diff --git a/src/plugins/platforms/xcb/qxcbimage.h b/src/plugins/platforms/xcb/qxcbimage.h
index a9071a45de..d718089ec2 100644
--- a/src/plugins/platforms/xcb/qxcbimage.h
+++ b/src/plugins/platforms/xcb/qxcbimage.h
@@ -48,8 +48,8 @@
QT_BEGIN_NAMESPACE
-QImage::Format qt_xcb_imageFormatForVisual(QXcbConnection *connection,
- uint8_t depth, const xcb_visualtype_t *visual);
+bool qt_xcb_imageFormatForVisual(QXcbConnection *connection, uint8_t depth, const xcb_visualtype_t *visual,
+ QImage::Format *imageFormat, bool *needsRgbSwap = nullptr);
QPixmap qt_xcb_pixmapFromXPixmap(QXcbConnection *connection, xcb_pixmap_t pixmap,
int width, int height, int depth,
const xcb_visualtype_t *visual);
diff --git a/src/plugins/platforms/xcb/qxcbintegration.cpp b/src/plugins/platforms/xcb/qxcbintegration.cpp
index c9ecdceb0d..471287eb44 100644
--- a/src/plugins/platforms/xcb/qxcbintegration.cpp
+++ b/src/plugins/platforms/xcb/qxcbintegration.cpp
@@ -65,6 +65,11 @@
#if QT_CONFIG(xcb_xlib)
#include <X11/Xlib.h>
+#if QT_CONFIG(xcb_native_painting)
+#include "qxcbnativepainting.h"
+#include "qpixmap_x11_p.h"
+#include "qbackingstore_x11_p.h"
+#endif
#endif
#include <qpa/qplatforminputcontextfactory_p.h>
@@ -83,6 +88,11 @@
#include <QtCore/QFileInfo>
+#if QT_CONFIG(vulkan)
+#include "qxcbvulkaninstance.h"
+#include "qxcbvulkanwindow.h"
+#endif
+
QT_BEGIN_NAMESPACE
// Find out if our parent process is gdb by looking at the 'exe' symlink under /proc,.
@@ -111,7 +121,7 @@ static bool runningUnderDebugger()
#endif
}
-QXcbIntegration *QXcbIntegration::m_instance = Q_NULLPTR;
+QXcbIntegration *QXcbIntegration::m_instance = nullptr;
QXcbIntegration::QXcbIntegration(const QStringList &parameters, int &argc, char **argv)
: m_services(new QGenericUnixServices)
@@ -166,8 +176,8 @@ QXcbIntegration::QXcbIntegration(const QStringList &parameters, int &argc, char
#if defined(QT_DEBUG)
if (!noGrabArg && !doGrabArg && underDebugger) {
- qDebug("Qt: gdb: -nograb added to command-line options.\n"
- "\t Use the -dograb option to enforce grabbing.");
+ qCDebug(lcQpaXcb, "Qt: gdb: -nograb added to command-line options.\n"
+ "\t Use the -dograb option to enforce grabbing.");
}
#endif
m_canGrab = (!underDebugger && !noGrabArg) || (underDebugger && doGrabArg);
@@ -200,23 +210,48 @@ QXcbIntegration::QXcbIntegration(const QStringList &parameters, int &argc, char
}
m_fontDatabase.reset(new QGenericUnixFontDatabase());
+
+#if QT_CONFIG(xcb_native_painting)
+ if (nativePaintingEnabled()) {
+ qCDebug(lcQpaXcb, "QXCB USING NATIVE PAINTING");
+ qt_xcb_native_x11_info_init(defaultConnection());
+ }
+#endif
}
QXcbIntegration::~QXcbIntegration()
{
qDeleteAll(m_connections);
- m_instance = Q_NULLPTR;
+ m_instance = nullptr;
+}
+
+QPlatformPixmap *QXcbIntegration::createPlatformPixmap(QPlatformPixmap::PixelType type) const
+{
+#if QT_CONFIG(xcb_native_painting)
+ if (nativePaintingEnabled())
+ return new QX11PlatformPixmap(type);
+#endif
+
+ return QPlatformIntegration::createPlatformPixmap(type);
}
QPlatformWindow *QXcbIntegration::createPlatformWindow(QWindow *window) const
{
QXcbScreen *screen = static_cast<QXcbScreen *>(window->screen()->handle());
QXcbGlIntegration *glIntegration = screen->connection()->glIntegration();
- if (window->type() != Qt::Desktop && window->supportsOpenGL()) {
- if (glIntegration) {
- QXcbWindow *xcbWindow = glIntegration->createWindow(window);
+ if (window->type() != Qt::Desktop) {
+ if (window->supportsOpenGL()) {
+ if (glIntegration) {
+ QXcbWindow *xcbWindow = glIntegration->createWindow(window);
+ xcbWindow->create();
+ return xcbWindow;
+ }
+#if QT_CONFIG(vulkan)
+ } else if (window->surfaceType() == QSurface::VulkanSurface) {
+ QXcbWindow *xcbWindow = new QXcbVulkanWindow(window);
xcbWindow->create();
return xcbWindow;
+#endif
}
}
@@ -239,7 +274,7 @@ QPlatformOpenGLContext *QXcbIntegration::createPlatformOpenGLContext(QOpenGLCont
QXcbGlIntegration *glIntegration = screen->connection()->glIntegration();
if (!glIntegration) {
qWarning("QXcbIntegration: Cannot create platform OpenGL context, neither GLX nor EGL are enabled");
- return Q_NULLPTR;
+ return nullptr;
}
return glIntegration->createPlatformOpenGLContext(context);
}
@@ -247,6 +282,11 @@ QPlatformOpenGLContext *QXcbIntegration::createPlatformOpenGLContext(QOpenGLCont
QPlatformBackingStore *QXcbIntegration::createPlatformBackingStore(QWindow *window) const
{
+#if QT_CONFIG(xcb_native_painting)
+ if (nativePaintingEnabled())
+ return new QXcbNativeBackingStore(window);
+#endif
+
return new QXcbBackingStore(window);
}
@@ -256,7 +296,7 @@ QPlatformOffscreenSurface *QXcbIntegration::createPlatformOffscreenSurface(QOffs
QXcbGlIntegration *glIntegration = screen->connection()->glIntegration();
if (!glIntegration) {
qWarning("QXcbIntegration: Cannot create platform offscreen surface, neither GLX nor EGL are enabled");
- return Q_NULLPTR;
+ return nullptr;
}
return glIntegration->createPlatformOffscreenSurface(surface);
}
@@ -498,4 +538,21 @@ void QXcbIntegration::beep() const
xcb_bell(connection, 0);
}
+bool QXcbIntegration::nativePaintingEnabled() const
+{
+#if QT_CONFIG(xcb_native_painting)
+ static bool enabled = qEnvironmentVariableIsSet("QT_XCB_NATIVE_PAINTING");
+ return enabled;
+#else
+ return false;
+#endif
+}
+
+#if QT_CONFIG(vulkan)
+QPlatformVulkanInstance *QXcbIntegration::createPlatformVulkanInstance(QVulkanInstance *instance) const
+{
+ return new QXcbVulkanInstance(instance);
+}
+#endif
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/qxcbintegration.h b/src/plugins/platforms/xcb/qxcbintegration.h
index b3d72c19d0..186b6c5ddd 100644
--- a/src/plugins/platforms/xcb/qxcbintegration.h
+++ b/src/plugins/platforms/xcb/qxcbintegration.h
@@ -61,6 +61,7 @@ public:
QXcbIntegration(const QStringList &parameters, int &argc, char **argv);
~QXcbIntegration();
+ QPlatformPixmap *createPlatformPixmap(QPlatformPixmap::PixelType type) const override;
QPlatformWindow *createPlatformWindow(QWindow *window) const override;
QPlatformWindow *createForeignWindow(QWindow *window, WId nativeHandle) const override;
#ifndef QT_NO_OPENGL
@@ -114,6 +115,12 @@ public:
void beep() const override;
+ bool nativePaintingEnabled() const;
+
+#if QT_CONFIG(vulkan)
+ QPlatformVulkanInstance *createPlatformVulkanInstance(QVulkanInstance *instance) const override;
+#endif
+
static QXcbIntegration *instance() { return m_instance; }
private:
diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.cpp b/src/plugins/platforms/xcb/qxcbkeyboard.cpp
index 2e29c208c7..2ed66394c9 100644
--- a/src/plugins/platforms/xcb/qxcbkeyboard.cpp
+++ b/src/plugins/platforms/xcb/qxcbkeyboard.cpp
@@ -53,24 +53,91 @@
#include <stdio.h>
#include <X11/keysym.h>
-#ifdef XCB_USE_XINPUT22
+#if QT_CONFIG(xinput2)
#include <X11/extensions/XI2proto.h>
#undef KeyPress
#undef KeyRelease
#endif
+#if QT_CONFIG(xcb_xlib)
+#include <X11/Xutil.h>
+#undef KeyPress
+#undef KeyRelease
+#endif
+
#ifndef XK_ISO_Left_Tab
#define XK_ISO_Left_Tab 0xFE20
#endif
-#ifndef XK_dead_hook
-#define XK_dead_hook 0xFE61
+#ifndef XK_dead_a
+#define XK_dead_a 0xFE80
+#endif
+
+#ifndef XK_dead_A
+#define XK_dead_A 0xFE81
+#endif
+
+#ifndef XK_dead_e
+#define XK_dead_e 0xFE82
+#endif
+
+#ifndef XK_dead_E
+#define XK_dead_E 0xFE83
+#endif
+
+#ifndef XK_dead_i
+#define XK_dead_i 0xFE84
+#endif
+
+#ifndef XK_dead_I
+#define XK_dead_I 0xFE85
+#endif
+
+#ifndef XK_dead_o
+#define XK_dead_o 0xFE86
+#endif
+
+#ifndef XK_dead_O
+#define XK_dead_O 0xFE87
+#endif
+
+#ifndef XK_dead_u
+#define XK_dead_u 0xFE88
+#endif
+
+#ifndef XK_dead_U
+#define XK_dead_U 0xFE89
+#endif
+
+#ifndef XK_dead_small_schwa
+#define XK_dead_small_schwa 0xFE8A
+#endif
+
+#ifndef XK_dead_capital_schwa
+#define XK_dead_capital_schwa 0xFE8B
+#endif
+
+#ifndef XK_dead_greek
+#define XK_dead_greek 0xFE8C
+#endif
+
+#ifndef XK_dead_lowline
+#define XK_dead_lowline 0xFE90
#endif
-#ifndef XK_dead_horn
-#define XK_dead_horn 0xFE62
+#ifndef XK_dead_aboveverticalline
+#define XK_dead_aboveverticalline 0xFE91
#endif
+#ifndef XK_dead_belowverticalline
+#define XK_dead_belowverticalline 0xFE92
+#endif
+
+#ifndef XK_dead_longsolidusoverlay
+#define XK_dead_longsolidusoverlay 0xFE93
+#endif
+
+
#ifndef XK_Codeinput
#define XK_Codeinput 0xFF37
#endif
@@ -429,6 +496,36 @@ static const unsigned int KeyTbl[] = {
XK_dead_belowdot, Qt::Key_Dead_Belowdot,
XK_dead_hook, Qt::Key_Dead_Hook,
XK_dead_horn, Qt::Key_Dead_Horn,
+ XK_dead_stroke, Qt::Key_Dead_Stroke,
+ XK_dead_abovecomma, Qt::Key_Dead_Abovecomma,
+ XK_dead_abovereversedcomma, Qt::Key_Dead_Abovereversedcomma,
+ XK_dead_doublegrave, Qt::Key_Dead_Doublegrave,
+ XK_dead_belowring, Qt::Key_Dead_Belowring,
+ XK_dead_belowmacron, Qt::Key_Dead_Belowmacron,
+ XK_dead_belowcircumflex, Qt::Key_Dead_Belowcircumflex,
+ XK_dead_belowtilde, Qt::Key_Dead_Belowtilde,
+ XK_dead_belowbreve, Qt::Key_Dead_Belowbreve,
+ XK_dead_belowdiaeresis, Qt::Key_Dead_Belowdiaeresis,
+ XK_dead_invertedbreve, Qt::Key_Dead_Invertedbreve,
+ XK_dead_belowcomma, Qt::Key_Dead_Belowcomma,
+ XK_dead_currency, Qt::Key_Dead_Currency,
+ XK_dead_a, Qt::Key_Dead_a,
+ XK_dead_A, Qt::Key_Dead_A,
+ XK_dead_e, Qt::Key_Dead_e,
+ XK_dead_E, Qt::Key_Dead_E,
+ XK_dead_i, Qt::Key_Dead_i,
+ XK_dead_I, Qt::Key_Dead_I,
+ XK_dead_o, Qt::Key_Dead_o,
+ XK_dead_O, Qt::Key_Dead_O,
+ XK_dead_u, Qt::Key_Dead_u,
+ XK_dead_U, Qt::Key_Dead_U,
+ XK_dead_small_schwa, Qt::Key_Dead_Small_Schwa,
+ XK_dead_capital_schwa, Qt::Key_Dead_Capital_Schwa,
+ XK_dead_greek, Qt::Key_Dead_Greek,
+ XK_dead_lowline, Qt::Key_Dead_Lowline,
+ XK_dead_aboveverticalline, Qt::Key_Dead_Aboveverticalline,
+ XK_dead_belowverticalline, Qt::Key_Dead_Belowverticalline,
+ XK_dead_longsolidusoverlay, Qt::Key_Dead_Longsolidusoverlay,
// Special keys from X.org - This include multimedia keys,
// wireless/bluetooth/uwb keys, special launcher keys, etc.
@@ -612,23 +709,18 @@ Qt::KeyboardModifiers QXcbKeyboard::translateModifiers(int s) const
void QXcbKeyboard::readXKBConfig()
{
clearXKBConfig();
- xcb_generic_error_t *error;
- xcb_get_property_cookie_t cookie;
- xcb_get_property_reply_t *config_reply;
xcb_connection_t *c = xcb_connection();
xcb_window_t rootWindow = connection()->rootWindow();
- cookie = xcb_get_property(c, 0, rootWindow,
- atom(QXcbAtom::_XKB_RULES_NAMES), XCB_ATOM_STRING, 0, 1024);
-
- config_reply = xcb_get_property_reply(c, cookie, &error);
+ auto config_reply = Q_XCB_REPLY(xcb_get_property, c, 0, rootWindow,
+ atom(QXcbAtom::_XKB_RULES_NAMES), XCB_ATOM_STRING, 0, 1024);
if (!config_reply) {
qWarning("Qt: Couldn't interpret the _XKB_RULES_NAMES property");
return;
}
- char *xkb_config = (char *)xcb_get_property_value(config_reply);
- int length = xcb_get_property_value_length(config_reply);
+ char *xkb_config = (char *)xcb_get_property_value(config_reply.get());
+ int length = xcb_get_property_value_length(config_reply.get());
// on old X servers xkb_config can be 0 even if config_reply indicates a succesfull read
if (!xkb_config || length == 0)
@@ -653,8 +745,6 @@ void QXcbKeyboard::readXKBConfig()
xkb_names.layout = qstrdup(names[2]);
xkb_names.variant = qstrdup(names[3]);
xkb_names.options = qstrdup(names[4]);
-
- free(config_reply);
}
void QXcbKeyboard::clearXKBConfig()
@@ -685,9 +775,303 @@ void QXcbKeyboard::printKeymapError(const char *error) const
"directory contains recent enough contents, to update please see http://cgit.freedesktop.org/xkeyboard-config/ .");
}
+#if QT_CONFIG(xcb_xlib)
+/* Look at a pair of unshifted and shifted key symbols.
+ * If the 'unshifted' symbol is uppercase and there is no shifted symbol,
+ * return the matching lowercase symbol; otherwise return 0.
+ * The caller can then use the previously 'unshifted' symbol as the new
+ * 'shifted' (uppercase) symbol and the symbol returned by the function
+ * as the new 'unshifted' (lowercase) symbol.) */
+static xcb_keysym_t getUnshiftedXKey(xcb_keysym_t unshifted, xcb_keysym_t shifted)
+{
+ if (shifted != XKB_KEY_NoSymbol) // Has a shifted symbol
+ return 0;
+
+ KeySym xlower;
+ KeySym xupper;
+ /* libxkbcommon >= 0.8.0 will have public API functions providing
+ * functionality equivalent to XConvertCase(), use these once the
+ * minimal libxkbcommon version is high enough. After that the
+ * xcb-xlib dependency can be removed */
+ XConvertCase(static_cast<KeySym>(unshifted), &xlower, &xupper);
+
+ if (xlower != xupper // Check if symbol is cased
+ && unshifted == static_cast<xcb_keysym_t>(xupper)) { // Unshifted must be upper case
+ return static_cast<xcb_keysym_t>(xlower);
+ }
+ return 0;
+}
+
+static QByteArray symbolsGroupString(const xcb_keysym_t *symbols, int count)
+{
+ // Don't output trailing NoSymbols
+ while (count > 0 && symbols[count - 1] == XKB_KEY_NoSymbol)
+ count--;
+
+ QByteArray groupString;
+ for (int symIndex = 0; symIndex < count; symIndex++) {
+ xcb_keysym_t sym = symbols[symIndex];
+ char symString[64];
+ if (sym == XKB_KEY_NoSymbol)
+ strcpy(symString, "NoSymbol");
+ else
+ xkb_keysym_get_name(sym, symString, sizeof(symString));
+
+ if (!groupString.isEmpty())
+ groupString += ", ";
+ groupString += symString;
+ }
+ return groupString;
+}
+
+struct xkb_keymap *QXcbKeyboard::keymapFromCore()
+{
+ /* Construct an XKB keymap string from information queried from
+ * the X server */
+ QByteArray keymap;
+ keymap += "xkb_keymap {\n";
+
+ const xcb_keycode_t minKeycode = connection()->setup()->min_keycode;
+ const xcb_keycode_t maxKeycode = connection()->setup()->max_keycode;
+
+ // Generate symbolic names from keycodes
+ {
+ keymap +=
+ "xkb_keycodes \"core\" {\n"
+ "\tminimum = " + QByteArray::number(minKeycode) + ";\n"
+ "\tmaximum = " + QByteArray::number(maxKeycode) + ";\n";
+ for (int code = minKeycode; code <= maxKeycode; code++) {
+ auto codeStr = QByteArray::number(code);
+ keymap += "<K" + codeStr + "> = " + codeStr + ";\n";
+ }
+ /* TODO: indicators?
+ */
+ keymap += "};\n"; // xkb_keycodes
+ }
+
+ /* Set up default types (xkbcommon automatically assigns these to
+ * symbols, but doesn't have shift info) */
+ keymap +=
+ "xkb_types \"core\" {\n"
+ "virtual_modifiers NumLock,Alt,LevelThree;\n"
+ "type \"ONE_LEVEL\" {\n"
+ "modifiers= none;\n"
+ "level_name[Level1] = \"Any\";\n"
+ "};\n"
+ "type \"TWO_LEVEL\" {\n"
+ "modifiers= Shift;\n"
+ "map[Shift]= Level2;\n"
+ "level_name[Level1] = \"Base\";\n"
+ "level_name[Level2] = \"Shift\";\n"
+ "};\n"
+ "type \"ALPHABETIC\" {\n"
+ "modifiers= Shift+Lock;\n"
+ "map[Shift]= Level2;\n"
+ "map[Lock]= Level2;\n"
+ "level_name[Level1] = \"Base\";\n"
+ "level_name[Level2] = \"Caps\";\n"
+ "};\n"
+ "type \"KEYPAD\" {\n"
+ "modifiers= Shift+NumLock;\n"
+ "map[Shift]= Level2;\n"
+ "map[NumLock]= Level2;\n"
+ "level_name[Level1] = \"Base\";\n"
+ "level_name[Level2] = \"Number\";\n"
+ "};\n"
+ "type \"FOUR_LEVEL\" {\n"
+ "modifiers= Shift+LevelThree;\n"
+ "map[Shift]= Level2;\n"
+ "map[LevelThree]= Level3;\n"
+ "map[Shift+LevelThree]= Level4;\n"
+ "level_name[Level1] = \"Base\";\n"
+ "level_name[Level2] = \"Shift\";\n"
+ "level_name[Level3] = \"Alt Base\";\n"
+ "level_name[Level4] = \"Shift Alt\";\n"
+ "};\n"
+ "type \"FOUR_LEVEL_ALPHABETIC\" {\n"
+ "modifiers= Shift+Lock+LevelThree;\n"
+ "map[Shift]= Level2;\n"
+ "map[Lock]= Level2;\n"
+ "map[LevelThree]= Level3;\n"
+ "map[Shift+LevelThree]= Level4;\n"
+ "map[Lock+LevelThree]= Level4;\n"
+ "map[Shift+Lock+LevelThree]= Level3;\n"
+ "level_name[Level1] = \"Base\";\n"
+ "level_name[Level2] = \"Shift\";\n"
+ "level_name[Level3] = \"Alt Base\";\n"
+ "level_name[Level4] = \"Shift Alt\";\n"
+ "};\n"
+ "type \"FOUR_LEVEL_SEMIALPHABETIC\" {\n"
+ "modifiers= Shift+Lock+LevelThree;\n"
+ "map[Shift]= Level2;\n"
+ "map[Lock]= Level2;\n"
+ "map[LevelThree]= Level3;\n"
+ "map[Shift+LevelThree]= Level4;\n"
+ "map[Lock+LevelThree]= Level3;\n"
+ "preserve[Lock+LevelThree]= Lock;\n"
+ "map[Shift+Lock+LevelThree]= Level4;\n"
+ "preserve[Shift+Lock+LevelThree]= Lock;\n"
+ "level_name[Level1] = \"Base\";\n"
+ "level_name[Level2] = \"Shift\";\n"
+ "level_name[Level3] = \"Alt Base\";\n"
+ "level_name[Level4] = \"Shift Alt\";\n"
+ "};\n"
+ "type \"FOUR_LEVEL_KEYPAD\" {\n"
+ "modifiers= Shift+NumLock+LevelThree;\n"
+ "map[Shift]= Level2;\n"
+ "map[NumLock]= Level2;\n"
+ "map[LevelThree]= Level3;\n"
+ "map[Shift+LevelThree]= Level4;\n"
+ "map[NumLock+LevelThree]= Level4;\n"
+ "map[Shift+NumLock+LevelThree]= Level3;\n"
+ "level_name[Level1] = \"Base\";\n"
+ "level_name[Level2] = \"Number\";\n"
+ "level_name[Level3] = \"Alt Base\";\n"
+ "level_name[Level4] = \"Alt Number\";\n"
+ "};\n"
+ "};\n"; // xkb_types
+
+ // Generate mapping between symbolic names and keysyms
+ {
+ QVector<xcb_keysym_t> xkeymap;
+ int keysymsPerKeycode = 0;
+ {
+ int keycodeCount = maxKeycode - minKeycode + 1;
+ if (auto keymapReply = Q_XCB_REPLY(xcb_get_keyboard_mapping, xcb_connection(),
+ minKeycode, keycodeCount)) {
+ keysymsPerKeycode = keymapReply->keysyms_per_keycode;
+ int numSyms = keycodeCount * keysymsPerKeycode;
+ auto keymapPtr = xcb_get_keyboard_mapping_keysyms(keymapReply.get());
+ xkeymap.resize(numSyms);
+ for (int i = 0; i < numSyms; i++)
+ xkeymap[i] = keymapPtr[i];
+ }
+ }
+ if (xkeymap.isEmpty())
+ return nullptr;
+
+ KeysymModifierMap keysymMods(keysymsToModifiers());
+ static const char *const builtinModifiers[] =
+ { "Shift", "Lock", "Control", "Mod1", "Mod2", "Mod3", "Mod4", "Mod5" };
+
+ /* Level 3 symbols (e.g. AltGr+something) seem to come in two flavors:
+ * - as a proper level 3 in group 1, at least on recent X.org versions
+ * - 'disguised' as group 2, on 'legacy' X servers
+ * In the 2nd case, remap group 2 to level 3, that seems to work better
+ * in practice */
+ bool mapGroup2ToLevel3 = keysymsPerKeycode < 5;
+
+ keymap += "xkb_symbols \"core\" {\n";
+ for (int code = minKeycode; code <= maxKeycode; code++) {
+ auto codeMap = xkeymap.constData() + (code - minKeycode) * keysymsPerKeycode;
+
+ const int maxGroup1 = 4; // We only support 4 shift states anyway
+ const int maxGroup2 = 2; // Only 3rd and 4th keysym are group 2
+ xcb_keysym_t symbolsGroup1[maxGroup1];
+ xcb_keysym_t symbolsGroup2[maxGroup2];
+ for (int i = 0; i < maxGroup1 + maxGroup2; i++) {
+ xcb_keysym_t sym = i < keysymsPerKeycode ? codeMap[i] : XKB_KEY_NoSymbol;
+ if (mapGroup2ToLevel3) {
+ // Merge into single group
+ if (i < maxGroup1)
+ symbolsGroup1[i] = sym;
+ } else {
+ // Preserve groups
+ if (i < 2)
+ symbolsGroup1[i] = sym;
+ else if (i < 4)
+ symbolsGroup2[i - 2] = sym;
+ else
+ symbolsGroup1[i - 2] = sym;
+ }
+ }
+
+ /* Fix symbols so the unshifted and shifted symbols have
+ * lower resp. upper case */
+ if (auto lowered = getUnshiftedXKey(symbolsGroup1[0], symbolsGroup1[1])) {
+ symbolsGroup1[1] = symbolsGroup1[0];
+ symbolsGroup1[0] = lowered;
+ }
+ if (auto lowered = getUnshiftedXKey(symbolsGroup2[0], symbolsGroup2[1])) {
+ symbolsGroup2[1] = symbolsGroup2[0];
+ symbolsGroup2[0] = lowered;
+ }
+
+ QByteArray groupStr1 = symbolsGroupString(symbolsGroup1, maxGroup1);
+ if (groupStr1.isEmpty())
+ continue;
+
+ keymap += "key <K" + QByteArray::number(code) + "> { ";
+ keymap += "symbols[Group1] = [ " + groupStr1 + " ]";
+ QByteArray groupStr2 = symbolsGroupString(symbolsGroup2, maxGroup2);
+ if (!groupStr2.isEmpty())
+ keymap += ", symbols[Group2] = [ " + groupStr2 + " ]";
+
+ // See if this key code is for a modifier
+ xcb_keysym_t modifierSym = XKB_KEY_NoSymbol;
+ for (int symIndex = 0; symIndex < keysymsPerKeycode; symIndex++) {
+ xcb_keysym_t sym = codeMap[symIndex];
+
+ if (sym == XKB_KEY_Alt_L
+ || sym == XKB_KEY_Meta_L
+ || sym == XKB_KEY_Mode_switch
+ || sym == XKB_KEY_Super_L
+ || sym == XKB_KEY_Super_R
+ || sym == XKB_KEY_Hyper_L
+ || sym == XKB_KEY_Hyper_R) {
+ modifierSym = sym;
+ break;
+ }
+ }
+
+ // AltGr
+ if (modifierSym == XKB_KEY_Mode_switch)
+ keymap += ", virtualMods=LevelThree";
+ keymap += " };\n"; // key
+
+ // Generate modifier mappings
+ int modNum = keysymMods.value(modifierSym, -1);
+ if (modNum != -1) {
+ // Here modNum is always < 8 (see keysymsToModifiers())
+ keymap += QByteArray("modifier_map ") + builtinModifiers[modNum]
+ + " { <K" + QByteArray::number(code) + "> };\n";
+ }
+ }
+ // TODO: indicators?
+ keymap += "};\n"; // xkb_symbols
+ }
+
+ // We need an "Alt" modifier, provide via the xkb_compatibility section
+ keymap +=
+ "xkb_compatibility \"core\" {\n"
+ "virtual_modifiers NumLock,Alt,LevelThree;\n"
+ "interpret Alt_L+AnyOf(all) {\n"
+ "virtualModifier= Alt;\n"
+ "action= SetMods(modifiers=modMapMods,clearLocks);\n"
+ "};\n"
+ "interpret Alt_R+AnyOf(all) {\n"
+ "virtualModifier= Alt;\n"
+ "action= SetMods(modifiers=modMapMods,clearLocks);\n"
+ "};\n"
+ "};\n";
+
+ /* TODO: There is an issue with modifier state not being handled
+ * correctly if using Xming with XKEYBOARD disabled. */
+
+ keymap += "};\n"; // xkb_keymap
+
+ return xkb_keymap_new_from_buffer(xkb_context,
+ keymap.constData(),
+ keymap.size(),
+ XKB_KEYMAP_FORMAT_TEXT_V1,
+ static_cast<xkb_keymap_compile_flags>(0));
+}
+#endif
+
void QXcbKeyboard::updateKeymap()
{
m_config = true;
+ m_keymap_is_core = false;
// set xkb context object
if (!xkb_context) {
if (qEnvironmentVariableIsSet("QT_XKB_CONFIG_ROOT")) {
@@ -721,9 +1105,23 @@ void QXcbKeyboard::updateKeymap()
}
#endif
if (!xkb_keymap) {
- // Compile a keymap from RMLVO (rules, models, layouts, variants and options) names
+ // Read xkb RMLVO (rules, models, layouts, variants and options) names
readXKBConfig();
- xkb_keymap = xkb_keymap_new_from_names(xkb_context, &xkb_names, (xkb_keymap_compile_flags)0);
+#if QT_CONFIG(xcb_xlib)
+ bool rmlvo_is_incomplete = !xkb_names.rules || !(*xkb_names.rules)
+ || !xkb_names.model || !(*xkb_names.model)
+ || !xkb_names.layout || !(*xkb_names.layout);
+ if (rmlvo_is_incomplete) {
+ // Try to build xkb map from core mapping information
+ xkb_keymap = keymapFromCore();
+ m_keymap_is_core = xkb_keymap != 0;
+ }
+#endif
+ if (!xkb_keymap) {
+ // Compile a keymap from RMLVO
+ xkb_keymap = xkb_keymap_new_from_names(xkb_context, &xkb_names,
+ static_cast<xkb_keymap_compile_flags> (0));
+ }
if (!xkb_keymap) {
// last fallback is to used hard-coded keymap name, see DEFAULT_XKB_* in xkbcommon.pri
qWarning() << "Qt: Could not determine keyboard configuration data"
@@ -807,7 +1205,7 @@ void QXcbKeyboard::updateXKBStateFromCore(quint16 state)
}
}
-#ifdef XCB_USE_XINPUT22
+#if QT_CONFIG(xinput2)
void QXcbKeyboard::updateXKBStateFromXI(void *modInfo, void *groupInfo)
{
if (m_config && !connection()->hasXKB()) {
@@ -915,9 +1313,11 @@ xkb_keysym_t QXcbKeyboard::lookupLatinKeysym(xkb_keycode_t keycode) const
// If user layouts don't contain any layout that results in a latin key, we query a
// key from "US" layout, this allows for latin-key-based shorcuts to work even when
// users have only one (non-latin) layout set.
+ // But don't do this if using keymap obtained through the core protocol, as the key
+ // codes may not match up with those expected by the XKB keymap.
xkb_mod_mask_t latchedMods = xkb_state_serialize_mods(xkb_state, XKB_STATE_MODS_LATCHED);
xkb_mod_mask_t lockedMods = xkb_state_serialize_mods(xkb_state, XKB_STATE_MODS_LOCKED);
- if (sym == XKB_KEY_NoSymbol && !m_hasLatinLayout) {
+ if (sym == XKB_KEY_NoSymbol && !m_hasLatinLayout && !m_keymap_is_core) {
if (!latin_keymap) {
const struct xkb_rule_names names = { xkb_names.rules, xkb_names.model, "us", 0, 0 };
latin_keymap = xkb_keymap_new_from_names(xkb_context, &names, (xkb_keymap_compile_flags)0);
@@ -1124,7 +1524,7 @@ int QXcbKeyboard::keysymToQtKey(xcb_keysym_t keysym, Qt::KeyboardModifiers &modi
modifiers |= Qt::KeypadModifier;
} else if (text.length() == 1 && text.unicode()->unicode() > 0x1f
&& text.unicode()->unicode() != 0x7f
- && !(keysym >= XK_dead_grave && keysym <= XK_dead_currency)) {
+ && !(keysym >= XK_dead_grave && keysym <= XK_dead_longsolidusoverlay)) {
code = text.unicode()->toUpper().unicode();
} else {
// any other keys
@@ -1172,23 +1572,19 @@ QXcbKeyboard::~QXcbKeyboard()
void QXcbKeyboard::updateVModMapping()
{
#if QT_CONFIG(xkb)
- xcb_xkb_get_names_cookie_t names_cookie;
- xcb_xkb_get_names_reply_t *name_reply;
xcb_xkb_get_names_value_list_t names_list;
memset(&vmod_masks, 0, sizeof(vmod_masks));
- names_cookie = xcb_xkb_get_names(xcb_connection(),
- XCB_XKB_ID_USE_CORE_KBD,
- XCB_XKB_NAME_DETAIL_VIRTUAL_MOD_NAMES);
-
- name_reply = xcb_xkb_get_names_reply(xcb_connection(), names_cookie, 0);
+ auto name_reply = Q_XCB_REPLY(xcb_xkb_get_names, xcb_connection(),
+ XCB_XKB_ID_USE_CORE_KBD,
+ XCB_XKB_NAME_DETAIL_VIRTUAL_MOD_NAMES);
if (!name_reply) {
qWarning("Qt: failed to retrieve the virtual modifier names from XKB");
return;
}
- const void *buffer = xcb_xkb_get_names_value_list(name_reply);
+ const void *buffer = xcb_xkb_get_names_value_list(name_reply.get());
xcb_xkb_get_names_value_list_unpack(buffer,
name_reply->nTypes,
name_reply->indicators,
@@ -1233,32 +1629,27 @@ void QXcbKeyboard::updateVModMapping()
else if (qstrcmp(vmod_name, "Hyper") == 0)
vmod_masks.hyper = bit;
}
-
- free(name_reply);
#endif
}
void QXcbKeyboard::updateVModToRModMapping()
{
#if QT_CONFIG(xkb)
- xcb_xkb_get_map_cookie_t map_cookie;
- xcb_xkb_get_map_reply_t *map_reply;
xcb_xkb_get_map_map_t map;
memset(&rmod_masks, 0, sizeof(rmod_masks));
- map_cookie = xcb_xkb_get_map(xcb_connection(),
- XCB_XKB_ID_USE_CORE_KBD,
- XCB_XKB_MAP_PART_VIRTUAL_MODS,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
-
- map_reply = xcb_xkb_get_map_reply(xcb_connection(), map_cookie, 0);
+ auto map_reply = Q_XCB_REPLY(xcb_xkb_get_map,
+ xcb_connection(),
+ XCB_XKB_ID_USE_CORE_KBD,
+ XCB_XKB_MAP_PART_VIRTUAL_MODS,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
if (!map_reply) {
qWarning("Qt: failed to retrieve the virtual modifier map from XKB");
return;
}
- const void *buffer = xcb_xkb_get_map_map(map_reply);
+ const void *buffer = xcb_xkb_get_map_map(map_reply.get());
xcb_xkb_get_map_map_unpack(buffer,
map_reply->nTypes,
map_reply->nKeySyms,
@@ -1301,29 +1692,60 @@ void QXcbKeyboard::updateVModToRModMapping()
rmod_masks.hyper = modmap;
}
- free(map_reply);
resolveMaskConflicts();
#endif
}
+// Small helper: set modifier bit, if modifier position is valid
+static inline void applyModifier(uint *mask, int modifierBit)
+{
+ if (modifierBit >= 0 && modifierBit < 8)
+ *mask |= 1 << modifierBit;
+}
+
void QXcbKeyboard::updateModifiers()
{
+ memset(&rmod_masks, 0, sizeof(rmod_masks));
+
+ // Compute X modifier bits for Qt modifiers
+ KeysymModifierMap keysymMods(keysymsToModifiers());
+ applyModifier(&rmod_masks.alt, keysymMods.value(XK_Alt_L, -1));
+ applyModifier(&rmod_masks.alt, keysymMods.value(XK_Alt_R, -1));
+ applyModifier(&rmod_masks.meta, keysymMods.value(XK_Meta_L, -1));
+ applyModifier(&rmod_masks.meta, keysymMods.value(XK_Meta_R, -1));
+ applyModifier(&rmod_masks.altgr, keysymMods.value(XK_Mode_switch, -1));
+ applyModifier(&rmod_masks.super, keysymMods.value(XK_Super_L, -1));
+ applyModifier(&rmod_masks.super, keysymMods.value(XK_Super_R, -1));
+ applyModifier(&rmod_masks.hyper, keysymMods.value(XK_Hyper_L, -1));
+ applyModifier(&rmod_masks.hyper, keysymMods.value(XK_Hyper_R, -1));
+
+ resolveMaskConflicts();
+}
+
+// Small helper: check if an array of xcb_keycode_t contains a certain code
+static inline bool keycodes_contains(xcb_keycode_t *codes, xcb_keycode_t which)
+{
+ while (*codes != XCB_NO_SYMBOL) {
+ if (*codes == which) return true;
+ codes++;
+ }
+ return false;
+}
+
+QXcbKeyboard::KeysymModifierMap QXcbKeyboard::keysymsToModifiers()
+{
// The core protocol does not provide a convenient way to determine the mapping
// of modifier bits. Clients must retrieve and search the modifier map to determine
// the keycodes bound to each modifier, and then retrieve and search the keyboard
// mapping to determine the keysyms bound to the keycodes. They must repeat this
// process for all modifiers whenever any part of the modifier mapping is changed.
- memset(&rmod_masks, 0, sizeof(rmod_masks));
- xcb_generic_error_t *error = 0;
- xcb_connection_t *conn = xcb_connection();
- xcb_get_modifier_mapping_cookie_t modMapCookie = xcb_get_modifier_mapping(conn);
- xcb_get_modifier_mapping_reply_t *modMapReply =
- xcb_get_modifier_mapping_reply(conn, modMapCookie, &error);
- if (error) {
+ KeysymModifierMap map;
+
+ auto modMapReply = Q_XCB_REPLY(xcb_get_modifier_mapping, xcb_connection());
+ if (!modMapReply) {
qWarning("Qt: failed to get modifier mapping");
- free(error);
- return;
+ return map;
}
// for Alt and Meta L and R are the same
@@ -1338,36 +1760,63 @@ void QXcbKeyboard::updateModifiers()
for (size_t i = 0; i < numSymbols; ++i)
modKeyCodes[i] = xcb_key_symbols_get_keycode(m_key_symbols, symbols[i]);
- xcb_keycode_t *modMap = xcb_get_modifier_mapping_keycodes(modMapReply);
- const int w = modMapReply->keycodes_per_modifier;
- for (size_t i = 0; i < numSymbols; ++i) {
- for (int bit = 0; bit < 8; ++bit) {
- uint mask = 1 << bit;
- for (int x = 0; x < w; ++x) {
- xcb_keycode_t keyCode = modMap[x + bit * w];
- xcb_keycode_t *itk = modKeyCodes[i];
- while (itk && *itk != XCB_NO_SYMBOL)
- if (*itk++ == keyCode) {
- uint sym = symbols[i];
- if ((sym == XK_Alt_L || sym == XK_Alt_R))
- rmod_masks.alt = mask;
- if ((sym == XK_Meta_L || sym == XK_Meta_R))
- rmod_masks.meta = mask;
- if (sym == XK_Mode_switch)
- rmod_masks.altgr = mask;
- if ((sym == XK_Super_L) || (sym == XK_Super_R))
- rmod_masks.super = mask;
- if ((sym == XK_Hyper_L) || (sym == XK_Hyper_R))
- rmod_masks.hyper = mask;
- }
+ xcb_keycode_t *modMap = xcb_get_modifier_mapping_keycodes(modMapReply.get());
+ const int modMapLength = xcb_get_modifier_mapping_keycodes_length(modMapReply.get());
+ /* For each modifier of "Shift, Lock, Control, Mod1, Mod2, Mod3,
+ * Mod4, and Mod5" the modifier map contains keycodes_per_modifier
+ * key codes that are associated with a modifier.
+ *
+ * As an example, take this 'xmodmap' output:
+ * xmodmap: up to 4 keys per modifier, (keycodes in parentheses):
+ *
+ * shift Shift_L (0x32), Shift_R (0x3e)
+ * lock Caps_Lock (0x42)
+ * control Control_L (0x25), Control_R (0x69)
+ * mod1 Alt_L (0x40), Alt_R (0x6c), Meta_L (0xcd)
+ * mod2 Num_Lock (0x4d)
+ * mod3
+ * mod4 Super_L (0x85), Super_R (0x86), Super_L (0xce), Hyper_L (0xcf)
+ * mod5 ISO_Level3_Shift (0x5c), Mode_switch (0xcb)
+ *
+ * The corresponding raw modifier map would contain keycodes for:
+ * Shift_L (0x32), Shift_R (0x3e), 0, 0,
+ * Caps_Lock (0x42), 0, 0, 0,
+ * Control_L (0x25), Control_R (0x69), 0, 0,
+ * Alt_L (0x40), Alt_R (0x6c), Meta_L (0xcd), 0,
+ * Num_Lock (0x4d), 0, 0, 0,
+ * 0,0,0,0,
+ * Super_L (0x85), Super_R (0x86), Super_L (0xce), Hyper_L (0xcf),
+ * ISO_Level3_Shift (0x5c), Mode_switch (0xcb), 0, 0
+ */
+
+ /* Create a map between a modifier keysym (as per the symbols array)
+ * and the modifier bit it's associated with (if any).
+ * As modMap contains key codes, search modKeyCodes for a match;
+ * if one is found we can look up the associated keysym.
+ * Together with the modifier index this will be used
+ * to compute a mapping between X modifier bits and Qt's
+ * modifiers (Alt, Ctrl etc). */
+ for (int i = 0; i < modMapLength; i++) {
+ if (modMap[i] == XCB_NO_SYMBOL)
+ continue;
+ // Get key symbol for key code
+ for (size_t k = 0; k < numSymbols; k++) {
+ if (modKeyCodes[k] && keycodes_contains(modKeyCodes[k], modMap[i])) {
+ // Key code is for modifier. Record mapping
+ xcb_keysym_t sym = symbols[k];
+ /* As per modMap layout explanation above, dividing
+ * by keycodes_per_modifier gives the 'row' in the
+ * modifier map, which in turn is the modifier bit. */
+ map[sym] = i / modMapReply->keycodes_per_modifier;
+ break;
}
}
}
for (size_t i = 0; i < numSymbols; ++i)
free(modKeyCodes[i]);
- free(modMapReply);
- resolveMaskConflicts();
+
+ return map;
}
void QXcbKeyboard::resolveMaskConflicts()
@@ -1449,8 +1898,6 @@ private:
void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type, xcb_keycode_t code,
quint16 state, xcb_timestamp_t time)
{
- Q_XCB_NOOP(connection());
-
if (!m_config)
return;
@@ -1471,30 +1918,6 @@ void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type,
updateXKBStateFromState(kb_state, state);
xcb_keysym_t sym = xkb_state_key_get_one_sym(kb_state, code);
-
- QPlatformInputContext *inputContext = QGuiApplicationPrivate::platformIntegration()->inputContext();
- QMetaMethod method;
-
- if (inputContext) {
- int methodIndex = inputContext->metaObject()->indexOfMethod("x11FilterEvent(uint,uint,uint,bool)");
- if (methodIndex != -1)
- method = inputContext->metaObject()->method(methodIndex);
- }
-
- if (method.isValid()) {
- bool retval = false;
- method.invoke(inputContext, Qt::DirectConnection,
- Q_RETURN_ARG(bool, retval),
- Q_ARG(uint, sym),
- Q_ARG(uint, code),
- Q_ARG(uint, state),
- Q_ARG(bool, type == QEvent::KeyPress));
- if (retval) {
- xkb_state_unref(kb_state);
- return;
- }
- }
-
QString string = lookupString(kb_state, code);
// Ιf control modifier is set we should prefer latin character, this is
@@ -1526,6 +1949,7 @@ void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type,
}
bool filtered = false;
+ QPlatformInputContext *inputContext = QGuiApplicationPrivate::platformIntegration()->inputContext();
if (inputContext) {
QKeyEvent event(type, qtcode, modifiers, code, sym, state, string, isAutoRepeat, string.length());
event.setTimestamp(time);
@@ -1548,16 +1972,7 @@ void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type,
if (isAutoRepeat && type == QEvent::KeyRelease) {
// since we removed it from the event queue using checkEvent we need to send the key press here
filtered = false;
- if (method.isValid()) {
- method.invoke(inputContext, Qt::DirectConnection,
- Q_RETURN_ARG(bool, filtered),
- Q_ARG(uint, sym),
- Q_ARG(uint, code),
- Q_ARG(uint, state),
- Q_ARG(bool, true));
- }
-
- if (!filtered && inputContext) {
+ if (inputContext) {
QKeyEvent event(QEvent::KeyPress, qtcode, modifiers, code, sym, state, string, isAutoRepeat, string.length());
event.setTimestamp(time);
filtered = inputContext->filterEvent(&event);
diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.h b/src/plugins/platforms/xcb/qxcbkeyboard.h
index 74f9da0353..7ee8e9e90d 100644
--- a/src/plugins/platforms/xcb/qxcbkeyboard.h
+++ b/src/plugins/platforms/xcb/qxcbkeyboard.h
@@ -74,7 +74,7 @@ public:
void updateXKBMods();
quint32 xkbModMask(quint16 state);
void updateXKBStateFromCore(quint16 state);
-#ifdef XCB_USE_XINPUT22
+#if QT_CONFIG(xinput2)
void updateXKBStateFromXI(void *modInfo, void *groupInfo);
#endif
#if QT_CONFIG(xkb)
@@ -94,8 +94,13 @@ protected:
void readXKBConfig();
void clearXKBConfig();
+#if QT_CONFIG(xcb_xlib)
+ struct xkb_keymap *keymapFromCore();
+#endif
// when XKEYBOARD not present on the X server
void updateModifiers();
+ typedef QMap<xcb_keysym_t, int> KeysymModifierMap;
+ KeysymModifierMap keysymsToModifiers();
// when XKEYBOARD is present on the X server
void updateVModMapping();
void updateVModToRModMapping();
@@ -107,6 +112,7 @@ private:
void updateXKBStateFromState(struct xkb_state *kb_state, quint16 state);
bool m_config = false;
+ bool m_keymap_is_core = false;
xcb_keycode_t m_autorepeat_code = 0;
struct xkb_context *xkb_context = nullptr;
diff --git a/src/plugins/platforms/xcb/qxcbmime.cpp b/src/plugins/platforms/xcb/qxcbmime.cpp
index 2848446098..58e2e8c0e6 100644
--- a/src/plugins/platforms/xcb/qxcbmime.cpp
+++ b/src/plugins/platforms/xcb/qxcbmime.cpp
@@ -160,9 +160,10 @@ QVector<xcb_atom_t> QXcbMime::mimeAtomsForFormat(QXcbConnection *connection, con
return atoms;
}
-QVariant QXcbMime::mimeConvertToFormat(QXcbConnection *connection, xcb_atom_t a, const QByteArray &data, const QString &format,
+QVariant QXcbMime::mimeConvertToFormat(QXcbConnection *connection, xcb_atom_t a, const QByteArray &d, const QString &format,
QVariant::Type requestedType, const QByteArray &encoding)
{
+ QByteArray data = d;
QString atomName = mimeAtomToString(connection, a);
// qDebug() << "mimeConvertDataToFormat" << format << atomName << data;
@@ -182,8 +183,11 @@ QVariant QXcbMime::mimeConvertToFormat(QXcbConnection *connection, xcb_atom_t a,
// special cases for string types
if (format == QLatin1String("text/plain")) {
- if (a == connection->atom(QXcbAtom::UTF8_STRING))
+ if (data.endsWith('\0'))
+ data.chop(1);
+ if (a == connection->atom(QXcbAtom::UTF8_STRING)) {
return QString::fromUtf8(data);
+ }
if (a == XCB_ATOM_STRING ||
a == connection->atom(QXcbAtom::TEXT))
return QString::fromLatin1(data);
@@ -221,6 +225,9 @@ QVariant QXcbMime::mimeConvertToFormat(QXcbConnection *connection, xcb_atom_t a,
}
}
}
+ // 8 byte encoding, remove a possible 0 at the end
+ if (data.endsWith('\0'))
+ data.chop(1);
}
if (atomName == format)
diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp
index 97dcb8f328..22d90d6ac2 100644
--- a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp
+++ b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp
@@ -63,6 +63,10 @@
#include "qxcbnativeinterfacehandler.h"
+#if QT_CONFIG(vulkan)
+#include "qxcbvulkanwindow.h"
+#endif
+
QT_BEGIN_NAMESPACE
// return QXcbNativeInterface::ResourceType for the key.
@@ -78,7 +82,11 @@ static int resourceType(const QByteArray &key)
QByteArrayLiteral("rootwindow"),
QByteArrayLiteral("subpixeltype"), QByteArrayLiteral("antialiasingenabled"),
QByteArrayLiteral("atspibus"),
- QByteArrayLiteral("compositingenabled")
+ QByteArrayLiteral("compositingenabled"),
+ QByteArrayLiteral("vksurface"),
+ QByteArrayLiteral("generatepeekerid"),
+ QByteArrayLiteral("removepeekerid"),
+ QByteArrayLiteral("peekeventqueue")
};
const QByteArray *end = names + sizeof(names) / sizeof(names[0]);
const QByteArray *result = std::find(names, end, key);
@@ -93,7 +101,7 @@ QXcbNativeInterface::QXcbNativeInterface() :
static inline QXcbSystemTrayTracker *systemTrayTracker(const QScreen *s)
{
if (!s)
- return Q_NULLPTR;
+ return nullptr;
return static_cast<const QXcbScreen *>(s->handle())->connection()->systemTrayTracker();
}
@@ -117,28 +125,19 @@ xcb_window_t QXcbNativeInterface::locateSystemTray(xcb_connection_t *conn, const
{
if (m_sysTraySelectionAtom == XCB_ATOM_NONE) {
const QByteArray net_sys_tray = QString::fromLatin1("_NET_SYSTEM_TRAY_S%1").arg(screen->screenNumber()).toLatin1();
- xcb_intern_atom_cookie_t intern_c =
- xcb_intern_atom_unchecked(conn, true, net_sys_tray.length(), net_sys_tray);
-
- xcb_intern_atom_reply_t *intern_r = xcb_intern_atom_reply(conn, intern_c, 0);
-
+ auto intern_r = Q_XCB_REPLY_UNCHECKED(xcb_intern_atom, conn,
+ true, net_sys_tray.length(), net_sys_tray);
if (!intern_r)
return XCB_WINDOW_NONE;
m_sysTraySelectionAtom = intern_r->atom;
- free(intern_r);
}
- xcb_get_selection_owner_cookie_t sel_owner_c = xcb_get_selection_owner_unchecked(conn, m_sysTraySelectionAtom);
- xcb_get_selection_owner_reply_t *sel_owner_r = xcb_get_selection_owner_reply(conn, sel_owner_c, 0);
-
+ auto sel_owner_r = Q_XCB_REPLY_UNCHECKED(xcb_get_selection_owner, conn, m_sysTraySelectionAtom);
if (!sel_owner_r)
return XCB_WINDOW_NONE;
- xcb_window_t selection_window = sel_owner_r->owner;
- free(sel_owner_r);
-
- return selection_window;
+ return sel_owner_r->owner;
}
bool QXcbNativeInterface::systrayVisualHasAlphaChannel()
@@ -195,7 +194,7 @@ void *QXcbNativeInterface::nativeResourceForScreen(const QByteArray &resourceStr
{
if (!screen) {
qWarning("nativeResourceForScreen: null screen");
- return Q_NULLPTR;
+ return nullptr;
}
QByteArray lowerCaseResource = resourceString.toLower();
@@ -237,7 +236,7 @@ void *QXcbNativeInterface::nativeResourceForScreen(const QByteArray &resourceStr
break;
case CompositingEnabled:
if (QXcbVirtualDesktop *vd = xcbScreen->virtualDesktop())
- result = vd->compositingActive() ? this : Q_NULLPTR;
+ result = vd->compositingActive() ? this : nullptr;
break;
default:
break;
@@ -262,6 +261,14 @@ void *QXcbNativeInterface::nativeResourceForWindow(const QByteArray &resourceStr
case Screen:
result = screenForWindow(window);
break;
+#if QT_CONFIG(vulkan)
+ case VkSurface:
+ if (window->surfaceType() == QSurface::VulkanSurface && window->handle()) {
+ // return a pointer to the VkSurfaceKHR value, not the value itself
+ result = static_cast<QXcbVulkanWindow *>(window->handle())->surface();
+ }
+ break;
+#endif
default:
break;
}
@@ -287,7 +294,7 @@ void *QXcbNativeInterface::nativeResourceForCursor(const QByteArray &resource, c
}
}
}
- return Q_NULLPTR;
+ return nullptr;
}
#endif // !QT_NO_CURSOR
@@ -300,6 +307,13 @@ QPlatformNativeInterface::NativeResourceForIntegrationFunction QXcbNativeInterfa
if (lowerCaseResource == "setstartupid")
return NativeResourceForIntegrationFunction(setStartupId);
+ if (lowerCaseResource == "generatepeekerid")
+ return NativeResourceForIntegrationFunction(generatePeekerId);
+ if (lowerCaseResource == "removepeekerid")
+ return NativeResourceForIntegrationFunction(removePeekerId);
+ if (lowerCaseResource == "peekeventqueue")
+ return NativeResourceForIntegrationFunction(peekEventQueue);
+
return 0;
}
@@ -309,7 +323,7 @@ QPlatformNativeInterface::NativeResourceForContextFunction QXcbNativeInterface::
QPlatformNativeInterface::NativeResourceForContextFunction func = handlerNativeResourceFunctionForContext(lowerCaseResource);
if (func)
return func;
- return Q_NULLPTR;
+ return nullptr;
}
QPlatformNativeInterface::NativeResourceForScreenFunction QXcbNativeInterface::nativeResourceFunctionForScreen(const QByteArray &resource)
@@ -376,13 +390,13 @@ QFunctionPointer QXcbNativeInterface::platformFunction(const QByteArray &functio
if (function == QXcbScreenFunctions::virtualDesktopNumberIdentifier())
return QFunctionPointer(QXcbScreenFunctions::VirtualDesktopNumber(QXcbScreen::virtualDesktopNumberStatic));
- return Q_NULLPTR;
+ return nullptr;
}
void *QXcbNativeInterface::appTime(const QXcbScreen *screen)
{
if (!screen)
- return Q_NULLPTR;
+ return nullptr;
return reinterpret_cast<void *>(quintptr(screen->connection()->time()));
}
@@ -390,7 +404,7 @@ void *QXcbNativeInterface::appTime(const QXcbScreen *screen)
void *QXcbNativeInterface::appUserTime(const QXcbScreen *screen)
{
if (!screen)
- return Q_NULLPTR;
+ return nullptr;
return reinterpret_cast<void *>(quintptr(screen->connection()->netWmUserTime()));
}
@@ -398,7 +412,7 @@ void *QXcbNativeInterface::appUserTime(const QXcbScreen *screen)
void *QXcbNativeInterface::getTimestamp(const QXcbScreen *screen)
{
if (!screen)
- return Q_NULLPTR;
+ return nullptr;
return reinterpret_cast<void *>(quintptr(screen->connection()->getTimestamp()));
}
@@ -438,7 +452,7 @@ void *QXcbNativeInterface::display()
if (defaultConnection)
return defaultConnection->xlib_display();
#endif
- return Q_NULLPTR;
+ return nullptr;
}
void *QXcbNativeInterface::connection()
@@ -453,21 +467,13 @@ void *QXcbNativeInterface::atspiBus()
QXcbConnection *defaultConnection = integration->defaultConnection();
if (defaultConnection) {
xcb_atom_t atspiBusAtom = defaultConnection->internAtom("AT_SPI_BUS");
- xcb_get_property_cookie_t cookie = Q_XCB_CALL2(xcb_get_property(
- defaultConnection->xcb_connection(),
- false, defaultConnection->rootWindow(),
- atspiBusAtom, XCB_ATOM_STRING, 0, 128),
- defaultConnection);
- xcb_get_property_reply_t *reply = Q_XCB_CALL2(xcb_get_property_reply(
- defaultConnection->xcb_connection(),
- cookie, 0),
- defaultConnection);
+ auto reply = Q_XCB_REPLY(xcb_get_property, defaultConnection->xcb_connection(),
+ false, defaultConnection->rootWindow(),
+ atspiBusAtom, XCB_ATOM_STRING, 0, 128);
Q_ASSERT(!reply->bytes_after);
- char *data = (char *)xcb_get_property_value(reply);
- int length = xcb_get_property_value_length(reply);
- QByteArray *busAddress = new QByteArray(data, length);
- free(reply);
- return busAddress;
+ char *data = (char *)xcb_get_property_value(reply.get());
+ int length = xcb_get_property_value_length(reply.get());
+ return new QByteArray(data, length);
}
return 0;
}
@@ -486,6 +492,25 @@ void QXcbNativeInterface::setAppUserTime(QScreen* screen, xcb_timestamp_t time)
}
}
+qint32 QXcbNativeInterface::generatePeekerId()
+{
+ QXcbIntegration *integration = QXcbIntegration::instance();
+ return integration->defaultConnection()->generatePeekerId();
+}
+
+bool QXcbNativeInterface::removePeekerId(qint32 peekerId)
+{
+ QXcbIntegration *integration = QXcbIntegration::instance();
+ return integration->defaultConnection()->removePeekerId(peekerId);
+}
+
+bool QXcbNativeInterface::peekEventQueue(QXcbConnection::PeekerCallback peeker, void *peekerData,
+ QXcbConnection::PeekOptions option, qint32 peekerId)
+{
+ QXcbIntegration *integration = QXcbIntegration::instance();
+ return integration->defaultConnection()->peekEventQueue(peeker, peekerData, option, peekerId);
+}
+
void QXcbNativeInterface::setStartupId(const char *data)
{
QByteArray startupId(data);
@@ -500,10 +525,10 @@ QXcbScreen *QXcbNativeInterface::qPlatformScreenForWindow(QWindow *window)
QXcbScreen *screen;
if (window) {
QScreen *qs = window->screen();
- screen = static_cast<QXcbScreen *>(qs ? qs->handle() : Q_NULLPTR);
+ screen = static_cast<QXcbScreen *>(qs ? qs->handle() : nullptr);
} else {
QScreen *qs = QGuiApplication::primaryScreen();
- screen = static_cast<QXcbScreen *>(qs ? qs->handle() : Q_NULLPTR);
+ screen = static_cast<QXcbScreen *>(qs ? qs->handle() : nullptr);
}
return screen;
}
@@ -512,23 +537,23 @@ void *QXcbNativeInterface::displayForWindow(QWindow *window)
{
#if QT_CONFIG(xcb_xlib)
QXcbScreen *screen = qPlatformScreenForWindow(window);
- return screen ? screen->connection()->xlib_display() : Q_NULLPTR;
+ return screen ? screen->connection()->xlib_display() : nullptr;
#else
Q_UNUSED(window);
- return Q_NULLPTR;
+ return nullptr;
#endif
}
void *QXcbNativeInterface::connectionForWindow(QWindow *window)
{
QXcbScreen *screen = qPlatformScreenForWindow(window);
- return screen ? screen->xcb_connection() : Q_NULLPTR;
+ return screen ? screen->xcb_connection() : nullptr;
}
void *QXcbNativeInterface::screenForWindow(QWindow *window)
{
QXcbScreen *screen = qPlatformScreenForWindow(window);
- return screen ? screen->screen() : Q_NULLPTR;
+ return screen ? screen->screen() : nullptr;
}
void QXcbNativeInterface::addHandler(QXcbNativeInterfaceHandler *handler)
@@ -550,7 +575,7 @@ QPlatformNativeInterface::NativeResourceForIntegrationFunction QXcbNativeInterfa
if (result)
return result;
}
- return Q_NULLPTR;
+ return nullptr;
}
QPlatformNativeInterface::NativeResourceForContextFunction QXcbNativeInterface::handlerNativeResourceFunctionForContext(const QByteArray &resource) const
@@ -561,7 +586,7 @@ QPlatformNativeInterface::NativeResourceForContextFunction QXcbNativeInterface::
if (result)
return result;
}
- return Q_NULLPTR;
+ return nullptr;
}
QPlatformNativeInterface::NativeResourceForScreenFunction QXcbNativeInterface::handlerNativeResourceFunctionForScreen(const QByteArray &resource) const
@@ -572,7 +597,7 @@ QPlatformNativeInterface::NativeResourceForScreenFunction QXcbNativeInterface::h
if (result)
return result;
}
- return Q_NULLPTR;
+ return nullptr;
}
QPlatformNativeInterface::NativeResourceForWindowFunction QXcbNativeInterface::handlerNativeResourceFunctionForWindow(const QByteArray &resource) const
@@ -583,7 +608,7 @@ QPlatformNativeInterface::NativeResourceForWindowFunction QXcbNativeInterface::h
if (result)
return result;
}
- return Q_NULLPTR;
+ return nullptr;
}
QPlatformNativeInterface::NativeResourceForBackingStoreFunction QXcbNativeInterface::handlerNativeResourceFunctionForBackingStore(const QByteArray &resource) const
@@ -594,7 +619,7 @@ QPlatformNativeInterface::NativeResourceForBackingStoreFunction QXcbNativeInterf
if (result)
return result;
}
- return Q_NULLPTR;
+ return nullptr;
}
QFunctionPointer QXcbNativeInterface::handlerPlatformFunction(const QByteArray &function) const
@@ -605,7 +630,7 @@ QFunctionPointer QXcbNativeInterface::handlerPlatformFunction(const QByteArray &
if (func)
return func;
}
- return Q_NULLPTR;
+ return nullptr;
}
void *QXcbNativeInterface::handlerNativeResourceForIntegration(const QByteArray &resource) const
@@ -613,7 +638,7 @@ void *QXcbNativeInterface::handlerNativeResourceForIntegration(const QByteArray
NativeResourceForIntegrationFunction func = handlerNativeResourceFunctionForIntegration(resource);
if (func)
return func();
- return Q_NULLPTR;
+ return nullptr;
}
void *QXcbNativeInterface::handlerNativeResourceForContext(const QByteArray &resource, QOpenGLContext *context) const
@@ -621,7 +646,7 @@ void *QXcbNativeInterface::handlerNativeResourceForContext(const QByteArray &res
NativeResourceForContextFunction func = handlerNativeResourceFunctionForContext(resource);
if (func)
return func(context);
- return Q_NULLPTR;
+ return nullptr;
}
void *QXcbNativeInterface::handlerNativeResourceForScreen(const QByteArray &resource, QScreen *screen) const
@@ -629,7 +654,7 @@ void *QXcbNativeInterface::handlerNativeResourceForScreen(const QByteArray &reso
NativeResourceForScreenFunction func = handlerNativeResourceFunctionForScreen(resource);
if (func)
return func(screen);
- return Q_NULLPTR;
+ return nullptr;
}
void *QXcbNativeInterface::handlerNativeResourceForWindow(const QByteArray &resource, QWindow *window) const
@@ -637,7 +662,7 @@ void *QXcbNativeInterface::handlerNativeResourceForWindow(const QByteArray &reso
NativeResourceForWindowFunction func = handlerNativeResourceFunctionForWindow(resource);
if (func)
return func(window);
- return Q_NULLPTR;
+ return nullptr;
}
void *QXcbNativeInterface::handlerNativeResourceForBackingStore(const QByteArray &resource, QBackingStore *backingStore) const
@@ -645,7 +670,63 @@ void *QXcbNativeInterface::handlerNativeResourceForBackingStore(const QByteArray
NativeResourceForBackingStoreFunction func = handlerNativeResourceFunctionForBackingStore(resource);
if (func)
return func(backingStore);
- return Q_NULLPTR;
+ return nullptr;
+}
+
+static void dumpNativeWindowsRecursion(const QXcbConnection *connection, xcb_window_t window,
+ int level, QTextStream &str)
+{
+ if (level)
+ str << QByteArray(2 * level, ' ');
+
+ xcb_connection_t *conn = connection->xcb_connection();
+ auto geomReply = Q_XCB_REPLY(xcb_get_geometry, conn, window);
+ if (!geomReply)
+ return;
+ const QRect geom(geomReply->x, geomReply->y, geomReply->width, geomReply->height);
+ if (!geom.isValid() || (geom.width() <= 3 && geom.height() <= 3))
+ return; // Skip helper/dummy windows.
+ str << "0x";
+ const int oldFieldWidth = str.fieldWidth();
+ const QChar oldPadChar =str.padChar();
+ str.setFieldWidth(8);
+ str.setPadChar(QLatin1Char('0'));
+ str << hex << window;
+ str.setFieldWidth(oldFieldWidth);
+ str.setPadChar(oldPadChar);
+ str << dec << " \""
+ << QXcbWindow::windowTitle(connection, window) << "\" "
+ << geom.width() << 'x' << geom.height() << forcesign << geom.x() << geom.y()
+ << noforcesign << '\n';
+
+ auto reply = Q_XCB_REPLY(xcb_query_tree, conn, window);
+ if (reply) {
+ const int count = xcb_query_tree_children_length(reply.get());
+ const xcb_window_t *children = xcb_query_tree_children(reply.get());
+ for (int i = 0; i < count; ++i)
+ dumpNativeWindowsRecursion(connection, children[i], level + 1, str);
+ }
+}
+
+QString QXcbNativeInterface::dumpConnectionNativeWindows(const QXcbConnection *connection, WId root) const
+{
+ QString result;
+ QTextStream str(&result);
+ if (root) {
+ dumpNativeWindowsRecursion(connection, xcb_window_t(root), 0, str);
+ } else {
+ for (const QXcbScreen *screen : connection->screens()) {
+ str << "Screen: \"" << screen->name() << "\"\n";
+ dumpNativeWindowsRecursion(connection, screen->root(), 0, str);
+ str << '\n';
+ }
+ }
+ return result;
+}
+
+QString QXcbNativeInterface::dumpNativeWindows(WId root) const
+{
+ return dumpConnectionNativeWindows(QXcbIntegration::instance()->defaultConnection(), root);
}
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.h b/src/plugins/platforms/xcb/qxcbnativeinterface.h
index 4186d77f4d..6a752c68ca 100644
--- a/src/plugins/platforms/xcb/qxcbnativeinterface.h
+++ b/src/plugins/platforms/xcb/qxcbnativeinterface.h
@@ -46,12 +46,11 @@
#include <QtCore/QRect>
#include "qxcbexport.h"
+#include "qxcbconnection.h"
QT_BEGIN_NAMESPACE
-class QWidget;
class QXcbScreen;
-class QXcbConnection;
class QXcbNativeInterfaceHandler;
class Q_XCB_EXPORT QXcbNativeInterface : public QPlatformNativeInterface
@@ -73,7 +72,11 @@ public:
ScreenSubpixelType,
ScreenAntialiasingEnabled,
AtspiBus,
- CompositingEnabled
+ CompositingEnabled,
+ VkSurface,
+ GeneratePeekerId,
+ RemovePeekerId,
+ PeekEventQueue
};
QXcbNativeInterface();
@@ -113,11 +116,19 @@ public:
static void setAppTime(QScreen *screen, xcb_timestamp_t time);
static void setAppUserTime(QScreen *screen, xcb_timestamp_t time);
+ static qint32 generatePeekerId();
+ static bool removePeekerId(qint32 peekerId);
+ static bool peekEventQueue(QXcbConnection::PeekerCallback peeker, void *peekerData = nullptr,
+ QXcbConnection::PeekOptions option = QXcbConnection::PeekDefault,
+ qint32 peekerId = -1);
+
Q_INVOKABLE bool systemTrayAvailable(const QScreen *screen) const;
Q_INVOKABLE void setParentRelativeBackPixmap(QWindow *window);
Q_INVOKABLE bool systrayVisualHasAlphaChannel();
Q_INVOKABLE bool requestSystemTrayWindowDock(const QWindow *window);
Q_INVOKABLE QRect systemTrayWindowGlobalGeometry(const QWindow *window);
+ Q_INVOKABLE QString dumpConnectionNativeWindows(const QXcbConnection *connection, WId root) const;
+ Q_INVOKABLE QString dumpNativeWindows(WId root = 0) const;
void addHandler(QXcbNativeInterfaceHandler *handler);
void removeHandler(QXcbNativeInterfaceHandler *handler);
diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp
index 5e136b5d7e..49bf5181cc 100644
--- a/src/plugins/platforms/xcb/qxcbscreen.cpp
+++ b/src/plugins/platforms/xcb/qxcbscreen.cpp
@@ -65,6 +65,59 @@ QXcbVirtualDesktop::QXcbVirtualDesktop(QXcbConnection *connection, xcb_screen_t
m_compositingActive = connection->getSelectionOwner(m_net_wm_cm_atom);
m_workArea = getWorkArea();
+
+ readXResources();
+
+ auto rootAttribs = Q_XCB_REPLY_UNCHECKED(xcb_get_window_attributes, xcb_connection(),
+ screen->root);
+ const quint32 existingEventMask = !rootAttribs ? 0 : rootAttribs->your_event_mask;
+
+ const quint32 mask = XCB_CW_EVENT_MASK;
+ const quint32 values[] = {
+ // XCB_CW_EVENT_MASK
+ XCB_EVENT_MASK_ENTER_WINDOW
+ | XCB_EVENT_MASK_LEAVE_WINDOW
+ | XCB_EVENT_MASK_PROPERTY_CHANGE
+ | XCB_EVENT_MASK_STRUCTURE_NOTIFY // for the "MANAGER" atom (system tray notification).
+ | existingEventMask // don't overwrite the event mask on the root window
+ };
+
+ xcb_change_window_attributes(xcb_connection(), screen->root, mask, values);
+
+ auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, xcb_connection(),
+ false, screen->root,
+ atom(QXcbAtom::_NET_SUPPORTING_WM_CHECK),
+ XCB_ATOM_WINDOW, 0, 1024);
+ if (reply && reply->format == 32 && reply->type == XCB_ATOM_WINDOW) {
+ xcb_window_t windowManager = *((xcb_window_t *)xcb_get_property_value(reply.get()));
+
+ if (windowManager != XCB_WINDOW_NONE)
+ m_windowManagerName = QXcbWindow::windowTitle(connection, windowManager);
+ }
+
+ const xcb_query_extension_reply_t *sync_reply = xcb_get_extension_data(xcb_connection(), &xcb_sync_id);
+ if (!sync_reply || !sync_reply->present)
+ m_syncRequestSupported = false;
+ else
+ m_syncRequestSupported = true;
+
+ xcb_depth_iterator_t depth_iterator =
+ xcb_screen_allowed_depths_iterator(screen);
+
+ while (depth_iterator.rem) {
+ xcb_depth_t *depth = depth_iterator.data;
+ xcb_visualtype_iterator_t visualtype_iterator =
+ xcb_depth_visuals_iterator(depth);
+
+ while (visualtype_iterator.rem) {
+ xcb_visualtype_t *visualtype = visualtype_iterator.data;
+ m_visuals.insert(visualtype->visual_id, *visualtype);
+ m_visualDepths.insert(visualtype->visual_id, depth->depth);
+ xcb_visualtype_next(&visualtype_iterator);
+ }
+
+ xcb_depth_next(&depth_iterator);
+ }
}
QXcbVirtualDesktop::~QXcbVirtualDesktop()
@@ -79,7 +132,7 @@ QXcbScreen *QXcbVirtualDesktop::screenAt(const QPoint &pos) const
if (screen->virtualDesktop() == this && screen->geometry().contains(pos))
return screen;
}
- return Q_NULLPTR;
+ return nullptr;
}
void QXcbVirtualDesktop::addScreen(QPlatformScreen *s)
@@ -123,18 +176,33 @@ void QXcbVirtualDesktop::subscribeToXFixesSelectionNotify()
const uint32_t mask = XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER |
XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY |
XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE;
- Q_XCB_CALL(xcb_xfixes_select_selection_input_checked(xcb_connection(), connection()->getQtSelectionOwner(), m_net_wm_cm_atom, mask));
+ xcb_xfixes_select_selection_input_checked(xcb_connection(), connection()->getQtSelectionOwner(), m_net_wm_cm_atom, mask);
}
}
+/*! \internal
+
+ Using _NET_WORKAREA to calculate the available desktop geometry on multi-head systems (systems
+ with more than one monitor) is unreliable. Different WMs have different interpretations of what
+ _NET_WORKAREA means with multiple attached monitors. This gets worse when monitors have
+ different dimensions and/or screens are not virtually aligned. In Qt we want the available
+ geometry per monitor (QScreen), not desktop (represented by _NET_WORKAREA). WM specification
+ does not have an atom for this. Thus, QScreen is limted by the lack of support from the
+ underlying system.
+
+ One option could be that Qt does WM's job of calculating this by subtracting geometries of
+ _NET_WM_STRUT_PARTIAL and windows where _NET_WM_WINDOW_TYPE(ATOM) = _NET_WM_WINDOW_TYPE_DOCK.
+ But this won't work on Gnome 3 shell as it seems that on this desktop environment the tool panel
+ is painted directly on the root window. Maybe there is some Gnome/GTK API that could be used
+ to get height of the panel, but I did not find one. Maybe other WMs have their own tricks, so
+ the reliability of this approach is questionable.
+ */
QRect QXcbVirtualDesktop::getWorkArea() const
{
QRect r;
- xcb_get_property_reply_t * workArea =
- xcb_get_property_reply(xcb_connection(),
- xcb_get_property_unchecked(xcb_connection(), false, screen()->root,
- atom(QXcbAtom::_NET_WORKAREA),
- XCB_ATOM_CARDINAL, 0, 1024), NULL);
+ auto workArea = Q_XCB_REPLY_UNCHECKED(xcb_get_property, xcb_connection(), false, screen()->root,
+ atom(QXcbAtom::_NET_WORKAREA),
+ XCB_ATOM_CARDINAL, 0, 1024);
if (workArea && workArea->type == XCB_ATOM_CARDINAL && workArea->format == 32 && workArea->value_len >= 4) {
// If workArea->value_len > 4, the remaining ones seem to be for WM's virtual desktops
// (don't mess with QXcbVirtualDesktop which represents an X screen).
@@ -142,12 +210,11 @@ QRect QXcbVirtualDesktop::getWorkArea() const
// "docked" panel (with _NET_WM_STRUT_PARTIAL atom set) on just one desktop.
// But for now just assume the first 4 values give us the geometry of the
// "work area", AKA "available geometry"
- uint32_t *geom = (uint32_t*)xcb_get_property_value(workArea);
+ uint32_t *geom = (uint32_t*)xcb_get_property_value(workArea.get());
r = QRect(geom[0], geom[1], geom[2], geom[3]);
} else {
r = QRect(QPoint(), size());
}
- free(workArea);
return r;
}
@@ -167,6 +234,170 @@ static inline QSizeF sizeInMillimeters(const QSize &size, const QDpi &dpi)
Q_MM_PER_INCH * size.height() / dpi.second);
}
+bool QXcbVirtualDesktop::xResource(const QByteArray &identifier,
+ const QByteArray &expectedIdentifier,
+ QByteArray& stringValue)
+{
+ if (identifier.startsWith(expectedIdentifier)) {
+ stringValue = identifier.mid(expectedIdentifier.size());
+ return true;
+ }
+ return false;
+}
+
+static bool parseXftInt(const QByteArray& stringValue, int *value)
+{
+ Q_ASSERT(value != 0);
+ bool ok;
+ *value = stringValue.toInt(&ok);
+ return ok;
+}
+
+static QFontEngine::HintStyle parseXftHintStyle(const QByteArray& stringValue)
+{
+ if (stringValue == "hintfull")
+ return QFontEngine::HintFull;
+ else if (stringValue == "hintnone")
+ return QFontEngine::HintNone;
+ else if (stringValue == "hintmedium")
+ return QFontEngine::HintMedium;
+ else if (stringValue == "hintslight")
+ return QFontEngine::HintLight;
+
+ return QFontEngine::HintStyle(-1);
+}
+
+static QFontEngine::SubpixelAntialiasingType parseXftRgba(const QByteArray& stringValue)
+{
+ if (stringValue == "none")
+ return QFontEngine::Subpixel_None;
+ else if (stringValue == "rgb")
+ return QFontEngine::Subpixel_RGB;
+ else if (stringValue == "bgr")
+ return QFontEngine::Subpixel_BGR;
+ else if (stringValue == "vrgb")
+ return QFontEngine::Subpixel_VRGB;
+ else if (stringValue == "vbgr")
+ return QFontEngine::Subpixel_VBGR;
+
+ return QFontEngine::SubpixelAntialiasingType(-1);
+}
+
+void QXcbVirtualDesktop::readXResources()
+{
+ int offset = 0;
+ QByteArray resources;
+ while (true) {
+ auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, xcb_connection(),
+ false, screen()->root,
+ XCB_ATOM_RESOURCE_MANAGER,
+ XCB_ATOM_STRING, offset/4, 8192);
+ bool more = false;
+ if (reply && reply->format == 8 && reply->type == XCB_ATOM_STRING) {
+ resources += QByteArray((const char *)xcb_get_property_value(reply.get()), xcb_get_property_value_length(reply.get()));
+ offset += xcb_get_property_value_length(reply.get());
+ more = reply->bytes_after != 0;
+ }
+
+ if (!more)
+ break;
+ }
+
+ QList<QByteArray> split = resources.split('\n');
+ for (int i = 0; i < split.size(); ++i) {
+ const QByteArray &r = split.at(i);
+ int value;
+ QByteArray stringValue;
+ if (xResource(r, "Xft.dpi:\t", stringValue)) {
+ if (parseXftInt(stringValue, &value))
+ m_forcedDpi = value;
+ } else if (xResource(r, "Xft.hintstyle:\t", stringValue)) {
+ m_hintStyle = parseXftHintStyle(stringValue);
+ } else if (xResource(r, "Xft.antialias:\t", stringValue)) {
+ if (parseXftInt(stringValue, &value))
+ m_antialiasingEnabled = value;
+ } else if (xResource(r, "Xft.rgba:\t", stringValue)) {
+ m_subpixelType = parseXftRgba(stringValue);
+ }
+ }
+}
+
+QSurfaceFormat QXcbVirtualDesktop::surfaceFormatFor(const QSurfaceFormat &format) const
+{
+ const xcb_visualid_t xcb_visualid = connection()->hasDefaultVisualId() ? connection()->defaultVisualId()
+ : screen()->root_visual;
+ const xcb_visualtype_t *xcb_visualtype = visualForId(xcb_visualid);
+
+ const int redSize = qPopulationCount(xcb_visualtype->red_mask);
+ const int greenSize = qPopulationCount(xcb_visualtype->green_mask);
+ const int blueSize = qPopulationCount(xcb_visualtype->blue_mask);
+
+ QSurfaceFormat result = format;
+
+ if (result.redBufferSize() < 0)
+ result.setRedBufferSize(redSize);
+
+ if (result.greenBufferSize() < 0)
+ result.setGreenBufferSize(greenSize);
+
+ if (result.blueBufferSize() < 0)
+ result.setBlueBufferSize(blueSize);
+
+ return result;
+}
+
+const xcb_visualtype_t *QXcbVirtualDesktop::visualForFormat(const QSurfaceFormat &format) const
+{
+ const xcb_visualtype_t *candidate = nullptr;
+
+ for (const xcb_visualtype_t &xcb_visualtype : m_visuals) {
+
+ const int redSize = qPopulationCount(xcb_visualtype.red_mask);
+ const int greenSize = qPopulationCount(xcb_visualtype.green_mask);
+ const int blueSize = qPopulationCount(xcb_visualtype.blue_mask);
+ const int alphaSize = depthOfVisual(xcb_visualtype.visual_id) - redSize - greenSize - blueSize;
+
+ if (format.redBufferSize() != -1 && redSize != format.redBufferSize())
+ continue;
+
+ if (format.greenBufferSize() != -1 && greenSize != format.greenBufferSize())
+ continue;
+
+ if (format.blueBufferSize() != -1 && blueSize != format.blueBufferSize())
+ continue;
+
+ if (format.alphaBufferSize() != -1 && alphaSize != format.alphaBufferSize())
+ continue;
+
+ // Try to find a RGB visual rather than e.g. BGR or GBR
+ if (qCountTrailingZeroBits(xcb_visualtype.blue_mask) == 0)
+ return &xcb_visualtype;
+
+ // In case we do not find anything we like, just remember the first one
+ // and hope for the best:
+ if (!candidate)
+ candidate = &xcb_visualtype;
+ }
+
+ return candidate;
+}
+
+const xcb_visualtype_t *QXcbVirtualDesktop::visualForId(xcb_visualid_t visualid) const
+{
+ QMap<xcb_visualid_t, xcb_visualtype_t>::const_iterator it = m_visuals.find(visualid);
+ if (it == m_visuals.constEnd())
+ return 0;
+ return &*it;
+}
+
+quint8 QXcbVirtualDesktop::depthOfVisual(xcb_visualid_t visualid) const
+{
+ QMap<xcb_visualid_t, quint8>::const_iterator it = m_visualDepths.find(visualid);
+ if (it == m_visualDepths.constEnd())
+ return 0;
+ return *it;
+}
+
QXcbScreen::QXcbScreen(QXcbConnection *connection, QXcbVirtualDesktop *virtualDesktop,
xcb_randr_output_t outputId, xcb_randr_get_output_info_reply_t *output,
const xcb_xinerama_screen_info_t *xineramaScreenInfo, int xineramaScreenIdx)
@@ -181,14 +412,11 @@ QXcbScreen::QXcbScreen(QXcbConnection *connection, QXcbVirtualDesktop *virtualDe
{
if (connection->hasXRandr()) {
xcb_randr_select_input(xcb_connection(), screen()->root, true);
- xcb_randr_get_crtc_info_cookie_t crtcCookie =
- xcb_randr_get_crtc_info_unchecked(xcb_connection(), m_crtc, output ? output->timestamp : 0);
- xcb_randr_get_crtc_info_reply_t *crtc =
- xcb_randr_get_crtc_info_reply(xcb_connection(), crtcCookie, NULL);
+ auto crtc = Q_XCB_REPLY_UNCHECKED(xcb_randr_get_crtc_info, xcb_connection(),
+ m_crtc, output ? output->timestamp : 0);
if (crtc) {
updateGeometry(QRect(crtc->x, crtc->y, crtc->width, crtc->height), crtc->rotation);
updateRefreshRate(crtc->mode);
- free(crtc);
}
} else if (xineramaScreenInfo) {
m_geometry = QRect(xineramaScreenInfo->x_org, xineramaScreenInfo->y_org,
@@ -208,74 +436,26 @@ QXcbScreen::QXcbScreen(QXcbConnection *connection, QXcbVirtualDesktop *virtualDe
if (m_sizeMillimeters.isEmpty())
m_sizeMillimeters = m_virtualSizeMillimeters;
- readXResources();
-
- QScopedPointer<xcb_get_window_attributes_reply_t, QScopedPointerPodDeleter> rootAttribs(
- xcb_get_window_attributes_reply(xcb_connection(),
- xcb_get_window_attributes_unchecked(xcb_connection(), screen()->root), NULL));
- const quint32 existingEventMask = rootAttribs.isNull() ? 0 : rootAttribs->your_event_mask;
-
- const quint32 mask = XCB_CW_EVENT_MASK;
- const quint32 values[] = {
- // XCB_CW_EVENT_MASK
- XCB_EVENT_MASK_ENTER_WINDOW
- | XCB_EVENT_MASK_LEAVE_WINDOW
- | XCB_EVENT_MASK_PROPERTY_CHANGE
- | XCB_EVENT_MASK_STRUCTURE_NOTIFY // for the "MANAGER" atom (system tray notification).
- | existingEventMask // don't overwrite the event mask on the root window
- };
-
- xcb_change_window_attributes(xcb_connection(), screen()->root, mask, values);
-
- xcb_get_property_reply_t *reply =
- xcb_get_property_reply(xcb_connection(),
- xcb_get_property_unchecked(xcb_connection(), false, screen()->root,
- atom(QXcbAtom::_NET_SUPPORTING_WM_CHECK),
- XCB_ATOM_WINDOW, 0, 1024), NULL);
-
- if (reply && reply->format == 32 && reply->type == XCB_ATOM_WINDOW) {
- xcb_window_t windowManager = *((xcb_window_t *)xcb_get_property_value(reply));
-
- if (windowManager != XCB_WINDOW_NONE) {
- xcb_get_property_reply_t *windowManagerReply =
- xcb_get_property_reply(xcb_connection(),
- xcb_get_property_unchecked(xcb_connection(), false, windowManager,
- atom(QXcbAtom::_NET_WM_NAME),
- atom(QXcbAtom::UTF8_STRING), 0, 1024), NULL);
- if (windowManagerReply && windowManagerReply->format == 8 && windowManagerReply->type == atom(QXcbAtom::UTF8_STRING)) {
- m_windowManagerName = QString::fromUtf8((const char *)xcb_get_property_value(windowManagerReply), xcb_get_property_value_length(windowManagerReply));
- }
-
- free(windowManagerReply);
- }
- }
- free(reply);
-
- const xcb_query_extension_reply_t *sync_reply = xcb_get_extension_data(xcb_connection(), &xcb_sync_id);
- if (!sync_reply || !sync_reply->present)
- m_syncRequestSupported = false;
- else
- m_syncRequestSupported = true;
-
- xcb_depth_iterator_t depth_iterator =
- xcb_screen_allowed_depths_iterator(screen());
-
- while (depth_iterator.rem) {
- xcb_depth_t *depth = depth_iterator.data;
- xcb_visualtype_iterator_t visualtype_iterator =
- xcb_depth_visuals_iterator(depth);
+ m_cursor = new QXcbCursor(connection, this);
- while (visualtype_iterator.rem) {
- xcb_visualtype_t *visualtype = visualtype_iterator.data;
- m_visuals.insert(visualtype->visual_id, *visualtype);
- m_visualDepths.insert(visualtype->visual_id, depth->depth);
- xcb_visualtype_next(&visualtype_iterator);
+ if (connection->hasXRandr()) { // Parse EDID
+ QByteArray edid = getEdid();
+ if (m_edid.parse(edid)) {
+ qCDebug(lcQpaScreen, "EDID data for output \"%s\": identifier '%s', manufacturer '%s',"
+ "model '%s', serial '%s', physical size: %.2fx%.2f",
+ name().toLatin1().constData(),
+ m_edid.identifier.toLatin1().constData(),
+ m_edid.manufacturer.toLatin1().constData(),
+ m_edid.model.toLatin1().constData(),
+ m_edid.serialNumber.toLatin1().constData(),
+ m_edid.physicalSize.width(), m_edid.physicalSize.height());
+ } else {
+ // This property is defined by the xrandr spec. Parsing failure indicates a valid error,
+ // but keep this as debug, for details see 4f515815efc318ddc909a0399b71b8a684962f38.
+ qCDebug(lcQpaScreen) << "Failed to parse EDID data for output" << name() <<
+ "edid data: " << edid;
}
-
- xcb_depth_next(&depth_iterator);
}
-
- m_cursor = new QXcbCursor(connection, this);
}
QXcbScreen::~QXcbScreen()
@@ -300,6 +480,21 @@ QString QXcbScreen::getOutputName(xcb_randr_get_output_info_reply_t *outputInfo)
return name;
}
+QString QXcbScreen::manufacturer() const
+{
+ return m_edid.manufacturer;
+}
+
+QString QXcbScreen::model() const
+{
+ return m_edid.model;
+}
+
+QString QXcbScreen::serialNumber() const
+{
+ return m_edid.serialNumber;
+}
+
QWindow *QXcbScreen::topLevelAt(const QPoint &p) const
{
xcb_window_t root = screen()->root;
@@ -311,12 +506,7 @@ QWindow *QXcbScreen::topLevelAt(const QPoint &p) const
xcb_window_t child = root;
do {
- xcb_translate_coordinates_cookie_t translate_cookie =
- xcb_translate_coordinates_unchecked(xcb_connection(), parent, child, x, y);
-
- xcb_translate_coordinates_reply_t *translate_reply =
- xcb_translate_coordinates_reply(xcb_connection(), translate_cookie, NULL);
-
+ auto translate_reply = Q_XCB_REPLY_UNCHECKED(xcb_translate_coordinates, xcb_connection(), parent, child, x, y);
if (!translate_reply) {
return 0;
}
@@ -326,8 +516,6 @@ QWindow *QXcbScreen::topLevelAt(const QPoint &p) const
x = translate_reply->dst_x;
y = translate_reply->dst_y;
- free(translate_reply);
-
if (!child || child == root)
return 0;
@@ -350,62 +538,12 @@ void QXcbScreen::windowShown(QXcbWindow *window)
QSurfaceFormat QXcbScreen::surfaceFormatFor(const QSurfaceFormat &format) const
{
- const xcb_visualid_t xcb_visualid = connection()->hasDefaultVisualId() ? connection()->defaultVisualId()
- : screen()->root_visual;
- const xcb_visualtype_t *xcb_visualtype = visualForId(xcb_visualid);
-
- const int redSize = qPopulationCount(xcb_visualtype->red_mask);
- const int greenSize = qPopulationCount(xcb_visualtype->green_mask);
- const int blueSize = qPopulationCount(xcb_visualtype->blue_mask);
-
- QSurfaceFormat result = format;
-
- if (result.redBufferSize() < 0)
- result.setRedBufferSize(redSize);
-
- if (result.greenBufferSize() < 0)
- result.setGreenBufferSize(greenSize);
-
- if (result.blueBufferSize() < 0)
- result.setBlueBufferSize(blueSize);
-
- return result;
+ return m_virtualDesktop->surfaceFormatFor(format);
}
-const xcb_visualtype_t *QXcbScreen::visualForFormat(const QSurfaceFormat &format) const
+const xcb_visualtype_t *QXcbScreen::visualForId(xcb_visualid_t visualid) const
{
- const xcb_visualtype_t *candidate = nullptr;
-
- for (const xcb_visualtype_t &xcb_visualtype : m_visuals) {
-
- const int redSize = qPopulationCount(xcb_visualtype.red_mask);
- const int greenSize = qPopulationCount(xcb_visualtype.green_mask);
- const int blueSize = qPopulationCount(xcb_visualtype.blue_mask);
- const int alphaSize = depthOfVisual(xcb_visualtype.visual_id) - redSize - greenSize - blueSize;
-
- if (format.redBufferSize() != -1 && redSize != format.redBufferSize())
- continue;
-
- if (format.greenBufferSize() != -1 && greenSize != format.greenBufferSize())
- continue;
-
- if (format.blueBufferSize() != -1 && blueSize != format.blueBufferSize())
- continue;
-
- if (format.alphaBufferSize() != -1 && alphaSize != format.alphaBufferSize())
- continue;
-
- // Try to find a RGB visual rather than e.g. BGR or GBR
- if (qCountTrailingZeroBits(xcb_visualtype.blue_mask) == 0)
- return &xcb_visualtype;
-
- // In case we do not find anything we like, just remember the first one
- // and hope for the best:
- if (!candidate)
- candidate = &xcb_visualtype;
- }
-
- return candidate;
+ return m_virtualDesktop->visualForId(visualid);
}
void QXcbScreen::sendStartupMessage(const QByteArray &message) const
@@ -434,25 +572,19 @@ void QXcbScreen::sendStartupMessage(const QByteArray &message) const
} while (sent < length);
}
-const xcb_visualtype_t *QXcbScreen::visualForId(xcb_visualid_t visualid) const
+QRect QXcbScreen::availableGeometry() const
{
- QMap<xcb_visualid_t, xcb_visualtype_t>::const_iterator it = m_visuals.find(visualid);
- if (it == m_visuals.constEnd())
- return 0;
- return &*it;
-}
-
-quint8 QXcbScreen::depthOfVisual(xcb_visualid_t visualid) const
-{
- QMap<xcb_visualid_t, quint8>::const_iterator it = m_visualDepths.find(visualid);
- if (it == m_visualDepths.constEnd())
- return 0;
- return *it;
+ static bool enforceNetWorkarea = !qEnvironmentVariableIsEmpty("QT_RELY_ON_NET_WORKAREA_ATOM");
+ bool isMultiHeadSystem = virtualSiblings().length() > 1;
+ bool useScreenGeometry = isMultiHeadSystem && !enforceNetWorkarea;
+ return useScreenGeometry ? m_geometry : m_availableGeometry;
}
QImage::Format QXcbScreen::format() const
{
- return qt_xcb_imageFormatForVisual(connection(), screen()->root_depth, visualForId(screen()->root_visual));
+ QImage::Format format;
+ qt_xcb_imageFormatForVisual(connection(), screen()->root_depth, visualForId(screen()->root_visual), &format);
+ return format;
}
QDpi QXcbScreen::virtualDpi() const
@@ -468,8 +600,9 @@ QDpi QXcbScreen::logicalDpi() const
if (overrideDpi)
return QDpi(overrideDpi, overrideDpi);
- if (m_forcedDpi > 0) {
- return QDpi(m_forcedDpi, m_forcedDpi);
+ const int forcedDpi = m_virtualDesktop->forcedDpi();
+ if (forcedDpi > 0) {
+ return QDpi(forcedDpi, forcedDpi);
}
return virtualDpi();
}
@@ -581,19 +714,14 @@ void QXcbScreen::updateGeometry(xcb_timestamp_t timestamp)
if (!connection()->hasXRandr())
return;
- xcb_randr_get_crtc_info_cookie_t crtcCookie =
- xcb_randr_get_crtc_info_unchecked(xcb_connection(), m_crtc, timestamp);
- xcb_randr_get_crtc_info_reply_t *crtc =
- xcb_randr_get_crtc_info_reply(xcb_connection(), crtcCookie, NULL);
- if (crtc) {
+ auto crtc = Q_XCB_REPLY_UNCHECKED(xcb_randr_get_crtc_info, xcb_connection(),
+ m_crtc, timestamp);
+ if (crtc)
updateGeometry(QRect(crtc->x, crtc->y, crtc->width, crtc->height), crtc->rotation);
- free(crtc);
- }
}
-void QXcbScreen::updateGeometry(const QRect &geom, uint8_t rotation)
+void QXcbScreen::updateGeometry(const QRect &geometry, uint8_t rotation)
{
- QRect xGeometry = geom;
switch (rotation) {
case XCB_RANDR_ROTATION_ROTATE_0: // xrandr --rotate normal
m_orientation = Qt::LandscapeOrientation;
@@ -617,12 +745,12 @@ void QXcbScreen::updateGeometry(const QRect &geom, uint8_t rotation)
// is known (probably back-calculated from DPI and resolution),
// e.g. on VNC or with some hardware.
if (m_sizeMillimeters.isEmpty())
- m_sizeMillimeters = sizeInMillimeters(xGeometry.size(), virtualDpi());
+ m_sizeMillimeters = sizeInMillimeters(geometry.size(), virtualDpi());
- qreal dpi = xGeometry.width() / physicalSize().width() * qreal(25.4);
+ qreal dpi = geometry.width() / physicalSize().width() * qreal(25.4);
m_pixelDensity = qMax(1, qRound(dpi/96));
- m_geometry = QRect(xGeometry.topLeft(), xGeometry.size());
- m_availableGeometry = xGeometry & m_virtualDesktop->workArea();
+ m_geometry = geometry;
+ m_availableGeometry = geometry & m_virtualDesktop->workArea();
QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), m_geometry, m_availableGeometry);
}
@@ -645,13 +773,11 @@ void QXcbScreen::updateRefreshRate(xcb_randr_mode_t mode)
// we can safely use get_screen_resources_current here, because in order to
// get here, we must have called get_screen_resources before
- xcb_randr_get_screen_resources_current_cookie_t resourcesCookie =
- xcb_randr_get_screen_resources_current_unchecked(xcb_connection(), screen()->root);
- xcb_randr_get_screen_resources_current_reply_t *resources =
- xcb_randr_get_screen_resources_current_reply(xcb_connection(), resourcesCookie, NULL);
+ auto resources = Q_XCB_REPLY_UNCHECKED(xcb_randr_get_screen_resources_current,
+ xcb_connection(), screen()->root);
if (resources) {
xcb_randr_mode_info_iterator_t modesIter =
- xcb_randr_get_screen_resources_current_modes_iterator(resources);
+ xcb_randr_get_screen_resources_current_modes_iterator(resources.get());
for (; modesIter.rem; xcb_randr_mode_info_next(&modesIter)) {
xcb_randr_mode_info_t *modeInfo = modesIter.data;
if (modeInfo->id == mode) {
@@ -662,29 +788,19 @@ void QXcbScreen::updateRefreshRate(xcb_randr_mode_t mode)
}
}
- free(resources);
QWindowSystemInterface::handleScreenRefreshRateChange(QPlatformScreen::screen(), m_refreshRate);
}
}
-static xcb_get_geometry_reply_t *getGeometryUnchecked(xcb_connection_t *connection, xcb_window_t window)
-{
- const xcb_get_geometry_cookie_t geometry_cookie = xcb_get_geometry_unchecked(connection, window);
- return xcb_get_geometry_reply(connection, geometry_cookie, NULL);
-}
-
static inline bool translate(xcb_connection_t *connection, xcb_window_t child, xcb_window_t parent,
int *x, int *y)
{
- const xcb_translate_coordinates_cookie_t translate_cookie =
- xcb_translate_coordinates_unchecked(connection, child, parent, *x, *y);
- xcb_translate_coordinates_reply_t *translate_reply =
- xcb_translate_coordinates_reply(connection, translate_cookie, NULL);
+ auto translate_reply = Q_XCB_REPLY_UNCHECKED(xcb_translate_coordinates,
+ connection, child, parent, *x, *y);
if (!translate_reply)
return false;
*x = translate_reply->dst_x;
*y = translate_reply->dst_y;
- free(translate_reply);
return true;
}
@@ -698,22 +814,20 @@ QPixmap QXcbScreen::grabWindow(WId window, int xIn, int yIn, int width, int heig
QXcbScreen *screen = const_cast<QXcbScreen *>(this);
xcb_window_t root = screen->root();
- xcb_get_geometry_reply_t *rootReply = getGeometryUnchecked(xcb_connection(), root);
+ auto rootReply = Q_XCB_REPLY_UNCHECKED(xcb_get_geometry, xcb_connection(), root);
if (!rootReply)
return QPixmap();
const quint8 rootDepth = rootReply->depth;
- free(rootReply);
QSize windowSize;
quint8 effectiveDepth = 0;
if (window) {
- xcb_get_geometry_reply_t *windowReply = getGeometryUnchecked(xcb_connection(), window);
+ auto windowReply = Q_XCB_REPLY_UNCHECKED(xcb_get_geometry, xcb_connection(), window);
if (!windowReply)
return QPixmap();
windowSize = QSize(windowReply->width, windowReply->height);
effectiveDepth = windowReply->depth;
- free(windowReply);
if (effectiveDepth == rootDepth) {
// if the depth of the specified window and the root window are the
// same, grab pixels from the root window (so that we get the any
@@ -738,14 +852,12 @@ QPixmap QXcbScreen::grabWindow(WId window, int xIn, int yIn, int width, int heig
if (height < 0)
height = windowSize.height() - yIn;
- xcb_get_window_attributes_reply_t *attributes_reply =
- xcb_get_window_attributes_reply(xcb_connection(), xcb_get_window_attributes_unchecked(xcb_connection(), window), NULL);
+ auto attributes_reply = Q_XCB_REPLY_UNCHECKED(xcb_get_window_attributes, xcb_connection(), window);
if (!attributes_reply)
return QPixmap();
const xcb_visualtype_t *visual = screen->visualForId(attributes_reply->visual);
- free(attributes_reply);
xcb_pixmap_t pixmap = xcb_generate_id(xcb_connection());
xcb_create_pixmap(xcb_connection(), effectiveDepth, pixmap, window, width, height);
@@ -765,101 +877,45 @@ QPixmap QXcbScreen::grabWindow(WId window, int xIn, int yIn, int width, int heig
return result;
}
-static bool parseXftInt(const QByteArray& stringValue, int *value)
-{
- Q_ASSERT(value != 0);
- bool ok;
- *value = stringValue.toInt(&ok);
- return ok;
-}
-
-static QFontEngine::HintStyle parseXftHintStyle(const QByteArray& stringValue)
+QXcbXSettings *QXcbScreen::xSettings() const
{
- if (stringValue == "hintfull")
- return QFontEngine::HintFull;
- else if (stringValue == "hintnone")
- return QFontEngine::HintNone;
- else if (stringValue == "hintmedium")
- return QFontEngine::HintMedium;
- else if (stringValue == "hintslight")
- return QFontEngine::HintLight;
-
- return QFontEngine::HintStyle(-1);
+ return m_virtualDesktop->xSettings();
}
-static QFontEngine::SubpixelAntialiasingType parseXftRgba(const QByteArray& stringValue)
+QByteArray QXcbScreen::getOutputProperty(xcb_atom_t atom) const
{
- if (stringValue == "none")
- return QFontEngine::Subpixel_None;
- else if (stringValue == "rgb")
- return QFontEngine::Subpixel_RGB;
- else if (stringValue == "bgr")
- return QFontEngine::Subpixel_BGR;
- else if (stringValue == "vrgb")
- return QFontEngine::Subpixel_VRGB;
- else if (stringValue == "vbgr")
- return QFontEngine::Subpixel_VBGR;
+ QByteArray result;
- return QFontEngine::SubpixelAntialiasingType(-1);
-}
-
-bool QXcbScreen::xResource(const QByteArray &identifier,
- const QByteArray &expectedIdentifier,
- QByteArray& stringValue)
-{
- if (identifier.startsWith(expectedIdentifier)) {
- stringValue = identifier.mid(expectedIdentifier.size());
- return true;
+ auto reply = Q_XCB_REPLY(xcb_randr_get_output_property, xcb_connection(),
+ m_output, atom, XCB_ATOM_ANY, 0, 100, false, false);
+ if (reply && reply->type == XCB_ATOM_INTEGER && reply->format == 8) {
+ quint8 *data = new quint8[reply->num_items];
+ memcpy(data, xcb_randr_get_output_property_data(reply.get()), reply->num_items);
+ result = QByteArray(reinterpret_cast<const char *>(data), reply->num_items);
+ delete[] data;
}
- return false;
+
+ return result;
}
-void QXcbScreen::readXResources()
+QByteArray QXcbScreen::getEdid() const
{
- int offset = 0;
- QByteArray resources;
- while(1) {
- xcb_get_property_reply_t *reply =
- xcb_get_property_reply(xcb_connection(),
- xcb_get_property_unchecked(xcb_connection(), false, screen()->root,
- XCB_ATOM_RESOURCE_MANAGER,
- XCB_ATOM_STRING, offset/4, 8192), NULL);
- bool more = false;
- if (reply && reply->format == 8 && reply->type == XCB_ATOM_STRING) {
- resources += QByteArray((const char *)xcb_get_property_value(reply), xcb_get_property_value_length(reply));
- offset += xcb_get_property_value_length(reply);
- more = reply->bytes_after != 0;
- }
-
- if (reply)
- free(reply);
-
- if (!more)
- break;
+ QByteArray result;
+ if (!connection()->hasXRandr())
+ return result;
+
+ // Try a bunch of atoms
+ xcb_atom_t atom = connection()->internAtom("EDID");
+ result = getOutputProperty(atom);
+ if (result.isEmpty()) {
+ atom = connection()->internAtom("EDID_DATA");
+ result = getOutputProperty(atom);
}
-
- QList<QByteArray> split = resources.split('\n');
- for (int i = 0; i < split.size(); ++i) {
- const QByteArray &r = split.at(i);
- int value;
- QByteArray stringValue;
- if (xResource(r, "Xft.dpi:\t", stringValue)) {
- if (parseXftInt(stringValue, &value))
- m_forcedDpi = value;
- } else if (xResource(r, "Xft.hintstyle:\t", stringValue)) {
- m_hintStyle = parseXftHintStyle(stringValue);
- } else if (xResource(r, "Xft.antialias:\t", stringValue)) {
- if (parseXftInt(stringValue, &value))
- m_antialiasingEnabled = value;
- } else if (xResource(r, "Xft.rgba:\t", stringValue)) {
- m_subpixelType = parseXftRgba(stringValue);
- }
+ if (result.isEmpty()) {
+ atom = connection()->internAtom("XFree86_DDC_EDID1_RAWDATA");
+ result = getOutputProperty(atom);
}
-}
-
-QXcbXSettings *QXcbScreen::xSettings() const
-{
- return m_virtualDesktop->xSettings();
+ return result;
}
static inline void formatRect(QDebug &debug, const QRect r)
diff --git a/src/plugins/platforms/xcb/qxcbscreen.h b/src/plugins/platforms/xcb/qxcbscreen.h
index 4163be2969..4a9b1bd209 100644
--- a/src/plugins/platforms/xcb/qxcbscreen.h
+++ b/src/plugins/platforms/xcb/qxcbscreen.h
@@ -53,6 +53,8 @@
#include <private/qfontengine_p.h>
+#include <QtEdidSupport/private/qedidparser_p.h>
+
QT_BEGIN_NAMESPACE
class QXcbConnection;
@@ -91,9 +93,28 @@ public:
void handleXFixesSelectionNotify(xcb_xfixes_selection_notify_event_t *notify_event);
void subscribeToXFixesSelectionNotify();
+ int forcedDpi() const { return m_forcedDpi; }
+ QFontEngine::HintStyle hintStyle() const { return m_hintStyle; }
+ QFontEngine::SubpixelAntialiasingType subpixelType() const { return m_subpixelType; }
+ int antialiasingEnabled() const { return m_antialiasingEnabled; }
+
+ QString windowManagerName() const { return m_windowManagerName; }
+ bool syncRequestSupported() const { return m_syncRequestSupported; }
+
+ QSurfaceFormat surfaceFormatFor(const QSurfaceFormat &format) const;
+
+ const xcb_visualtype_t *visualForFormat(const QSurfaceFormat &format) const;
+ const xcb_visualtype_t *visualForId(xcb_visualid_t) const;
+ quint8 depthOfVisual(xcb_visualid_t) const;
+
private:
QRect getWorkArea() const;
+ static bool xResource(const QByteArray &identifier,
+ const QByteArray &expectedIdentifier,
+ QByteArray &stringValue);
+ void readXResources();
+
xcb_screen_t *m_screen;
const int m_number;
QList<QPlatformScreen *> m_screens;
@@ -103,6 +124,15 @@ private:
bool m_compositingActive = false;
QRect m_workArea;
+
+ int m_forcedDpi = -1;
+ QFontEngine::HintStyle m_hintStyle = QFontEngine::HintStyle(-1);
+ QFontEngine::SubpixelAntialiasingType m_subpixelType = QFontEngine::SubpixelAntialiasingType(-1);
+ int m_antialiasingEnabled = -1;
+ QString m_windowManagerName;
+ bool m_syncRequestSupported = false;
+ QMap<xcb_visualid_t, xcb_visualtype_t> m_visuals;
+ QMap<xcb_visualid_t, quint8> m_visualDepths;
};
class Q_XCB_EXPORT QXcbScreen : public QXcbObject, public QPlatformScreen
@@ -110,7 +140,7 @@ class Q_XCB_EXPORT QXcbScreen : public QXcbObject, public QPlatformScreen
public:
QXcbScreen(QXcbConnection *connection, QXcbVirtualDesktop *virtualDesktop,
xcb_randr_output_t outputId, xcb_randr_get_output_info_reply_t *outputInfo,
- const xcb_xinerama_screen_info_t *xineramaScreenInfo = Q_NULLPTR, int xineramaScreenIdx = -1);
+ const xcb_xinerama_screen_info_t *xineramaScreenInfo = nullptr, int xineramaScreenIdx = -1);
~QXcbScreen();
QString getOutputName(xcb_randr_get_output_info_reply_t *outputInfo);
@@ -119,8 +149,12 @@ public:
QWindow *topLevelAt(const QPoint &point) const override;
+ QString manufacturer() const override;
+ QString model() const override;
+ QString serialNumber() const override;
+
QRect geometry() const override { return m_geometry; }
- QRect availableGeometry() const override {return m_availableGeometry;}
+ QRect availableGeometry() const override;
int depth() const override { return screen()->root_depth; }
QImage::Format format() const override;
QSizeF physicalSize() const override { return m_sizeMillimeters; }
@@ -152,37 +186,35 @@ public:
void setCrtc(xcb_randr_crtc_t crtc) { m_crtc = crtc; }
void windowShown(QXcbWindow *window);
- QString windowManagerName() const { return m_windowManagerName; }
- bool syncRequestSupported() const { return m_syncRequestSupported; }
+ QString windowManagerName() const { return m_virtualDesktop->windowManagerName(); }
+ bool syncRequestSupported() const { return m_virtualDesktop->syncRequestSupported(); }
QSurfaceFormat surfaceFormatFor(const QSurfaceFormat &format) const;
- const xcb_visualtype_t *visualForFormat(const QSurfaceFormat &format) const;
- const xcb_visualtype_t *visualForId(xcb_visualid_t) const;
- quint8 depthOfVisual(xcb_visualid_t) const;
+ const xcb_visualtype_t *visualForFormat(const QSurfaceFormat &format) const { return m_virtualDesktop->visualForFormat(format); }
+ const xcb_visualtype_t *visualForId(xcb_visualid_t visualid) const;
+ quint8 depthOfVisual(xcb_visualid_t visualid) const { return m_virtualDesktop->depthOfVisual(visualid); }
QString name() const override { return m_outputName; }
void handleScreenChange(xcb_randr_screen_change_notify_event_t *change_event);
- void updateGeometry(const QRect &geom, uint8_t rotation);
+ void updateGeometry(const QRect &geometry, uint8_t rotation);
void updateGeometry(xcb_timestamp_t timestamp = XCB_TIME_CURRENT_TIME);
void updateAvailableGeometry();
void updateRefreshRate(xcb_randr_mode_t mode);
- void readXResources();
-
- QFontEngine::HintStyle hintStyle() const { return m_hintStyle; }
- QFontEngine::SubpixelAntialiasingType subpixelType() const { return m_subpixelType; }
- int antialiasingEnabled() const { return m_antialiasingEnabled; }
+ QFontEngine::HintStyle hintStyle() const { return m_virtualDesktop->hintStyle(); }
+ QFontEngine::SubpixelAntialiasingType subpixelType() const { return m_virtualDesktop->subpixelType(); }
+ int antialiasingEnabled() const { return m_virtualDesktop->antialiasingEnabled(); }
QXcbXSettings *xSettings() const;
private:
- static bool xResource(const QByteArray &identifier,
- const QByteArray &expectedIdentifier,
- QByteArray &stringValue);
void sendStartupMessage(const QByteArray &message) const;
+ QByteArray getOutputProperty(xcb_atom_t atom) const;
+ QByteArray getEdid() const;
+
QXcbVirtualDesktop *m_virtualDesktop;
xcb_randr_output_t m_output;
xcb_randr_crtc_t m_crtc;
@@ -198,17 +230,10 @@ private:
QSize m_virtualSize;
QSizeF m_virtualSizeMillimeters;
Qt::ScreenOrientation m_orientation = Qt::PrimaryOrientation;
- QString m_windowManagerName;
- bool m_syncRequestSupported = false;
- QMap<xcb_visualid_t, xcb_visualtype_t> m_visuals;
- QMap<xcb_visualid_t, quint8> m_visualDepths;
QXcbCursor *m_cursor;
int m_refreshRate = 60;
- int m_forcedDpi = -1;
int m_pixelDensity = 1;
- QFontEngine::HintStyle m_hintStyle = QFontEngine::HintStyle(-1);
- QFontEngine::SubpixelAntialiasingType m_subpixelType = QFontEngine::SubpixelAntialiasingType(-1);
- int m_antialiasingEnabled = -1;
+ QEdidParser m_edid;
};
#ifndef QT_NO_DEBUG_STREAM
diff --git a/src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp b/src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp
index fb0a4a3939..c98879c7df 100644
--- a/src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp
+++ b/src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp
@@ -85,13 +85,10 @@ QXcbSystemTrayTracker::QXcbSystemTrayTracker(QXcbConnection *connection,
xcb_window_t QXcbSystemTrayTracker::locateTrayWindow(const QXcbConnection *connection, xcb_atom_t selection)
{
- xcb_get_selection_owner_cookie_t cookie = xcb_get_selection_owner(connection->xcb_connection(), selection);
- xcb_get_selection_owner_reply_t *reply = xcb_get_selection_owner_reply(connection->xcb_connection(), cookie, 0);
+ auto reply = Q_XCB_REPLY(xcb_get_selection_owner, connection->xcb_connection(), selection);
if (!reply)
return 0;
- const xcb_window_t result = reply->owner;
- free(reply);
- return result;
+ return reply->owner;
}
// API for QPlatformNativeInterface/QPlatformSystemTrayIcon: Request a window
@@ -119,7 +116,7 @@ xcb_window_t QXcbSystemTrayTracker::trayWindow()
m_connection->addWindowEventListener(m_trayWindow, this);
const quint32 mask = XCB_CW_EVENT_MASK;
const quint32 value = XCB_EVENT_MASK_STRUCTURE_NOTIFY;
- Q_XCB_CALL2(xcb_change_window_attributes(m_connection->xcb_connection(), m_trayWindow, mask, &value), m_connection);
+ xcb_change_window_attributes(m_connection->xcb_connection(), m_trayWindow, mask, &value);
}
}
return m_trayWindow;
@@ -130,23 +127,16 @@ xcb_window_t QXcbSystemTrayTracker::trayWindow()
// does not work for the QWindow parented on the tray.
QRect QXcbSystemTrayTracker::systemTrayWindowGlobalGeometry(xcb_window_t window) const
{
-
xcb_connection_t *conn = m_connection->xcb_connection();
- xcb_get_geometry_reply_t *geomReply =
- xcb_get_geometry_reply(conn, xcb_get_geometry(conn, window), 0);
+ auto geomReply = Q_XCB_REPLY(xcb_get_geometry, conn, window);
if (!geomReply)
return QRect();
- xcb_translate_coordinates_reply_t *translateReply =
- xcb_translate_coordinates_reply(conn, xcb_translate_coordinates(conn, window, m_connection->rootWindow(), 0, 0), 0);
- if (!translateReply) {
- free(geomReply);
+ auto translateReply = Q_XCB_REPLY(xcb_translate_coordinates, conn, window, m_connection->rootWindow(), 0, 0);
+ if (!translateReply)
return QRect();
- }
- const QRect result(QPoint(translateReply->dst_x, translateReply->dst_y), QSize(geomReply->width, geomReply->height));
- free(translateReply);
- return result;
+ return QRect(QPoint(translateReply->dst_x, translateReply->dst_y), QSize(geomReply->width, geomReply->height));
}
inline void QXcbSystemTrayTracker::emitSystemTrayWindowChanged()
@@ -180,24 +170,18 @@ bool QXcbSystemTrayTracker::visualHasAlphaChannel()
xcb_atom_t tray_atom = m_connection->atom(QXcbAtom::_NET_SYSTEM_TRAY_VISUAL);
// Get the xcb property for the _NET_SYSTEM_TRAY_VISUAL atom
- xcb_get_property_cookie_t systray_atom_cookie;
- xcb_get_property_reply_t *systray_atom_reply;
-
- systray_atom_cookie = xcb_get_property_unchecked(m_connection->xcb_connection(), false, m_trayWindow,
+ auto systray_atom_reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, m_connection->xcb_connection(),
+ false, m_trayWindow,
tray_atom, XCB_ATOM_VISUALID, 0, 1);
- systray_atom_reply = xcb_get_property_reply(m_connection->xcb_connection(), systray_atom_cookie, 0);
-
if (!systray_atom_reply)
return false;
xcb_visualid_t systrayVisualId = XCB_NONE;
- if (systray_atom_reply->value_len > 0 && xcb_get_property_value_length(systray_atom_reply) > 0) {
- xcb_visualid_t * vids = (uint32_t *)xcb_get_property_value(systray_atom_reply);
+ if (systray_atom_reply->value_len > 0 && xcb_get_property_value_length(systray_atom_reply.get()) > 0) {
+ xcb_visualid_t * vids = (uint32_t *)xcb_get_property_value(systray_atom_reply.get());
systrayVisualId = vids[0];
}
- free(systray_atom_reply);
-
if (systrayVisualId != XCB_NONE) {
quint8 depth = m_connection->primaryScreen()->depthOfVisual(systrayVisualId);
return depth == 32;
diff --git a/src/plugins/platforms/xcb/qxcbvulkaninstance.cpp b/src/plugins/platforms/xcb/qxcbvulkaninstance.cpp
new file mode 100644
index 0000000000..4d540defa9
--- /dev/null
+++ b/src/plugins/platforms/xcb/qxcbvulkaninstance.cpp
@@ -0,0 +1,156 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qxcbvulkaninstance.h"
+#include "qxcbwindow.h"
+#include "qxcbscreen.h"
+
+QT_BEGIN_NAMESPACE
+
+QXcbVulkanInstance::QXcbVulkanInstance(QVulkanInstance *instance)
+ : m_instance(instance),
+ m_getPhysDevPresSupport(nullptr),
+ m_createSurface(nullptr),
+ m_destroySurface(nullptr)
+{
+ if (qEnvironmentVariableIsSet("QT_VULKAN_LIB"))
+ m_lib.setFileName(QString::fromUtf8(qgetenv("QT_VULKAN_LIB")));
+ else
+ m_lib.setFileName(QStringLiteral("vulkan"));
+
+ if (!m_lib.load()) {
+ qWarning("Failed to load %s: %s", qPrintable(m_lib.fileName()), qPrintable(m_lib.errorString()));
+ return;
+ }
+
+ init(&m_lib);
+}
+
+QXcbVulkanInstance::~QXcbVulkanInstance()
+{
+}
+
+void QXcbVulkanInstance::createOrAdoptInstance()
+{
+ initInstance(m_instance, QByteArrayList() << QByteArrayLiteral("VK_KHR_xcb_surface"));
+
+ if (!m_vkInst)
+ return;
+
+ m_getPhysDevPresSupport = reinterpret_cast<PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR>(
+ m_vkGetInstanceProcAddr(m_vkInst, "vkGetPhysicalDeviceXcbPresentationSupportKHR"));
+ if (!m_getPhysDevPresSupport)
+ qWarning("Failed to find vkGetPhysicalDeviceXcbPresentationSupportKHR");
+}
+
+bool QXcbVulkanInstance::supportsPresent(VkPhysicalDevice physicalDevice,
+ uint32_t queueFamilyIndex,
+ QWindow *window)
+{
+ if (!m_getPhysDevPresSupport || !m_getPhysDevSurfaceSupport)
+ return true;
+
+ QXcbWindow *w = static_cast<QXcbWindow *>(window->handle());
+ if (!w) {
+ qWarning("Attempted to call supportsPresent() without a valid platform window");
+ return false;
+ }
+ xcb_connection_t *connection = w->xcbScreen()->xcb_connection();
+ bool ok = m_getPhysDevPresSupport(physicalDevice, queueFamilyIndex, connection, w->visualId());
+
+ VkSurfaceKHR surface = QVulkanInstance::surfaceForWindow(window);
+ VkBool32 supported = false;
+ m_getPhysDevSurfaceSupport(physicalDevice, queueFamilyIndex, surface, &supported);
+ ok &= bool(supported);
+
+ return ok;
+}
+
+VkSurfaceKHR QXcbVulkanInstance::createSurface(QXcbWindow *window)
+{
+ VkSurfaceKHR surface = 0;
+
+ if (!m_createSurface) {
+ m_createSurface = reinterpret_cast<PFN_vkCreateXcbSurfaceKHR>(
+ m_vkGetInstanceProcAddr(m_vkInst, "vkCreateXcbSurfaceKHR"));
+ }
+ if (!m_createSurface) {
+ qWarning("Failed to find vkCreateXcbSurfaceKHR");
+ return surface;
+ }
+ if (!m_destroySurface) {
+ m_destroySurface = reinterpret_cast<PFN_vkDestroySurfaceKHR>(
+ m_vkGetInstanceProcAddr(m_vkInst, "vkDestroySurfaceKHR"));
+ }
+ if (!m_destroySurface) {
+ qWarning("Failed to find vkDestroySurfaceKHR");
+ return surface;
+ }
+
+ VkXcbSurfaceCreateInfoKHR surfaceInfo;
+ memset(&surfaceInfo, 0, sizeof(surfaceInfo));
+ surfaceInfo.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
+ surfaceInfo.connection = window->xcbScreen()->xcb_connection();
+ surfaceInfo.window = window->xcb_window();
+ VkResult err = m_createSurface(m_vkInst, &surfaceInfo, nullptr, &surface);
+ if (err != VK_SUCCESS)
+ qWarning("Failed to create Vulkan surface: %d", err);
+
+ return surface;
+}
+
+void QXcbVulkanInstance::destroySurface(VkSurfaceKHR surface)
+{
+ if (m_destroySurface && surface)
+ m_destroySurface(m_vkInst, surface, nullptr);
+}
+
+void QXcbVulkanInstance::presentQueued(QWindow *window)
+{
+ QXcbWindow *w = static_cast<QXcbWindow *>(window->handle());
+ if (!w) {
+ qWarning("Attempted to call presentQueued() without a valid platform window");
+ return;
+ }
+
+ if (w->needsSync())
+ w->postSyncWindowRequest();
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/qxcbvulkaninstance.h b/src/plugins/platforms/xcb/qxcbvulkaninstance.h
new file mode 100644
index 0000000000..dbe057d944
--- /dev/null
+++ b/src/plugins/platforms/xcb/qxcbvulkaninstance.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QXCBVULKANINSTANCE_H
+#define QXCBVULKANINSTANCE_H
+
+#if defined(VULKAN_H_) && !defined(VK_USE_PLATFORM_XCB_KHR)
+#error "vulkan.h included without xcb WSI"
+#endif
+
+#define VK_USE_PLATFORM_XCB_KHR
+
+#include <QtVulkanSupport/private/qbasicvulkanplatforminstance_p.h>
+#include <QLibrary>
+
+QT_BEGIN_NAMESPACE
+
+class QXcbWindow;
+
+class QXcbVulkanInstance : public QBasicPlatformVulkanInstance
+{
+public:
+ QXcbVulkanInstance(QVulkanInstance *instance);
+ ~QXcbVulkanInstance();
+
+ void createOrAdoptInstance() override;
+ bool supportsPresent(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, QWindow *window) override;
+ void presentQueued(QWindow *window) override;
+
+ VkSurfaceKHR createSurface(QXcbWindow *window);
+ void destroySurface(VkSurfaceKHR surface);
+
+private:
+ QVulkanInstance *m_instance;
+ QLibrary m_lib;
+ PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR m_getPhysDevPresSupport;
+ PFN_vkCreateXcbSurfaceKHR m_createSurface;
+ PFN_vkDestroySurfaceKHR m_destroySurface;
+};
+
+QT_END_NAMESPACE
+
+#endif // QXCBVULKANINSTANCE_H
diff --git a/src/plugins/platforms/xcb/qxcbvulkanwindow.cpp b/src/plugins/platforms/xcb/qxcbvulkanwindow.cpp
new file mode 100644
index 0000000000..25bc340f97
--- /dev/null
+++ b/src/plugins/platforms/xcb/qxcbvulkanwindow.cpp
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qxcbvulkanwindow.h"
+
+QT_BEGIN_NAMESPACE
+
+QXcbVulkanWindow::QXcbVulkanWindow(QWindow *window)
+ : QXcbWindow(window),
+ m_surface(0)
+{
+}
+
+QXcbVulkanWindow::~QXcbVulkanWindow()
+{
+ if (m_surface) {
+ QVulkanInstance *inst = window()->vulkanInstance();
+ if (inst)
+ static_cast<QXcbVulkanInstance *>(inst->handle())->destroySurface(m_surface);
+ }
+}
+
+void QXcbVulkanWindow::resolveFormat(const QSurfaceFormat &format)
+{
+ m_format = format;
+ if (m_format.redBufferSize() <= 0)
+ m_format.setRedBufferSize(8);
+ if (m_format.greenBufferSize() <= 0)
+ m_format.setGreenBufferSize(8);
+ if (m_format.blueBufferSize() <= 0)
+ m_format.setBlueBufferSize(8);
+}
+
+// No createVisual() needed, use the default that picks one based on the R/G/B/A size.
+
+VkSurfaceKHR *QXcbVulkanWindow::surface()
+{
+ if (m_surface)
+ return &m_surface;
+
+ QVulkanInstance *inst = window()->vulkanInstance();
+ if (!inst) {
+ qWarning("Attempted to create Vulkan surface without an instance; was QWindow::setVulkanInstance() called?");
+ return nullptr;
+ }
+ QXcbVulkanInstance *xcbinst = static_cast<QXcbVulkanInstance *>(inst->handle());
+ m_surface = xcbinst->createSurface(this);
+
+ return &m_surface;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/qxcbvulkanwindow.h b/src/plugins/platforms/xcb/qxcbvulkanwindow.h
new file mode 100644
index 0000000000..43c96820c5
--- /dev/null
+++ b/src/plugins/platforms/xcb/qxcbvulkanwindow.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QXCBVULKANWINDOW_H
+#define QXCBVULKANWINDOW_H
+
+#include "qxcbwindow.h"
+#include "qxcbvulkaninstance.h"
+
+QT_BEGIN_NAMESPACE
+
+class QXcbVulkanWindow : public QXcbWindow
+{
+public:
+ QXcbVulkanWindow(QWindow *window);
+ ~QXcbVulkanWindow();
+
+ VkSurfaceKHR *surface();
+
+protected:
+ void resolveFormat(const QSurfaceFormat &format) override;
+
+private:
+ VkSurfaceKHR m_surface;
+};
+
+QT_END_NAMESPACE
+
+#endif // QXCBVULKANWINDOW_H
diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp
index 37d6336a72..61cfed4db7 100644
--- a/src/plugins/platforms/xcb/qxcbwindow.cpp
+++ b/src/plugins/platforms/xcb/qxcbwindow.cpp
@@ -51,6 +51,7 @@
#include "qxcbscreen.h"
#include "qxcbdrag.h"
#include "qxcbkeyboard.h"
+#include "qxcbimage.h"
#include "qxcbwmsupport.h"
#include "qxcbimage.h"
#include "qxcbnativeinterface.h"
@@ -176,74 +177,23 @@ static inline bool isTransient(const QWindow *w)
|| w->type() == Qt::Popup;
}
-static inline QImage::Format imageFormatForVisual(int depth, quint32 red_mask, quint32 blue_mask, bool *rgbSwap)
+void QXcbWindow::setImageFormatForVisual(const xcb_visualtype_t *visual)
{
- if (rgbSwap)
- *rgbSwap = false;
- switch (depth) {
- case 32:
- if (blue_mask == 0xff)
- return QImage::Format_ARGB32_Premultiplied;
- if (red_mask == 0x3ff)
- return QImage::Format_A2BGR30_Premultiplied;
- if (blue_mask == 0x3ff)
- return QImage::Format_A2RGB30_Premultiplied;
- if (red_mask == 0xff) {
- if (rgbSwap)
- *rgbSwap = true;
- return QImage::Format_ARGB32_Premultiplied;
- }
- break;
- case 30:
- if (red_mask == 0x3ff)
- return QImage::Format_BGR30;
- if (blue_mask == 0x3ff)
- return QImage::Format_RGB30;
- break;
- case 24:
- if (blue_mask == 0xff)
- return QImage::Format_RGB32;
- if (red_mask == 0xff) {
- if (rgbSwap)
- *rgbSwap = true;
- return QImage::Format_RGB32;
- }
- break;
- case 16:
- if (blue_mask == 0x1f)
- return QImage::Format_RGB16;
- if (red_mask == 0x1f) {
- if (rgbSwap)
- *rgbSwap = true;
- return QImage::Format_RGB16;
- }
- break;
- case 15:
- if (blue_mask == 0x1f)
- return QImage::Format_RGB555;
- if (red_mask == 0x1f) {
- if (rgbSwap)
- *rgbSwap = true;
- return QImage::Format_RGB555;
- }
- break;
- default:
- break;
- }
- qWarning("Unsupported screen format: depth: %d, red_mask: %x, blue_mask: %x", depth, red_mask, blue_mask);
+ if (qt_xcb_imageFormatForVisual(connection(), m_depth, visual, &m_imageFormat, &m_imageRgbSwap))
+ return;
- switch (depth) {
+ switch (m_depth) {
+ case 32:
case 24:
qWarning("Using RGB32 fallback, if this works your X11 server is reporting a bad screen format.");
- return QImage::Format_RGB32;
+ m_imageFormat = QImage::Format_RGB32;
+ break;
case 16:
qWarning("Using RGB16 fallback, if this works your X11 server is reporting a bad screen format.");
- return QImage::Format_RGB16;
+ m_imageFormat = QImage::Format_RGB16;
default:
break;
}
-
- return QImage::Format_Invalid;
}
static inline bool positionIncludesFrame(QWindow *w)
@@ -276,12 +226,12 @@ static inline XTextProperty* qstringToXTP(Display *dpy, const QString& s)
tl[1] = 0;
errCode = XmbTextListToTextProperty(dpy, tl, 1, XStdICCTextStyle, &tp);
if (errCode < 0)
- qDebug("XmbTextListToTextProperty result code %d", errCode);
+ qCDebug(lcQpaXcb, "XmbTextListToTextProperty result code %d", errCode);
}
if (!mapper || errCode < 0) {
mapper = QTextCodec::codecForName("latin1");
if (!mapper || !mapper->canEncode(s))
- return Q_NULLPTR;
+ return nullptr;
#endif
static QByteArray qcs;
qcs = s.toLatin1();
@@ -316,7 +266,7 @@ static QWindow *childWindowAt(QWindow *win, const QPoint &p)
&& win->geometry().contains(win->parent()->mapFromGlobal(p))) {
return win;
}
- return Q_NULLPTR;
+ return nullptr;
}
static const char *wm_window_type_property_id = "_q_xcb_wm_window_type";
@@ -375,7 +325,7 @@ void QXcbWindow::create()
}
if (!visual)
visual = platformScreen->visualForId(m_visualId);
- m_imageFormat = imageFormatForVisual(m_depth, visual->red_mask, visual->blue_mask, &m_imageRgbSwap);
+ setImageFormatForVisual(visual);
connection()->addWindowEventListener(m_window, this);
return;
}
@@ -412,7 +362,7 @@ void QXcbWindow::create()
resolveFormat(platformScreen->surfaceFormatFor(window()->requestedFormat()));
- const xcb_visualtype_t *visual = Q_NULLPTR;
+ const xcb_visualtype_t *visual = nullptr;
if (connection()->hasDefaultVisualId()) {
visual = platformScreen->visualForId(connection()->defaultVisualId());
@@ -420,6 +370,19 @@ void QXcbWindow::create()
qWarning() << "Failed to use requested visual id.";
}
+ if (parent()) {
+ // When using a Vulkan QWindow via QWidget::createWindowContainer() we
+ // must make sure the visuals are compatible. Now, the parent will be
+ // of RasterGLSurface which typically chooses a GLX/EGL compatible
+ // visual which may not be what the Vulkan window would choose.
+ // Therefore, take the parent's visual.
+ if (window()->surfaceType() == QSurface::VulkanSurface
+ && parent()->window()->surfaceType() != QSurface::VulkanSurface)
+ {
+ visual = platformScreen->visualForId(static_cast<QXcbWindow *>(parent())->visualId());
+ }
+ }
+
if (!visual)
visual = createVisual();
@@ -432,7 +395,7 @@ void QXcbWindow::create()
m_visualId = visual->visual_id;
m_depth = platformScreen->depthOfVisual(m_visualId);
- m_imageFormat = imageFormatForVisual(m_depth, visual->red_mask, visual->blue_mask, &m_imageRgbSwap);
+ setImageFormatForVisual(visual);
quint32 mask = XCB_CW_BACK_PIXMAP
| XCB_CW_BORDER_PIXEL
@@ -445,11 +408,11 @@ void QXcbWindow::create()
if ((window()->supportsOpenGL() && haveOpenGL) || m_format.hasAlpha()) {
m_cmap = xcb_generate_id(xcb_connection());
- Q_XCB_CALL(xcb_create_colormap(xcb_connection(),
- XCB_COLORMAP_ALLOC_NONE,
- m_cmap,
- xcb_parent_id,
- m_visualId));
+ xcb_create_colormap(xcb_connection(),
+ XCB_COLORMAP_ALLOC_NONE,
+ m_cmap,
+ xcb_parent_id,
+ m_visualId);
mask |= XCB_CW_COLORMAP;
}
@@ -465,23 +428,23 @@ void QXcbWindow::create()
};
m_window = xcb_generate_id(xcb_connection());
- Q_XCB_CALL(xcb_create_window(xcb_connection(),
- m_depth,
- m_window, // window id
- xcb_parent_id, // parent window id
- rect.x(),
- rect.y(),
- rect.width(),
- rect.height(),
- 0, // border width
- XCB_WINDOW_CLASS_INPUT_OUTPUT, // window class
- m_visualId, // visual
- mask,
- values));
+ xcb_create_window(xcb_connection(),
+ m_depth,
+ m_window, // window id
+ xcb_parent_id, // parent window id
+ rect.x(),
+ rect.y(),
+ rect.width(),
+ rect.height(),
+ 0, // border width
+ XCB_WINDOW_CLASS_INPUT_OUTPUT, // window class
+ m_visualId, // visual
+ mask,
+ values);
connection()->addWindowEventListener(m_window, this);
- Q_XCB_CALL(xcb_change_window_attributes(xcb_connection(), m_window, mask, values));
+ xcb_change_window_attributes(xcb_connection(), m_window, mask, values);
propagateSizeHints();
@@ -499,43 +462,43 @@ void QXcbWindow::create()
if (window()->flags() & Qt::WindowContextHelpButtonHint)
properties[propertyCount++] = atom(QXcbAtom::_NET_WM_CONTEXT_HELP);
- Q_XCB_CALL(xcb_change_property(xcb_connection(),
- XCB_PROP_MODE_REPLACE,
- m_window,
- atom(QXcbAtom::WM_PROTOCOLS),
- XCB_ATOM_ATOM,
- 32,
- propertyCount,
- properties));
+ xcb_change_property(xcb_connection(),
+ XCB_PROP_MODE_REPLACE,
+ m_window,
+ atom(QXcbAtom::WM_PROTOCOLS),
+ XCB_ATOM_ATOM,
+ 32,
+ propertyCount,
+ properties);
m_syncValue.hi = 0;
m_syncValue.lo = 0;
const QByteArray wmClass = QXcbIntegration::instance()->wmClass();
if (!wmClass.isEmpty()) {
- Q_XCB_CALL(xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE,
- m_window, atom(QXcbAtom::WM_CLASS),
- XCB_ATOM_STRING, 8, wmClass.size(), wmClass.constData()));
+ xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE,
+ m_window, atom(QXcbAtom::WM_CLASS),
+ XCB_ATOM_STRING, 8, wmClass.size(), wmClass.constData());
}
if (m_usingSyncProtocol) {
m_syncCounter = xcb_generate_id(xcb_connection());
- Q_XCB_CALL(xcb_sync_create_counter(xcb_connection(), m_syncCounter, m_syncValue));
+ xcb_sync_create_counter(xcb_connection(), m_syncCounter, m_syncValue);
- Q_XCB_CALL(xcb_change_property(xcb_connection(),
- XCB_PROP_MODE_REPLACE,
- m_window,
- atom(QXcbAtom::_NET_WM_SYNC_REQUEST_COUNTER),
- XCB_ATOM_CARDINAL,
- 32,
- 1,
- &m_syncCounter));
+ xcb_change_property(xcb_connection(),
+ XCB_PROP_MODE_REPLACE,
+ m_window,
+ atom(QXcbAtom::_NET_WM_SYNC_REQUEST_COUNTER),
+ XCB_ATOM_CARDINAL,
+ 32,
+ 1,
+ &m_syncCounter);
}
// set the PID to let the WM kill the application if unresponsive
quint32 pid = getpid();
- Q_XCB_CALL(xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
- atom(QXcbAtom::_NET_WM_PID), XCB_ATOM_CARDINAL, 32,
- 1, &pid));
+ xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
+ atom(QXcbAtom::_NET_WM_PID), XCB_ATOM_CARDINAL, 32,
+ 1, &pid);
xcb_wm_hints_t hints;
memset(&hints, 0, sizeof(hints));
@@ -546,23 +509,27 @@ void QXcbWindow::create()
xcb_set_wm_hints(xcb_connection(), m_window, &hints);
xcb_window_t leader = connection()->clientLeader();
- Q_XCB_CALL(xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
- atom(QXcbAtom::WM_CLIENT_LEADER), XCB_ATOM_WINDOW, 32,
- 1, &leader));
+ xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
+ atom(QXcbAtom::WM_CLIENT_LEADER), XCB_ATOM_WINDOW, 32,
+ 1, &leader);
/* Add XEMBED info; this operation doesn't initiate the embedding. */
quint32 data[] = { XEMBED_VERSION, XEMBED_MAPPED };
- Q_XCB_CALL(xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
- atom(QXcbAtom::_XEMBED_INFO),
- atom(QXcbAtom::_XEMBED_INFO),
- 32, 2, (void *)data));
-
+ xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
+ atom(QXcbAtom::_XEMBED_INFO),
+ atom(QXcbAtom::_XEMBED_INFO),
+ 32, 2, (void *)data);
#if QT_CONFIG(xinput2)
- connection()->xi2Select(m_window);
+ if (connection()->hasXInput2()) {
+ if (connection()->xi2MouseEventsDisabled())
+ connection()->xi2SelectDeviceEventsCompatibility(m_window);
+ else
+ connection()->xi2SelectDeviceEvents(m_window);
+ }
#endif
- setWindowState(window()->windowState());
+ setWindowState(window()->windowStates());
setWindowFlags(window()->flags());
setWindowTitle(window()->title());
@@ -571,7 +538,7 @@ void QXcbWindow::create()
#if QT_CONFIG(xcb_xlib)
// force sync to read outstanding requests - see QTBUG-29106
- XSync(DISPLAY_FROM_XCB(platformScreen), false);
+ XSync(static_cast<Display*>(platformScreen->connection()->xlib_display()), false);
#endif
#ifndef QT_NO_DRAGANDDROP
@@ -581,6 +548,9 @@ void QXcbWindow::create()
const qreal opacity = qt_window_private(window())->opacity;
if (!qFuzzyCompare(opacity, qreal(1.0)))
setOpacity(opacity);
+
+ setMask(QHighDpi::toNativeLocalRegion(window()->mask(), window()));
+
if (window()->isTopLevel())
setWindowIcon(window()->icon());
@@ -615,10 +585,10 @@ void QXcbWindow::destroy()
if (connection()->focusWindow() == this)
doFocusOut();
if (connection()->mouseGrabber() == this)
- connection()->setMouseGrabber(Q_NULLPTR);
+ connection()->setMouseGrabber(nullptr);
if (m_syncCounter && m_usingSyncProtocol)
- Q_XCB_CALL(xcb_sync_destroy_counter(xcb_connection(), m_syncCounter));
+ xcb_sync_destroy_counter(xcb_connection(), m_syncCounter);
if (m_window) {
if (m_netWmUserTimeWindow) {
xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_NET_WM_USER_TIME_WINDOW));
@@ -629,7 +599,7 @@ void QXcbWindow::destroy()
m_netWmUserTimeWindow = XCB_NONE;
}
connection()->removeWindowEventListener(m_window);
- Q_XCB_CALL(xcb_destroy_window(xcb_connection(), m_window));
+ xcb_destroy_window(xcb_connection(), m_window);
m_window = 0;
}
if (m_cmap) {
@@ -664,7 +634,7 @@ void QXcbWindow::setGeometry(const QRect &rect)
qBound<qint32>(1, wmGeometry.width(), XCOORD_MAX),
qBound<qint32>(1, wmGeometry.height(), XCOORD_MAX),
};
- Q_XCB_CALL(xcb_configure_window(xcb_connection(), m_window, mask, reinterpret_cast<const quint32*>(values)));
+ xcb_configure_window(xcb_connection(), m_window, mask, reinterpret_cast<const quint32*>(values));
} else {
const quint32 mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
const qint32 values[] = {
@@ -673,7 +643,16 @@ void QXcbWindow::setGeometry(const QRect &rect)
qBound<qint32>(1, wmGeometry.width(), XCOORD_MAX),
qBound<qint32>(1, wmGeometry.height(), XCOORD_MAX),
};
- Q_XCB_CALL(xcb_configure_window(xcb_connection(), m_window, mask, reinterpret_cast<const quint32*>(values)));
+ xcb_configure_window(xcb_connection(), m_window, mask, reinterpret_cast<const quint32*>(values));
+ if (window()->parent() && !window()->transientParent()) {
+ // Wait for server reply for parented windows to ensure that a few window
+ // moves will come as a one event. This is important when native widget is
+ // moved a few times in X and Y directions causing native scroll. Widget
+ // must get single event to not trigger unwanted widget position changes
+ // and then expose events causing backingstore flushes with incorrect
+ // offset causing image crruption.
+ connection()->sync();
+ }
}
xcb_flush(xcb_connection());
@@ -683,12 +662,10 @@ QMargins QXcbWindow::frameMargins() const
{
if (m_dirtyFrameMargins) {
if (connection()->wmSupport()->isSupportedByWM(atom(QXcbAtom::_NET_FRAME_EXTENTS))) {
- xcb_get_property_cookie_t cookie = xcb_get_property(xcb_connection(), false, m_window,
- atom(QXcbAtom::_NET_FRAME_EXTENTS), XCB_ATOM_CARDINAL, 0, 4);
- QScopedPointer<xcb_get_property_reply_t, QScopedPointerPodDeleter> reply(
- xcb_get_property_reply(xcb_connection(), cookie, NULL));
+ auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(), false, m_window,
+ atom(QXcbAtom::_NET_FRAME_EXTENTS), XCB_ATOM_CARDINAL, 0, 4);
if (reply && reply->type == XCB_ATOM_CARDINAL && reply->format == 32 && reply->value_len == 4) {
- quint32 *data = (quint32 *)xcb_get_property_value(reply.data());
+ quint32 *data = (quint32 *)xcb_get_property_value(reply.get());
// _NET_FRAME_EXTENTS format is left, right, top, bottom
m_frameMargins = QMargins(data[0], data[2], data[1], data[3]);
m_dirtyFrameMargins = false;
@@ -707,9 +684,7 @@ QMargins QXcbWindow::frameMargins() const
connection()->wmSupport()->virtualRoots();
while (!foundRoot) {
- xcb_query_tree_cookie_t cookie = xcb_query_tree_unchecked(xcb_connection(), parent);
-
- xcb_query_tree_reply_t *reply = xcb_query_tree_reply(xcb_connection(), cookie, NULL);
+ auto reply = Q_XCB_REPLY_UNCHECKED(xcb_query_tree, xcb_connection(), parent);
if (reply) {
if (reply->root == reply->parent || virtualRoots.indexOf(reply->parent) != -1 || reply->parent == XCB_WINDOW_NONE) {
foundRoot = true;
@@ -717,8 +692,6 @@ QMargins QXcbWindow::frameMargins() const
window = parent;
parent = reply->parent;
}
-
- free(reply);
} else {
m_dirtyFrameMargins = false;
m_frameMargins = QMargins();
@@ -728,23 +701,12 @@ QMargins QXcbWindow::frameMargins() const
QPoint offset;
- xcb_translate_coordinates_reply_t *reply =
- xcb_translate_coordinates_reply(
- xcb_connection(),
- xcb_translate_coordinates(xcb_connection(), window, parent, 0, 0),
- NULL);
-
+ auto reply = Q_XCB_REPLY(xcb_translate_coordinates, xcb_connection(), window, parent, 0, 0);
if (reply) {
offset = QPoint(reply->dst_x, reply->dst_y);
- free(reply);
}
- xcb_get_geometry_reply_t *geom =
- xcb_get_geometry_reply(
- xcb_connection(),
- xcb_get_geometry(xcb_connection(), parent),
- NULL);
-
+ auto geom = Q_XCB_REPLY(xcb_get_geometry, xcb_connection(), parent);
if (geom) {
// --
// add the border_width for the window managers frame... some window managers
@@ -761,8 +723,6 @@ QMargins QXcbWindow::frameMargins() const
int bottom = geom->height + geom->border_width - geometry().height() - offset.y();
m_frameMargins = QMargins(left, top, right, bottom);
-
- free(geom);
}
m_dirtyFrameMargins = false;
@@ -789,12 +749,13 @@ static inline bool testShowWithoutActivating(const QWindow *window)
void QXcbWindow::show()
{
if (window()->isTopLevel()) {
+
xcb_get_property_cookie_t cookie = xcb_get_wm_hints_unchecked(xcb_connection(), m_window);
xcb_wm_hints_t hints;
xcb_get_wm_hints_reply(xcb_connection(), cookie, &hints, NULL);
- if (window()->windowState() & Qt::WindowMinimized)
+ if (window()->windowStates() & Qt::WindowMinimized)
xcb_wm_hints_set_iconic(&hints);
else
xcb_wm_hints_set_normal(&hints);
@@ -820,13 +781,13 @@ void QXcbWindow::show()
if (!transientXcbParent)
transientXcbParent = connection()->clientLeader();
if (transientXcbParent) { // ICCCM 4.1.2.6
- Q_XCB_CALL(xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
- XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, 32,
- 1, &transientXcbParent));
+ xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
+ XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, 32,
+ 1, &transientXcbParent);
}
}
if (!transientXcbParent)
- Q_XCB_CALL(xcb_delete_property(xcb_connection(), m_window, XCB_ATOM_WM_TRANSIENT_FOR));
+ xcb_delete_property(xcb_connection(), m_window, XCB_ATOM_WM_TRANSIENT_FOR);
// update _MOTIF_WM_HINTS
updateMotifWmHintsBeforeMap();
@@ -843,7 +804,7 @@ void QXcbWindow::show()
if (window()->objectName() == QLatin1String("QSystemTrayIconSysWindow"))
return; // defer showing until XEMBED_EMBEDDED_NOTIFY
- Q_XCB_CALL(xcb_map_window(xcb_connection(), m_window));
+ xcb_map_window(xcb_connection(), m_window);
if (QGuiApplication::modalWindow() == window())
requestActivateWindow();
@@ -855,7 +816,7 @@ void QXcbWindow::show()
void QXcbWindow::hide()
{
- Q_XCB_CALL(xcb_unmap_window(xcb_connection(), m_window));
+ xcb_unmap_window(xcb_connection(), m_window);
// send synthetic UnmapNotify event according to icccm 4.1.4
Q_DECLARE_XCB_EVENT(event, xcb_unmap_notify_event_t);
@@ -863,18 +824,18 @@ void QXcbWindow::hide()
event.event = xcbScreen()->root();
event.window = m_window;
event.from_configure = false;
- Q_XCB_CALL(xcb_send_event(xcb_connection(), false, xcbScreen()->root(),
- XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&event));
+ xcb_send_event(xcb_connection(), false, xcbScreen()->root(),
+ XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&event);
xcb_flush(xcb_connection());
if (connection()->mouseGrabber() == this)
- connection()->setMouseGrabber(Q_NULLPTR);
+ connection()->setMouseGrabber(nullptr);
if (QPlatformWindow *w = connection()->mousePressWindow()) {
// Unset mousePressWindow when it (or one of its parents) is unmapped
while (w) {
if (w == this) {
- connection()->setMousePressWindow(Q_NULLPTR);
+ connection()->setMousePressWindow(nullptr);
break;
}
w = w->parent();
@@ -892,7 +853,7 @@ void QXcbWindow::hide()
// Find the top level window at cursor position.
// Don't use QGuiApplication::topLevelAt(): search only the virtual siblings of this window's screen
- QWindow *enterWindow = Q_NULLPTR;
+ QWindow *enterWindow = nullptr;
const auto screens = xcbScreen()->virtualSiblings();
for (QPlatformScreen *screen : screens) {
if (screen->geometry().contains(cursorPos)) {
@@ -1014,15 +975,11 @@ static QtMotifWmHints getMotifWmHints(QXcbConnection *c, xcb_window_t window)
{
QtMotifWmHints hints;
- xcb_get_property_cookie_t get_cookie =
- xcb_get_property_unchecked(c->xcb_connection(), 0, window, c->atom(QXcbAtom::_MOTIF_WM_HINTS),
- c->atom(QXcbAtom::_MOTIF_WM_HINTS), 0, 20);
-
- xcb_get_property_reply_t *reply =
- xcb_get_property_reply(c->xcb_connection(), get_cookie, NULL);
+ auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, c->xcb_connection(), 0, window,
+ c->atom(QXcbAtom::_MOTIF_WM_HINTS), c->atom(QXcbAtom::_MOTIF_WM_HINTS), 0, 20);
if (reply && reply->format == 32 && reply->type == c->atom(QXcbAtom::_MOTIF_WM_HINTS)) {
- hints = *((QtMotifWmHints *)xcb_get_property_value(reply));
+ hints = *((QtMotifWmHints *)xcb_get_property_value(reply.get()));
} else {
hints.flags = 0L;
hints.functions = MWM_FUNC_ALL;
@@ -1031,24 +988,22 @@ static QtMotifWmHints getMotifWmHints(QXcbConnection *c, xcb_window_t window)
hints.status = 0L;
}
- free(reply);
-
return hints;
}
static void setMotifWmHints(QXcbConnection *c, xcb_window_t window, const QtMotifWmHints &hints)
{
if (hints.flags != 0l) {
- Q_XCB_CALL2(xcb_change_property(c->xcb_connection(),
- XCB_PROP_MODE_REPLACE,
- window,
- c->atom(QXcbAtom::_MOTIF_WM_HINTS),
- c->atom(QXcbAtom::_MOTIF_WM_HINTS),
- 32,
- 5,
- &hints), c);
+ xcb_change_property(c->xcb_connection(),
+ XCB_PROP_MODE_REPLACE,
+ window,
+ c->atom(QXcbAtom::_MOTIF_WM_HINTS),
+ c->atom(QXcbAtom::_MOTIF_WM_HINTS),
+ 32,
+ 5,
+ &hints);
} else {
- Q_XCB_CALL2(xcb_delete_property(c->xcb_connection(), window, c->atom(QXcbAtom::_MOTIF_WM_HINTS)), c);
+ xcb_delete_property(c->xcb_connection(), window, c->atom(QXcbAtom::_MOTIF_WM_HINTS));
}
}
@@ -1056,15 +1011,12 @@ QXcbWindow::NetWmStates QXcbWindow::netWmStates()
{
NetWmStates result(0);
- xcb_get_property_cookie_t get_cookie =
- xcb_get_property_unchecked(xcb_connection(), 0, m_window, atom(QXcbAtom::_NET_WM_STATE),
- XCB_ATOM_ATOM, 0, 1024);
-
- xcb_get_property_reply_t *reply =
- xcb_get_property_reply(xcb_connection(), get_cookie, NULL);
+ auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, xcb_connection(),
+ 0, m_window, atom(QXcbAtom::_NET_WM_STATE),
+ XCB_ATOM_ATOM, 0, 1024);
if (reply && reply->format == 32 && reply->type == XCB_ATOM_ATOM) {
- const xcb_atom_t *states = static_cast<const xcb_atom_t *>(xcb_get_property_value(reply));
+ const xcb_atom_t *states = static_cast<const xcb_atom_t *>(xcb_get_property_value(reply.get()));
const xcb_atom_t *statesEnd = states + reply->length;
if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_ABOVE)))
result |= NetWmStateAbove;
@@ -1082,7 +1034,6 @@ QXcbWindow::NetWmStates QXcbWindow::netWmStates()
result |= NetWmStateStaysOnTop;
if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_DEMANDS_ATTENTION)))
result |= NetWmStateDemandsAttention;
- free(reply);
} else {
#ifdef NET_WM_STATE_DEBUG
printf("getting net wm state (%x), empty\n", m_window);
@@ -1096,21 +1047,15 @@ void QXcbWindow::setNetWmStates(NetWmStates states)
{
QVector<xcb_atom_t> atoms;
- xcb_get_property_cookie_t get_cookie =
- xcb_get_property_unchecked(xcb_connection(), 0, m_window, atom(QXcbAtom::_NET_WM_STATE),
- XCB_ATOM_ATOM, 0, 1024);
-
- xcb_get_property_reply_t *reply =
- xcb_get_property_reply(xcb_connection(), get_cookie, NULL);
-
+ auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, xcb_connection(),
+ 0, m_window, atom(QXcbAtom::_NET_WM_STATE),
+ XCB_ATOM_ATOM, 0, 1024);
if (reply && reply->format == 32 && reply->type == XCB_ATOM_ATOM && reply->value_len > 0) {
- const xcb_atom_t *data = static_cast<const xcb_atom_t *>(xcb_get_property_value(reply));
+ const xcb_atom_t *data = static_cast<const xcb_atom_t *>(xcb_get_property_value(reply.get()));
atoms.resize(reply->value_len);
memcpy((void *)&atoms.first(), (void *)data, reply->value_len * sizeof(xcb_atom_t));
}
- free(reply);
-
if (states & NetWmStateAbove && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_ABOVE)))
atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_ABOVE));
if (states & NetWmStateBelow && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_BELOW)))
@@ -1129,11 +1074,11 @@ void QXcbWindow::setNetWmStates(NetWmStates states)
atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_DEMANDS_ATTENTION));
if (atoms.isEmpty()) {
- Q_XCB_CALL(xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_NET_WM_STATE)));
+ xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_NET_WM_STATE));
} else {
- Q_XCB_CALL(xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
- atom(QXcbAtom::_NET_WM_STATE), XCB_ATOM_ATOM, 32,
- atoms.count(), atoms.constData()));
+ xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
+ atom(QXcbAtom::_NET_WM_STATE), XCB_ATOM_ATOM, 32,
+ atoms.count(), atoms.constData());
}
xcb_flush(xcb_connection());
}
@@ -1256,67 +1201,48 @@ void QXcbWindow::changeNetWmState(bool set, xcb_atom_t one, xcb_atom_t two)
event.data.data32[3] = 0;
event.data.data32[4] = 0;
- Q_XCB_CALL(xcb_send_event(xcb_connection(), 0, xcbScreen()->root(), XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&event));
+ xcb_send_event(xcb_connection(), 0, xcbScreen()->root(),
+ XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
+ (const char *)&event);
}
-void QXcbWindow::setWindowState(Qt::WindowState state)
+void QXcbWindow::setWindowState(Qt::WindowStates state)
{
if (state == m_windowState)
return;
- // unset old state
- switch (m_windowState) {
- case Qt::WindowMinimized:
- Q_XCB_CALL(xcb_map_window(xcb_connection(), m_window));
- break;
- case Qt::WindowMaximized:
- changeNetWmState(false,
- atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_HORZ),
- atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_VERT));
- break;
- case Qt::WindowFullScreen:
- changeNetWmState(false, atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN));
- break;
- default:
- break;
+ if ((m_windowState & Qt::WindowMinimized) && !(state & Qt::WindowMinimized)) {
+ xcb_map_window(xcb_connection(), m_window);
+ } else if (!(m_windowState & Qt::WindowMinimized) && (state & Qt::WindowMinimized)) {
+ xcb_client_message_event_t event;
+
+ event.response_type = XCB_CLIENT_MESSAGE;
+ event.format = 32;
+ event.sequence = 0;
+ event.window = m_window;
+ event.type = atom(QXcbAtom::WM_CHANGE_STATE);
+ event.data.data32[0] = XCB_WM_STATE_ICONIC;
+ event.data.data32[1] = 0;
+ event.data.data32[2] = 0;
+ event.data.data32[3] = 0;
+ event.data.data32[4] = 0;
+
+ xcb_send_event(xcb_connection(), 0, xcbScreen()->root(),
+ XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
+ (const char *)&event);
+ m_minimized = true;
}
- // set new state
- switch (state) {
- case Qt::WindowMinimized:
- {
- xcb_client_message_event_t event;
-
- event.response_type = XCB_CLIENT_MESSAGE;
- event.format = 32;
- event.sequence = 0;
- event.window = m_window;
- event.type = atom(QXcbAtom::WM_CHANGE_STATE);
- event.data.data32[0] = XCB_WM_STATE_ICONIC;
- event.data.data32[1] = 0;
- event.data.data32[2] = 0;
- event.data.data32[3] = 0;
- event.data.data32[4] = 0;
-
- Q_XCB_CALL(xcb_send_event(xcb_connection(), 0, xcbScreen()->root(), XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&event));
- }
- break;
- case Qt::WindowMaximized:
- changeNetWmState(true,
- atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_HORZ),
+ if ((m_windowState ^ state) & Qt::WindowMaximized) {
+ changeNetWmState(state & Qt::WindowMaximized, atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_HORZ),
atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_VERT));
- break;
- case Qt::WindowFullScreen:
- changeNetWmState(true, atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN));
- break;
- case Qt::WindowNoState:
- break;
- default:
- break;
}
- connection()->sync();
+ if ((m_windowState ^ state) & Qt::WindowFullScreen) {
+ changeNetWmState(state & Qt::WindowFullScreen, atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN));
+ }
+ connection()->sync();
m_windowState = state;
}
@@ -1388,10 +1314,10 @@ void QXcbWindow::updateNetWmStateBeforeMap()
states |= NetWmStateBelow;
}
- if (window()->windowState() & Qt::WindowFullScreen)
+ if (window()->windowStates() & Qt::WindowFullScreen)
states |= NetWmStateFullScreen;
- if (window()->windowState() & Qt::WindowMaximized) {
+ if (window()->windowStates() & Qt::WindowMaximized) {
states |= NetWmStateMaximizedHorz;
states |= NetWmStateMaximizedVert;
}
@@ -1424,30 +1350,30 @@ void QXcbWindow::updateNetWmUserTime(xcb_timestamp_t timestamp)
if (m_netWmUserTimeWindow || isSupportedByWM) {
if (!m_netWmUserTimeWindow) {
m_netWmUserTimeWindow = xcb_generate_id(xcb_connection());
- Q_XCB_CALL(xcb_create_window(xcb_connection(),
- XCB_COPY_FROM_PARENT, // depth -- same as root
- m_netWmUserTimeWindow, // window id
- m_window, // parent window id
- -1, -1, 1, 1,
- 0, // border width
- XCB_WINDOW_CLASS_INPUT_OUTPUT, // window class
- m_visualId, // visual
- 0, // value mask
- 0)); // value list
+ xcb_create_window(xcb_connection(),
+ XCB_COPY_FROM_PARENT, // depth -- same as root
+ m_netWmUserTimeWindow, // window id
+ m_window, // parent window id
+ -1, -1, 1, 1,
+ 0, // border width
+ XCB_WINDOW_CLASS_INPUT_OUTPUT, // window class
+ m_visualId, // visual
+ 0, // value mask
+ 0); // value list
wid = m_netWmUserTimeWindow;
xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window, atom(QXcbAtom::_NET_WM_USER_TIME_WINDOW),
XCB_ATOM_WINDOW, 32, 1, &m_netWmUserTimeWindow);
xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_NET_WM_USER_TIME));
#ifndef QT_NO_DEBUG
QByteArray ba("Qt NET_WM user time window");
- Q_XCB_CALL(xcb_change_property(xcb_connection(),
- XCB_PROP_MODE_REPLACE,
- m_netWmUserTimeWindow,
- atom(QXcbAtom::_NET_WM_NAME),
- atom(QXcbAtom::UTF8_STRING),
- 8,
- ba.length(),
- ba.constData()));
+ xcb_change_property(xcb_connection(),
+ XCB_PROP_MODE_REPLACE,
+ m_netWmUserTimeWindow,
+ atom(QXcbAtom::_NET_WM_NAME),
+ atom(QXcbAtom::UTF8_STRING),
+ 8,
+ ba.length(),
+ ba.constData());
#endif
} else if (!isSupportedByWM) {
// WM no longer supports it, then we should remove the
@@ -1521,26 +1447,27 @@ void QXcbWindow::setParent(const QPlatformWindow *parent)
xcb_parent_id = xcbScreen()->root();
m_embedded = false;
}
- Q_XCB_CALL(xcb_reparent_window(xcb_connection(), xcb_window(), xcb_parent_id, topLeft.x(), topLeft.y()));
+ xcb_reparent_window(xcb_connection(), xcb_window(), xcb_parent_id, topLeft.x(), topLeft.y());
}
void QXcbWindow::setWindowTitle(const QString &title)
{
QString fullTitle = formatWindowTitle(title, QString::fromUtf8(" \xe2\x80\x94 ")); // unicode character U+2014, EM DASH
const QByteArray ba = std::move(fullTitle).toUtf8();
- Q_XCB_CALL(xcb_change_property(xcb_connection(),
+ xcb_change_property(xcb_connection(),
XCB_PROP_MODE_REPLACE,
m_window,
atom(QXcbAtom::_NET_WM_NAME),
atom(QXcbAtom::UTF8_STRING),
8,
ba.length(),
- ba.constData()));
+ ba.constData());
#if QT_CONFIG(xcb_xlib)
- XTextProperty *text = qstringToXTP(DISPLAY_FROM_XCB(this), title);
+ Display *dpy = static_cast<Display *>(connection()->xlib_display());
+ XTextProperty *text = qstringToXTP(dpy, title);
if (text)
- XSetWMName(DISPLAY_FROM_XCB(this), m_window, text);
+ XSetWMName(dpy, m_window, text);
#endif
xcb_flush(xcb_connection());
}
@@ -1548,14 +1475,14 @@ void QXcbWindow::setWindowTitle(const QString &title)
void QXcbWindow::setWindowIconText(const QString &title)
{
const QByteArray ba = title.toUtf8();
- Q_XCB_CALL(xcb_change_property(xcb_connection(),
- XCB_PROP_MODE_REPLACE,
- m_window,
- atom(QXcbAtom::_NET_WM_ICON_NAME),
- atom(QXcbAtom::UTF8_STRING),
- 8,
- ba.length(),
- ba.constData()));
+ xcb_change_property(xcb_connection(),
+ XCB_PROP_MODE_REPLACE,
+ m_window,
+ atom(QXcbAtom::_NET_WM_ICON_NAME),
+ atom(QXcbAtom::UTF8_STRING),
+ 8,
+ ba.length(),
+ ba.constData());
}
void QXcbWindow::setWindowIcon(const QIcon &icon)
@@ -1585,18 +1512,18 @@ void QXcbWindow::setWindowIcon(const QIcon &icon)
}
if (!icon_data.isEmpty()) {
- Q_XCB_CALL(xcb_change_property(xcb_connection(),
- XCB_PROP_MODE_REPLACE,
- m_window,
- atom(QXcbAtom::_NET_WM_ICON),
- atom(QXcbAtom::CARDINAL),
- 32,
- icon_data.size(),
- (unsigned char *) icon_data.data()));
+ xcb_change_property(xcb_connection(),
+ XCB_PROP_MODE_REPLACE,
+ m_window,
+ atom(QXcbAtom::_NET_WM_ICON),
+ atom(QXcbAtom::CARDINAL),
+ 32,
+ icon_data.size(),
+ (unsigned char *) icon_data.data());
} else {
- Q_XCB_CALL(xcb_delete_property(xcb_connection(),
- m_window,
- atom(QXcbAtom::_NET_WM_ICON)));
+ xcb_delete_property(xcb_connection(),
+ m_window,
+ atom(QXcbAtom::_NET_WM_ICON));
}
}
@@ -1604,14 +1531,14 @@ void QXcbWindow::raise()
{
const quint32 mask = XCB_CONFIG_WINDOW_STACK_MODE;
const quint32 values[] = { XCB_STACK_MODE_ABOVE };
- Q_XCB_CALL(xcb_configure_window(xcb_connection(), m_window, mask, values));
+ xcb_configure_window(xcb_connection(), m_window, mask, values);
}
void QXcbWindow::lower()
{
const quint32 mask = XCB_CONFIG_WINDOW_STACK_MODE;
const quint32 values[] = { XCB_STACK_MODE_BELOW };
- Q_XCB_CALL(xcb_configure_window(xcb_connection(), m_window, mask, values));
+ xcb_configure_window(xcb_connection(), m_window, mask, values);
}
// Adapt the geometry to match the WM expection with regards
@@ -1705,9 +1632,11 @@ void QXcbWindow::requestActivateWindow()
event.data.data32[3] = 0;
event.data.data32[4] = 0;
- Q_XCB_CALL(xcb_send_event(xcb_connection(), 0, xcbScreen()->root(), XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&event));
+ xcb_send_event(xcb_connection(), 0, xcbScreen()->root(),
+ XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
+ (const char *)&event);
} else {
- Q_XCB_CALL(xcb_set_input_focus(xcb_connection(), XCB_INPUT_FOCUS_PARENT, m_window, connection()->time()));
+ xcb_set_input_focus(xcb_connection(), XCB_INPUT_FOCUS_PARENT, m_window, connection()->time());
}
connection()->sync();
@@ -1751,15 +1680,11 @@ QXcbWindowFunctions::WmWindowTypes QXcbWindow::wmWindowTypes() const
{
QXcbWindowFunctions::WmWindowTypes result(0);
- xcb_get_property_cookie_t get_cookie =
- xcb_get_property_unchecked(xcb_connection(), 0, m_window, atom(QXcbAtom::_NET_WM_WINDOW_TYPE),
- XCB_ATOM_ATOM, 0, 1024);
-
- xcb_get_property_reply_t *reply =
- xcb_get_property_reply(xcb_connection(), get_cookie, NULL);
-
+ auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, xcb_connection(),
+ 0, m_window, atom(QXcbAtom::_NET_WM_WINDOW_TYPE),
+ XCB_ATOM_ATOM, 0, 1024);
if (reply && reply->format == 32 && reply->type == XCB_ATOM_ATOM) {
- const xcb_atom_t *types = static_cast<const xcb_atom_t *>(xcb_get_property_value(reply));
+ const xcb_atom_t *types = static_cast<const xcb_atom_t *>(xcb_get_property_value(reply.get()));
const xcb_atom_t *types_end = types + reply->length;
for (; types != types_end; types++) {
QXcbAtom::Atom type = connection()->qatom(*types);
@@ -1813,7 +1738,6 @@ QXcbWindowFunctions::WmWindowTypes QXcbWindow::wmWindowTypes() const
break;
}
}
- free(reply);
}
return result;
}
@@ -1896,20 +1820,20 @@ void QXcbWindow::setWmWindowType(QXcbWindowFunctions::WmWindowTypes types, Qt::W
atoms.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_NORMAL));
if (atoms.isEmpty()) {
- Q_XCB_CALL(xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_NET_WM_WINDOW_TYPE)));
+ xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_NET_WM_WINDOW_TYPE));
} else {
- Q_XCB_CALL(xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
- atom(QXcbAtom::_NET_WM_WINDOW_TYPE), XCB_ATOM_ATOM, 32,
- atoms.count(), atoms.constData()));
+ xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
+ atom(QXcbAtom::_NET_WM_WINDOW_TYPE), XCB_ATOM_ATOM, 32,
+ atoms.count(), atoms.constData());
}
xcb_flush(xcb_connection());
}
void QXcbWindow::setWmWindowRole(const QByteArray &role)
{
- Q_XCB_CALL(xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
- atom(QXcbAtom::WM_WINDOW_ROLE), XCB_ATOM_STRING, 8,
- role.size(), role.constData()));
+ xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
+ atom(QXcbAtom::WM_WINDOW_ROLE), XCB_ATOM_STRING, 8,
+ role.size(), role.constData());
}
void QXcbWindow::setParentRelativeBackPixmapStatic(QWindow *window)
@@ -1922,7 +1846,7 @@ void QXcbWindow::setParentRelativeBackPixmap()
{
const quint32 mask = XCB_CW_BACK_PIXMAP;
const quint32 values[] = { XCB_BACK_PIXMAP_PARENT_RELATIVE };
- Q_XCB_CALL(xcb_change_window_attributes(xcb_connection(), m_window, mask, values));
+ xcb_change_window_attributes(xcb_connection(), m_window, mask, values);
}
bool QXcbWindow::requestSystemTrayWindowDockStatic(const QWindow *window)
@@ -2013,11 +1937,7 @@ void QXcbWindow::handleExposeEvent(const xcb_expose_event_t *event)
{
QRect rect(event->x, event->y, event->width, event->height);
- if (m_exposeRegion.isEmpty())
- m_exposeRegion = rect;
- else
- m_exposeRegion |= rect;
-
+ m_exposeRegion |= rect;
bool pending = compressExposeEvent(m_exposeRegion);
// if count is non-zero there are more expose events pending
@@ -2033,13 +1953,14 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even
return;
if (event->type == atom(QXcbAtom::WM_PROTOCOLS)) {
- if (event->data.data32[0] == atom(QXcbAtom::WM_DELETE_WINDOW)) {
+ xcb_atom_t protocolAtom = event->data.data32[0];
+ if (protocolAtom == atom(QXcbAtom::WM_DELETE_WINDOW)) {
QWindowSystemInterface::handleCloseEvent(window());
- } else if (event->data.data32[0] == atom(QXcbAtom::WM_TAKE_FOCUS)) {
+ } else if (protocolAtom == atom(QXcbAtom::WM_TAKE_FOCUS)) {
connection()->setTime(event->data.data32[1]);
relayFocusToModalWindow();
return;
- } else if (event->data.data32[0] == atom(QXcbAtom::_NET_WM_PING)) {
+ } else if (protocolAtom == atom(QXcbAtom::_NET_WM_PING)) {
if (event->window == xcbScreen()->root())
return;
@@ -2048,20 +1969,23 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even
reply.response_type = XCB_CLIENT_MESSAGE;
reply.window = xcbScreen()->root();
- xcb_send_event(xcb_connection(), 0, xcbScreen()->root(), XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&reply);
+ xcb_send_event(xcb_connection(), 0, xcbScreen()->root(),
+ XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
+ (const char *)&reply);
xcb_flush(xcb_connection());
- } else if (event->data.data32[0] == atom(QXcbAtom::_NET_WM_SYNC_REQUEST)) {
+ } else if (protocolAtom == atom(QXcbAtom::_NET_WM_SYNC_REQUEST)) {
connection()->setTime(event->data.data32[1]);
m_syncValue.lo = event->data.data32[2];
m_syncValue.hi = event->data.data32[3];
if (m_usingSyncProtocol)
m_syncState = SyncReceived;
#ifndef QT_NO_WHATSTHIS
- } else if (event->data.data32[0] == atom(QXcbAtom::_NET_WM_CONTEXT_HELP)) {
+ } else if (protocolAtom == atom(QXcbAtom::_NET_WM_CONTEXT_HELP)) {
QWindowSystemInterface::handleEnterWhatsThisEvent();
#endif
} else {
- qWarning() << "QXcbWindow: Unhandled WM_PROTOCOLS message:" << connection()->atomName(event->data.data32[0]);
+ qCWarning(lcQpaXcb, "Unhandled WM_PROTOCOLS (%s)",
+ connection()->atomName(protocolAtom).constData());
}
#ifndef QT_NO_DRAGANDDROP
} else if (event->type == atom(QXcbAtom::XdndEnter)) {
@@ -2089,7 +2013,7 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even
|| event->type == atom(QXcbAtom::_GTK_LOAD_ICONTHEMES)) {
//silence the _COMPIZ and _GTK messages for now
} else {
- qWarning() << "QXcbWindow: Unhandled client message:" << connection()->atomName(event->type);
+ qCWarning(lcQpaXcb) << "Unhandled client message: " << connection()->atomName(event->type);
}
}
@@ -2099,13 +2023,11 @@ void QXcbWindow::handleConfigureNotifyEvent(const xcb_configure_notify_event_t *
QPoint pos(event->x, event->y);
if (!parent() && !fromSendEvent) {
// Do not trust the position, query it instead.
- xcb_translate_coordinates_cookie_t cookie = xcb_translate_coordinates(xcb_connection(), xcb_window(),
- xcbScreen()->root(), 0, 0);
- xcb_translate_coordinates_reply_t *reply = xcb_translate_coordinates_reply(xcb_connection(), cookie, NULL);
+ auto reply = Q_XCB_REPLY(xcb_translate_coordinates, xcb_connection(),
+ xcb_window(), xcbScreen()->root(), 0, 0);
if (reply) {
pos.setX(reply->dst_x);
pos.setY(reply->dst_y);
- free(reply);
}
}
@@ -2114,14 +2036,6 @@ void QXcbWindow::handleConfigureNotifyEvent(const xcb_configure_notify_event_t *
if (!newScreen)
return;
- // Persist the actual geometry so that QWindow::geometry() can
- // be queried in the resize event.
- QPlatformWindow::setGeometry(actualGeometry);
-
- // FIXME: In the case of the requestedGeometry not matching the actualGeometry due
- // to e.g. the window manager applying restrictions to the geometry, the application
- // will never see a move/resize event if the actualGeometry is the same as the current
- // geometry, and may think the requested geometry was fulfilled.
QWindowSystemInterface::handleGeometryChange(window(), actualGeometry);
// QPlatformScreen::screen() is updated asynchronously, so we can't compare it
@@ -2160,15 +2074,12 @@ QPoint QXcbWindow::mapToGlobal(const QPoint &pos) const
return QPlatformWindow::mapToGlobal(pos);
QPoint ret;
- xcb_translate_coordinates_cookie_t cookie =
- xcb_translate_coordinates(xcb_connection(), xcb_window(), xcbScreen()->root(),
- pos.x(), pos.y());
- xcb_translate_coordinates_reply_t *reply =
- xcb_translate_coordinates_reply(xcb_connection(), cookie, NULL);
+ auto reply = Q_XCB_REPLY(xcb_translate_coordinates, xcb_connection(),
+ xcb_window(), xcbScreen()->root(),
+ pos.x(), pos.y());
if (reply) {
ret.setX(reply->dst_x);
ret.setY(reply->dst_y);
- free(reply);
}
return ret;
@@ -2180,15 +2091,12 @@ QPoint QXcbWindow::mapFromGlobal(const QPoint &pos) const
return QPlatformWindow::mapFromGlobal(pos);
QPoint ret;
- xcb_translate_coordinates_cookie_t cookie =
- xcb_translate_coordinates(xcb_connection(), xcbScreen()->root(), xcb_window(),
- pos.x(), pos.y());
- xcb_translate_coordinates_reply_t *reply =
- xcb_translate_coordinates_reply(xcb_connection(), cookie, NULL);
+ auto reply = Q_XCB_REPLY(xcb_translate_coordinates, xcb_connection(),
+ xcbScreen()->root(), xcb_window(),
+ pos.x(), pos.y());
if (reply) {
ret.setX(reply->dst_x);
ret.setY(reply->dst_y);
- free(reply);
}
return ret;
@@ -2214,7 +2122,8 @@ void QXcbWindow::handleUnmapNotifyEvent(const xcb_unmap_notify_event_t *event)
}
void QXcbWindow::handleButtonPressEvent(int event_x, int event_y, int root_x, int root_y,
- int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp, Qt::MouseEventSource source)
+ int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp,
+ QEvent::Type type, Qt::MouseEventSource source)
{
const bool isWheel = detail >= 4 && detail <= 7;
if (!isWheel && window() != QGuiApplication::focusWindow()) {
@@ -2240,26 +2149,35 @@ void QXcbWindow::handleButtonPressEvent(int event_x, int event_y, int root_x, in
QPoint global(root_x, root_y);
if (isWheel) {
+#if QT_CONFIG(xinput2)
if (!connection()->isAtLeastXI21()) {
- // Logic borrowed from qapplication_x11.cpp
- int delta = 120 * ((detail == 4 || detail == 6) ? 1 : -1);
- bool hor = (((detail == 4 || detail == 5)
- && (modifiers & Qt::AltModifier))
- || (detail == 6 || detail == 7));
-
- QWindowSystemInterface::handleWheelEvent(window(), timestamp,
- local, global, delta, hor ? Qt::Horizontal : Qt::Vertical, modifiers);
+#endif
+ QPoint angleDelta;
+ if (detail == 4)
+ angleDelta.setY(120);
+ else if (detail == 5)
+ angleDelta.setY(-120);
+ else if (detail == 6)
+ angleDelta.setX(120);
+ else if (detail == 7)
+ angleDelta.setX(-120);
+ if (modifiers & Qt::AltModifier)
+ std::swap(angleDelta.rx(), angleDelta.ry());
+ QWindowSystemInterface::handleWheelEvent(window(), timestamp, local, global, QPoint(), angleDelta, modifiers);
+#if QT_CONFIG(xinput2)
}
+#endif
return;
}
connection()->setMousePressWindow(this);
- handleMouseEvent(timestamp, local, global, modifiers, source);
+ handleMouseEvent(timestamp, local, global, modifiers, type, source);
}
void QXcbWindow::handleButtonReleaseEvent(int event_x, int event_y, int root_x, int root_y,
- int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp, Qt::MouseEventSource source)
+ int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp,
+ QEvent::Type type, Qt::MouseEventSource source)
{
QPoint local(event_x, event_y);
QPoint global(root_x, root_y);
@@ -2269,10 +2187,10 @@ void QXcbWindow::handleButtonReleaseEvent(int event_x, int event_y, int root_x,
return;
}
- if (connection()->buttons() == Qt::NoButton)
- connection()->setMousePressWindow(Q_NULLPTR);
+ if (connection()->buttonState() == Qt::NoButton)
+ connection()->setMousePressWindow(nullptr);
- handleMouseEvent(timestamp, local, global, modifiers, source);
+ handleMouseEvent(timestamp, local, global, modifiers, type, source);
}
static inline bool doCheckUnGrabAncestor(QXcbConnection *conn)
@@ -2284,9 +2202,10 @@ static inline bool doCheckUnGrabAncestor(QXcbConnection *conn)
* not pressed, otherwise (e.g. on Alt+Tab) it can igonre important enter/leave events.
*/
if (conn) {
- const bool mouseButtonsPressed = (conn->buttons() != Qt::NoButton);
-#ifdef XCB_USE_XINPUT22
- return mouseButtonsPressed || (conn->isAtLeastXI22() && conn->xi2MouseEvents());
+
+ const bool mouseButtonsPressed = (conn->buttonState() != Qt::NoButton);
+#if QT_CONFIG(xinput2)
+ return mouseButtonsPressed || (conn->hasXInput2() && !conn->xi2MouseEventsDisabled());
#else
return mouseButtonsPressed;
#endif
@@ -2294,7 +2213,7 @@ static inline bool doCheckUnGrabAncestor(QXcbConnection *conn)
return true;
}
-static bool ignoreLeaveEvent(quint8 mode, quint8 detail, QXcbConnection *conn = Q_NULLPTR)
+static bool ignoreLeaveEvent(quint8 mode, quint8 detail, QXcbConnection *conn = nullptr)
{
return ((doCheckUnGrabAncestor(conn)
&& mode == XCB_NOTIFY_MODE_GRAB && detail == XCB_NOTIFY_DETAIL_ANCESTOR)
@@ -2303,7 +2222,7 @@ static bool ignoreLeaveEvent(quint8 mode, quint8 detail, QXcbConnection *conn =
|| detail == XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL);
}
-static bool ignoreEnterEvent(quint8 mode, quint8 detail, QXcbConnection *conn = Q_NULLPTR)
+static bool ignoreEnterEvent(quint8 mode, quint8 detail, QXcbConnection *conn = nullptr)
{
return ((doCheckUnGrabAncestor(conn)
&& mode == XCB_NOTIFY_MODE_UNGRAB && detail == XCB_NOTIFY_DETAIL_ANCESTOR)
@@ -2335,7 +2254,8 @@ void QXcbWindow::handleEnterNotifyEvent(int event_x, int event_y, int root_x, in
{
connection()->setTime(timestamp);
#ifdef XCB_USE_XINPUT21
- connection()->handleEnterEvent();
+ // Updates scroll valuators, as user might have done some scrolling outside our X client.
+ connection()->xi2UpdateScrollingDevices();
#endif
const QPoint global = QPoint(root_x, root_y);
@@ -2371,51 +2291,51 @@ void QXcbWindow::handleLeaveNotifyEvent(int root_x, int root_y,
}
void QXcbWindow::handleMotionNotifyEvent(int event_x, int event_y, int root_x, int root_y,
- Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp, Qt::MouseEventSource source)
+ Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp,
+ QEvent::Type type, Qt::MouseEventSource source)
{
QPoint local(event_x, event_y);
QPoint global(root_x, root_y);
// "mousePressWindow" can be NULL i.e. if a window will be grabbed or unmapped, so set it again here.
// Unset "mousePressWindow" when mouse button isn't pressed - in some cases the release event won't arrive.
- const bool isMouseButtonPressed = (connection()->buttons() != Qt::NoButton);
- const bool hasMousePressWindow = (connection()->mousePressWindow() != Q_NULLPTR);
+ const bool isMouseButtonPressed = (connection()->buttonState() != Qt::NoButton);
+ const bool hasMousePressWindow = (connection()->mousePressWindow() != nullptr);
if (isMouseButtonPressed && !hasMousePressWindow)
connection()->setMousePressWindow(this);
else if (hasMousePressWindow && !isMouseButtonPressed)
- connection()->setMousePressWindow(Q_NULLPTR);
+ connection()->setMousePressWindow(nullptr);
- handleMouseEvent(timestamp, local, global, modifiers, source);
+ handleMouseEvent(timestamp, local, global, modifiers, type, source);
}
-// Handlers for plain xcb events. Used only when XI 2.2 or newer is not available.
void QXcbWindow::handleButtonPressEvent(const xcb_button_press_event_t *event)
{
Qt::KeyboardModifiers modifiers = connection()->keyboard()->translateModifiers(event->state);
handleButtonPressEvent(event->event_x, event->event_y, event->root_x, event->root_y, event->detail,
- modifiers, event->time);
+ modifiers, event->time, QEvent::MouseButtonPress);
}
void QXcbWindow::handleButtonReleaseEvent(const xcb_button_release_event_t *event)
{
Qt::KeyboardModifiers modifiers = connection()->keyboard()->translateModifiers(event->state);
handleButtonReleaseEvent(event->event_x, event->event_y, event->root_x, event->root_y, event->detail,
- modifiers, event->time);
+ modifiers, event->time, QEvent::MouseButtonRelease);
}
void QXcbWindow::handleMotionNotifyEvent(const xcb_motion_notify_event_t *event)
{
Qt::KeyboardModifiers modifiers = connection()->keyboard()->translateModifiers(event->state);
- handleMotionNotifyEvent(event->event_x, event->event_y, event->root_x, event->root_y, modifiers, event->time);
+ handleMotionNotifyEvent(event->event_x, event->event_y, event->root_x, event->root_y, modifiers,
+ event->time, QEvent::MouseMove);
}
-#ifdef XCB_USE_XINPUT22
+#if QT_CONFIG(xinput2)
static inline int fixed1616ToInt(FP1616 val)
{
return int((qreal(val >> 16)) + (val & 0xFFFF) / (qreal)0xFFFF);
}
-// With XI 2.2+ press/release/motion comes here instead of the above handlers.
void QXcbWindow::handleXIMouseEvent(xcb_ge_event_t *event, Qt::MouseEventSource source)
{
QXcbConnection *conn = connection();
@@ -2433,7 +2353,7 @@ void QXcbWindow::handleXIMouseEvent(xcb_ge_event_t *event, Qt::MouseEventSource
return;
}
for (int i = 1; i <= 15; ++i)
- conn->setButton(conn->translateMouseButton(i), XIMaskIsSet(buttonMask, i));
+ conn->setButtonState(conn->translateMouseButton(i), XIMaskIsSet(buttonMask, i));
}
const Qt::KeyboardModifiers modifiers = conn->keyboard()->translateModifiers(ev->mods.effective_mods);
@@ -2457,19 +2377,19 @@ void QXcbWindow::handleXIMouseEvent(xcb_ge_event_t *event, Qt::MouseEventSource
case XI_ButtonPress:
if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
qCDebug(lcQpaXInputEvents, "XI2 mouse press, button %d, time %d, source %s", button, ev->time, sourceName);
- conn->setButton(button, true);
- handleButtonPressEvent(event_x, event_y, root_x, root_y, ev->detail, modifiers, ev->time, source);
+ conn->setButtonState(button, true);
+ handleButtonPressEvent(event_x, event_y, root_x, root_y, ev->detail, modifiers, ev->time, QEvent::MouseButtonPress, source);
break;
case XI_ButtonRelease:
if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
qCDebug(lcQpaXInputEvents, "XI2 mouse release, button %d, time %d, source %s", button, ev->time, sourceName);
- conn->setButton(button, false);
- handleButtonReleaseEvent(event_x, event_y, root_x, root_y, ev->detail, modifiers, ev->time, source);
+ conn->setButtonState(button, false);
+ handleButtonReleaseEvent(event_x, event_y, root_x, root_y, ev->detail, modifiers, ev->time, QEvent::MouseButtonRelease, source);
break;
case XI_Motion:
if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
qCDebug(lcQpaXInputEvents, "XI2 mouse motion %d,%d, time %d, source %s", event_x, event_y, ev->time, sourceName);
- handleMotionNotifyEvent(event_x, event_y, root_x, root_y, modifiers, ev->time, source);
+ handleMotionNotifyEvent(event_x, event_y, root_x, root_y, modifiers, ev->time, QEvent::MouseMove, source);
break;
default:
qWarning() << "Unrecognized XI2 mouse event" << ev->evtype;
@@ -2477,7 +2397,6 @@ void QXcbWindow::handleXIMouseEvent(xcb_ge_event_t *event, Qt::MouseEventSource
}
}
-// With XI 2.2+ enter/leave comes here and are blocked in plain xcb events
void QXcbWindow::handleXIEnterLeave(xcb_ge_event_t *event)
{
xXIEnterEvent *ev = reinterpret_cast<xXIEnterEvent *>(event);
@@ -2497,12 +2416,14 @@ void QXcbWindow::handleXIEnterLeave(xcb_ge_event_t *event)
case XI_Enter: {
const int event_x = fixed1616ToInt(ev->event_x);
const int event_y = fixed1616ToInt(ev->event_y);
- qCDebug(lcQpaXInput, "XI2 mouse enter %d,%d, mode %d, detail %d, time %d", event_x, event_y, ev->mode, ev->detail, ev->time);
+ qCDebug(lcQpaXInputEvents, "XI2 mouse enter %d,%d, mode %d, detail %d, time %d",
+ event_x, event_y, ev->mode, ev->detail, ev->time);
handleEnterNotifyEvent(event_x, event_y, root_x, root_y, ev->mode, ev->detail, ev->time);
break;
}
case XI_Leave:
- qCDebug(lcQpaXInput, "XI2 mouse leave, mode %d, detail %d, time %d", ev->mode, ev->detail, ev->time);
+ qCDebug(lcQpaXInputEvents, "XI2 mouse leave, mode %d, detail %d, time %d",
+ ev->mode, ev->detail, ev->time);
connection()->keyboard()->updateXKBStateFromXI(&ev->mods, &ev->group);
handleLeaveNotifyEvent(root_x, root_y, ev->mode, ev->detail, ev->time);
break;
@@ -2513,10 +2434,13 @@ void QXcbWindow::handleXIEnterLeave(xcb_ge_event_t *event)
QXcbWindow *QXcbWindow::toWindow() { return this; }
void QXcbWindow::handleMouseEvent(xcb_timestamp_t time, const QPoint &local, const QPoint &global,
- Qt::KeyboardModifiers modifiers, Qt::MouseEventSource source)
+ Qt::KeyboardModifiers modifiers, QEvent::Type type, Qt::MouseEventSource source)
{
connection()->setTime(time);
- QWindowSystemInterface::handleMouseEvent(window(), time, local, global, connection()->buttons(), modifiers, source);
+ Qt::MouseButton button = type == QEvent::MouseMove ? Qt::NoButton : connection()->button();
+ QWindowSystemInterface::handleMouseEvent(window(), time, local, global,
+ connection()->buttonState(), button,
+ type, modifiers, source);
}
void QXcbWindow::handleEnterNotifyEvent(const xcb_enter_notify_event_t *event)
@@ -2539,45 +2463,34 @@ void QXcbWindow::handlePropertyNotifyEvent(const xcb_property_notify_event_t *ev
if (propertyDeleted)
return;
- Qt::WindowState newState = Qt::WindowNoState;
- if (event->atom == atom(QXcbAtom::WM_STATE)) { // WM_STATE: Quick check for 'Minimize'.
- const xcb_get_property_cookie_t get_cookie =
- xcb_get_property(xcb_connection(), 0, m_window, atom(QXcbAtom::WM_STATE),
- XCB_ATOM_ANY, 0, 1024);
-
- xcb_get_property_reply_t *reply =
- xcb_get_property_reply(xcb_connection(), get_cookie, NULL);
+ Qt::WindowStates newState = Qt::WindowNoState;
+ if (event->atom == atom(QXcbAtom::WM_STATE)) { // WM_STATE: Quick check for 'Minimize'.
+ auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(),
+ 0, m_window, atom(QXcbAtom::WM_STATE),
+ XCB_ATOM_ANY, 0, 1024);
if (reply && reply->format == 32 && reply->type == atom(QXcbAtom::WM_STATE)) {
- const quint32 *data = (const quint32 *)xcb_get_property_value(reply);
- if (reply->length != 0) {
- if (data[0] == XCB_WM_STATE_ICONIC
- || (data[0] == XCB_WM_STATE_WITHDRAWN
- && m_lastWindowStateEvent == Qt::WindowMinimized)) {
- newState = Qt::WindowMinimized;
- }
- }
+ const quint32 *data = (const quint32 *)xcb_get_property_value(reply.get());
+ if (reply->length != 0)
+ m_minimized = (data[0] == XCB_WM_STATE_ICONIC
+ || (data[0] == XCB_WM_STATE_WITHDRAWN && m_minimized));
}
- free(reply);
- } else { // _NET_WM_STATE can't change minimized state
- if (m_lastWindowStateEvent == Qt::WindowMinimized)
- newState = Qt::WindowMinimized;
- }
-
- if (newState != Qt::WindowMinimized) { // Something else changed, get _NET_WM_STATE.
- const NetWmStates states = netWmStates();
- if (states & NetWmStateFullScreen)
- newState = Qt::WindowFullScreen;
- else if ((states & NetWmStateMaximizedHorz) && (states & NetWmStateMaximizedVert))
- newState = Qt::WindowMaximized;
}
+ if (m_minimized)
+ newState = Qt::WindowMinimized;
+
+ const NetWmStates states = netWmStates();
+ if (states & NetWmStateFullScreen)
+ newState |= Qt::WindowFullScreen;
+ if ((states & NetWmStateMaximizedHorz) && (states & NetWmStateMaximizedVert))
+ newState |= Qt::WindowMaximized;
// Send Window state, compress events in case other flags (modality, etc) are changed.
if (m_lastWindowStateEvent != newState) {
QWindowSystemInterface::handleWindowStateChanged(window(), newState);
m_lastWindowStateEvent = newState;
m_windowState = newState;
- if (m_windowState == Qt::WindowMinimized && connection()->mouseGrabber() == this)
- connection()->setMouseGrabber(Q_NULLPTR);
+ if ((m_windowState & Qt::WindowMinimized) && connection()->mouseGrabber() == this)
+ connection()->setMouseGrabber(nullptr);
}
return;
} else if (event->atom == atom(QXcbAtom::_NET_FRAME_EXTENTS)) {
@@ -2611,7 +2524,7 @@ void QXcbWindow::updateSyncRequestCounter()
return;
}
if (m_usingSyncProtocol && (m_syncValue.lo != 0 || m_syncValue.hi != 0)) {
- Q_XCB_CALL(xcb_sync_set_counter(xcb_connection(), m_syncCounter, m_syncValue));
+ xcb_sync_set_counter(xcb_connection(), m_syncCounter, m_syncValue);
xcb_flush(xcb_connection());
m_syncValue.lo = 0;
@@ -2635,44 +2548,44 @@ bool QXcbWindow::setKeyboardGrabEnabled(bool grab)
xcb_ungrab_keyboard(xcb_connection(), XCB_TIME_CURRENT_TIME);
return true;
}
- xcb_grab_keyboard_cookie_t cookie = xcb_grab_keyboard(xcb_connection(), false,
- m_window, XCB_TIME_CURRENT_TIME,
- XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
- xcb_grab_keyboard_reply_t *reply = xcb_grab_keyboard_reply(xcb_connection(), cookie, NULL);
- bool result = !(!reply || reply->status != XCB_GRAB_STATUS_SUCCESS);
- free(reply);
- return result;
+
+ auto reply = Q_XCB_REPLY(xcb_grab_keyboard, xcb_connection(), false,
+ m_window, XCB_TIME_CURRENT_TIME,
+ XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
+ return reply && reply->status == XCB_GRAB_STATUS_SUCCESS;
}
bool QXcbWindow::setMouseGrabEnabled(bool grab)
{
if (!grab && connection()->mouseGrabber() == this)
- connection()->setMouseGrabber(Q_NULLPTR);
-#ifdef XCB_USE_XINPUT22
- if (connection()->isAtLeastXI22() && connection()->xi2MouseEvents()) {
+ connection()->setMouseGrabber(nullptr);
+
+ if (grab && !connection()->canGrab())
+ return false;
+
+#if QT_CONFIG(xinput2)
+ if (connection()->hasXInput2() && !connection()->xi2MouseEventsDisabled()) {
bool result = connection()->xi2SetMouseGrabEnabled(m_window, grab);
if (grab && result)
connection()->setMouseGrabber(this);
return result;
}
#endif
- if (grab && !connection()->canGrab())
- return false;
if (!grab) {
xcb_ungrab_pointer(xcb_connection(), XCB_TIME_CURRENT_TIME);
return true;
}
- xcb_grab_pointer_cookie_t cookie = xcb_grab_pointer(xcb_connection(), false, m_window,
- (XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE
- | XCB_EVENT_MASK_BUTTON_MOTION | XCB_EVENT_MASK_ENTER_WINDOW
- | XCB_EVENT_MASK_LEAVE_WINDOW | XCB_EVENT_MASK_POINTER_MOTION),
- XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC,
- XCB_WINDOW_NONE, XCB_CURSOR_NONE,
- XCB_TIME_CURRENT_TIME);
- xcb_grab_pointer_reply_t *reply = xcb_grab_pointer_reply(xcb_connection(), cookie, NULL);
- bool result = !(!reply || reply->status != XCB_GRAB_STATUS_SUCCESS);
- free(reply);
+
+ auto reply = Q_XCB_REPLY(xcb_grab_pointer, xcb_connection(),
+ false, m_window,
+ (XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE
+ | XCB_EVENT_MASK_BUTTON_MOTION | XCB_EVENT_MASK_ENTER_WINDOW
+ | XCB_EVENT_MASK_LEAVE_WINDOW | XCB_EVENT_MASK_POINTER_MOTION),
+ XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC,
+ XCB_WINDOW_NONE, XCB_CURSOR_NONE,
+ XCB_TIME_CURRENT_TIME);
+ bool result = reply && reply->status == XCB_GRAB_STATUS_SUCCESS;
if (result)
connection()->setMouseGrabber(this);
return result;
@@ -2727,18 +2640,28 @@ void QXcbWindow::windowEvent(QEvent *event)
bool QXcbWindow::startSystemResize(const QPoint &pos, Qt::Corner corner)
{
+ return startSystemMoveResize(pos, corner);
+}
+
+bool QXcbWindow::startSystemMove(const QPoint &pos)
+{
+ return startSystemMoveResize(pos, 4);
+}
+
+bool QXcbWindow::startSystemMoveResize(const QPoint &pos, int corner)
+{
const xcb_atom_t moveResize = connection()->atom(QXcbAtom::_NET_WM_MOVERESIZE);
if (!connection()->wmSupport()->isSupportedByWM(moveResize))
return false;
const QPoint globalPos = QHighDpi::toNativePixels(window()->mapToGlobal(pos), window()->screen());
#ifdef XCB_USE_XINPUT22
- if (connection()->startSystemResizeForTouchBegin(m_window, globalPos, corner))
+ if (connection()->startSystemMoveResizeForTouchBegin(m_window, globalPos, corner))
return true;
#endif
- return doStartSystemResize(globalPos, corner);
+ return doStartSystemMoveResize(globalPos, corner);
}
-bool QXcbWindow::doStartSystemResize(const QPoint &globalPos, Qt::Corner corner)
+bool QXcbWindow::doStartSystemMoveResize(const QPoint &globalPos, int corner)
{
const xcb_atom_t moveResize = connection()->atom(QXcbAtom::_NET_WM_MOVERESIZE);
xcb_client_message_event_t xev;
@@ -2749,12 +2672,16 @@ bool QXcbWindow::doStartSystemResize(const QPoint &globalPos, Qt::Corner corner)
xev.format = 32;
xev.data.data32[0] = globalPos.x();
xev.data.data32[1] = globalPos.y();
- const bool bottom = corner == Qt::BottomRightCorner || corner == Qt::BottomLeftCorner;
- const bool left = corner == Qt::BottomLeftCorner || corner == Qt::TopLeftCorner;
- if (bottom)
- xev.data.data32[2] = left ? 6 : 4; // bottomleft/bottomright
- else
- xev.data.data32[2] = left ? 0 : 2; // topleft/topright
+ if (corner == 4) {
+ xev.data.data32[2] = 8; // move
+ } else {
+ const bool bottom = corner == Qt::BottomRightCorner || corner == Qt::BottomLeftCorner;
+ const bool left = corner == Qt::BottomLeftCorner || corner == Qt::TopLeftCorner;
+ if (bottom)
+ xev.data.data32[2] = left ? 6 : 4; // bottomleft/bottomright
+ else
+ xev.data.data32[2] = left ? 0 : 2; // topleft/topright
+ }
xev.data.data32[3] = XCB_BUTTON_INDEX_1;
xev.data.data32[4] = 0;
xcb_ungrab_pointer(connection()->xcb_connection(), XCB_CURRENT_TIME);
@@ -2780,8 +2707,7 @@ void QXcbWindow::sendXEmbedMessage(xcb_window_t window, quint32 message,
event.data.data32[2] = detail;
event.data.data32[3] = data1;
event.data.data32[4] = data2;
- Q_XCB_CALL(xcb_send_event(xcb_connection(), false, window,
- XCB_EVENT_MASK_NO_EVENT, (const char *)&event));
+ xcb_send_event(xcb_connection(), false, window, XCB_EVENT_MASK_NO_EVENT, (const char *)&event);
}
static bool activeWindowChangeQueued(const QWindow *window)
@@ -2803,12 +2729,12 @@ void QXcbWindow::handleXEmbedMessage(const xcb_client_message_event_t *event)
case XEMBED_WINDOW_DEACTIVATE:
break;
case XEMBED_EMBEDDED_NOTIFY:
- Q_XCB_CALL(xcb_map_window(xcb_connection(), m_window));
+ xcb_map_window(xcb_connection(), m_window);
xcbScreen()->windowShown(this);
// Without Qt::WA_TranslucentBackground, we use a ParentRelative BackPixmap.
// Clear the whole tray icon window to its background color as early as possible
// so that we can get a clean result from grabWindow() later.
- Q_XCB_CALL(xcb_clear_area(xcb_connection(), false, m_window, 0, 0, geometry().width(), geometry().height()));
+ xcb_clear_area(xcb_connection(), false, m_window, 0, 0, geometry().width(), geometry().height());
xcb_flush(xcb_connection());
break;
case XEMBED_FOCUS_IN:
@@ -2855,14 +2781,23 @@ void QXcbWindow::setOpacity(qreal level)
quint32 value = qRound64(qBound(qreal(0), level, qreal(1)) * 0xffffffff);
- Q_XCB_CALL(xcb_change_property(xcb_connection(),
- XCB_PROP_MODE_REPLACE,
- m_window,
- atom(QXcbAtom::_NET_WM_WINDOW_OPACITY),
- XCB_ATOM_CARDINAL,
- 32,
- 1,
- (uchar *)&value));
+ xcb_change_property(xcb_connection(),
+ XCB_PROP_MODE_REPLACE,
+ m_window,
+ atom(QXcbAtom::_NET_WM_WINDOW_OPACITY),
+ XCB_ATOM_CARDINAL,
+ 32,
+ 1,
+ (uchar *)&value);
+}
+
+QVector<xcb_rectangle_t> qRegionToXcbRectangleList(const QRegion &region)
+{
+ QVector<xcb_rectangle_t> rects;
+ rects.reserve(region.rectCount());
+ for (const QRect &r : region)
+ rects.push_back(qRectToXCBRectangle(r));
+ return rects;
}
void QXcbWindow::setMask(const QRegion &region)
@@ -2873,10 +2808,7 @@ void QXcbWindow::setMask(const QRegion &region)
xcb_shape_mask(connection()->xcb_connection(), XCB_SHAPE_SO_SET,
XCB_SHAPE_SK_BOUNDING, xcb_window(), 0, 0, XCB_NONE);
} else {
- QVector<xcb_rectangle_t> rects;
- rects.reserve(region.rectCount());
- for (const QRect &r : region)
- rects.push_back(qRectToXCBRectangle(r));
+ const auto rects = qRegionToXcbRectangleList(region);
xcb_shape_rectangles(connection()->xcb_connection(), XCB_SHAPE_SO_SET,
XCB_SHAPE_SK_BOUNDING, XCB_CLIP_ORDERING_UNSORTED,
xcb_window(), 0, 0, rects.size(), &rects[0]);
@@ -2917,5 +2849,18 @@ QXcbScreen *QXcbWindow::xcbScreen() const
return static_cast<QXcbScreen *>(screen());
}
+QString QXcbWindow::windowTitle(const QXcbConnection *conn, xcb_window_t window)
+{
+ const xcb_atom_t utf8Atom = conn->atom(QXcbAtom::UTF8_STRING);
+ auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, conn->xcb_connection(),
+ false, window, conn->atom(QXcbAtom::_NET_WM_NAME),
+ utf8Atom, 0, 1024);
+ if (reply && reply->format == 8 && reply->type == utf8Atom) {
+ const char *name = reinterpret_cast<const char *>(xcb_get_property_value(reply.get()));
+ return QString::fromUtf8(name);
+ }
+ return QString();
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/qxcbwindow.h b/src/plugins/platforms/xcb/qxcbwindow.h
index f38343b6c2..957c4e9cbd 100644
--- a/src/plugins/platforms/xcb/qxcbwindow.h
+++ b/src/plugins/platforms/xcb/qxcbwindow.h
@@ -82,7 +82,7 @@ public:
void setVisible(bool visible) override;
void setWindowFlags(Qt::WindowFlags flags) override;
- void setWindowState(Qt::WindowState state) override;
+ void setWindowState(Qt::WindowStates state) override;
WId winId() const override;
void setParent(const QPlatformWindow *window) override;
@@ -110,6 +110,7 @@ public:
void windowEvent(QEvent *event) override;
bool startSystemResize(const QPoint &pos, Qt::Corner corner) override;
+ bool startSystemMove(const QPoint &pos) override;
void setOpacity(qreal level) override;
void setMask(const QRegion &region) override;
@@ -138,7 +139,7 @@ public:
void handleFocusInEvent(const xcb_focus_in_event_t *event) override;
void handleFocusOutEvent(const xcb_focus_out_event_t *event) override;
void handlePropertyNotifyEvent(const xcb_property_notify_event_t *event) override;
-#ifdef XCB_USE_XINPUT22
+#if QT_CONFIG(xinput2)
void handleXIMouseEvent(xcb_ge_event_t *, Qt::MouseEventSource source = Qt::MouseEventNotSynthesized) override;
void handleXIEnterLeave(xcb_ge_event_t *) override;
#endif
@@ -146,7 +147,7 @@ public:
QXcbWindow *toWindow() override;
void handleMouseEvent(xcb_timestamp_t time, const QPoint &local, const QPoint &global,
- Qt::KeyboardModifiers modifiers, Qt::MouseEventSource source);
+ Qt::KeyboardModifiers modifiers, QEvent::Type type, Qt::MouseEventSource source);
void updateNetWmUserTime(xcb_timestamp_t timestamp);
@@ -177,17 +178,21 @@ public:
QXcbScreen *xcbScreen() const;
- bool doStartSystemResize(const QPoint &globalPos, Qt::Corner corner);
+ bool startSystemMoveResize(const QPoint &pos, int corner);
+ bool doStartSystemMoveResize(const QPoint &globalPos, int corner);
virtual void create();
virtual void destroy();
+ static QString windowTitle(const QXcbConnection *conn, xcb_window_t window);
+
public Q_SLOTS:
void updateSyncRequestCounter();
protected:
virtual void resolveFormat(const QSurfaceFormat &format) { m_format = format; }
virtual const xcb_visualtype_t *createVisual();
+ void setImageFormatForVisual(const xcb_visualtype_t *visual);
QXcbScreen *parentScreen();
@@ -220,13 +225,16 @@ protected:
bool compressExposeEvent(QRegion &exposeRegion);
void handleButtonPressEvent(int event_x, int event_y, int root_x, int root_y,
- int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp, Qt::MouseEventSource source = Qt::MouseEventNotSynthesized);
+ int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp,
+ QEvent::Type type, Qt::MouseEventSource source = Qt::MouseEventNotSynthesized);
void handleButtonReleaseEvent(int event_x, int event_y, int root_x, int root_y,
- int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp, Qt::MouseEventSource source = Qt::MouseEventNotSynthesized);
+ int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp,
+ QEvent::Type type, Qt::MouseEventSource source = Qt::MouseEventNotSynthesized);
void handleMotionNotifyEvent(int event_x, int event_y, int root_x, int root_y,
- Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp, Qt::MouseEventSource source = Qt::MouseEventNotSynthesized);
+ Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp,
+ QEvent::Type type, Qt::MouseEventSource source = Qt::MouseEventNotSynthesized);
void handleEnterNotifyEvent(int event_x, int event_y, int root_x, int root_y,
quint8 mode, quint8 detail, xcb_timestamp_t timestamp);
@@ -244,7 +252,7 @@ protected:
xcb_sync_int64_t m_syncValue;
xcb_sync_counter_t m_syncCounter = 0;
- Qt::WindowState m_windowState = Qt::WindowNoState;
+ Qt::WindowStates m_windowState = Qt::WindowNoState;
xcb_gravity_t m_gravity = XCB_GRAVITY_STATIC;
@@ -254,6 +262,7 @@ protected:
bool m_deferredActivation = false;
bool m_embedded = false;
bool m_alertState = false;
+ bool m_minimized = false;
xcb_window_t m_netWmUserTimeWindow = XCB_NONE;
QSurfaceFormat m_format;
@@ -265,7 +274,8 @@ protected:
QSize m_oldWindowSize;
xcb_visualid_t m_visualId = 0;
- int m_lastWindowStateEvent = -1;
+ // Last sent state. Initialized to an invalid state, on purpose.
+ Qt::WindowStates m_lastWindowStateEvent = Qt::WindowActive;
enum SyncState {
NoSyncNeeded,
@@ -290,6 +300,8 @@ protected:
void create() override {} // No-op
};
+QVector<xcb_rectangle_t> qRegionToXcbRectangleList(const QRegion &region);
+
QT_END_NAMESPACE
Q_DECLARE_METATYPE(QXcbWindow*)
diff --git a/src/plugins/platforms/xcb/qxcbwmsupport.cpp b/src/plugins/platforms/xcb/qxcbwmsupport.cpp
index 470f021314..2619892b19 100644
--- a/src/plugins/platforms/xcb/qxcbwmsupport.cpp
+++ b/src/plugins/platforms/xcb/qxcbwmsupport.cpp
@@ -66,16 +66,15 @@ void QXcbWMSupport::updateNetWMAtoms()
int offset = 0;
int remaining = 0;
do {
- xcb_get_property_cookie_t cookie = xcb_get_property(xcb_connection(), false, root, atom(QXcbAtom::_NET_SUPPORTED), XCB_ATOM_ATOM, offset, 1024);
- xcb_get_property_reply_t *reply = xcb_get_property_reply(xcb_connection(), cookie, NULL);
+ auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(), false, root, atom(QXcbAtom::_NET_SUPPORTED), XCB_ATOM_ATOM, offset, 1024);
if (!reply)
break;
remaining = 0;
if (reply->type == XCB_ATOM_ATOM && reply->format == 32) {
- int len = xcb_get_property_value_length(reply)/sizeof(xcb_atom_t);
- xcb_atom_t *atoms = (xcb_atom_t *)xcb_get_property_value(reply);
+ int len = xcb_get_property_value_length(reply.get())/sizeof(xcb_atom_t);
+ xcb_atom_t *atoms = (xcb_atom_t *)xcb_get_property_value(reply.get());
int s = net_wm_atoms.size();
net_wm_atoms.resize(s + len);
memcpy(net_wm_atoms.data() + s, atoms, len*sizeof(xcb_atom_t));
@@ -83,8 +82,6 @@ void QXcbWMSupport::updateNetWMAtoms()
remaining = reply->bytes_after;
offset += len;
}
-
- free(reply);
} while (remaining > 0);
}
@@ -100,16 +97,16 @@ void QXcbWMSupport::updateVirtualRoots()
int offset = 0;
int remaining = 0;
do {
- xcb_get_property_cookie_t cookie = xcb_get_property(xcb_connection(), false, root, atom(QXcbAtom::_NET_VIRTUAL_ROOTS), XCB_ATOM_WINDOW, offset, 1024);
- xcb_get_property_reply_t *reply = xcb_get_property_reply(xcb_connection(), cookie, NULL);
+ auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(),
+ false, root, atom(QXcbAtom::_NET_VIRTUAL_ROOTS), XCB_ATOM_WINDOW, offset, 1024);
if (!reply)
break;
remaining = 0;
if (reply->type == XCB_ATOM_WINDOW && reply->format == 32) {
- int len = xcb_get_property_value_length(reply)/sizeof(xcb_window_t);
- xcb_window_t *roots = (xcb_window_t *)xcb_get_property_value(reply);
+ int len = xcb_get_property_value_length(reply.get())/sizeof(xcb_window_t);
+ xcb_window_t *roots = (xcb_window_t *)xcb_get_property_value(reply.get());
int s = net_virtual_roots.size();
net_virtual_roots.resize(s + len);
memcpy(net_virtual_roots.data() + s, roots, len*sizeof(xcb_window_t));
@@ -118,10 +115,10 @@ void QXcbWMSupport::updateVirtualRoots()
offset += len;
}
- free(reply);
} while (remaining > 0);
-#ifdef Q_XCB_DEBUG
+//#define VIRTUAL_ROOTS_DEBUG
+#ifdef VIRTUAL_ROOTS_DEBUG
qDebug("======== updateVirtualRoots");
for (int i = 0; i < net_virtual_roots.size(); ++i)
qDebug() << connection()->atomName(net_virtual_roots.at(i));
diff --git a/src/plugins/platforms/xcb/qxcbxsettings.cpp b/src/plugins/platforms/xcb/qxcbxsettings.cpp
index 88933c6c22..bd398ea049 100644
--- a/src/plugins/platforms/xcb/qxcbxsettings.cpp
+++ b/src/plugins/platforms/xcb/qxcbxsettings.cpp
@@ -106,26 +106,23 @@ public:
QByteArray settings;
xcb_atom_t _xsettings_atom = screen->connection()->atom(QXcbAtom::_XSETTINGS_SETTINGS);
while (1) {
- xcb_get_property_cookie_t get_prop_cookie =
- xcb_get_property_unchecked(screen->xcb_connection(),
+ auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property,
+ screen->xcb_connection(),
false,
x_settings_window,
_xsettings_atom,
_xsettings_atom,
offset/4,
8192);
- xcb_get_property_reply_t *reply = xcb_get_property_reply(screen->xcb_connection(), get_prop_cookie, NULL);
bool more = false;
if (!reply)
return settings;
- const auto property_value_length = xcb_get_property_value_length(reply);
- settings.append(static_cast<const char *>(xcb_get_property_value(reply)), property_value_length);
+ const auto property_value_length = xcb_get_property_value_length(reply.get());
+ settings.append(static_cast<const char *>(xcb_get_property_value(reply.get())), property_value_length);
offset += property_value_length;
more = reply->bytes_after != 0;
- free(reply);
-
if (!more)
break;
}
@@ -228,34 +225,24 @@ QXcbXSettings::QXcbXSettings(QXcbVirtualDesktop *screen)
{
QByteArray settings_atom_for_screen("_XSETTINGS_S");
settings_atom_for_screen.append(QByteArray::number(screen->number()));
- xcb_intern_atom_cookie_t atom_cookie = xcb_intern_atom(screen->xcb_connection(),
- true,
- settings_atom_for_screen.length(),
- settings_atom_for_screen.constData());
- xcb_generic_error_t *error = 0;
- xcb_intern_atom_reply_t *atom_reply = xcb_intern_atom_reply(screen->xcb_connection(),atom_cookie,&error);
- if (error) {
- free(error);
+ auto atom_reply = Q_XCB_REPLY(xcb_intern_atom,
+ screen->xcb_connection(),
+ true,
+ settings_atom_for_screen.length(),
+ settings_atom_for_screen.constData());
+ if (!atom_reply)
return;
- }
- xcb_atom_t selection_owner_atom = atom_reply->atom;
- free(atom_reply);
- xcb_get_selection_owner_cookie_t selection_cookie =
- xcb_get_selection_owner(screen->xcb_connection(), selection_owner_atom);
+ xcb_atom_t selection_owner_atom = atom_reply->atom;
- xcb_get_selection_owner_reply_t *selection_result =
- xcb_get_selection_owner_reply(screen->xcb_connection(), selection_cookie, &error);
- if (error) {
- free(error);
+ auto selection_result = Q_XCB_REPLY(xcb_get_selection_owner,
+ screen->xcb_connection(), selection_owner_atom);
+ if (!selection_result)
return;
- }
d_ptr->x_settings_window = selection_result->owner;
- free(selection_result);
- if (!d_ptr->x_settings_window) {
+ if (!d_ptr->x_settings_window)
return;
- }
const uint32_t event = XCB_CW_EVENT_MASK;
const uint32_t event_mask[] = { XCB_EVENT_MASK_STRUCTURE_NOTIFY|XCB_EVENT_MASK_PROPERTY_CHANGE };
diff --git a/src/plugins/platforms/xcb/xcb-plugin.pro b/src/plugins/platforms/xcb/xcb-plugin.pro
index 01d493156d..a2c56a3dcf 100644
--- a/src/plugins/platforms/xcb/xcb-plugin.pro
+++ b/src/plugins/platforms/xcb/xcb-plugin.pro
@@ -4,6 +4,8 @@ QT += core-private gui-private xcb_qpa_lib-private
DEFINES += QT_NO_FOREACH
+macos: CONFIG += no_app_extension_api_only
+
SOURCES = \
qxcbmain.cpp
OTHER_FILES += xcb.json README
diff --git a/src/plugins/platforms/xcb/xcb_qpa_lib.pro b/src/plugins/platforms/xcb/xcb_qpa_lib.pro
index 55007b43a7..00cce13fd0 100644
--- a/src/plugins/platforms/xcb/xcb_qpa_lib.pro
+++ b/src/plugins/platforms/xcb/xcb_qpa_lib.pro
@@ -5,11 +5,14 @@ DEFINES += QT_NO_FOREACH
QT += \
core-private gui-private \
service_support-private theme_support-private \
- eventdispatcher_support-private fontdatabase_support-private
+ eventdispatcher_support-private fontdatabase_support-private \
+ edid_support-private
qtHaveModule(linuxaccessibility_support-private): \
QT += linuxaccessibility_support-private
+qtConfig(vulkan): QT += vulkan_support-private
+
SOURCES = \
qxcbclipboard.cpp \
qxcbconnection.cpp \
@@ -65,6 +68,17 @@ qtConfig(xcb-sm) {
}
include(gl_integrations/gl_integrations.pri)
+include(nativepainting/nativepainting.pri)
+
+qtConfig(vulkan) {
+ SOURCES += \
+ qxcbvulkaninstance.cpp \
+ qxcbvulkanwindow.cpp
+
+ HEADERS += \
+ qxcbvulkaninstance.h \
+ qxcbvulkanwindow.h
+}
!qtConfig(system-xcb) {
QMAKE_USE += xcb-static xcb
diff --git a/src/plugins/platformthemes/flatpak/flatpak.json b/src/plugins/platformthemes/flatpak/flatpak.json
new file mode 100644
index 0000000000..71f834fd08
--- /dev/null
+++ b/src/plugins/platformthemes/flatpak/flatpak.json
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "flatpak" ]
+}
diff --git a/src/plugins/platformthemes/flatpak/flatpak.pro b/src/plugins/platformthemes/flatpak/flatpak.pro
new file mode 100644
index 0000000000..1e5dbb7a6c
--- /dev/null
+++ b/src/plugins/platformthemes/flatpak/flatpak.pro
@@ -0,0 +1,17 @@
+TARGET = qflatpak
+
+PLUGIN_TYPE = platformthemes
+PLUGIN_EXTENDS = -
+PLUGIN_CLASS_NAME = QFlatpakThemePlugin
+load(qt_plugin)
+
+QT += core-private dbus gui-private theme_support-private
+
+HEADERS += \
+ qflatpaktheme.h \
+ qflatpakfiledialog_p.h
+
+SOURCES += \
+ main.cpp \
+ qflatpaktheme.cpp \
+ qflatpakfiledialog.cpp
diff --git a/src/plugins/platformthemes/flatpak/main.cpp b/src/plugins/platformthemes/flatpak/main.cpp
new file mode 100644
index 0000000000..7888eed8b2
--- /dev/null
+++ b/src/plugins/platformthemes/flatpak/main.cpp
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Red Hat, Inc
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qpa/qplatformthemeplugin.h>
+#include "qflatpaktheme.h"
+
+QT_BEGIN_NAMESPACE
+
+class QFlatpakThemePlugin : public QPlatformThemePlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QPlatformThemeFactoryInterface_iid FILE "flatpak.json")
+
+public:
+ QPlatformTheme *create(const QString &key, const QStringList &params) override;
+};
+
+QPlatformTheme *QFlatpakThemePlugin::create(const QString &key, const QStringList &params)
+{
+ Q_UNUSED(params);
+ if (!key.compare(QLatin1String("flatpak"), Qt::CaseInsensitive))
+ return new QFlatpakTheme;
+
+ return nullptr;
+}
+
+QT_END_NAMESPACE
+
+#include "main.moc"
diff --git a/src/plugins/platformthemes/flatpak/qflatpakfiledialog.cpp b/src/plugins/platformthemes/flatpak/qflatpakfiledialog.cpp
new file mode 100644
index 0000000000..1a24015ce5
--- /dev/null
+++ b/src/plugins/platformthemes/flatpak/qflatpakfiledialog.cpp
@@ -0,0 +1,352 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Red Hat, Inc
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qflatpakfiledialog_p.h"
+
+#include <QtCore/qeventloop.h>
+
+#include <QtDBus/QtDBus>
+#include <QDBusConnection>
+#include <QDBusMessage>
+#include <QDBusPendingCall>
+#include <QDBusPendingCallWatcher>
+#include <QDBusPendingReply>
+
+#include <QMetaType>
+#include <QMimeType>
+#include <QMimeDatabase>
+#include <QWindow>
+
+QT_BEGIN_NAMESPACE
+
+QDBusArgument &operator <<(QDBusArgument &arg, const QFlatpakFileDialog::FilterCondition &filterCondition)
+{
+ arg.beginStructure();
+ arg << filterCondition.type << filterCondition.pattern;
+ arg.endStructure();
+ return arg;
+}
+
+const QDBusArgument &operator >>(const QDBusArgument &arg, QFlatpakFileDialog::FilterCondition &filterCondition)
+{
+ uint type;
+ QString filterPattern;
+ arg.beginStructure();
+ arg >> type >> filterPattern;
+ filterCondition.type = (QFlatpakFileDialog::ConditionType)type;
+ filterCondition.pattern = filterPattern;
+ arg.endStructure();
+
+ return arg;
+}
+
+QDBusArgument &operator <<(QDBusArgument &arg, const QFlatpakFileDialog::Filter filter)
+{
+ arg.beginStructure();
+ arg << filter.name << filter.filterConditions;
+ arg.endStructure();
+ return arg;
+}
+
+const QDBusArgument &operator >>(const QDBusArgument &arg, QFlatpakFileDialog::Filter &filter)
+{
+ QString name;
+ QFlatpakFileDialog::FilterConditionList filterConditions;
+ arg.beginStructure();
+ arg >> name >> filterConditions;
+ filter.name = name;
+ filter.filterConditions = filterConditions;
+ arg.endStructure();
+
+ return arg;
+}
+
+class QFlatpakFileDialogPrivate
+{
+public:
+ WId winId = 0;
+ bool modal = false;
+ bool multipleFiles = false;
+ bool saveFile = false;
+ QString acceptLabel;
+ QString directory;
+ QString title;
+ QStringList nameFilters;
+ QStringList mimeTypesFilters;
+ QStringList selectedFiles;
+};
+
+QFlatpakFileDialog::QFlatpakFileDialog()
+ : QPlatformFileDialogHelper()
+ , d_ptr(new QFlatpakFileDialogPrivate)
+{
+}
+
+QFlatpakFileDialog::~QFlatpakFileDialog()
+{
+}
+
+void QFlatpakFileDialog::initializeDialog()
+{
+ Q_D(QFlatpakFileDialog);
+
+ if (options()->fileMode() == QFileDialogOptions::ExistingFiles)
+ d->multipleFiles = true;
+
+ if (options()->isLabelExplicitlySet(QFileDialogOptions::Accept))
+ d->acceptLabel = options()->labelText(QFileDialogOptions::Accept);
+
+ if (!options()->windowTitle().isEmpty())
+ d->title = options()->windowTitle();
+
+ if (options()->acceptMode() == QFileDialogOptions::AcceptSave)
+ d->saveFile = true;
+
+ if (!options()->nameFilters().isEmpty())
+ d->nameFilters = options()->nameFilters();
+
+ if (!options()->mimeTypeFilters().isEmpty())
+ d->mimeTypesFilters = options()->mimeTypeFilters();
+
+ setDirectory(options()->initialDirectory());
+}
+
+void QFlatpakFileDialog::openPortal()
+{
+ Q_D(const QFlatpakFileDialog);
+
+ QDBusMessage message = QDBusMessage::createMethodCall(QLatin1String("org.freedesktop.portal.Desktop"),
+ QLatin1String("/org/freedesktop/portal/desktop"),
+ QLatin1String("org.freedesktop.portal.FileChooser"),
+ d->saveFile ? QLatin1String("SaveFile") : QLatin1String("OpenFile"));
+ QString parentWindowId = QLatin1String("x11:") + QString::number(d->winId);
+
+ QVariantMap options;
+ if (!d->acceptLabel.isEmpty())
+ options.insert(QLatin1String("accept_label"), d->acceptLabel);
+
+ options.insert(QLatin1String("modal"), d->modal);
+ options.insert(QLatin1String("multiple"), d->multipleFiles);
+
+ if (d->saveFile) {
+ if (!d->directory.isEmpty())
+ options.insert(QLatin1String("current_folder"), d->directory.toLatin1());
+
+ if (!d->selectedFiles.isEmpty())
+ options.insert(QLatin1String("current_file"), d->selectedFiles.first().toLatin1());
+ }
+
+ // Insert filters
+ qDBusRegisterMetaType<FilterCondition>();
+ qDBusRegisterMetaType<FilterConditionList>();
+ qDBusRegisterMetaType<Filter>();
+ qDBusRegisterMetaType<FilterList>();
+
+ FilterList filterList;
+
+ if (!d->mimeTypesFilters.isEmpty()) {
+ for (const QString &mimeTypefilter : d->mimeTypesFilters) {
+ QMimeDatabase mimeDatabase;
+ QMimeType mimeType = mimeDatabase.mimeTypeForName(mimeTypefilter);
+
+ // Creates e.g. (1, "image/png")
+ FilterCondition filterCondition;
+ filterCondition.type = MimeType;
+ filterCondition.pattern = mimeTypefilter;
+
+ // Creates e.g. [((1, "image/png"))]
+ FilterConditionList filterConditions;
+ filterConditions << filterCondition;
+
+ // Creates e.g. [("Images", [((1, "image/png"))])]
+ Filter filter;
+ filter.name = mimeType.comment();
+ filter.filterConditions = filterConditions;
+
+ filterList << filter;
+ }
+ } else if (!d->nameFilters.isEmpty()) {
+ for (const QString &filter : d->nameFilters) {
+ // Do parsing:
+ // Supported format is ("Images (*.png *.jpg)")
+ QRegularExpression regexp(QPlatformFileDialogHelper::filterRegExp);
+ QRegularExpressionMatch match = regexp.match(filter);
+ if (match.hasMatch()) {
+ QString userVisibleName = match.captured(1);
+ QStringList filterStrings = match.captured(2).split(QLatin1String(" "));
+
+ FilterConditionList filterConditions;
+ for (const QString &filterString : filterStrings) {
+ FilterCondition filterCondition;
+ filterCondition.type = GlobalPattern;
+ filterCondition.pattern = filterString;
+ filterConditions << filterCondition;
+ }
+
+ Filter filter;
+ filter.name = userVisibleName;
+ filter.filterConditions = filterConditions;
+
+ filterList << filter;
+ }
+ }
+ }
+
+ if (!filterList.isEmpty())
+ options.insert(QLatin1String("filters"), QVariant::fromValue(filterList));
+
+ // TODO choices a(ssa(ss)s)
+ // List of serialized combo boxes to add to the file chooser.
+
+ message << parentWindowId << d->title << options;
+
+ QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(message);
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall);
+ connect(watcher, &QDBusPendingCallWatcher::finished, this, [this] (QDBusPendingCallWatcher *watcher) {
+ QDBusPendingReply<QDBusObjectPath> reply = *watcher;
+ if (reply.isError()) {
+ Q_EMIT reject();
+ } else {
+ QDBusConnection::sessionBus().connect(nullptr,
+ reply.value().path(),
+ QLatin1String("org.freedesktop.portal.Request"),
+ QLatin1String("Response"),
+ this,
+ SLOT(gotResponse(uint,QVariantMap)));
+ }
+ });
+}
+
+bool QFlatpakFileDialog::defaultNameFilterDisables() const
+{
+ return false;
+}
+
+void QFlatpakFileDialog::setDirectory(const QUrl &directory)
+{
+ Q_D(QFlatpakFileDialog);
+
+ d->directory = directory.path();
+}
+
+QUrl QFlatpakFileDialog::directory() const
+{
+ Q_D(const QFlatpakFileDialog);
+
+ return d->directory;
+}
+
+void QFlatpakFileDialog::selectFile(const QUrl &filename)
+{
+ Q_D(QFlatpakFileDialog);
+
+ d->selectedFiles << filename.path();
+}
+
+QList<QUrl> QFlatpakFileDialog::selectedFiles() const
+{
+ Q_D(const QFlatpakFileDialog);
+
+ QList<QUrl> files;
+ for (const QString &file : d->selectedFiles) {
+ files << QUrl(file);
+ }
+ return files;
+}
+
+void QFlatpakFileDialog::setFilter()
+{
+ // TODO
+}
+
+void QFlatpakFileDialog::selectNameFilter(const QString &filter)
+{
+ Q_UNUSED(filter);
+ // TODO
+}
+
+QString QFlatpakFileDialog::selectedNameFilter() const
+{
+ // TODO
+ return QString();
+}
+
+void QFlatpakFileDialog::exec()
+{
+ // HACK we have to avoid returning until we emit that the dialog was accepted or rejected
+ QEventLoop loop;
+ loop.connect(this, SIGNAL(accept()), SLOT(quit()));
+ loop.connect(this, SIGNAL(reject()), SLOT(quit()));
+ loop.exec();
+}
+
+void QFlatpakFileDialog::hide()
+{
+}
+
+bool QFlatpakFileDialog::show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent)
+{
+ Q_D(QFlatpakFileDialog);
+ Q_UNUSED(windowFlags);
+
+ initializeDialog();
+
+ d->modal = windowModality != Qt::NonModal;
+ d->winId = parent ? parent->winId() : 0;
+
+ openPortal();
+
+ return true;
+}
+
+void QFlatpakFileDialog::gotResponse(uint response, const QVariantMap &results)
+{
+ Q_D(QFlatpakFileDialog);
+
+ if (!response) {
+ if (results.contains(QLatin1String("uris")))
+ d->selectedFiles = results.value(QLatin1String("uris")).toStringList();
+
+ Q_EMIT accept();
+ } else {
+ Q_EMIT reject();
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platformthemes/flatpak/qflatpakfiledialog_p.h b/src/plugins/platformthemes/flatpak/qflatpakfiledialog_p.h
new file mode 100644
index 0000000000..f3e195faa0
--- /dev/null
+++ b/src/plugins/platformthemes/flatpak/qflatpakfiledialog_p.h
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Red Hat, Inc
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QFLATPAKFILEDIALOG_P_H
+#define QFLATPAKFILEDIALOG_P_H
+
+#include <qpa/qplatformdialoghelper.h>
+#include <QVector>
+
+QT_BEGIN_NAMESPACE
+
+class QFlatpakFileDialogPrivate;
+
+class QFlatpakFileDialog : public QPlatformFileDialogHelper
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QFlatpakFileDialog)
+public:
+ enum ConditionType : uint {
+ GlobalPattern = 0,
+ MimeType = 1
+ };
+ // Filters a(sa(us))
+ // Example: [('Images', [(0, '*.ico'), (1, 'image/png')]), ('Text', [(0, '*.txt')])]
+ struct FilterCondition {
+ ConditionType type;
+ QString pattern; // E.g. '*ico' or 'image/png'
+ };
+ typedef QVector<FilterCondition> FilterConditionList;
+
+ struct Filter {
+ QString name; // E.g. 'Images' or 'Text
+ FilterConditionList filterConditions;; // E.g. [(0, '*.ico'), (1, 'image/png')] or [(0, '*.txt')]
+ };
+ typedef QVector<Filter> FilterList;
+
+ QFlatpakFileDialog();
+ ~QFlatpakFileDialog();
+
+ bool defaultNameFilterDisables() const override;
+ QUrl directory() const override;
+ void setDirectory(const QUrl &directory) override;
+ void selectFile(const QUrl &filename) override;
+ QList<QUrl> selectedFiles() const override;
+ void setFilter() override;
+ void selectNameFilter(const QString &filter) override;
+ QString selectedNameFilter() const override;
+
+ void exec() override;
+ bool show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent) override;
+ void hide() override;
+
+private Q_SLOTS:
+ void gotResponse(uint response, const QVariantMap &results);
+
+private:
+ void initializeDialog();
+ void openPortal();
+
+ QScopedPointer<QFlatpakFileDialogPrivate> d_ptr;
+};
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(QFlatpakFileDialog::FilterCondition);
+Q_DECLARE_METATYPE(QFlatpakFileDialog::FilterConditionList);
+Q_DECLARE_METATYPE(QFlatpakFileDialog::Filter);
+Q_DECLARE_METATYPE(QFlatpakFileDialog::FilterList);
+
+#endif // QFLATPAKFILEDIALOG_P_H
+
diff --git a/src/plugins/platformthemes/flatpak/qflatpaktheme.cpp b/src/plugins/platformthemes/flatpak/qflatpaktheme.cpp
new file mode 100644
index 0000000000..04abd707e1
--- /dev/null
+++ b/src/plugins/platformthemes/flatpak/qflatpaktheme.cpp
@@ -0,0 +1,254 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qflatpaktheme.h"
+#include "qflatpakfiledialog_p.h"
+
+#include <private/qguiapplication_p.h>
+#include <qpa/qplatformtheme_p.h>
+#include <qpa/qplatformthemefactory_p.h>
+#include <qpa/qplatformintegration.h>
+
+QT_BEGIN_NAMESPACE
+
+class QFlatpakThemePrivate : public QPlatformThemePrivate
+{
+public:
+ QFlatpakThemePrivate()
+ : QPlatformThemePrivate()
+ { }
+
+ ~QFlatpakThemePrivate()
+ {
+ delete baseTheme;
+ }
+
+ QPlatformTheme *baseTheme;
+};
+
+QFlatpakTheme::QFlatpakTheme()
+ : d_ptr(new QFlatpakThemePrivate)
+{
+ Q_D(QFlatpakTheme);
+
+ QStringList themeNames;
+ themeNames += QGuiApplicationPrivate::platform_integration->themeNames();
+ // 1) Look for a theme plugin.
+ for (const QString &themeName : qAsConst(themeNames)) {
+ d->baseTheme = QPlatformThemeFactory::create(themeName, nullptr);
+ if (d->baseTheme)
+ break;
+ }
+
+ // 2) If no theme plugin was found ask the platform integration to
+ // create a theme
+ if (!d->baseTheme) {
+ for (const QString &themeName : qAsConst(themeNames)) {
+ d->baseTheme = QGuiApplicationPrivate::platform_integration->createPlatformTheme(themeName);
+ if (d->baseTheme)
+ break;
+ }
+ // No error message; not having a theme plugin is allowed.
+ }
+
+ // 3) Fall back on the built-in "null" platform theme.
+ if (!d->baseTheme)
+ d->baseTheme = new QPlatformTheme;
+}
+
+QPlatformMenuItem* QFlatpakTheme::createPlatformMenuItem() const
+{
+ Q_D(const QFlatpakTheme);
+
+ if (d->baseTheme)
+ return d->baseTheme->createPlatformMenuItem();
+
+ return QPlatformTheme::createPlatformMenuItem();
+}
+
+QPlatformMenu* QFlatpakTheme::createPlatformMenu() const
+{
+ Q_D(const QFlatpakTheme);
+
+ if (d->baseTheme)
+ return d->baseTheme->createPlatformMenu();
+
+ return QPlatformTheme::createPlatformMenu();
+}
+
+QPlatformMenuBar* QFlatpakTheme::createPlatformMenuBar() const
+{
+ Q_D(const QFlatpakTheme);
+
+ if (d->baseTheme)
+ return d->baseTheme->createPlatformMenuBar();
+
+ return QFlatpakTheme::createPlatformMenuBar();
+}
+
+void QFlatpakTheme::showPlatformMenuBar()
+{
+ Q_D(const QFlatpakTheme);
+
+ if (d->baseTheme)
+ return d->baseTheme->showPlatformMenuBar();
+
+ return QFlatpakTheme::showPlatformMenuBar();
+}
+
+bool QFlatpakTheme::usePlatformNativeDialog(DialogType type) const
+{
+ Q_D(const QFlatpakTheme);
+
+ if (type == FileDialog)
+ return true;
+
+ if (d->baseTheme)
+ return d->baseTheme->usePlatformNativeDialog(type);
+
+ return QFlatpakTheme::usePlatformNativeDialog(type);
+}
+
+QPlatformDialogHelper* QFlatpakTheme::createPlatformDialogHelper(DialogType type) const
+{
+ Q_D(const QFlatpakTheme);
+
+ if (type == FileDialog)
+ return new QFlatpakFileDialog;
+
+ if (d->baseTheme)
+ return d->baseTheme->createPlatformDialogHelper(type);
+
+ return QFlatpakTheme::createPlatformDialogHelper(type);
+}
+
+#ifndef QT_NO_SYSTEMTRAYICON
+QPlatformSystemTrayIcon* QFlatpakTheme::createPlatformSystemTrayIcon() const
+{
+ Q_D(const QFlatpakTheme);
+
+ if (d->baseTheme)
+ return d->baseTheme->createPlatformSystemTrayIcon();
+
+ return QPlatformTheme::createPlatformSystemTrayIcon();
+}
+#endif
+
+const QPalette *QFlatpakTheme::palette(Palette type) const
+{
+ Q_D(const QFlatpakTheme);
+
+ if (d->baseTheme)
+ return d->baseTheme->palette(type);
+
+ return QPlatformTheme::palette(type);
+}
+
+const QFont* QFlatpakTheme::font(Font type) const
+{
+ Q_D(const QFlatpakTheme);
+
+ if (d->baseTheme)
+ return d->baseTheme->font(type);
+
+ return QPlatformTheme::font(type);
+}
+
+QVariant QFlatpakTheme::themeHint(ThemeHint hint) const
+{
+ Q_D(const QFlatpakTheme);
+
+ if (d->baseTheme)
+ return d->baseTheme->themeHint(hint);
+
+ return QPlatformTheme::themeHint(hint);
+}
+
+QPixmap QFlatpakTheme::standardPixmap(StandardPixmap sp, const QSizeF &size) const
+{
+ Q_D(const QFlatpakTheme);
+
+ if (d->baseTheme)
+ return d->baseTheme->standardPixmap(sp, size);
+
+ return QPlatformTheme::standardPixmap(sp, size);
+}
+
+QIcon QFlatpakTheme::fileIcon(const QFileInfo &fileInfo,
+ QPlatformTheme::IconOptions iconOptions) const
+{
+ Q_D(const QFlatpakTheme);
+
+ if (d->baseTheme)
+ return d->baseTheme->fileIcon(fileInfo, iconOptions);
+
+ return QPlatformTheme::fileIcon(fileInfo, iconOptions);
+}
+
+QIconEngine * QFlatpakTheme::createIconEngine(const QString &iconName) const
+{
+ Q_D(const QFlatpakTheme);
+
+ if (d->baseTheme)
+ return d->baseTheme->createIconEngine(iconName);
+
+ return QPlatformTheme::createIconEngine(iconName);
+}
+
+QList<QKeySequence> QFlatpakTheme::keyBindings(QKeySequence::StandardKey key) const
+{
+ Q_D(const QFlatpakTheme);
+
+ if (d->baseTheme)
+ return d->baseTheme->keyBindings(key);
+
+ return QPlatformTheme::keyBindings(key);
+}
+
+QString QFlatpakTheme::standardButtonText(int button) const
+{
+ Q_D(const QFlatpakTheme);
+
+ if (d->baseTheme)
+ return d->baseTheme->standardButtonText(button);
+
+ return QPlatformTheme::standardButtonText(button);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platformthemes/flatpak/qflatpaktheme.h b/src/plugins/platformthemes/flatpak/qflatpaktheme.h
new file mode 100644
index 0000000000..fcaac5b673
--- /dev/null
+++ b/src/plugins/platformthemes/flatpak/qflatpaktheme.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFLATPAKTHEME_H
+#define QFLATPAKTHEME_H
+
+#include <qpa/qplatformtheme.h>
+
+QT_BEGIN_NAMESPACE
+
+class QFlatpakThemePrivate;
+
+class QFlatpakTheme : public QPlatformTheme
+{
+ Q_DECLARE_PRIVATE(QFlatpakTheme)
+public:
+ QFlatpakTheme();
+
+ QPlatformMenuItem *createPlatformMenuItem() const override;
+ QPlatformMenu *createPlatformMenu() const override;
+ QPlatformMenuBar *createPlatformMenuBar() const override;
+ void showPlatformMenuBar() override;
+
+ bool usePlatformNativeDialog(DialogType type) const override;
+ QPlatformDialogHelper *createPlatformDialogHelper(DialogType type) const override;
+
+#ifndef QT_NO_SYSTEMTRAYICON
+ QPlatformSystemTrayIcon *createPlatformSystemTrayIcon() const override;
+#endif
+
+ const QPalette *palette(Palette type = SystemPalette) const override;
+
+ const QFont *font(Font type = SystemFont) const override;
+
+ QVariant themeHint(ThemeHint hint) const override;
+
+ QPixmap standardPixmap(StandardPixmap sp, const QSizeF &size) const override;
+ QIcon fileIcon(const QFileInfo &fileInfo,
+ QPlatformTheme::IconOptions iconOptions = 0) const;
+
+ QIconEngine *createIconEngine(const QString &iconName) const override;
+
+ QList<QKeySequence> keyBindings(QKeySequence::StandardKey key) const override;
+
+ QString standardButtonText(int button) const override;
+
+private:
+ QScopedPointer<QFlatpakThemePrivate> d_ptr;
+ Q_DISABLE_COPY(QFlatpakTheme)
+};
+
+QT_END_NAMESPACE
+
+#endif // QFLATPAKTHEME_H
diff --git a/src/plugins/platformthemes/gtk3/main.cpp b/src/plugins/platformthemes/gtk3/main.cpp
index c4cd66c33b..fb1c425d8e 100644
--- a/src/plugins/platformthemes/gtk3/main.cpp
+++ b/src/plugins/platformthemes/gtk3/main.cpp
@@ -48,7 +48,7 @@ class QGtk3ThemePlugin : public QPlatformThemePlugin
Q_PLUGIN_METADATA(IID QPlatformThemeFactoryInterface_iid FILE "gtk3.json")
public:
- QPlatformTheme *create(const QString &key, const QStringList &params) Q_DECL_OVERRIDE;
+ QPlatformTheme *create(const QString &key, const QStringList &params) override;
};
QPlatformTheme *QGtk3ThemePlugin::create(const QString &key, const QStringList &params)
diff --git a/src/plugins/platformthemes/gtk3/qgtk3dialoghelpers.h b/src/plugins/platformthemes/gtk3/qgtk3dialoghelpers.h
index ba43046e04..e78a7fc6d1 100644
--- a/src/plugins/platformthemes/gtk3/qgtk3dialoghelpers.h
+++ b/src/plugins/platformthemes/gtk3/qgtk3dialoghelpers.h
@@ -63,12 +63,12 @@ public:
QGtk3ColorDialogHelper();
~QGtk3ColorDialogHelper();
- bool show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) Q_DECL_OVERRIDE;
- void exec() Q_DECL_OVERRIDE;
- void hide() Q_DECL_OVERRIDE;
+ bool show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) override;
+ void exec() override;
+ void hide() override;
- void setCurrentColor(const QColor &color) Q_DECL_OVERRIDE;
- QColor currentColor() const Q_DECL_OVERRIDE;
+ void setCurrentColor(const QColor &color) override;
+ QColor currentColor() const override;
private Q_SLOTS:
void onAccepted();
@@ -88,18 +88,18 @@ public:
QGtk3FileDialogHelper();
~QGtk3FileDialogHelper();
- bool show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) Q_DECL_OVERRIDE;
- void exec() Q_DECL_OVERRIDE;
- void hide() Q_DECL_OVERRIDE;
+ bool show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) override;
+ void exec() override;
+ void hide() override;
- bool defaultNameFilterDisables() const Q_DECL_OVERRIDE;
- void setDirectory(const QUrl &directory) Q_DECL_OVERRIDE;
- QUrl directory() const Q_DECL_OVERRIDE;
- void selectFile(const QUrl &filename) Q_DECL_OVERRIDE;
- QList<QUrl> selectedFiles() const Q_DECL_OVERRIDE;
- void setFilter() Q_DECL_OVERRIDE;
- void selectNameFilter(const QString &filter) Q_DECL_OVERRIDE;
- QString selectedNameFilter() const Q_DECL_OVERRIDE;
+ bool defaultNameFilterDisables() const override;
+ void setDirectory(const QUrl &directory) override;
+ QUrl directory() const override;
+ void selectFile(const QUrl &filename) override;
+ QList<QUrl> selectedFiles() const override;
+ void setFilter() override;
+ void selectNameFilter(const QString &filter) override;
+ QString selectedNameFilter() const override;
private Q_SLOTS:
void onAccepted();
@@ -128,12 +128,12 @@ public:
QGtk3FontDialogHelper();
~QGtk3FontDialogHelper();
- bool show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) Q_DECL_OVERRIDE;
- void exec() Q_DECL_OVERRIDE;
- void hide() Q_DECL_OVERRIDE;
+ bool show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) override;
+ void exec() override;
+ void hide() override;
- void setCurrentFont(const QFont &font) Q_DECL_OVERRIDE;
- QFont currentFont() const Q_DECL_OVERRIDE;
+ void setCurrentFont(const QFont &font) override;
+ QFont currentFont() const override;
private Q_SLOTS:
void onAccepted();
diff --git a/src/plugins/platformthemes/gtk3/qgtk3menu.cpp b/src/plugins/platformthemes/gtk3/qgtk3menu.cpp
index cdd1abaf7d..ec4ff68e8d 100644
--- a/src/plugins/platformthemes/gtk3/qgtk3menu.cpp
+++ b/src/plugins/platformthemes/gtk3/qgtk3menu.cpp
@@ -87,7 +87,6 @@ QGtk3MenuItem::QGtk3MenuItem()
m_enabled(true),
m_underline(false),
m_invalid(true),
- m_tag(reinterpret_cast<quintptr>(this)),
m_menu(nullptr),
m_item(nullptr)
{
@@ -150,16 +149,6 @@ GtkWidget *QGtk3MenuItem::handle() const
return m_item;
}
-quintptr QGtk3MenuItem::tag() const
-{
- return m_tag;
-}
-
-void QGtk3MenuItem::setTag(quintptr tag)
-{
- m_tag = tag;
-}
-
QString QGtk3MenuItem::text() const
{
return m_text;
@@ -348,7 +337,6 @@ void QGtk3MenuItem::onToggle(GtkCheckMenuItem *check, void *data)
}
QGtk3Menu::QGtk3Menu()
- : m_tag(reinterpret_cast<quintptr>(this))
{
m_menu = gtk_menu_new();
@@ -409,16 +397,6 @@ void QGtk3Menu::syncSeparatorsCollapsible(bool enable)
Q_UNUSED(enable);
}
-quintptr QGtk3Menu::tag() const
-{
- return m_tag;
-}
-
-void QGtk3Menu::setTag(quintptr tag)
-{
- m_tag = tag;
-}
-
void QGtk3Menu::setEnabled(bool enabled)
{
gtk_widget_set_sensitive(m_menu, enabled);
diff --git a/src/plugins/platformthemes/gtk3/qgtk3menu.h b/src/plugins/platformthemes/gtk3/qgtk3menu.h
index c4dd89cefc..cce800fbd8 100644
--- a/src/plugins/platformthemes/gtk3/qgtk3menu.h
+++ b/src/plugins/platformthemes/gtk3/qgtk3menu.h
@@ -61,9 +61,6 @@ public:
GtkWidget *create();
GtkWidget *handle() const;
- quintptr tag() const override;
- void setTag(quintptr tag) override;
-
QString text() const;
void setText(const QString &text) override;
@@ -112,7 +109,6 @@ private:
bool m_exclusive;
bool m_underline;
bool m_invalid;
- quintptr m_tag;
QGtk3Menu *m_menu;
GtkWidget *m_item;
QString m_text;
@@ -136,9 +132,6 @@ public:
void syncMenuItem(QPlatformMenuItem *item) override;
void syncSeparatorsCollapsible(bool enable) override;
- quintptr tag() const override;
- void setTag(quintptr tag) override;
-
void setEnabled(bool enabled) override;
void setVisible(bool visible) override;
@@ -161,7 +154,6 @@ protected:
static void onHide(GtkWidget *menu, void *data);
private:
- quintptr m_tag;
GtkWidget *m_menu;
QPoint m_targetPos;
QVector<QGtk3MenuItem *> m_items;
diff --git a/src/plugins/platformthemes/gtk3/qgtk3theme.h b/src/plugins/platformthemes/gtk3/qgtk3theme.h
index abc5dbfd17..54296d2ff1 100644
--- a/src/plugins/platformthemes/gtk3/qgtk3theme.h
+++ b/src/plugins/platformthemes/gtk3/qgtk3theme.h
@@ -49,14 +49,14 @@ class QGtk3Theme : public QGnomeTheme
public:
QGtk3Theme();
- virtual QVariant themeHint(ThemeHint hint) const Q_DECL_OVERRIDE;
- virtual QString gtkFontName() const Q_DECL_OVERRIDE;
+ virtual QVariant themeHint(ThemeHint hint) const override;
+ virtual QString gtkFontName() const override;
- bool usePlatformNativeDialog(DialogType type) const Q_DECL_OVERRIDE;
- QPlatformDialogHelper *createPlatformDialogHelper(DialogType type) const Q_DECL_OVERRIDE;
+ bool usePlatformNativeDialog(DialogType type) const override;
+ QPlatformDialogHelper *createPlatformDialogHelper(DialogType type) const override;
- QPlatformMenu* createPlatformMenu() const Q_DECL_OVERRIDE;
- QPlatformMenuItem* createPlatformMenuItem() const Q_DECL_OVERRIDE;
+ QPlatformMenu* createPlatformMenu() const override;
+ QPlatformMenuItem* createPlatformMenuItem() const override;
static const char *name;
private:
diff --git a/src/plugins/platformthemes/platformthemes.pro b/src/plugins/platformthemes/platformthemes.pro
index 0e2812bed3..ebf92ba9d5 100644
--- a/src/plugins/platformthemes/platformthemes.pro
+++ b/src/plugins/platformthemes/platformthemes.pro
@@ -1,4 +1,6 @@
TEMPLATE = subdirs
QT_FOR_CONFIG += widgets-private
+qtConfig(dbus): SUBDIRS += flatpak
+
qtHaveModule(widgets):qtConfig(gtk3): SUBDIRS += gtk3
diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro
index 6d62420bd6..cd4d301194 100644
--- a/src/plugins/plugins.pro
+++ b/src/plugins/plugins.pro
@@ -8,6 +8,7 @@ qtHaveModule(gui) {
qtConfig(imageformatplugin): SUBDIRS *= imageformats
!android:qtConfig(library): SUBDIRS *= generic
}
+qtHaveModule(widgets): SUBDIRS += styles
!winrt:qtHaveModule(printsupport): \
SUBDIRS += printsupport
diff --git a/src/plugins/printsupport/cups/main.cpp b/src/plugins/printsupport/cups/main.cpp
index 39309a0031..cf263a7f52 100644
--- a/src/plugins/printsupport/cups/main.cpp
+++ b/src/plugins/printsupport/cups/main.cpp
@@ -52,7 +52,7 @@ class QCupsPrinterSupportPlugin : public QPlatformPrinterSupportPlugin
public:
QStringList keys() const;
- QPlatformPrinterSupport *create(const QString &) Q_DECL_OVERRIDE;
+ QPlatformPrinterSupport *create(const QString &) override;
};
QStringList QCupsPrinterSupportPlugin::keys() const
diff --git a/src/plugins/printsupport/cups/qcupsprintengine.cpp b/src/plugins/printsupport/cups/qcupsprintengine.cpp
index 6a4eecb06d..bd0d641e79 100644
--- a/src/plugins/printsupport/cups/qcupsprintengine.cpp
+++ b/src/plugins/printsupport/cups/qcupsprintengine.cpp
@@ -104,10 +104,9 @@ void QCupsPrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &v
break;
case PPK_QPageLayout: {
QPageLayout pageLayout = value.value<QPageLayout>();
- if (pageLayout.isValid() && d->m_printDevice.isValidPageLayout(pageLayout, d->resolution)) {
+ if (pageLayout.isValid() && (d->m_printDevice.isValidPageLayout(pageLayout, d->resolution) || d->m_printDevice.supportsCustomPageSizes())) {
d->m_pageLayout = pageLayout;
- // Replace the page size with the CUPS page size
- d->setPageSize(d->m_printDevice.supportedPageSize(pageLayout.pageSize()));
+ d->setPageSize(pageLayout.pageSize());
}
break;
}
@@ -239,47 +238,18 @@ void QCupsPrintEnginePrivate::closePrintDevice()
cupsOptStruct.append(opt);
}
- // Print the file.
+ // Print the file
+ // Cups expect the printer original name without instance, the full name is used only to retrieve the configuration
+ const auto parts = printerName.splitRef(QLatin1Char('/'));
+ const auto printerOriginalName = parts.at(0);
cups_option_t* optPtr = cupsOptStruct.size() ? &cupsOptStruct.first() : 0;
- cupsPrintFile(printerName.toLocal8Bit().constData(), tempFile.toLocal8Bit().constData(),
+ cupsPrintFile(printerOriginalName.toLocal8Bit().constData(), tempFile.toLocal8Bit().constData(),
title.toLocal8Bit().constData(), cupsOptStruct.size(), optPtr);
QFile::remove(tempFile);
}
}
-void QCupsPrintEnginePrivate::setupDefaultPrinter()
-{
- // Should never have reached here if no plugin available, but check just in case
- QPlatformPrinterSupport *ps = QPlatformPrinterSupportPlugin::get();
- if (!ps)
- return;
-
- // Get default printer id, if no default then use the first available
- // TODO Find way to remove printerName from base class?
- printerName = ps->defaultPrintDeviceId();
- if (printerName.isEmpty()) {
- QStringList list = ps->availablePrintDeviceIds();
- if (list.size() > 0)
- printerName = list.at(0);
- }
-
- // Should never have reached here if no printers available, but check just in case
- if (printerName.isEmpty())
- return;
-
- m_printDevice = ps->createPrintDevice(printerName);
- if (!m_printDevice.isValid())
- return;
-
- // Setup the printer defaults
- duplex = m_printDevice.defaultDuplexMode();
- grayscale = m_printDevice.defaultColorMode() == QPrint::GrayScale;
- // CUPS server always supports collation, even if individual m_printDevice doesn't
- collate = true;
- setPageSize(m_printDevice.defaultPageSize());
-}
-
void QCupsPrintEnginePrivate::changePrinter(const QString &newPrinter)
{
// Don't waste time if same printer name
diff --git a/src/plugins/printsupport/cups/qcupsprintengine_p.h b/src/plugins/printsupport/cups/qcupsprintengine_p.h
index 0ebf6e7a0f..d5363bb8cc 100644
--- a/src/plugins/printsupport/cups/qcupsprintengine_p.h
+++ b/src/plugins/printsupport/cups/qcupsprintengine_p.h
@@ -72,8 +72,8 @@ public:
virtual ~QCupsPrintEngine();
// reimplementations QPdfPrintEngine
- void setProperty(PrintEnginePropertyKey key, const QVariant &value) Q_DECL_OVERRIDE;
- QVariant property(PrintEnginePropertyKey key) const Q_DECL_OVERRIDE;
+ void setProperty(PrintEnginePropertyKey key, const QVariant &value) override;
+ QVariant property(PrintEnginePropertyKey key) const override;
// end reimplementations QPdfPrintEngine
private:
@@ -87,13 +87,12 @@ public:
QCupsPrintEnginePrivate(QPrinter::PrinterMode m);
~QCupsPrintEnginePrivate();
- bool openPrintDevice() Q_DECL_OVERRIDE;
- void closePrintDevice() Q_DECL_OVERRIDE;
+ bool openPrintDevice() override;
+ void closePrintDevice() override;
private:
Q_DISABLE_COPY(QCupsPrintEnginePrivate)
- void setupDefaultPrinter();
void changePrinter(const QString &newPrinter);
void setPageSize(const QPageSize &pageSize);
diff --git a/src/plugins/printsupport/cups/qcupsprintersupport_p.h b/src/plugins/printsupport/cups/qcupsprintersupport_p.h
index df6507d324..42de28aec0 100644
--- a/src/plugins/printsupport/cups/qcupsprintersupport_p.h
+++ b/src/plugins/printsupport/cups/qcupsprintersupport_p.h
@@ -64,12 +64,12 @@ public:
QCupsPrinterSupport();
~QCupsPrinterSupport();
- QPrintEngine *createNativePrintEngine(QPrinter::PrinterMode printerMode, const QString &deviceId = QString()) Q_DECL_OVERRIDE;
- QPaintEngine *createPaintEngine(QPrintEngine *printEngine, QPrinter::PrinterMode) Q_DECL_OVERRIDE;
+ QPrintEngine *createNativePrintEngine(QPrinter::PrinterMode printerMode, const QString &deviceId = QString()) override;
+ QPaintEngine *createPaintEngine(QPrintEngine *printEngine, QPrinter::PrinterMode) override;
- QPrintDevice createPrintDevice(const QString &id) Q_DECL_OVERRIDE;
- QStringList availablePrintDeviceIds() const Q_DECL_OVERRIDE;
- QString defaultPrintDeviceId() const Q_DECL_OVERRIDE;
+ QPrintDevice createPrintDevice(const QString &id) override;
+ QStringList availablePrintDeviceIds() const override;
+ QString defaultPrintDeviceId() const override;
private:
QString cupsOption(int i, const QString &key) const;
diff --git a/src/plugins/printsupport/cups/qppdprintdevice.cpp b/src/plugins/printsupport/cups/qppdprintdevice.cpp
index 9efa83d409..340b1a1ff4 100644
--- a/src/plugins/printsupport/cups/qppdprintdevice.cpp
+++ b/src/plugins/printsupport/cups/qppdprintdevice.cpp
@@ -42,6 +42,8 @@
#include <QtCore/QMimeDatabase>
#include <qdebug.h>
+#include "private/qcups_p.h" // Only needed for PDPK_*
+
#ifndef QT_LINUXBASE // LSB merges everything into cups.h
#include <cups/language.h>
#endif
@@ -421,6 +423,46 @@ QPrint::ColorMode QPpdPrintDevice::defaultColorMode() const
return QPrint::GrayScale;
}
+QVariant QPpdPrintDevice::property(QPrintDevice::PrintDevicePropertyKey key) const
+{
+ if (key == PDPK_PpdFile)
+ return QVariant::fromValue<ppd_file_t *>(m_ppd);
+ else if (key == PDPK_CupsJobPriority)
+ return printerOption(QStringLiteral("job-priority"));
+ else if (key == PDPK_CupsJobSheets)
+ return printerOption(QStringLiteral("job-sheets"));
+ else if (key == PDPK_CupsJobBilling)
+ return printerOption(QStringLiteral("job-billing"));
+ else if (key == PDPK_CupsJobHoldUntil)
+ return printerOption(QStringLiteral("job-hold-until"));
+
+ return QPlatformPrintDevice::property(key);
+}
+
+bool QPpdPrintDevice::setProperty(QPrintDevice::PrintDevicePropertyKey key, const QVariant &value)
+{
+ if (key == PDPK_PpdOption) {
+ const QStringList values = value.toStringList();
+ if (values.count() == 2) {
+ ppdMarkOption(m_ppd, values[0].toLatin1(), values[1].toLatin1());
+ return true;
+ }
+ }
+
+ return QPlatformPrintDevice::setProperty(key, value);
+}
+
+bool QPpdPrintDevice::isFeatureAvailable(QPrintDevice::PrintDevicePropertyKey key, const QVariant &params) const
+{
+ if (key == PDPK_PpdChoiceIsInstallableConflict) {
+ const QStringList values = params.toStringList();
+ if (values.count() == 2)
+ return ppdInstallableConflict(m_ppd, values[0].toLatin1(), values[1].toLatin1());
+ }
+
+ return QPlatformPrintDevice::isFeatureAvailable(key, params);
+}
+
#ifndef QT_NO_MIMETYPE
void QPpdPrintDevice::loadMimeTypes() const
{
@@ -452,7 +494,7 @@ void QPpdPrintDevice::loadPrinter()
}
// Get the print instance and PPD file
- m_cupsDest = cupsGetNamedDest(CUPS_HTTP_DEFAULT, m_cupsName, m_cupsInstance);
+ m_cupsDest = cupsGetNamedDest(CUPS_HTTP_DEFAULT, m_cupsName, m_cupsInstance.isNull() ? nullptr : m_cupsInstance.constData());
if (m_cupsDest) {
const char *ppdFile = cupsGetPPD(m_cupsName);
if (ppdFile) {
@@ -461,6 +503,8 @@ void QPpdPrintDevice::loadPrinter()
}
if (m_ppd) {
ppdMarkDefaults(m_ppd);
+ cupsMarkOptions(m_ppd, m_cupsDest->num_options, m_cupsDest->options);
+ ppdLocalize(m_ppd);
} else {
cupsFreeDests(1, m_cupsDest);
m_cupsDest = 0;
diff --git a/src/plugins/printsupport/cups/qppdprintdevice.h b/src/plugins/printsupport/cups/qppdprintdevice.h
index fab7077ce4..9867083bd7 100644
--- a/src/plugins/printsupport/cups/qppdprintdevice.h
+++ b/src/plugins/printsupport/cups/qppdprintdevice.h
@@ -69,35 +69,39 @@ public:
explicit QPpdPrintDevice(const QString &id);
virtual ~QPpdPrintDevice();
- bool isValid() const Q_DECL_OVERRIDE;
- bool isDefault() const Q_DECL_OVERRIDE;
+ bool isValid() const override;
+ bool isDefault() const override;
- QPrint::DeviceState state() const Q_DECL_OVERRIDE;
+ QPrint::DeviceState state() const override;
- QPageSize defaultPageSize() const Q_DECL_OVERRIDE;
+ QPageSize defaultPageSize() const override;
QMarginsF printableMargins(const QPageSize &pageSize, QPageLayout::Orientation orientation,
- int resolution) const Q_DECL_OVERRIDE;
+ int resolution) const override;
- int defaultResolution() const Q_DECL_OVERRIDE;
+ int defaultResolution() const override;
- QPrint::InputSlot defaultInputSlot() const Q_DECL_OVERRIDE;
+ QPrint::InputSlot defaultInputSlot() const override;
- QPrint::OutputBin defaultOutputBin() const Q_DECL_OVERRIDE;
+ QPrint::OutputBin defaultOutputBin() const override;
- QPrint::DuplexMode defaultDuplexMode() const Q_DECL_OVERRIDE;
+ QPrint::DuplexMode defaultDuplexMode() const override;
- QPrint::ColorMode defaultColorMode() const Q_DECL_OVERRIDE;
+ QPrint::ColorMode defaultColorMode() const override;
+
+ QVariant property(QPrintDevice::PrintDevicePropertyKey key) const override;
+ bool setProperty(QPrintDevice::PrintDevicePropertyKey key, const QVariant &value) override;
+ bool isFeatureAvailable(QPrintDevice::PrintDevicePropertyKey key, const QVariant &params) const override;
protected:
- void loadPageSizes() const Q_DECL_OVERRIDE;
- void loadResolutions() const Q_DECL_OVERRIDE;
- void loadInputSlots() const Q_DECL_OVERRIDE;
- void loadOutputBins() const Q_DECL_OVERRIDE;
- void loadDuplexModes() const Q_DECL_OVERRIDE;
- void loadColorModes() const Q_DECL_OVERRIDE;
+ void loadPageSizes() const override;
+ void loadResolutions() const override;
+ void loadInputSlots() const override;
+ void loadOutputBins() const override;
+ void loadDuplexModes() const override;
+ void loadColorModes() const override;
#ifndef QT_NO_MIMETYPE
- void loadMimeTypes() const Q_DECL_OVERRIDE;
+ void loadMimeTypes() const override;
#endif
private:
diff --git a/src/plugins/printsupport/windows/qwindowsprintdevice.cpp b/src/plugins/printsupport/windows/qwindowsprintdevice.cpp
index 1cb14514ee..b1589c0738 100644
--- a/src/plugins/printsupport/windows/qwindowsprintdevice.cpp
+++ b/src/plugins/printsupport/windows/qwindowsprintdevice.cpp
@@ -87,13 +87,13 @@ static LPDEVMODE getDevmode(HANDLE hPrinter, const QString &printerId)
// Allocate the required DEVMODE buffer
LONG dmSize = DocumentProperties(NULL, hPrinter, printerIdUtf16, NULL, NULL, 0);
if (dmSize <= 0)
- return Q_NULLPTR;
+ return nullptr;
LPDEVMODE pDevMode = reinterpret_cast<LPDEVMODE>(malloc(dmSize));
// Get the default DevMode
LONG result = DocumentProperties(NULL, hPrinter, printerIdUtf16, pDevMode, NULL, DM_OUT_BUFFER);
if (result != IDOK) {
free(pDevMode);
- pDevMode = Q_NULLPTR;
+ pDevMode = nullptr;
}
return pDevMode;
}
diff --git a/src/plugins/printsupport/windows/qwindowsprintdevice.h b/src/plugins/printsupport/windows/qwindowsprintdevice.h
index d95a4316cc..6b51ee8785 100644
--- a/src/plugins/printsupport/windows/qwindowsprintdevice.h
+++ b/src/plugins/printsupport/windows/qwindowsprintdevice.h
@@ -64,34 +64,34 @@ public:
explicit QWindowsPrintDevice(const QString &id);
virtual ~QWindowsPrintDevice();
- bool isValid() const Q_DECL_OVERRIDE;
- bool isDefault() const Q_DECL_OVERRIDE;
+ bool isValid() const override;
+ bool isDefault() const override;
- QPrint::DeviceState state() const Q_DECL_OVERRIDE;
+ QPrint::DeviceState state() const override;
- QPageSize defaultPageSize() const Q_DECL_OVERRIDE;
+ QPageSize defaultPageSize() const override;
QMarginsF printableMargins(const QPageSize &pageSize, QPageLayout::Orientation orientation,
- int resolution) const Q_DECL_OVERRIDE;
+ int resolution) const override;
- int defaultResolution() const Q_DECL_OVERRIDE;
+ int defaultResolution() const override;
- QPrint::InputSlot defaultInputSlot() const Q_DECL_OVERRIDE;
+ QPrint::InputSlot defaultInputSlot() const override;
- QPrint::DuplexMode defaultDuplexMode() const Q_DECL_OVERRIDE;
+ QPrint::DuplexMode defaultDuplexMode() const override;
- QPrint::ColorMode defaultColorMode() const Q_DECL_OVERRIDE;
+ QPrint::ColorMode defaultColorMode() const override;
static QStringList availablePrintDeviceIds();
static QString defaultPrintDeviceId();
protected:
- void loadPageSizes() const Q_DECL_OVERRIDE;
- void loadResolutions() const Q_DECL_OVERRIDE;
- void loadInputSlots() const Q_DECL_OVERRIDE;
- void loadOutputBins() const Q_DECL_OVERRIDE;
- void loadDuplexModes() const Q_DECL_OVERRIDE;
- void loadColorModes() const Q_DECL_OVERRIDE;
+ void loadPageSizes() const override;
+ void loadResolutions() const override;
+ void loadInputSlots() const override;
+ void loadOutputBins() const override;
+ void loadDuplexModes() const override;
+ void loadColorModes() const override;
private:
HANDLE m_hPrinter;
diff --git a/src/plugins/printsupport/windows/qwindowsprintersupport.h b/src/plugins/printsupport/windows/qwindowsprintersupport.h
index 84b60a8207..c42e7aa551 100644
--- a/src/plugins/printsupport/windows/qwindowsprintersupport.h
+++ b/src/plugins/printsupport/windows/qwindowsprintersupport.h
@@ -50,12 +50,12 @@ public:
QWindowsPrinterSupport();
~QWindowsPrinterSupport();
- QPrintEngine *createNativePrintEngine(QPrinter::PrinterMode printerMode, const QString &deviceId = QString()) Q_DECL_OVERRIDE;
- QPaintEngine *createPaintEngine(QPrintEngine *printEngine, QPrinter::PrinterMode) Q_DECL_OVERRIDE;
+ QPrintEngine *createNativePrintEngine(QPrinter::PrinterMode printerMode, const QString &deviceId = QString()) override;
+ QPaintEngine *createPaintEngine(QPrintEngine *printEngine, QPrinter::PrinterMode) override;
- QPrintDevice createPrintDevice(const QString &id) Q_DECL_OVERRIDE;
- QStringList availablePrintDeviceIds() const Q_DECL_OVERRIDE;
- QString defaultPrintDeviceId() const Q_DECL_OVERRIDE;
+ QPrintDevice createPrintDevice(const QString &id) override;
+ QStringList availablePrintDeviceIds() const override;
+ QString defaultPrintDeviceId() const override;
};
QT_END_NAMESPACE
diff --git a/src/plugins/sqldrivers/db2/qsql_db2.cpp b/src/plugins/sqldrivers/db2/qsql_db2.cpp
index 839d80466f..1d7e985731 100644
--- a/src/plugins/sqldrivers/db2/qsql_db2.cpp
+++ b/src/plugins/sqldrivers/db2/qsql_db2.cpp
@@ -66,6 +66,10 @@
QT_BEGIN_NAMESPACE
static const int COLNAMESIZE = 255;
+// Based on what is mentioned in the documentation here:
+// https://www.ibm.com/support/knowledgecenter/en/SSEPEK_10.0.0/sqlref/src/tpc/db2z_limits.html
+// The limit is 128 bytes for table names
+static const SQLSMALLINT TABLENAMESIZE = 128;
static const SQLSMALLINT qParamType[4] = { SQL_PARAM_INPUT, SQL_PARAM_INPUT, SQL_PARAM_OUTPUT, SQL_PARAM_INPUT_OUTPUT };
class QDB2DriverPrivate : public QSqlDriverPrivate
@@ -88,24 +92,24 @@ class QDB2Result: public QSqlResult
public:
QDB2Result(const QDB2Driver *drv);
~QDB2Result();
- bool prepare(const QString &query) Q_DECL_OVERRIDE;
- bool exec() Q_DECL_OVERRIDE;
- QVariant handle() const Q_DECL_OVERRIDE;
+ bool prepare(const QString &query) override;
+ bool exec() override;
+ QVariant handle() const override;
protected:
- QVariant data(int field) Q_DECL_OVERRIDE;
- bool reset(const QString &query) Q_DECL_OVERRIDE;
- bool fetch(int i) Q_DECL_OVERRIDE;
- bool fetchNext() Q_DECL_OVERRIDE;
- bool fetchFirst() Q_DECL_OVERRIDE;
- bool fetchLast() Q_DECL_OVERRIDE;
- bool isNull(int i) Q_DECL_OVERRIDE;
- int size() Q_DECL_OVERRIDE;
- int numRowsAffected() Q_DECL_OVERRIDE;
- QSqlRecord record() const Q_DECL_OVERRIDE;
- void virtual_hook(int id, void *data) Q_DECL_OVERRIDE;
- void detachFromResultSet() Q_DECL_OVERRIDE;
- bool nextResult() Q_DECL_OVERRIDE;
+ QVariant data(int field) override;
+ bool reset(const QString &query) override;
+ bool fetch(int i) override;
+ bool fetchNext() override;
+ bool fetchFirst() override;
+ bool fetchLast() override;
+ bool isNull(int i) override;
+ int size() override;
+ int numRowsAffected() override;
+ QSqlRecord record() const override;
+ void virtual_hook(int id, void *data) override;
+ void detachFromResultSet() override;
+ bool nextResult() override;
};
class QDB2ResultPrivate: public QSqlResultPrivate
@@ -152,7 +156,7 @@ static SQLTCHAR* qToTChar(const QString& str)
return (SQLTCHAR*)str.utf16();
}
-static QString qWarnDB2Handle(int handleType, SQLHANDLE handle)
+static QString qWarnDB2Handle(int handleType, SQLHANDLE handle, int *errorCode)
{
SQLINTEGER nativeCode;
SQLSMALLINT msgLen;
@@ -167,22 +171,51 @@ static QString qWarnDB2Handle(int handleType, SQLHANDLE handle)
(SQLTCHAR*) description,
SQL_MAX_MESSAGE_LENGTH - 1, /* in bytes, not in characters */
&msgLen);
- if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO)
+ if (r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) {
+ if (errorCode)
+ *errorCode = nativeCode;
return QString(qFromTChar(description));
+ }
return QString();
}
-static QString qDB2Warn(const QDB2DriverPrivate* d)
+static QString qDB2Warn(const QDB2DriverPrivate* d, QStringList *errorCodes = nullptr)
{
- return (qWarnDB2Handle(SQL_HANDLE_ENV, d->hEnv) + QLatin1Char(' ')
- + qWarnDB2Handle(SQL_HANDLE_DBC, d->hDbc));
+ int errorCode = 0;
+ QString error = qWarnDB2Handle(SQL_HANDLE_ENV, d->hEnv, &errorCode);
+ if (errorCodes && errorCode != 0) {
+ *errorCodes << QString::number(errorCode);
+ errorCode = 0;
+ }
+ if (!error.isEmpty())
+ error += QLatin1Char(' ');
+ error += qWarnDB2Handle(SQL_HANDLE_DBC, d->hDbc, &errorCode);
+ if (errorCodes && errorCode != 0)
+ *errorCodes << QString::number(errorCode);
+ return error;
}
-static QString qDB2Warn(const QDB2ResultPrivate* d)
+static QString qDB2Warn(const QDB2ResultPrivate* d, QStringList *errorCodes = nullptr)
{
- return (qWarnDB2Handle(SQL_HANDLE_ENV, d->drv_d_func()->hEnv) + QLatin1Char(' ')
- + qWarnDB2Handle(SQL_HANDLE_DBC, d->drv_d_func()->hDbc)
- + qWarnDB2Handle(SQL_HANDLE_STMT, d->hStmt));
+ int errorCode = 0;
+ QString error = qWarnDB2Handle(SQL_HANDLE_ENV, d->drv_d_func()->hEnv, &errorCode);
+ if (errorCodes && errorCode != 0) {
+ *errorCodes << QString::number(errorCode);
+ errorCode = 0;
+ }
+ if (!error.isEmpty())
+ error += QLatin1Char(' ');
+ error += qWarnDB2Handle(SQL_HANDLE_DBC, d->drv_d_func()->hDbc, &errorCode);
+ if (errorCodes && errorCode != 0) {
+ *errorCodes << QString::number(errorCode);
+ errorCode = 0;
+ }
+ if (!error.isEmpty())
+ error += QLatin1Char(' ');
+ error += qWarnDB2Handle(SQL_HANDLE_STMT, d->hStmt, &errorCode);
+ if (errorCodes && errorCode != 0)
+ *errorCodes << QString::number(errorCode);
+ return error;
}
static void qSqlWarning(const QString& message, const QDB2DriverPrivate* d)
@@ -200,13 +233,19 @@ static void qSqlWarning(const QString& message, const QDB2ResultPrivate* d)
static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type,
const QDB2DriverPrivate* p)
{
- return QSqlError(QLatin1String("QDB2: ") + err, qDB2Warn(p), type);
+ QStringList errorCodes;
+ const QString error = qDB2Warn(p, &errorCodes);
+ return QSqlError(QStringLiteral("QDB2: ") + err, error, type,
+ errorCodes.join(QLatin1Char(';')));
}
static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type,
const QDB2ResultPrivate* p)
{
- return QSqlError(QLatin1String("QDB2: ") + err, qDB2Warn(p), type);
+ QStringList errorCodes;
+ const QString error = qDB2Warn(p, &errorCodes);
+ return QSqlError(QStringLiteral("QDB2: ") + err, error, type,
+ errorCodes.join(QLatin1Char(';')));
}
static QVariant::Type qDecodeDB2Type(SQLSMALLINT sqltype)
@@ -297,6 +336,12 @@ static QSqlField qMakeFieldInfo(const QDB2ResultPrivate* d, int i)
f.setLength(colSize == 0 ? -1 : int(colSize));
f.setPrecision(colScale == 0 ? -1 : int(colScale));
f.setSqlType(int(colType));
+ SQLTCHAR tableName[TABLENAMESIZE];
+ SQLSMALLINT tableNameLen;
+ r = SQLColAttribute(d->hStmt, i + 1, SQL_DESC_BASE_TABLE_NAME, tableName,
+ TABLENAMESIZE, &tableNameLen, 0);
+ if (r == SQL_SUCCESS)
+ f.setTableName(qFromTChar(tableName));
return f;
}
@@ -1394,7 +1439,9 @@ QSqlRecord QDB2Driver::record(const QString& tableName) const
SQL_FETCH_NEXT,
0);
while (r == SQL_SUCCESS) {
- fil.append(qMakeFieldInfo(hStmt));
+ QSqlField fld = qMakeFieldInfo(hStmt);
+ fld.setTableName(tableName);
+ fil.append(fld);
r = SQLFetchScroll(hStmt,
SQL_FETCH_NEXT,
0);
diff --git a/src/plugins/sqldrivers/db2/qsql_db2_p.h b/src/plugins/sqldrivers/db2/qsql_db2_p.h
index fa6d739479..79ae54ab2d 100644
--- a/src/plugins/sqldrivers/db2/qsql_db2_p.h
+++ b/src/plugins/sqldrivers/db2/qsql_db2_p.h
@@ -75,24 +75,24 @@ public:
explicit QDB2Driver(QObject* parent = 0);
QDB2Driver(Qt::HANDLE env, Qt::HANDLE con, QObject* parent = 0);
~QDB2Driver();
- bool hasFeature(DriverFeature) const Q_DECL_OVERRIDE;
- void close() Q_DECL_OVERRIDE;
- QSqlRecord record(const QString &tableName) const Q_DECL_OVERRIDE;
- QStringList tables(QSql::TableType type) const Q_DECL_OVERRIDE;
- QSqlResult *createResult() const Q_DECL_OVERRIDE;
- QSqlIndex primaryIndex(const QString &tablename) const Q_DECL_OVERRIDE;
- bool beginTransaction() Q_DECL_OVERRIDE;
- bool commitTransaction() Q_DECL_OVERRIDE;
- bool rollbackTransaction() Q_DECL_OVERRIDE;
- QString formatValue(const QSqlField &field, bool trimStrings) const Q_DECL_OVERRIDE;
- QVariant handle() const Q_DECL_OVERRIDE;
+ bool hasFeature(DriverFeature) const override;
+ void close() override;
+ QSqlRecord record(const QString &tableName) const override;
+ QStringList tables(QSql::TableType type) const override;
+ QSqlResult *createResult() const override;
+ QSqlIndex primaryIndex(const QString &tablename) const override;
+ bool beginTransaction() override;
+ bool commitTransaction() override;
+ bool rollbackTransaction() override;
+ QString formatValue(const QSqlField &field, bool trimStrings) const override;
+ QVariant handle() const override;
bool open(const QString &db,
const QString &user,
const QString &password,
const QString &host,
int port,
- const QString& connOpts) Q_DECL_OVERRIDE;
- QString escapeIdentifier(const QString &identifier, IdentifierType type) const Q_DECL_OVERRIDE;
+ const QString& connOpts) override;
+ QString escapeIdentifier(const QString &identifier, IdentifierType type) const override;
private:
bool setAutoCommit(bool autoCommit);
diff --git a/src/plugins/sqldrivers/ibase/qsql_ibase.cpp b/src/plugins/sqldrivers/ibase/qsql_ibase.cpp
index 2b75f3c964..d89051191c 100644
--- a/src/plugins/sqldrivers/ibase/qsql_ibase.cpp
+++ b/src/plugins/sqldrivers/ibase/qsql_ibase.cpp
@@ -356,16 +356,16 @@ class QIBaseResult : public QSqlCachedResult
public:
explicit QIBaseResult(const QIBaseDriver* db);
- bool prepare(const QString &query) Q_DECL_OVERRIDE;
- bool exec() Q_DECL_OVERRIDE;
- QVariant handle() const Q_DECL_OVERRIDE;
+ bool prepare(const QString &query) override;
+ bool exec() override;
+ QVariant handle() const override;
protected:
- bool gotoNext(QSqlCachedResult::ValueCache& row, int rowIdx) Q_DECL_OVERRIDE;
- bool reset (const QString &query) Q_DECL_OVERRIDE;
- int size() Q_DECL_OVERRIDE;
- int numRowsAffected() Q_DECL_OVERRIDE;
- QSqlRecord record() const Q_DECL_OVERRIDE;
+ bool gotoNext(QSqlCachedResult::ValueCache& row, int rowIdx) override;
+ bool reset (const QString &query) override;
+ int size() override;
+ int numRowsAffected() override;
+ QSqlRecord record() const override;
};
class QIBaseResultPrivate: public QSqlCachedResultPrivate
@@ -388,7 +388,8 @@ public:
return false;
q->setLastError(QSqlError(QCoreApplication::translate("QIBaseResult", msg),
- imsg, typ, int(sqlcode)));
+ imsg, typ,
+ sqlcode != -1 ? QString::number(sqlcode) : QString()));
return true;
}
@@ -1383,7 +1384,8 @@ QSqlRecord QIBaseResult::record() const
for (int i = 0; i < d->sqlda->sqld; ++i) {
v = d->sqlda->sqlvar[i];
QSqlField f(QString::fromLatin1(v.aliasname, v.aliasname_length).simplified(),
- qIBaseTypeName2(v.sqltype, v.sqlscale < 0));
+ qIBaseTypeName2(v.sqltype, v.sqlscale < 0),
+ QString::fromLatin1(v.relname, v.relname_length));
f.setLength(v.sqllen);
f.setPrecision(qAbs(v.sqlscale));
f.setRequiredStatus((v.sqltype & 1) == 0 ? QSqlField::Required : QSqlField::Optional);
@@ -1686,7 +1688,7 @@ QSqlRecord QIBaseDriver::record(const QString& tablename) const
while (q.next()) {
int type = q.value(1).toInt();
bool hasScale = q.value(3).toInt() < 0;
- QSqlField f(q.value(0).toString().simplified(), qIBaseTypeName(type, hasScale));
+ QSqlField f(q.value(0).toString().simplified(), qIBaseTypeName(type, hasScale), tablename);
if(hasScale) {
f.setLength(q.value(4).toInt());
f.setPrecision(qAbs(q.value(3).toInt()));
@@ -1727,7 +1729,9 @@ QSqlIndex QIBaseDriver::primaryIndex(const QString &table) const
"ORDER BY b.RDB$FIELD_POSITION"));
while (q.next()) {
- QSqlField field(q.value(1).toString().simplified(), qIBaseTypeName(q.value(2).toInt(), q.value(3).toInt() < 0));
+ QSqlField field(q.value(1).toString().simplified(),
+ qIBaseTypeName(q.value(2).toInt(), q.value(3).toInt() < 0),
+ tablename);
index.append(field); //TODO: asc? desc?
index.setName(q.value(0).toString());
}
diff --git a/src/plugins/sqldrivers/ibase/qsql_ibase_p.h b/src/plugins/sqldrivers/ibase/qsql_ibase_p.h
index c7cee41462..295f6c0cec 100644
--- a/src/plugins/sqldrivers/ibase/qsql_ibase_p.h
+++ b/src/plugins/sqldrivers/ibase/qsql_ibase_p.h
@@ -74,36 +74,36 @@ public:
explicit QIBaseDriver(QObject *parent = 0);
explicit QIBaseDriver(isc_db_handle connection, QObject *parent = 0);
virtual ~QIBaseDriver();
- bool hasFeature(DriverFeature f) const Q_DECL_OVERRIDE;
+ bool hasFeature(DriverFeature f) const override;
bool open(const QString &db,
const QString &user,
const QString &password,
const QString &host,
int port,
- const QString &connOpts) Q_DECL_OVERRIDE;
+ const QString &connOpts) override;
bool open(const QString &db,
const QString &user,
const QString &password,
const QString &host,
int port) { return open(db, user, password, host, port, QString()); }
- void close() Q_DECL_OVERRIDE;
- QSqlResult *createResult() const Q_DECL_OVERRIDE;
- bool beginTransaction() Q_DECL_OVERRIDE;
- bool commitTransaction() Q_DECL_OVERRIDE;
- bool rollbackTransaction() Q_DECL_OVERRIDE;
- QStringList tables(QSql::TableType) const Q_DECL_OVERRIDE;
+ void close() override;
+ QSqlResult *createResult() const override;
+ bool beginTransaction() override;
+ bool commitTransaction() override;
+ bool rollbackTransaction() override;
+ QStringList tables(QSql::TableType) const override;
- QSqlRecord record(const QString& tablename) const Q_DECL_OVERRIDE;
- QSqlIndex primaryIndex(const QString &table) const Q_DECL_OVERRIDE;
+ QSqlRecord record(const QString& tablename) const override;
+ QSqlIndex primaryIndex(const QString &table) const override;
- QString formatValue(const QSqlField &field, bool trimStrings) const Q_DECL_OVERRIDE;
- QVariant handle() const Q_DECL_OVERRIDE;
+ QString formatValue(const QSqlField &field, bool trimStrings) const override;
+ QVariant handle() const override;
- QString escapeIdentifier(const QString &identifier, IdentifierType type) const Q_DECL_OVERRIDE;
+ QString escapeIdentifier(const QString &identifier, IdentifierType type) const override;
- bool subscribeToNotification(const QString &name) Q_DECL_OVERRIDE;
- bool unsubscribeFromNotification(const QString &name) Q_DECL_OVERRIDE;
- QStringList subscribedToNotifications() const Q_DECL_OVERRIDE;
+ bool subscribeToNotification(const QString &name) override;
+ bool unsubscribeFromNotification(const QString &name) override;
+ QStringList subscribedToNotifications() const override;
private Q_SLOTS:
void qHandleEventNotification(void* updatedResultBuffer);
diff --git a/src/plugins/sqldrivers/mysql/main.cpp b/src/plugins/sqldrivers/mysql/main.cpp
index 00d9c5bb34..d8d70483ef 100644
--- a/src/plugins/sqldrivers/mysql/main.cpp
+++ b/src/plugins/sqldrivers/mysql/main.cpp
@@ -51,7 +51,7 @@ class QMYSQLDriverPlugin : public QSqlDriverPlugin
public:
QMYSQLDriverPlugin();
- QSqlDriver* create(const QString &) Q_DECL_OVERRIDE;
+ QSqlDriver* create(const QString &) override;
};
QMYSQLDriverPlugin::QMYSQLDriverPlugin()
diff --git a/src/plugins/sqldrivers/mysql/qsql_mysql.cpp b/src/plugins/sqldrivers/mysql/qsql_mysql.cpp
index 28d5ccb4a2..53a72779a9 100644
--- a/src/plugins/sqldrivers/mysql/qsql_mysql.cpp
+++ b/src/plugins/sqldrivers/mysql/qsql_mysql.cpp
@@ -175,26 +175,26 @@ public:
explicit QMYSQLResult(const QMYSQLDriver *db);
~QMYSQLResult();
- QVariant handle() const Q_DECL_OVERRIDE;
+ QVariant handle() const override;
protected:
void cleanup();
- bool fetch(int i) Q_DECL_OVERRIDE;
- bool fetchNext() Q_DECL_OVERRIDE;
- bool fetchLast() Q_DECL_OVERRIDE;
- bool fetchFirst() Q_DECL_OVERRIDE;
- QVariant data(int field) Q_DECL_OVERRIDE;
- bool isNull(int field) Q_DECL_OVERRIDE;
- bool reset (const QString& query) Q_DECL_OVERRIDE;
- int size() Q_DECL_OVERRIDE;
- int numRowsAffected() Q_DECL_OVERRIDE;
- QVariant lastInsertId() const Q_DECL_OVERRIDE;
- QSqlRecord record() const Q_DECL_OVERRIDE;
- void virtual_hook(int id, void *data) Q_DECL_OVERRIDE;
- bool nextResult() Q_DECL_OVERRIDE;
+ bool fetch(int i) override;
+ bool fetchNext() override;
+ bool fetchLast() override;
+ bool fetchFirst() override;
+ QVariant data(int field) override;
+ bool isNull(int field) override;
+ bool reset (const QString& query) override;
+ int size() override;
+ int numRowsAffected() override;
+ QVariant lastInsertId() const override;
+ QSqlRecord record() const override;
+ void virtual_hook(int id, void *data) override;
+ bool nextResult() override;
#if MYSQL_VERSION_ID >= 40108
- bool prepare(const QString &stmt) Q_DECL_OVERRIDE;
- bool exec() Q_DECL_OVERRIDE;
+ bool prepare(const QString &stmt) override;
+ bool exec() override;
#endif
};
@@ -269,7 +269,7 @@ static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type,
const char *cerr = p->mysql ? mysql_error(p->mysql) : 0;
return QSqlError(QLatin1String("QMYSQL: ") + err,
p->tc ? toUnicode(p->tc, cerr) : QString::fromLatin1(cerr),
- type, mysql_errno(p->mysql));
+ type, QString::number(mysql_errno(p->mysql)));
}
@@ -331,7 +331,8 @@ static QVariant::Type qDecodeMYSQLType(int mysqltype, uint flags)
static QSqlField qToField(MYSQL_FIELD *field, QTextCodec *tc)
{
QSqlField f(toUnicode(tc, field->name),
- qDecodeMYSQLType(int(field->type), field->flags));
+ qDecodeMYSQLType(int(field->type), field->flags),
+ toUnicode(tc, field->table));
f.setRequired(IS_NOT_NULL(field->flags));
f.setLength(field->length);
f.setPrecision(field->decimals);
@@ -348,7 +349,7 @@ static QSqlError qMakeStmtError(const QString& err, QSqlError::ErrorType type,
const char *cerr = mysql_stmt_error(stmt);
return QSqlError(QLatin1String("QMYSQL3: ") + err,
QString::fromLatin1(cerr),
- type, mysql_stmt_errno(stmt));
+ type, QString::number(mysql_stmt_errno(stmt)));
}
static bool qIsBlob(int t)
diff --git a/src/plugins/sqldrivers/mysql/qsql_mysql_p.h b/src/plugins/sqldrivers/mysql/qsql_mysql_p.h
index 7641f9aa34..48b04fb1f5 100644
--- a/src/plugins/sqldrivers/mysql/qsql_mysql_p.h
+++ b/src/plugins/sqldrivers/mysql/qsql_mysql_p.h
@@ -78,29 +78,29 @@ public:
explicit QMYSQLDriver(QObject *parent=0);
explicit QMYSQLDriver(MYSQL *con, QObject * parent=0);
~QMYSQLDriver();
- bool hasFeature(DriverFeature f) const Q_DECL_OVERRIDE;
+ bool hasFeature(DriverFeature f) const override;
bool open(const QString & db,
const QString & user,
const QString & password,
const QString & host,
int port,
- const QString& connOpts) Q_DECL_OVERRIDE;
- void close() Q_DECL_OVERRIDE;
- QSqlResult *createResult() const Q_DECL_OVERRIDE;
- QStringList tables(QSql::TableType) const Q_DECL_OVERRIDE;
- QSqlIndex primaryIndex(const QString& tablename) const Q_DECL_OVERRIDE;
- QSqlRecord record(const QString& tablename) const Q_DECL_OVERRIDE;
+ const QString& connOpts) override;
+ void close() override;
+ QSqlResult *createResult() const override;
+ QStringList tables(QSql::TableType) const override;
+ QSqlIndex primaryIndex(const QString& tablename) const override;
+ QSqlRecord record(const QString& tablename) const override;
QString formatValue(const QSqlField &field,
- bool trimStrings) const Q_DECL_OVERRIDE;
- QVariant handle() const Q_DECL_OVERRIDE;
- QString escapeIdentifier(const QString &identifier, IdentifierType type) const Q_DECL_OVERRIDE;
+ bool trimStrings) const override;
+ QVariant handle() const override;
+ QString escapeIdentifier(const QString &identifier, IdentifierType type) const override;
- bool isIdentifierEscaped(const QString &identifier, IdentifierType type) const Q_DECL_OVERRIDE;
+ bool isIdentifierEscaped(const QString &identifier, IdentifierType type) const override;
protected:
- bool beginTransaction() Q_DECL_OVERRIDE;
- bool commitTransaction() Q_DECL_OVERRIDE;
- bool rollbackTransaction() Q_DECL_OVERRIDE;
+ bool beginTransaction() override;
+ bool commitTransaction() override;
+ bool rollbackTransaction() override;
private:
void init();
};
diff --git a/src/plugins/sqldrivers/oci/qsql_oci.cpp b/src/plugins/sqldrivers/oci/qsql_oci.cpp
index 9ce2fc1b55..272e1bc083 100644
--- a/src/plugins/sqldrivers/oci/qsql_oci.cpp
+++ b/src/plugins/sqldrivers/oci/qsql_oci.cpp
@@ -55,6 +55,7 @@
#include <qvarlengtharray.h>
#include <qvector.h>
#include <qdebug.h>
+#include <qtimezone.h>
// This is needed for oracle oci when compiling with mingw-w64 headers
#if defined(__MINGW64_VERSION_MAJOR) && defined(_WIN64)
@@ -112,9 +113,6 @@ static const ub2 qOraCharset = OCI_UCS2ID;
typedef QVarLengthArray<sb2, 32> IndicatorArray;
typedef QVarLengthArray<ub2, 32> SizeArray;
-static QByteArray qMakeOraDate(const QDateTime& dt);
-static QDateTime qMakeDate(const char* oraDate);
-
static QByteArray qMakeOCINumber(const qlonglong &ll, OCIError *err);
static QByteArray qMakeOCINumber(const qulonglong& ull, OCIError* err);
@@ -156,6 +154,60 @@ QOCIRowId::~QOCIRowId()
OCIDescriptorFree(id, OCI_DTYPE_ROWID);
}
+class QOCIDateTime
+{
+public:
+ QOCIDateTime(OCIEnv *env, OCIError *err, const QDateTime &dt = QDateTime());
+ ~QOCIDateTime();
+ OCIDateTime *dateTime;
+ static QDateTime fromOCIDateTime(OCIEnv *env, OCIError *err, OCIDateTime *dt);
+};
+
+QOCIDateTime::QOCIDateTime(OCIEnv *env, OCIError *err, const QDateTime &dt)
+ : dateTime(nullptr)
+{
+ OCIDescriptorAlloc(env, reinterpret_cast<void**>(&dateTime), OCI_DTYPE_TIMESTAMP_TZ, 0, 0);
+ if (dt.isValid()) {
+ const QDate date = dt.date();
+ const QTime time = dt.time();
+ // Zone in +hh:mm format (stripping UTC prefix from OffsetName)
+ QString timeZone = dt.timeZone().displayName(dt, QTimeZone::OffsetName).mid(3);
+ const OraText *tz = reinterpret_cast<const OraText *>(timeZone.utf16());
+ OCIDateTimeConstruct(env, err, dateTime, date.year(), date.month(), date.day(), time.hour(),
+ time.minute(), time.second(), time.msec() * 1000000,
+ const_cast<OraText *>(tz), timeZone.length() * sizeof(QChar));
+ }
+}
+
+QOCIDateTime::~QOCIDateTime()
+{
+ if (dateTime != nullptr)
+ OCIDescriptorFree(dateTime, OCI_DTYPE_TIMESTAMP_TZ);
+}
+
+QDateTime QOCIDateTime::fromOCIDateTime(OCIEnv *env, OCIError *err, OCIDateTime *dateTime)
+{
+ sb2 year;
+ ub1 month, day, hour, minute, second;
+ ub4 nsec;
+ sb1 tzHour, tzMinute;
+
+ OCIDateTimeGetDate(env, err, dateTime, &year, &month, &day);
+ OCIDateTimeGetTime(env, err, dateTime, &hour, &minute, &second, &nsec);
+ OCIDateTimeGetTimeZoneOffset(env, err, dateTime, &tzHour, &tzMinute);
+ int secondsOffset = (qAbs(tzHour) * 60 + tzMinute) * 60;
+ if (tzHour < 0)
+ secondsOffset = -secondsOffset;
+ // OCIDateTimeGetTime gives "fractions of second" as nanoseconds
+ return QDateTime(QDate(year, month, day), QTime(hour, minute, second, nsec / 1000000),
+ Qt::OffsetFromUTC, secondsOffset);
+}
+
+struct TempStorage {
+ QList<QByteArray> rawData;
+ QList<QOCIDateTime *> dateTimes;
+};
+
typedef QSharedDataPointer<QOCIRowId> QOCIRowIdPointer;
QT_BEGIN_INCLUDE_NAMESPACE
Q_DECLARE_METATYPE(QOCIRowIdPointer)
@@ -193,19 +245,19 @@ class QOCIResult: public QSqlCachedResult
public:
QOCIResult(const QOCIDriver *db);
~QOCIResult();
- bool prepare(const QString &query) Q_DECL_OVERRIDE;
- bool exec() Q_DECL_OVERRIDE;
- QVariant handle() const Q_DECL_OVERRIDE;
+ bool prepare(const QString &query) override;
+ bool exec() override;
+ QVariant handle() const override;
protected:
- bool gotoNext(ValueCache &values, int index) Q_DECL_OVERRIDE;
- bool reset(const QString &query) Q_DECL_OVERRIDE;
- int size() Q_DECL_OVERRIDE;
- int numRowsAffected() Q_DECL_OVERRIDE;
- QSqlRecord record() const Q_DECL_OVERRIDE;
- QVariant lastInsertId() const Q_DECL_OVERRIDE;
- bool execBatch(bool arrayBind = false) Q_DECL_OVERRIDE;
- void virtual_hook(int id, void *data) Q_DECL_OVERRIDE;
+ bool gotoNext(ValueCache &values, int index) override;
+ bool reset(const QString &query) override;
+ int size() override;
+ int numRowsAffected() override;
+ QSqlRecord record() const override;
+ QVariant lastInsertId() const override;
+ bool execBatch(bool arrayBind = false) override;
+ void virtual_hook(int id, void *data) override;
bool fetchNext() override;
};
@@ -228,11 +280,11 @@ public:
void setStatementAttributes();
int bindValue(OCIStmt *sql, OCIBind **hbnd, OCIError *err, int pos,
- const QVariant &val, dvoid *indPtr, ub2 *tmpSize, QList<QByteArray> &tmpStorage);
+ const QVariant &val, dvoid *indPtr, ub2 *tmpSize, TempStorage &tmpStorage);
int bindValues(QVector<QVariant> &values, IndicatorArray &indicators, SizeArray &tmpSizes,
- QList<QByteArray> &tmpStorage);
+ TempStorage &tmpStorage);
void outValues(QVector<QVariant> &values, IndicatorArray &indicators,
- QList<QByteArray> &tmpStorage);
+ TempStorage &tmpStorage);
inline bool isOutValue(int i) const
{ Q_Q(const QOCIResult); return q->bindValueType(i) & QSql::Out; }
inline bool isBinaryValue(int i) const
@@ -305,7 +357,7 @@ void QOCIResultPrivate::setStatementAttributes()
}
int QOCIResultPrivate::bindValue(OCIStmt *sql, OCIBind **hbnd, OCIError *err, int pos,
- const QVariant &val, dvoid *indPtr, ub2 *tmpSize, QList<QByteArray> &tmpStorage)
+ const QVariant &val, dvoid *indPtr, ub2 *tmpSize, TempStorage &tmpStorage)
{
int r = OCI_SUCCESS;
void *data = const_cast<void *>(val.constData());
@@ -323,14 +375,15 @@ int QOCIResultPrivate::bindValue(OCIStmt *sql, OCIBind **hbnd, OCIError *err, in
case QVariant::Time:
case QVariant::Date:
case QVariant::DateTime: {
- QByteArray ba = qMakeOraDate(val.toDateTime());
+ QOCIDateTime *ptr = new QOCIDateTime(env, err, val.toDateTime());
r = OCIBindByPos(sql, hbnd, err,
pos + 1,
- ba.data(),
- ba.size(),
- SQLT_DAT, indPtr, 0, 0, 0, 0, OCI_DEFAULT);
- tmpStorage.append(ba);
- break; }
+ &ptr->dateTime,
+ sizeof(OCIDateTime *),
+ SQLT_TIMESTAMP_TZ, indPtr, 0, 0, 0, 0, OCI_DEFAULT);
+ tmpStorage.dateTimes.append(ptr);
+ break;
+ }
case QVariant::Int:
r = OCIBindByPos(sql, hbnd, err,
pos + 1,
@@ -357,7 +410,7 @@ int QOCIResultPrivate::bindValue(OCIStmt *sql, OCIBind **hbnd, OCIError *err, in
ba.data(),
ba.size(),
SQLT_VNU, indPtr, 0, 0, 0, 0, OCI_DEFAULT);
- tmpStorage.append(ba);
+ tmpStorage.rawData.append(ba);
break;
}
case QVariant::ULongLong:
@@ -368,7 +421,7 @@ int QOCIResultPrivate::bindValue(OCIStmt *sql, OCIBind **hbnd, OCIError *err, in
ba.data(),
ba.size(),
SQLT_VNU, indPtr, 0, 0, 0, 0, OCI_DEFAULT);
- tmpStorage.append(ba);
+ tmpStorage.rawData.append(ba);
break;
}
case QVariant::Double:
@@ -438,7 +491,7 @@ int QOCIResultPrivate::bindValue(OCIStmt *sql, OCIBind **hbnd, OCIError *err, in
}
if (r == OCI_SUCCESS)
setCharset(*hbnd, OCI_HTYPE_BIND);
- tmpStorage.append(ba);
+ tmpStorage.rawData.append(ba);
break;
} // default case
} // switch
@@ -448,7 +501,7 @@ int QOCIResultPrivate::bindValue(OCIStmt *sql, OCIBind **hbnd, OCIError *err, in
}
int QOCIResultPrivate::bindValues(QVector<QVariant> &values, IndicatorArray &indicators,
- SizeArray &tmpSizes, QList<QByteArray> &tmpStorage)
+ SizeArray &tmpSizes, TempStorage &tmpStorage)
{
int r = OCI_SUCCESS;
for (int i = 0; i < values.count(); ++i) {
@@ -466,27 +519,30 @@ int QOCIResultPrivate::bindValues(QVector<QVariant> &values, IndicatorArray &ind
}
// will assign out value and remove its temp storage.
-static void qOraOutValue(QVariant &value, QList<QByteArray> &storage, OCIError* err)
+static void qOraOutValue(QVariant &value, TempStorage &tmpStorage, OCIEnv *env, OCIError* err)
{
switch (value.type()) {
case QVariant::Time:
- value = qMakeDate(storage.takeFirst()).time();
+ value = QOCIDateTime::fromOCIDateTime(env, err,
+ tmpStorage.dateTimes.takeFirst()->dateTime).time();
break;
case QVariant::Date:
- value = qMakeDate(storage.takeFirst()).date();
+ value = QOCIDateTime::fromOCIDateTime(env, err,
+ tmpStorage.dateTimes.takeFirst()->dateTime).date();
break;
case QVariant::DateTime:
- value = qMakeDate(storage.takeFirst());
+ value = QOCIDateTime::fromOCIDateTime(env, err,
+ tmpStorage.dateTimes.takeFirst()->dateTime);
break;
case QVariant::LongLong:
- value = qMakeLongLong(storage.takeFirst(), err);
+ value = qMakeLongLong(tmpStorage.rawData.takeFirst(), err);
break;
case QVariant::ULongLong:
- value = qMakeULongLong(storage.takeFirst(), err);
+ value = qMakeULongLong(tmpStorage.rawData.takeFirst(), err);
break;
case QVariant::String:
value = QString(
- reinterpret_cast<const QChar *>(storage.takeFirst().constData()));
+ reinterpret_cast<const QChar *>(tmpStorage.rawData.takeFirst().constData()));
break;
default:
break; //nothing
@@ -494,14 +550,14 @@ static void qOraOutValue(QVariant &value, QList<QByteArray> &storage, OCIError*
}
void QOCIResultPrivate::outValues(QVector<QVariant> &values, IndicatorArray &indicators,
- QList<QByteArray> &tmpStorage)
+ TempStorage &tmpStorage)
{
for (int i = 0; i < values.count(); ++i) {
if (!isOutValue(i))
continue;
- qOraOutValue(values[i], tmpStorage, err);
+ qOraOutValue(values[i], tmpStorage, env, err);
QVariant::Type typ = values.at(i).type();
if (indicators[i] == -1) // NULL
@@ -588,7 +644,8 @@ QSqlError qMakeError(const QString& errString, QSqlError::ErrorType type, OCIErr
{
int errorCode = 0;
const QString oraErrorString = qOraWarn(err, &errorCode);
- return QSqlError(errString, oraErrorString, type, errorCode);
+ return QSqlError(errString, oraErrorString, type,
+ errorCode != -1 ? QString::number(errorCode) : QString());
}
QVariant::Type qDecodeOCIType(const QString& ocitype, QSql::NumericalPrecisionPolicy precisionPolicy)
@@ -693,11 +750,9 @@ QVariant::Type qDecodeOCIType(int ocitype, QSql::NumericalPrecisionPolicy precis
break;
case SQLT_DAT:
case SQLT_ODT:
-#ifdef SQLT_TIMESTAMP
case SQLT_TIMESTAMP:
case SQLT_TIMESTAMP_TZ:
case SQLT_TIMESTAMP_LTZ:
-#endif
type = QVariant::DateTime;
break;
default:
@@ -724,27 +779,6 @@ static QSqlField qFromOraInf(const OraFieldInfo &ofi)
}
/*!
- \internal
-
- Convert QDateTime to the internal Oracle DATE format NB!
- It does not handle BCE dates.
-*/
-QByteArray qMakeOraDate(const QDateTime& dt)
-{
- QByteArray ba;
- ba.resize(7);
- int year = dt.date().year();
- ba[0]= (year / 100) + 100; // century
- ba[1]= (year % 100) + 100; // year
- ba[2]= dt.date().month();
- ba[3]= dt.date().day();
- ba[4]= dt.time().hour() + 1;
- ba[5]= dt.time().minute() + 1;
- ba[6]= dt.time().second() + 1;
- return ba;
-}
-
-/*!
\internal
Convert qlonglong to the internal Oracle OCINumber format.
@@ -794,22 +828,6 @@ qulonglong qMakeULongLong(const char* ociNumber, OCIError* err)
return qull;
}
-QDateTime qMakeDate(const char* oraDate)
-{
- int century = uchar(oraDate[0]);
- if(century >= 100){
- int year = uchar(oraDate[1]);
- year = ((century-100)*100) + (year-100);
- int month = oraDate[2];
- int day = oraDate[3];
- int hour = oraDate[4] - 1;
- int min = oraDate[5] - 1;
- int sec = oraDate[6] - 1;
- return QDateTime(QDate(year,month,day), QTime(hour,min,sec));
- }
- return QDateTime();
-}
-
class QOCICols
{
public:
@@ -832,7 +850,7 @@ private:
class OraFieldInf
{
public:
- OraFieldInf(): data(0), len(0), ind(0), typ(QVariant::Invalid), oraType(0), def(0), lob(0)
+ OraFieldInf() : data(0), len(0), ind(0), typ(QVariant::Invalid), oraType(0), def(0), lob(0), dataPtr(nullptr)
{}
~OraFieldInf();
char *data;
@@ -842,6 +860,7 @@ private:
ub4 oraType;
OCIDefine *def;
OCILobLocator *lob;
+ void *dataPtr;
};
QVector<OraFieldInf> fieldInf;
@@ -856,6 +875,20 @@ QOCICols::OraFieldInf::~OraFieldInf()
if (r != 0)
qWarning("QOCICols: Cannot free LOB descriptor");
}
+ if (dataPtr) {
+ switch (typ) {
+ case QVariant::Date:
+ case QVariant::Time:
+ case QVariant::DateTime: {
+ int r = OCIDescriptorFree(dataPtr, OCI_DTYPE_TIMESTAMP_TZ);
+ if (r != OCI_SUCCESS)
+ qWarning("QOCICols: Cannot free OCIDateTime descriptor");
+ break;
+ }
+ default:
+ break;
+ }
+ }
}
QOCICols::QOCICols(int size, QOCIResultPrivate* dp)
@@ -902,13 +935,18 @@ QOCICols::QOCICols(int size, QOCIResultPrivate* dp)
switch (ofi.type) {
case QVariant::DateTime:
+ r = OCIDescriptorAlloc(d->env, (void **)&fieldInf[idx].dataPtr, OCI_DTYPE_TIMESTAMP_TZ, 0, 0);
+ if (r != OCI_SUCCESS) {
+ qWarning("QOCICols: Unable to allocate the OCIDateTime descriptor");
+ break;
+ }
r = OCIDefineByPos(d->sql,
&dfn,
d->err,
count,
- create(idx, dataSize+1),
- dataSize+1,
- SQLT_DAT,
+ &fieldInf[idx].dataPtr,
+ sizeof(OCIDateTime *),
+ SQLT_TIMESTAMP_TZ,
&(fieldInf[idx].ind),
0, 0, OCI_DEFAULT);
break;
@@ -1323,11 +1361,10 @@ bool QOCICols::execBatch(QOCIResultPrivate *d, QVector<QVariant> &boundValues, b
fieldTypes.append(tp == QVariant::List ? boundValues.at(i).toList().value(0).type()
: tp);
}
-
- QList<QByteArray> tmpStorage;
SizeArray tmpSizes(columnCount);
QVector<QOCIBatchColumn> columns(columnCount);
QOCIBatchCleanupHandler cleaner(columns);
+ TempStorage tmpStorage;
// figuring out buffer sizes
for (i = 0; i < columnCount; ++i) {
@@ -1364,8 +1401,8 @@ bool QOCICols::execBatch(QOCIResultPrivate *d, QVector<QVariant> &boundValues, b
case QVariant::Time:
case QVariant::Date:
case QVariant::DateTime:
- col.bindAs = SQLT_DAT;
- col.maxLen = 7;
+ col.bindAs = SQLT_TIMESTAMP_TZ;
+ col.maxLen = sizeof(OCIDateTime *);
break;
case QVariant::Int:
@@ -1433,7 +1470,7 @@ bool QOCICols::execBatch(QOCIResultPrivate *d, QVector<QVariant> &boundValues, b
for (uint row = 0; row < col.recordCount; ++row) {
const QVariant &val = boundValues.at(i).toList().at(row);
- if (val.isNull()){
+ if (val.isNull() && !d->isOutValue(i)) {
columns[i].indicators[row] = -1;
columns[i].lengths[row] = 0;
} else {
@@ -1444,9 +1481,8 @@ bool QOCICols::execBatch(QOCIResultPrivate *d, QVector<QVariant> &boundValues, b
case QVariant::Date:
case QVariant::DateTime:{
columns[i].lengths[row] = columns[i].maxLen;
- const QByteArray ba = qMakeOraDate(val.toDateTime());
- Q_ASSERT(ba.size() == int(columns[i].maxLen));
- memcpy(dataPtr, ba.constData(), columns[i].maxLen);
+ QOCIDateTime *date = new QOCIDateTime(d->env, d->err, val.toDateTime());
+ *reinterpret_cast<OCIDateTime**>(dataPtr) = date->dateTime;
break;
}
case QVariant::Int:
@@ -1582,7 +1618,7 @@ bool QOCICols::execBatch(QOCIResultPrivate *d, QVector<QVariant> &boundValues, b
QVariant::Type tp = boundValues.at(i).type();
if (tp != QVariant::List) {
- qOraOutValue(boundValues[i], tmpStorage, d->err);
+ qOraOutValue(boundValues[i], tmpStorage, d->env, d->err);
if (*columns[i].indicators == -1)
boundValues[i] = QVariant(tp);
continue;
@@ -1594,16 +1630,16 @@ bool QOCICols::execBatch(QOCIResultPrivate *d, QVector<QVariant> &boundValues, b
for (uint r = 0; r < columns[i].recordCount; ++r){
if (columns[i].indicators[r] == -1) {
- (*list)[r] = QVariant();
+ (*list)[r] = QVariant(fieldTypes[i]);
continue;
}
switch(columns[i].bindAs) {
- case SQLT_DAT:
- (*list)[r] = qMakeDate(data + r * columns[i].maxLen);
+ case SQLT_TIMESTAMP_TZ:
+ (*list)[r] = QOCIDateTime::fromOCIDateTime(d->env, d->err,
+ *reinterpret_cast<OCIDateTime **>(data + r * columns[i].maxLen));
break;
-
case SQLT_INT:
(*list)[r] = *reinterpret_cast<int*>(data + r * columns[i].maxLen);
break;
@@ -1647,6 +1683,7 @@ bool QOCICols::execBatch(QOCIResultPrivate *d, QVector<QVariant> &boundValues, b
d->q_func()->setAt(QSql::BeforeFirstRow);
d->q_func()->setActive(true);
+ qDeleteAll(tmpStorage.dateTimes);
return true;
}
@@ -1755,7 +1792,8 @@ void QOCICols::getValues(QVector<QVariant> &v, int index)
switch (fld.typ) {
case QVariant::DateTime:
- v[index + i] = QVariant(qMakeDate(fld.data));
+ v[index + i] = QVariant(QOCIDateTime::fromOCIDateTime(d->env, d->err,
+ reinterpret_cast<OCIDateTime *>(fld.dataPtr)));
break;
case QVariant::Double:
case QVariant::Int:
@@ -1985,7 +2023,7 @@ bool QOCIResult::exec()
ub2 stmtType=0;
ub4 iters;
ub4 mode;
- QList<QByteArray> tmpStorage;
+ TempStorage tmpStorage;
IndicatorArray indicators(boundValueCount());
SizeArray tmpSizes(boundValueCount());
@@ -2056,7 +2094,7 @@ bool QOCIResult::exec()
if (hasOutValues())
d->outValues(boundValues(), indicators, tmpStorage);
-
+ qDeleteAll(tmpStorage.dateTimes);
return true;
}
diff --git a/src/plugins/sqldrivers/oci/qsql_oci_p.h b/src/plugins/sqldrivers/oci/qsql_oci_p.h
index 69911f4bee..295c131f1a 100644
--- a/src/plugins/sqldrivers/oci/qsql_oci_p.h
+++ b/src/plugins/sqldrivers/oci/qsql_oci_p.h
@@ -84,21 +84,21 @@ public:
const QString &password,
const QString &host,
int port,
- const QString &connOpts) Q_DECL_OVERRIDE;
- void close() Q_DECL_OVERRIDE;
- QSqlResult *createResult() const Q_DECL_OVERRIDE;
- QStringList tables(QSql::TableType) const Q_DECL_OVERRIDE;
- QSqlRecord record(const QString &tablename) const Q_DECL_OVERRIDE;
- QSqlIndex primaryIndex(const QString& tablename) const Q_DECL_OVERRIDE;
+ const QString &connOpts) override;
+ void close() override;
+ QSqlResult *createResult() const override;
+ QStringList tables(QSql::TableType) const override;
+ QSqlRecord record(const QString &tablename) const override;
+ QSqlIndex primaryIndex(const QString& tablename) const override;
QString formatValue(const QSqlField &field,
- bool trimStrings) const Q_DECL_OVERRIDE;
- QVariant handle() const Q_DECL_OVERRIDE;
- QString escapeIdentifier(const QString &identifier, IdentifierType) const Q_DECL_OVERRIDE;
+ bool trimStrings) const override;
+ QVariant handle() const override;
+ QString escapeIdentifier(const QString &identifier, IdentifierType) const override;
protected:
- bool beginTransaction() Q_DECL_OVERRIDE;
- bool commitTransaction() Q_DECL_OVERRIDE;
- bool rollbackTransaction() Q_DECL_OVERRIDE;
+ bool beginTransaction() override;
+ bool commitTransaction() override;
+ bool rollbackTransaction() override;
};
QT_END_NAMESPACE
diff --git a/src/plugins/sqldrivers/odbc/qsql_odbc.cpp b/src/plugins/sqldrivers/odbc/qsql_odbc.cpp
index 59ef42d609..547eb2043d 100644
--- a/src/plugins/sqldrivers/odbc/qsql_odbc.cpp
+++ b/src/plugins/sqldrivers/odbc/qsql_odbc.cpp
@@ -64,6 +64,7 @@ QT_BEGIN_NAMESPACE
#define ODBC_CHECK_DRIVER
static const int COLNAMESIZE = 256;
+static const SQLSMALLINT TABLENAMESIZE = 128;
//Map Qt parameter types to ODBC types
static const SQLSMALLINT qParamType[4] = { SQL_PARAM_INPUT, SQL_PARAM_INPUT, SQL_PARAM_OUTPUT, SQL_PARAM_INPUT_OUTPUT };
@@ -162,27 +163,27 @@ public:
QODBCResult(const QODBCDriver *db);
virtual ~QODBCResult();
- bool prepare(const QString &query) Q_DECL_OVERRIDE;
- bool exec() Q_DECL_OVERRIDE;
+ bool prepare(const QString &query) override;
+ bool exec() override;
- QVariant lastInsertId() const Q_DECL_OVERRIDE;
- QVariant handle() const Q_DECL_OVERRIDE;
+ QVariant lastInsertId() const override;
+ QVariant handle() const override;
protected:
- bool fetchNext() Q_DECL_OVERRIDE;
- bool fetchFirst() Q_DECL_OVERRIDE;
- bool fetchLast() Q_DECL_OVERRIDE;
- bool fetchPrevious() Q_DECL_OVERRIDE;
- bool fetch(int i) Q_DECL_OVERRIDE;
- bool reset(const QString &query) Q_DECL_OVERRIDE;
- QVariant data(int field) Q_DECL_OVERRIDE;
- bool isNull(int field) Q_DECL_OVERRIDE;
- int size() Q_DECL_OVERRIDE;
- int numRowsAffected() Q_DECL_OVERRIDE;
- QSqlRecord record() const Q_DECL_OVERRIDE;
- void virtual_hook(int id, void *data) Q_DECL_OVERRIDE;
- void detachFromResultSet() Q_DECL_OVERRIDE;
- bool nextResult() Q_DECL_OVERRIDE;
+ bool fetchNext() override;
+ bool fetchFirst() override;
+ bool fetchLast() override;
+ bool fetchPrevious() override;
+ bool fetch(int i) override;
+ bool reset(const QString &query) override;
+ QVariant data(int field) override;
+ bool isNull(int field) override;
+ int size() override;
+ int numRowsAffected() override;
+ QSqlRecord record() const override;
+ void virtual_hook(int id, void *data) override;
+ void detachFromResultSet() override;
+ bool nextResult() override;
};
class QODBCResultPrivate: public QSqlResultPrivate
@@ -334,7 +335,8 @@ static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type, const
{
int nativeCode = -1;
QString message = qODBCWarn(p, &nativeCode);
- return QSqlError(QLatin1String("QODBC3: ") + err, message, type, nativeCode);
+ return QSqlError(QLatin1String("QODBC3: ") + err, message, type,
+ nativeCode != -1 ? QString::number(nativeCode) : QString());
}
static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type,
@@ -342,7 +344,8 @@ static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type,
{
int nativeCode = -1;
QString message = qODBCWarn(p, &nativeCode);
- return QSqlError(QLatin1String("QODBC3: ") + err, qODBCWarn(p), type, nativeCode);
+ return QSqlError(QLatin1String("QODBC3: ") + err, qODBCWarn(p), type,
+ nativeCode != -1 ? QString::number(nativeCode) : QString());
}
static QVariant::Type qDecodeODBCType(SQLSMALLINT sqltype, bool isSigned = true)
@@ -730,6 +733,12 @@ static QSqlField qMakeFieldInfo(const SQLHANDLE hStmt, int i, QString *errorMess
f.setRequired(false);
// else we don't know
f.setAutoValue(isAutoValue(hStmt, i));
+ QVarLengthArray<SQLTCHAR> tableName(TABLENAMESIZE);
+ SQLSMALLINT tableNameLen;
+ r = SQLColAttribute(hStmt, i + 1, SQL_DESC_BASE_TABLE_NAME, tableName.data(),
+ TABLENAMESIZE, &tableNameLen, 0);
+ if (r == SQL_SUCCESS)
+ f.setTableName(fromSQLTCHAR(tableName, tableNameLen));
return f;
}
diff --git a/src/plugins/sqldrivers/odbc/qsql_odbc_p.h b/src/plugins/sqldrivers/odbc/qsql_odbc_p.h
index f4ce8bc243..ea0aa6fc8b 100644
--- a/src/plugins/sqldrivers/odbc/qsql_odbc_p.h
+++ b/src/plugins/sqldrivers/odbc/qsql_odbc_p.h
@@ -92,30 +92,30 @@ public:
explicit QODBCDriver(QObject *parent=0);
QODBCDriver(SQLHANDLE env, SQLHANDLE con, QObject * parent=0);
virtual ~QODBCDriver();
- bool hasFeature(DriverFeature f) const Q_DECL_OVERRIDE;
- void close() Q_DECL_OVERRIDE;
- QSqlResult *createResult() const Q_DECL_OVERRIDE;
- QStringList tables(QSql::TableType) const Q_DECL_OVERRIDE;
- QSqlRecord record(const QString &tablename) const Q_DECL_OVERRIDE;
- QSqlIndex primaryIndex(const QString &tablename) const Q_DECL_OVERRIDE;
- QVariant handle() const Q_DECL_OVERRIDE;
+ bool hasFeature(DriverFeature f) const override;
+ void close() override;
+ QSqlResult *createResult() const override;
+ QStringList tables(QSql::TableType) const override;
+ QSqlRecord record(const QString &tablename) const override;
+ QSqlIndex primaryIndex(const QString &tablename) const override;
+ QVariant handle() const override;
QString formatValue(const QSqlField &field,
- bool trimStrings) const Q_DECL_OVERRIDE;
+ bool trimStrings) const override;
bool open(const QString &db,
const QString &user,
const QString &password,
const QString &host,
int port,
- const QString &connOpts) Q_DECL_OVERRIDE;
+ const QString &connOpts) override;
- QString escapeIdentifier(const QString &identifier, IdentifierType type) const Q_DECL_OVERRIDE;
+ QString escapeIdentifier(const QString &identifier, IdentifierType type) const override;
- bool isIdentifierEscaped(const QString &identifier, IdentifierType type) const Q_DECL_OVERRIDE;
+ bool isIdentifierEscaped(const QString &identifier, IdentifierType type) const override;
protected:
- bool beginTransaction() Q_DECL_OVERRIDE;
- bool commitTransaction() Q_DECL_OVERRIDE;
- bool rollbackTransaction() Q_DECL_OVERRIDE;
+ bool beginTransaction() override;
+ bool commitTransaction() override;
+ bool rollbackTransaction() override;
private:
bool endTrans();
diff --git a/src/plugins/sqldrivers/psql/main.cpp b/src/plugins/sqldrivers/psql/main.cpp
index 7657fcbfdf..c5d546f6ff 100644
--- a/src/plugins/sqldrivers/psql/main.cpp
+++ b/src/plugins/sqldrivers/psql/main.cpp
@@ -51,7 +51,7 @@ class QPSQLDriverPlugin : public QSqlDriverPlugin
public:
QPSQLDriverPlugin();
- QSqlDriver* create(const QString &) Q_DECL_OVERRIDE;
+ QSqlDriver* create(const QString &) override;
};
QPSQLDriverPlugin::QPSQLDriverPlugin()
diff --git a/src/plugins/sqldrivers/psql/qsql_psql.cpp b/src/plugins/sqldrivers/psql/qsql_psql.cpp
index 6ba32a0c71..6f105f79ca 100644
--- a/src/plugins/sqldrivers/psql/qsql_psql.cpp
+++ b/src/plugins/sqldrivers/psql/qsql_psql.cpp
@@ -125,6 +125,14 @@ inline void qPQfreemem(void *buffer)
PQfreemem(buffer);
}
+/* Missing declaration of PGRES_SINGLE_TUPLE for PSQL below 9.2 */
+#if !defined PG_VERSION_NUM || PG_VERSION_NUM-0 < 90200
+static const int PGRES_SINGLE_TUPLE = 9;
+#endif
+
+typedef int StatementId;
+static const StatementId InvalidStatementId = 0;
+
class QPSQLResultPrivate;
class QPSQLResult: public QSqlResult
@@ -135,23 +143,25 @@ public:
QPSQLResult(const QPSQLDriver *db);
~QPSQLResult();
- QVariant handle() const Q_DECL_OVERRIDE;
- void virtual_hook(int id, void *data) Q_DECL_OVERRIDE;
+ QVariant handle() const override;
+ void virtual_hook(int id, void *data) override;
protected:
void cleanup();
- bool fetch(int i) Q_DECL_OVERRIDE;
- bool fetchFirst() Q_DECL_OVERRIDE;
- bool fetchLast() Q_DECL_OVERRIDE;
- QVariant data(int i) Q_DECL_OVERRIDE;
- bool isNull(int field) Q_DECL_OVERRIDE;
- bool reset (const QString &query) Q_DECL_OVERRIDE;
- int size() Q_DECL_OVERRIDE;
- int numRowsAffected() Q_DECL_OVERRIDE;
- QSqlRecord record() const Q_DECL_OVERRIDE;
- QVariant lastInsertId() const Q_DECL_OVERRIDE;
- bool prepare(const QString &query) Q_DECL_OVERRIDE;
- bool exec() Q_DECL_OVERRIDE;
+ bool fetch(int i) override;
+ bool fetchFirst() override;
+ bool fetchLast() override;
+ bool fetchNext() override;
+ bool nextResult() override;
+ QVariant data(int i) override;
+ bool isNull(int field) override;
+ bool reset (const QString &query) override;
+ int size() override;
+ int numRowsAffected() override;
+ QSqlRecord record() const override;
+ QVariant lastInsertId() const override;
+ bool prepare(const QString &query) override;
+ bool exec() override;
};
class QPSQLDriverPrivate : public QSqlDriverPrivate
@@ -164,7 +174,9 @@ public:
pro(QPSQLDriver::Version6),
sn(0),
pendingNotifyCheck(false),
- hasBackslashEscape(false)
+ hasBackslashEscape(false),
+ stmtCount(0),
+ currentStmtId(InvalidStatementId)
{ dbmsType = QSqlDriver::PostgreSQL; }
PGconn *connection;
@@ -174,10 +186,19 @@ public:
QStringList seid;
mutable bool pendingNotifyCheck;
bool hasBackslashEscape;
+ int stmtCount;
+ StatementId currentStmtId;
void appendTables(QStringList &tl, QSqlQuery &t, QChar type);
- PGresult * exec(const char * stmt) const;
- PGresult * exec(const QString & stmt) const;
+ PGresult *exec(const char *stmt);
+ PGresult *exec(const QString &stmt);
+ StatementId sendQuery(const QString &stmt);
+ bool setSingleRowMode() const;
+ PGresult *getResult(StatementId stmtId) const;
+ void finishQuery(StatementId stmtId);
+ void discardResults() const;
+ StatementId generateStatementId();
+ void checkPendingNotifications() const;
QPSQLDriver::Protocol getPSQLVersion();
bool setEncodingUtf8();
void setDatestyle();
@@ -187,18 +208,11 @@ public:
void QPSQLDriverPrivate::appendTables(QStringList &tl, QSqlQuery &t, QChar type)
{
- QString query;
- if (pro >= QPSQLDriver::Version7_3) {
- query = QString::fromLatin1("select pg_class.relname, pg_namespace.nspname from pg_class "
- "left join pg_namespace on (pg_class.relnamespace = pg_namespace.oid) "
- "where (pg_class.relkind = '%1') and (pg_class.relname !~ '^Inv') "
- "and (pg_class.relname !~ '^pg_') "
- "and (pg_namespace.nspname != 'information_schema') ").arg(type);
- } else {
- query = QString::fromLatin1("select relname, null from pg_class where (relkind = '%1') "
- "and (relname !~ '^Inv') "
- "and (relname !~ '^pg_') ").arg(type);
- }
+ QString query = QString::fromLatin1("select pg_class.relname, pg_namespace.nspname from pg_class "
+ "left join pg_namespace on (pg_class.relnamespace = pg_namespace.oid) "
+ "where (pg_class.relkind = '%1') and (pg_class.relname !~ '^Inv') "
+ "and (pg_class.relname !~ '^pg_') "
+ "and (pg_namespace.nspname != 'information_schema')").arg(type);
t.exec(query);
while (t.next()) {
QString schema = t.value(1).toString();
@@ -209,20 +223,89 @@ void QPSQLDriverPrivate::appendTables(QStringList &tl, QSqlQuery &t, QChar type)
}
}
-PGresult * QPSQLDriverPrivate::exec(const char * stmt) const
+PGresult *QPSQLDriverPrivate::exec(const char *stmt)
{
- Q_Q(const QPSQLDriver);
+ // PQexec() silently discards any prior query results that the application didn't eat.
PGresult *result = PQexec(connection, stmt);
- if (seid.size() && !pendingNotifyCheck) {
- pendingNotifyCheck = true;
- QMetaObject::invokeMethod(const_cast<QPSQLDriver*>(q), "_q_handleNotification", Qt::QueuedConnection, Q_ARG(int,0));
+ currentStmtId = result ? generateStatementId() : InvalidStatementId;
+ checkPendingNotifications();
+ return result;
+}
+
+PGresult *QPSQLDriverPrivate::exec(const QString &stmt)
+{
+ return exec((isUtf8 ? stmt.toUtf8() : stmt.toLocal8Bit()).constData());
+}
+
+StatementId QPSQLDriverPrivate::sendQuery(const QString &stmt)
+{
+ // Discard any prior query results that the application didn't eat.
+ // This is required for PQsendQuery()
+ discardResults();
+ const int result = PQsendQuery(connection,
+ (isUtf8 ? stmt.toUtf8() : stmt.toLocal8Bit()).constData());
+ currentStmtId = result ? generateStatementId() : InvalidStatementId;
+ return currentStmtId;
+}
+
+bool QPSQLDriverPrivate::setSingleRowMode() const
+{
+ // Activates single-row mode for last sent query, see:
+ // https://www.postgresql.org/docs/9.2/static/libpq-single-row-mode.html
+ // This method should be called immediately after the sendQuery() call.
+#if defined PG_VERSION_NUM && PG_VERSION_NUM-0 >= 90200
+ return PQsetSingleRowMode(connection) > 0;
+#else
+ return false;
+#endif
+}
+
+PGresult *QPSQLDriverPrivate::getResult(StatementId stmtId) const
+{
+ // Make sure the results of stmtId weren't discaded. This might
+ // happen for forward-only queries if somebody executed another
+ // SQL query on the same db connection.
+ if (stmtId != currentStmtId) {
+ // If you change the following warning, remember to update it
+ // on sql-driver.html page too.
+ qWarning("QPSQLDriver::getResult: Query results lost - "
+ "probably discarded on executing another SQL query.");
+ return nullptr;
}
+ PGresult *result = PQgetResult(connection);
+ checkPendingNotifications();
return result;
}
-PGresult * QPSQLDriverPrivate::exec(const QString & stmt) const
+void QPSQLDriverPrivate::finishQuery(StatementId stmtId)
+{
+ if (stmtId != InvalidStatementId && stmtId == currentStmtId) {
+ discardResults();
+ currentStmtId = InvalidStatementId;
+ }
+}
+
+void QPSQLDriverPrivate::discardResults() const
+{
+ while (PGresult *result = PQgetResult(connection))
+ PQclear(result);
+}
+
+StatementId QPSQLDriverPrivate::generateStatementId()
{
- return exec(isUtf8 ? stmt.toUtf8().constData() : stmt.toLocal8Bit().constData());
+ int stmtId = ++stmtCount;
+ if (stmtId <= 0)
+ stmtId = stmtCount = 1;
+ return stmtId;
+}
+
+void QPSQLDriverPrivate::checkPendingNotifications() const
+{
+ Q_Q(const QPSQLDriver);
+ if (seid.size() && !pendingNotifyCheck) {
+ pendingNotifyCheck = true;
+ QMetaObject::invokeMethod(const_cast<QPSQLDriver*>(q), "_q_handleNotification", Qt::QueuedConnection, Q_ARG(int,0));
+ }
}
class QPSQLResultPrivate : public QSqlResultPrivate
@@ -234,14 +317,19 @@ public:
: QSqlResultPrivate(q, drv),
result(0),
currentSize(-1),
+ canFetchMoreRows(false),
+ stmtId(InvalidStatementId),
preparedQueriesEnabled(false)
{ }
- QString fieldSerial(int i) const Q_DECL_OVERRIDE { return QLatin1Char('$') + QString::number(i + 1); }
+ QString fieldSerial(int i) const override { return QLatin1Char('$') + QString::number(i + 1); }
void deallocatePreparedStmt();
PGresult *result;
+ QList<PGresult*> nextResultSets;
int currentSize;
+ bool canFetchMoreRows;
+ StatementId stmtId;
bool preparedQueriesEnabled;
QString preparedStmtId;
@@ -264,21 +352,45 @@ static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type,
bool QPSQLResultPrivate::processResults()
{
Q_Q(QPSQLResult);
- if (!result)
+ if (!result) {
+ q->setSelect(false);
+ q->setActive(false);
+ currentSize = -1;
+ canFetchMoreRows = false;
+ if (stmtId != drv_d_func()->currentStmtId) {
+ q->setLastError(qMakeError(QCoreApplication::translate("QPSQLResult",
+ "Query results lost - probably discarded on executing "
+ "another SQL query."), QSqlError::StatementError, drv_d_func(), result));
+ }
return false;
-
+ }
int status = PQresultStatus(result);
- if (status == PGRES_TUPLES_OK) {
+ switch (status) {
+ case PGRES_TUPLES_OK:
q->setSelect(true);
q->setActive(true);
- currentSize = PQntuples(result);
+ currentSize = q->isForwardOnly() ? -1 : PQntuples(result);
+ canFetchMoreRows = false;
+ return true;
+ case PGRES_SINGLE_TUPLE:
+ q->setSelect(true);
+ q->setActive(true);
+ currentSize = -1;
+ canFetchMoreRows = true;
return true;
- } else if (status == PGRES_COMMAND_OK) {
+ case PGRES_COMMAND_OK:
q->setSelect(false);
q->setActive(true);
currentSize = -1;
+ canFetchMoreRows = false;
return true;
+ default:
+ break;
}
+ q->setSelect(false);
+ q->setActive(false);
+ currentSize = -1;
+ canFetchMoreRows = false;
q->setLastError(qMakeError(QCoreApplication::translate("QPSQLResult",
"Unable to create query"), QSqlError::StatementError, drv_d_func(), result));
return false;
@@ -368,9 +480,15 @@ void QPSQLResult::cleanup()
Q_D(QPSQLResult);
if (d->result)
PQclear(d->result);
- d->result = 0;
+ d->result = nullptr;
+ while (!d->nextResultSets.isEmpty())
+ PQclear(d->nextResultSets.takeFirst());
+ if (d->stmtId != InvalidStatementId)
+ d->drv_d_func()->finishQuery(d->stmtId);
+ d->stmtId = InvalidStatementId;
setAt(QSql::BeforeFirstRow);
d->currentSize = -1;
+ d->canFetchMoreRows = false;
setActive(false);
}
@@ -381,23 +499,150 @@ bool QPSQLResult::fetch(int i)
return false;
if (i < 0)
return false;
- if (i >= d->currentSize)
- return false;
if (at() == i)
return true;
+
+ if (isForwardOnly()) {
+ if (i < at())
+ return false;
+ bool ok = true;
+ while (ok && i > at())
+ ok = fetchNext();
+ return ok;
+ }
+
+ if (i >= d->currentSize)
+ return false;
setAt(i);
return true;
}
bool QPSQLResult::fetchFirst()
{
+ Q_D(const QPSQLResult);
+ if (!isActive())
+ return false;
+ if (at() == 0)
+ return true;
+
+ if (isForwardOnly()) {
+ if (at() == QSql::BeforeFirstRow) {
+ // First result has been already fetched by exec() or
+ // nextResult(), just check it has at least one row.
+ if (d->result && PQntuples(d->result) > 0) {
+ setAt(0);
+ return true;
+ }
+ }
+ return false;
+ }
+
return fetch(0);
}
bool QPSQLResult::fetchLast()
{
Q_D(const QPSQLResult);
- return fetch(PQntuples(d->result) - 1);
+ if (!isActive())
+ return false;
+
+ if (isForwardOnly()) {
+ // Cannot seek to last row in forwardOnly mode, so we have to use brute force
+ int i = at();
+ if (i == QSql::AfterLastRow)
+ return false;
+ if (i == QSql::BeforeFirstRow)
+ i = 0;
+ while (fetchNext())
+ ++i;
+ setAt(i);
+ return true;
+ }
+
+ return fetch(d->currentSize - 1);
+}
+
+bool QPSQLResult::fetchNext()
+{
+ Q_D(QPSQLResult);
+ if (!isActive())
+ return false;
+
+ const int currentRow = at(); // Small optimalization
+ if (currentRow == QSql::BeforeFirstRow)
+ return fetchFirst();
+ if (currentRow == QSql::AfterLastRow)
+ return false;
+
+ if (isForwardOnly()) {
+ if (!d->canFetchMoreRows)
+ return false;
+ PQclear(d->result);
+ d->result = d->drv_d_func()->getResult(d->stmtId);
+ if (!d->result) {
+ setLastError(qMakeError(QCoreApplication::translate("QPSQLResult",
+ "Unable to get result"), QSqlError::StatementError, d->drv_d_func(), d->result));
+ d->canFetchMoreRows = false;
+ return false;
+ }
+ int status = PQresultStatus(d->result);
+ switch (status) {
+ case PGRES_SINGLE_TUPLE:
+ // Fetched next row of current result set
+ Q_ASSERT(PQntuples(d->result) == 1);
+ Q_ASSERT(d->canFetchMoreRows);
+ setAt(currentRow + 1);
+ return true;
+ case PGRES_TUPLES_OK:
+ // In single-row mode PGRES_TUPLES_OK means end of current result set
+ Q_ASSERT(PQntuples(d->result) == 0);
+ d->canFetchMoreRows = false;
+ return false;
+ default:
+ setLastError(qMakeError(QCoreApplication::translate("QPSQLResult",
+ "Unable to get result"), QSqlError::StatementError, d->drv_d_func(), d->result));
+ d->canFetchMoreRows = false;
+ return false;
+ }
+ }
+
+ if (currentRow + 1 >= d->currentSize)
+ return false;
+ setAt(currentRow + 1);
+ return true;
+}
+
+bool QPSQLResult::nextResult()
+{
+ Q_D(QPSQLResult);
+ if (!isActive())
+ return false;
+
+ setAt(QSql::BeforeFirstRow);
+
+ if (isForwardOnly()) {
+ if (d->canFetchMoreRows) {
+ // Skip all rows from current result set
+ while (d->result && PQresultStatus(d->result) == PGRES_SINGLE_TUPLE) {
+ PQclear(d->result);
+ d->result = d->drv_d_func()->getResult(d->stmtId);
+ }
+ d->canFetchMoreRows = false;
+ // Check for unexpected errors
+ if (d->result && PQresultStatus(d->result) == PGRES_FATAL_ERROR)
+ return d->processResults();
+ }
+ // Fetch first result from next result set
+ if (d->result)
+ PQclear(d->result);
+ d->result = d->drv_d_func()->getResult(d->stmtId);
+ return d->processResults();
+ }
+
+ if (d->result)
+ PQclear(d->result);
+ d->result = d->nextResultSets.isEmpty() ? nullptr : d->nextResultSets.takeFirst();
+ return d->processResults();
}
QVariant QPSQLResult::data(int i)
@@ -407,11 +652,12 @@ QVariant QPSQLResult::data(int i)
qWarning("QPSQLResult::data: column %d out of range", i);
return QVariant();
}
+ const int currentRow = isForwardOnly() ? 0 : at();
int ptype = PQftype(d->result, i);
QVariant::Type type = qDecodePSQLType(ptype);
- const char *val = PQgetvalue(d->result, at(), i);
- if (PQgetisnull(d->result, at(), i))
+ if (PQgetisnull(d->result, currentRow, i))
return QVariant(type);
+ const char *val = PQgetvalue(d->result, currentRow, i);
switch (type) {
case QVariant::Bool:
return QVariant((bool)(val[0] == 't'));
@@ -495,8 +741,8 @@ QVariant QPSQLResult::data(int i)
bool QPSQLResult::isNull(int field)
{
Q_D(const QPSQLResult);
- PQgetvalue(d->result, at(), field);
- return PQgetisnull(d->result, at(), field);
+ const int currentRow = isForwardOnly() ? 0 : at();
+ return PQgetisnull(d->result, currentRow, field);
}
bool QPSQLResult::reset (const QString& query)
@@ -507,7 +753,23 @@ bool QPSQLResult::reset (const QString& query)
return false;
if (!driver()->isOpen() || driver()->isOpenError())
return false;
- d->result = d->drv_d_func()->exec(query);
+
+ d->stmtId = d->drv_d_func()->sendQuery(query);
+ if (d->stmtId == InvalidStatementId) {
+ setLastError(qMakeError(QCoreApplication::translate("QPSQLResult",
+ "Unable to send query"), QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+
+ if (isForwardOnly())
+ setForwardOnly(d->drv_d_func()->setSingleRowMode());
+
+ d->result = d->drv_d_func()->getResult(d->stmtId);
+ if (!isForwardOnly()) {
+ // Fetch all result sets right away
+ while (PGresult *nextResultSet = d->drv_d_func()->getResult(d->stmtId))
+ d->nextResultSets.append(nextResultSet);
+ }
return d->processResults();
}
@@ -553,6 +815,18 @@ QSqlRecord QPSQLResult::record() const
f.setName(QString::fromUtf8(PQfname(d->result, i)));
else
f.setName(QString::fromLocal8Bit(PQfname(d->result, i)));
+
+ // WARNING: We cannot execute any other SQL queries on
+ // the same db connection while forward-only mode is active
+ // (this would discard all results of forward-only query).
+ // So we just skip this...
+ if (!isForwardOnly()) {
+ QSqlQuery qry(driver()->createResult());
+ if (qry.exec(QStringLiteral("SELECT relname FROM pg_class WHERE pg_class.oid = %1")
+ .arg(PQftable(d->result, i))) && qry.next()) {
+ f.setTableName(qry.value(0).toString());
+ }
+ }
int ptype = PQftype(d->result, i);
f.setType(qDecodePSQLType(ptype));
int len = PQfsize(d->result, i);
@@ -670,8 +944,22 @@ bool QPSQLResult::exec()
else
stmt = QString::fromLatin1("EXECUTE %1 (%2)").arg(d->preparedStmtId, params);
- d->result = d->drv_d_func()->exec(stmt);
+ d->stmtId = d->drv_d_func()->sendQuery(stmt);
+ if (d->stmtId == InvalidStatementId) {
+ setLastError(qMakeError(QCoreApplication::translate("QPSQLResult",
+ "Unable to send query"), QSqlError::StatementError, d->drv_d_func()));
+ return false;
+ }
+
+ if (isForwardOnly())
+ setForwardOnly(d->drv_d_func()->setSingleRowMode());
+ d->result = d->drv_d_func()->getResult(d->stmtId);
+ if (!isForwardOnly()) {
+ // Fetch all result sets right away
+ while (PGresult *nextResultSet = d->drv_d_func()->getResult(d->stmtId))
+ d->nextResultSets.append(nextResultSet);
+ }
return d->processResults();
}
@@ -890,6 +1178,8 @@ bool QPSQLDriver::hasFeature(DriverFeature f) const
case LastInsertId:
case LowPrecisionNumbers:
case EventNotifications:
+ case MultipleResultSets:
+ case BLOB:
return true;
case PreparedQueries:
case PositionalPlaceholders:
@@ -898,11 +1188,8 @@ bool QPSQLDriver::hasFeature(DriverFeature f) const
case NamedPlaceholders:
case SimpleLocking:
case FinishQuery:
- case MultipleResultSets:
case CancelQuery:
return false;
- case BLOB:
- return d->pro >= QPSQLDriver::Version7_1;
case Unicode:
return d->isUtf8;
}
@@ -999,7 +1286,7 @@ QSqlResult *QPSQLDriver::createResult() const
bool QPSQLDriver::beginTransaction()
{
- Q_D(const QPSQLDriver);
+ Q_D(QPSQLDriver);
if (!isOpen()) {
qWarning("QPSQLDriver::beginTransaction: Database not open");
return false;
@@ -1096,12 +1383,10 @@ static void qSplitTableName(QString &tablename, QString &schema)
QSqlIndex QPSQLDriver::primaryIndex(const QString& tablename) const
{
- Q_D(const QPSQLDriver);
QSqlIndex idx(tablename);
if (!isOpen())
return idx;
QSqlQuery i(createResult());
- QString stmt;
QString tbl = tablename;
QString schema;
@@ -1117,46 +1402,24 @@ QSqlIndex QPSQLDriver::primaryIndex(const QString& tablename) const
else
schema = std::move(schema).toLower();
- if (d->pro == QPSQLDriver::Version6) {
- stmt = QLatin1String("select pg_att1.attname, int(pg_att1.atttypid), pg_cl.relname "
- "from pg_attribute pg_att1, pg_attribute pg_att2, pg_class pg_cl, pg_index pg_ind "
- "where pg_cl.relname = '%1_pkey' "
- "and pg_cl.oid = pg_ind.indexrelid "
- "and pg_att2.attrelid = pg_ind.indexrelid "
- "and pg_att1.attrelid = pg_ind.indrelid "
- "and pg_att1.attnum = pg_ind.indkey[pg_att2.attnum-1] "
- "order by pg_att2.attnum");
- } else if (d->pro == QPSQLDriver::Version7 || d->pro == QPSQLDriver::Version7_1) {
- stmt = QLatin1String("select pg_att1.attname, pg_att1.atttypid::int, pg_cl.relname "
- "from pg_attribute pg_att1, pg_attribute pg_att2, pg_class pg_cl, pg_index pg_ind "
- "where pg_cl.relname = '%1_pkey' "
- "and pg_cl.oid = pg_ind.indexrelid "
- "and pg_att2.attrelid = pg_ind.indexrelid "
- "and pg_att1.attrelid = pg_ind.indrelid "
- "and pg_att1.attnum = pg_ind.indkey[pg_att2.attnum-1] "
- "order by pg_att2.attnum");
- } else if (d->pro >= QPSQLDriver::Version7_3) {
- stmt = QLatin1String("SELECT pg_attribute.attname, pg_attribute.atttypid::int, "
- "pg_class.relname "
- "FROM pg_attribute, pg_class "
- "WHERE %1 pg_class.oid IN "
- "(SELECT indexrelid FROM pg_index WHERE indisprimary = true AND indrelid IN "
- " (SELECT oid FROM pg_class WHERE relname = '%2')) "
- "AND pg_attribute.attrelid = pg_class.oid "
- "AND pg_attribute.attisdropped = false "
- "ORDER BY pg_attribute.attnum");
- if (schema.isEmpty())
- stmt = stmt.arg(QLatin1String("pg_table_is_visible(pg_class.oid) AND"));
- else
- stmt = stmt.arg(QString::fromLatin1("pg_class.relnamespace = (select oid from "
- "pg_namespace where pg_namespace.nspname = '%1') AND ").arg(schema));
- } else {
- qFatal("QPSQLDriver::primaryIndex(tablename): unknown PSQL version, query statement not set");
- }
+ QString stmt = QLatin1String("SELECT pg_attribute.attname, pg_attribute.atttypid::int, "
+ "pg_class.relname "
+ "FROM pg_attribute, pg_class "
+ "WHERE %1 pg_class.oid IN "
+ "(SELECT indexrelid FROM pg_index WHERE indisprimary = true AND indrelid IN "
+ "(SELECT oid FROM pg_class WHERE relname = '%2')) "
+ "AND pg_attribute.attrelid = pg_class.oid "
+ "AND pg_attribute.attisdropped = false "
+ "ORDER BY pg_attribute.attnum");
+ if (schema.isEmpty())
+ stmt = stmt.arg(QLatin1String("pg_table_is_visible(pg_class.oid) AND"));
+ else
+ stmt = stmt.arg(QString::fromLatin1("pg_class.relnamespace = (select oid from "
+ "pg_namespace where pg_namespace.nspname = '%1') AND").arg(schema));
i.exec(stmt.arg(tbl));
while (i.isActive() && i.next()) {
- QSqlField f(i.value(0).toString(), qDecodePSQLType(i.value(1).toInt()));
+ QSqlField f(i.value(0).toString(), qDecodePSQLType(i.value(1).toInt()), tablename);
idx.append(f);
idx.setName(i.value(2).toString());
}
@@ -1165,7 +1428,6 @@ QSqlIndex QPSQLDriver::primaryIndex(const QString& tablename) const
QSqlRecord QPSQLDriver::record(const QString& tablename) const
{
- Q_D(const QPSQLDriver);
QSqlRecord info;
if (!isOpen())
return info;
@@ -1184,105 +1446,44 @@ QSqlRecord QPSQLDriver::record(const QString& tablename) const
else
schema = std::move(schema).toLower();
- QString stmt;
- if (d->pro == QPSQLDriver::Version6) {
- stmt = QLatin1String("select pg_attribute.attname, int(pg_attribute.atttypid), "
- "pg_attribute.attnotnull, pg_attribute.attlen, pg_attribute.atttypmod, "
- "int(pg_attribute.attrelid), pg_attribute.attnum "
- "from pg_class, pg_attribute "
- "where pg_class.relname = '%1' "
- "and pg_attribute.attnum > 0 "
- "and pg_attribute.attrelid = pg_class.oid ");
- } else if (d->pro == QPSQLDriver::Version7) {
- stmt = QLatin1String("select pg_attribute.attname, pg_attribute.atttypid::int, "
- "pg_attribute.attnotnull, pg_attribute.attlen, pg_attribute.atttypmod, "
- "pg_attribute.attrelid::int, pg_attribute.attnum "
- "from pg_class, pg_attribute "
- "where pg_class.relname = '%1' "
- "and pg_attribute.attnum > 0 "
- "and pg_attribute.attrelid = pg_class.oid ");
- } else if (d->pro == QPSQLDriver::Version7_1) {
- stmt = QLatin1String("select pg_attribute.attname, pg_attribute.atttypid::int, "
- "pg_attribute.attnotnull, pg_attribute.attlen, pg_attribute.atttypmod, "
- "pg_attrdef.adsrc "
- "from pg_class, pg_attribute "
- "left join pg_attrdef on (pg_attrdef.adrelid = "
- "pg_attribute.attrelid and pg_attrdef.adnum = pg_attribute.attnum) "
- "where pg_class.relname = '%1' "
- "and pg_attribute.attnum > 0 "
- "and pg_attribute.attrelid = pg_class.oid "
- "order by pg_attribute.attnum ");
- } else if (d->pro >= QPSQLDriver::Version7_3) {
- stmt = QLatin1String("select pg_attribute.attname, pg_attribute.atttypid::int, "
- "pg_attribute.attnotnull, pg_attribute.attlen, pg_attribute.atttypmod, "
- "pg_attrdef.adsrc "
- "from pg_class, pg_attribute "
- "left join pg_attrdef on (pg_attrdef.adrelid = "
- "pg_attribute.attrelid and pg_attrdef.adnum = pg_attribute.attnum) "
- "where %1 "
- "and pg_class.relname = '%2' "
- "and pg_attribute.attnum > 0 "
- "and pg_attribute.attrelid = pg_class.oid "
- "and pg_attribute.attisdropped = false "
- "order by pg_attribute.attnum ");
- if (schema.isEmpty())
- stmt = stmt.arg(QLatin1String("pg_table_is_visible(pg_class.oid)"));
- else
- stmt = stmt.arg(QString::fromLatin1("pg_class.relnamespace = (select oid from "
- "pg_namespace where pg_namespace.nspname = '%1')").arg(schema));
- } else {
- qFatal("QPSQLDriver::record(tablename): unknown PSQL version, query statement not set");
- }
+ QString stmt = QLatin1String("select pg_attribute.attname, pg_attribute.atttypid::int, "
+ "pg_attribute.attnotnull, pg_attribute.attlen, pg_attribute.atttypmod, "
+ "pg_attrdef.adsrc "
+ "from pg_class, pg_attribute "
+ "left join pg_attrdef on (pg_attrdef.adrelid = "
+ "pg_attribute.attrelid and pg_attrdef.adnum = pg_attribute.attnum) "
+ "where %1 "
+ "and pg_class.relname = '%2' "
+ "and pg_attribute.attnum > 0 "
+ "and pg_attribute.attrelid = pg_class.oid "
+ "and pg_attribute.attisdropped = false "
+ "order by pg_attribute.attnum");
+ if (schema.isEmpty())
+ stmt = stmt.arg(QLatin1String("pg_table_is_visible(pg_class.oid)"));
+ else
+ stmt = stmt.arg(QString::fromLatin1("pg_class.relnamespace = (select oid from "
+ "pg_namespace where pg_namespace.nspname = '%1')").arg(schema));
QSqlQuery query(createResult());
query.exec(stmt.arg(tbl));
- if (d->pro >= QPSQLDriver::Version7_1) {
- while (query.next()) {
- int len = query.value(3).toInt();
- int precision = query.value(4).toInt();
- // swap length and precision if length == -1
- if (len == -1 && precision > -1) {
- len = precision - 4;
- precision = -1;
- }
- QString defVal = query.value(5).toString();
- if (!defVal.isEmpty() && defVal.at(0) == QLatin1Char('\''))
- defVal = defVal.mid(1, defVal.length() - 2);
- QSqlField f(query.value(0).toString(), qDecodePSQLType(query.value(1).toInt()));
- f.setRequired(query.value(2).toBool());
- f.setLength(len);
- f.setPrecision(precision);
- f.setDefaultValue(defVal);
- f.setSqlType(query.value(1).toInt());
- info.append(f);
- }
- } else {
- // Postgres < 7.1 cannot handle outer joins
- while (query.next()) {
- QString defVal;
- QString stmt2 = QLatin1String("select pg_attrdef.adsrc from pg_attrdef where "
- "pg_attrdef.adrelid = %1 and pg_attrdef.adnum = %2 ");
- QSqlQuery query2(createResult());
- query2.exec(stmt2.arg(query.value(5).toInt()).arg(query.value(6).toInt()));
- if (query2.isActive() && query2.next())
- defVal = query2.value(0).toString();
- if (!defVal.isEmpty() && defVal.at(0) == QLatin1Char('\''))
- defVal = defVal.mid(1, defVal.length() - 2);
- int len = query.value(3).toInt();
- int precision = query.value(4).toInt();
- // swap length and precision if length == -1
- if (len == -1 && precision > -1) {
- len = precision - 4;
- precision = -1;
- }
- QSqlField f(query.value(0).toString(), qDecodePSQLType(query.value(1).toInt()));
- f.setRequired(query.value(2).toBool());
- f.setLength(len);
- f.setPrecision(precision);
- f.setDefaultValue(defVal);
- f.setSqlType(query.value(1).toInt());
- info.append(f);
+ while (query.next()) {
+ int len = query.value(3).toInt();
+ int precision = query.value(4).toInt();
+ // swap length and precision if length == -1
+ if (len == -1 && precision > -1) {
+ len = precision - 4;
+ precision = -1;
}
+ QString defVal = query.value(5).toString();
+ if (!defVal.isEmpty() && defVal.at(0) == QLatin1Char('\''))
+ defVal = defVal.mid(1, defVal.length() - 2);
+ QSqlField f(query.value(0).toString(), qDecodePSQLType(query.value(1).toInt()), tablename);
+ f.setRequired(query.value(2).toBool());
+ f.setLength(len);
+ f.setPrecision(precision);
+ f.setDefaultValue(defVal);
+ f.setSqlType(query.value(1).toInt());
+ info.append(f);
}
return info;
@@ -1430,6 +1631,7 @@ bool QPSQLDriver::subscribeToNotification(const QString &name)
QString query = QLatin1String("LISTEN ") + escapeIdentifier(name, QSqlDriver::TableName);
PGresult *result = d->exec(query);
if (PQresultStatus(result) != PGRES_COMMAND_OK) {
+ d->seid.removeLast();
setLastError(qMakeError(tr("Unable to subscribe"), QSqlError::StatementError, d, result));
PQclear(result);
return false;
diff --git a/src/plugins/sqldrivers/psql/qsql_psql_p.h b/src/plugins/sqldrivers/psql/qsql_psql_p.h
index f5cb2e9bd0..2873a9f851 100644
--- a/src/plugins/sqldrivers/psql/qsql_psql_p.h
+++ b/src/plugins/sqldrivers/psql/qsql_psql_p.h
@@ -98,34 +98,34 @@ public:
explicit QPSQLDriver(QObject *parent=0);
explicit QPSQLDriver(PGconn *conn, QObject *parent=0);
~QPSQLDriver();
- bool hasFeature(DriverFeature f) const Q_DECL_OVERRIDE;
+ bool hasFeature(DriverFeature f) const override;
bool open(const QString & db,
const QString & user,
const QString & password,
const QString & host,
int port,
- const QString& connOpts) Q_DECL_OVERRIDE;
- bool isOpen() const Q_DECL_OVERRIDE;
- void close() Q_DECL_OVERRIDE;
- QSqlResult *createResult() const Q_DECL_OVERRIDE;
- QStringList tables(QSql::TableType) const Q_DECL_OVERRIDE;
- QSqlIndex primaryIndex(const QString& tablename) const Q_DECL_OVERRIDE;
- QSqlRecord record(const QString& tablename) const Q_DECL_OVERRIDE;
+ const QString& connOpts) override;
+ bool isOpen() const override;
+ void close() override;
+ QSqlResult *createResult() const override;
+ QStringList tables(QSql::TableType) const override;
+ QSqlIndex primaryIndex(const QString& tablename) const override;
+ QSqlRecord record(const QString& tablename) const override;
Protocol protocol() const;
- QVariant handle() const Q_DECL_OVERRIDE;
+ QVariant handle() const override;
- QString escapeIdentifier(const QString &identifier, IdentifierType type) const Q_DECL_OVERRIDE;
- QString formatValue(const QSqlField &field, bool trimStrings) const Q_DECL_OVERRIDE;
+ QString escapeIdentifier(const QString &identifier, IdentifierType type) const override;
+ QString formatValue(const QSqlField &field, bool trimStrings) const override;
- bool subscribeToNotification(const QString &name) Q_DECL_OVERRIDE;
- bool unsubscribeFromNotification(const QString &name) Q_DECL_OVERRIDE;
- QStringList subscribedToNotifications() const Q_DECL_OVERRIDE;
+ bool subscribeToNotification(const QString &name) override;
+ bool unsubscribeFromNotification(const QString &name) override;
+ QStringList subscribedToNotifications() const override;
protected:
- bool beginTransaction() Q_DECL_OVERRIDE;
- bool commitTransaction() Q_DECL_OVERRIDE;
- bool rollbackTransaction() Q_DECL_OVERRIDE;
+ bool beginTransaction() override;
+ bool commitTransaction() override;
+ bool rollbackTransaction() override;
private Q_SLOTS:
void _q_handleNotification(int);
diff --git a/src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp b/src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp
index 13ec024924..d1a6582c5a 100644
--- a/src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp
+++ b/src/plugins/sqldrivers/sqlite/qsql_sqlite.cpp
@@ -51,6 +51,10 @@
#include <qstringlist.h>
#include <qvector.h>
#include <qdebug.h>
+#ifndef QT_NO_REGULAREXPRESSION
+#include <qcache.h>
+#include <qregularexpression.h>
+#endif
#include <QTimeZone>
#if defined Q_OS_WIN
@@ -119,19 +123,19 @@ class QSQLiteResult : public QSqlCachedResult
public:
explicit QSQLiteResult(const QSQLiteDriver* db);
~QSQLiteResult();
- QVariant handle() const Q_DECL_OVERRIDE;
+ QVariant handle() const override;
protected:
- bool gotoNext(QSqlCachedResult::ValueCache& row, int idx) Q_DECL_OVERRIDE;
- bool reset(const QString &query) Q_DECL_OVERRIDE;
- bool prepare(const QString &query) Q_DECL_OVERRIDE;
- bool exec() Q_DECL_OVERRIDE;
- int size() Q_DECL_OVERRIDE;
- int numRowsAffected() Q_DECL_OVERRIDE;
- QVariant lastInsertId() const Q_DECL_OVERRIDE;
- QSqlRecord record() const Q_DECL_OVERRIDE;
- void detachFromResultSet() Q_DECL_OVERRIDE;
- void virtual_hook(int id, void *data) Q_DECL_OVERRIDE;
+ bool gotoNext(QSqlCachedResult::ValueCache& row, int idx) override;
+ bool reset(const QString &query) override;
+ bool prepare(const QString &query) override;
+ bool exec() override;
+ int size() override;
+ int numRowsAffected() override;
+ QVariant lastInsertId() const override;
+ QSqlRecord record() const override;
+ void detachFromResultSet() override;
+ void virtual_hook(int id, void *data) override;
};
class QSQLiteDriverPrivate : public QSqlDriverPrivate
@@ -209,7 +213,9 @@ void QSQLiteResultPrivate::initColumns(bool emptyResultset)
QString colName = QString(reinterpret_cast<const QChar *>(
sqlite3_column_name16(stmt, i))
).remove(QLatin1Char('"'));
-
+ const QString tableName = QString(reinterpret_cast<const QChar *>(
+ sqlite3_column_table_name16(stmt, i))
+ ).remove(QLatin1Char('"'));
// must use typeName for resolving the type to match QSqliteDriver::record
QString typeName = QString(reinterpret_cast<const QChar *>(
sqlite3_column_decltype16(stmt, i)));
@@ -242,7 +248,7 @@ void QSQLiteResultPrivate::initColumns(bool emptyResultset)
}
}
- QSqlField fld(colName, fieldType);
+ QSqlField fld(colName, fieldType, tableName);
fld.setSqlType(stp);
rInf.append(fld);
}
@@ -440,7 +446,7 @@ static QString timespecToString(const QDateTime &dateTime)
bool QSQLiteResult::exec()
{
Q_D(QSQLiteResult);
- const QVector<QVariant> values = boundValues();
+ QVector<QVariant> values = boundValues();
d->skippedStatus = false;
d->skipRow = false;
@@ -455,8 +461,41 @@ bool QSQLiteResult::exec()
d->finalize();
return false;
}
+
int paramCount = sqlite3_bind_parameter_count(d->stmt);
- if (paramCount == values.count()) {
+ bool paramCountIsValid = paramCount == values.count();
+
+#if (SQLITE_VERSION_NUMBER >= 3003011)
+ // In the case of the reuse of a named placeholder
+ if (paramCount < values.count()) {
+ const auto countIndexes = [](int counter, const QList<int>& indexList) {
+ return counter + indexList.length();
+ };
+
+ const int bindParamCount = std::accumulate(d->indexes.cbegin(),
+ d->indexes.cend(),
+ 0,
+ countIndexes);
+
+ paramCountIsValid = bindParamCount == values.count();
+ // When using named placeholders, it will reuse the index for duplicated
+ // placeholders. So we need to ensure the QVector has only one instance of
+ // each value as SQLite will do the rest for us.
+ QVector<QVariant> prunedValues;
+ QList<int> handledIndexes;
+ for (int i = 0, currentIndex = 0; i < values.size(); ++i) {
+ if (handledIndexes.contains(i))
+ continue;
+ const auto placeHolder = QString::fromUtf8(sqlite3_bind_parameter_name(d->stmt, currentIndex + 1));
+ handledIndexes << d->indexes[placeHolder];
+ prunedValues << values.at(d->indexes[placeHolder].first());
+ ++currentIndex;
+ }
+ values = prunedValues;
+ }
+#endif
+
+ if (paramCountIsValid) {
for (int i = 0; i < paramCount; ++i) {
res = SQLITE_OK;
const QVariant value = values.at(i);
@@ -483,14 +522,14 @@ bool QSQLiteResult::exec()
break;
case QVariant::DateTime: {
const QDateTime dateTime = value.toDateTime();
- const QString str = dateTime.toString(QStringLiteral("yyyy-MM-ddThh:mm:ss.zzz") + timespecToString(dateTime));
+ const QString str = dateTime.toString(QLatin1String("yyyy-MM-ddThh:mm:ss.zzz") + timespecToString(dateTime));
res = sqlite3_bind_text16(d->stmt, i + 1, str.utf16(),
str.size() * sizeof(ushort), SQLITE_TRANSIENT);
break;
}
case QVariant::Time: {
const QTime time = value.toTime();
- const QString str = time.toString(QStringLiteral("hh:mm:ss.zzz"));
+ const QString str = time.toString(QStringViewLiteral("hh:mm:ss.zzz"));
res = sqlite3_bind_text16(d->stmt, i + 1, str.utf16(),
str.size() * sizeof(ushort), SQLITE_TRANSIENT);
break;
@@ -583,6 +622,40 @@ QVariant QSQLiteResult::handle() const
/////////////////////////////////////////////////////////
+#ifndef QT_NO_REGULAREXPRESSION
+static void _q_regexp(sqlite3_context* context, int argc, sqlite3_value** argv)
+{
+ if (Q_UNLIKELY(argc != 2)) {
+ sqlite3_result_int(context, 0);
+ return;
+ }
+
+ const QString pattern = QString::fromUtf8(
+ reinterpret_cast<const char*>(sqlite3_value_text(argv[0])));
+ const QString subject = QString::fromUtf8(
+ reinterpret_cast<const char*>(sqlite3_value_text(argv[1])));
+
+ auto cache = static_cast<QCache<QString, QRegularExpression>*>(sqlite3_user_data(context));
+ auto regexp = cache->object(pattern);
+ const bool wasCached = regexp;
+
+ if (!wasCached)
+ regexp = new QRegularExpression(pattern, QRegularExpression::DontCaptureOption | QRegularExpression::OptimizeOnFirstUsageOption);
+
+ const bool found = subject.contains(*regexp);
+
+ if (!wasCached)
+ cache->insert(pattern, regexp);
+
+ sqlite3_result_int(context, int(found));
+}
+
+static void _q_regexp_cleanup(void *cache)
+{
+ delete static_cast<QCache<QString, QRegularExpression>*>(cache);
+}
+#endif
+
QSQLiteDriver::QSQLiteDriver(QObject * parent)
: QSqlDriver(*new QSQLiteDriverPrivate, parent)
{
@@ -618,11 +691,17 @@ bool QSQLiteDriver::hasFeature(DriverFeature f) const
case EventNotifications:
return true;
case QuerySize:
- case NamedPlaceholders:
case BatchOperations:
case MultipleResultSets:
case CancelQuery:
return false;
+ case NamedPlaceholders:
+#if (SQLITE_VERSION_NUMBER < 3003011)
+ return false;
+#else
+ return true;
+#endif
+
}
return false;
}
@@ -642,6 +721,11 @@ bool QSQLiteDriver::open(const QString & db, const QString &, const QString &, c
bool sharedCache = false;
bool openReadOnlyOption = false;
bool openUriOption = false;
+#ifndef QT_NO_REGULAREXPRESSION
+ static const QLatin1String regexpConnectOption = QLatin1String("QSQLITE_ENABLE_REGEXP");
+ bool defineRegexp = false;
+ int regexpCacheSize = 25;
+#endif
const auto opts = conOpts.splitRef(QLatin1Char(';'));
for (auto option : opts) {
@@ -661,18 +745,42 @@ bool QSQLiteDriver::open(const QString & db, const QString &, const QString &, c
} else if (option == QLatin1String("QSQLITE_ENABLE_SHARED_CACHE")) {
sharedCache = true;
}
+#ifndef QT_NO_REGULAREXPRESSION
+ else if (option.startsWith(regexpConnectOption)) {
+ option = option.mid(regexpConnectOption.size()).trimmed();
+ if (option.isEmpty()) {
+ defineRegexp = true;
+ } else if (option.startsWith(QLatin1Char('='))) {
+ bool ok = false;
+ const int cacheSize = option.mid(1).trimmed().toInt(&ok);
+ if (ok) {
+ defineRegexp = true;
+ if (cacheSize > 0)
+ regexpCacheSize = cacheSize;
+ }
+ }
+ }
+#endif
}
int openMode = (openReadOnlyOption ? SQLITE_OPEN_READONLY : (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE));
+ openMode |= (sharedCache ? SQLITE_OPEN_SHAREDCACHE : SQLITE_OPEN_PRIVATECACHE);
if (openUriOption)
openMode |= SQLITE_OPEN_URI;
- sqlite3_enable_shared_cache(sharedCache);
+ openMode |= SQLITE_OPEN_NOMUTEX;
if (sqlite3_open_v2(db.toUtf8().constData(), &d->access, openMode, NULL) == SQLITE_OK) {
sqlite3_busy_timeout(d->access, timeOut);
setOpen(true);
setOpenError(false);
+#ifndef QT_NO_REGULAREXPRESSION
+ if (defineRegexp) {
+ auto cache = new QCache<QString, QRegularExpression>(regexpCacheSize);
+ sqlite3_create_function_v2(d->access, "regexp", 2, SQLITE_UTF8, cache, &_q_regexp, NULL,
+ NULL, &_q_regexp_cleanup);
+ }
+#endif
return true;
} else {
if (d->access) {
@@ -807,7 +915,7 @@ static QSqlIndex qGetTableInfo(QSqlQuery &q, const QString &tableName, bool only
if (onlyPIndex && !isPk)
continue;
QString typeName = q.value(2).toString().toLower();
- QSqlField fld(q.value(1).toString(), qGetColumnType(typeName));
+ QSqlField fld(q.value(1).toString(), qGetColumnType(typeName), tableName);
if (isPk && (typeName == QLatin1String("integer")))
// INTEGER PRIMARY KEY fields are auto-generated in sqlite
// INT PRIMARY KEY is not the same as INTEGER PRIMARY KEY!
diff --git a/src/plugins/sqldrivers/sqlite/qsql_sqlite_p.h b/src/plugins/sqldrivers/sqlite/qsql_sqlite_p.h
index ca969a4b53..61be4c937f 100644
--- a/src/plugins/sqldrivers/sqlite/qsql_sqlite_p.h
+++ b/src/plugins/sqldrivers/sqlite/qsql_sqlite_p.h
@@ -75,28 +75,28 @@ public:
explicit QSQLiteDriver(QObject *parent = 0);
explicit QSQLiteDriver(sqlite3 *connection, QObject *parent = 0);
~QSQLiteDriver();
- bool hasFeature(DriverFeature f) const Q_DECL_OVERRIDE;
+ bool hasFeature(DriverFeature f) const override;
bool open(const QString & db,
const QString & user,
const QString & password,
const QString & host,
int port,
- const QString & connOpts) Q_DECL_OVERRIDE;
- void close() Q_DECL_OVERRIDE;
- QSqlResult *createResult() const Q_DECL_OVERRIDE;
- bool beginTransaction() Q_DECL_OVERRIDE;
- bool commitTransaction() Q_DECL_OVERRIDE;
- bool rollbackTransaction() Q_DECL_OVERRIDE;
- QStringList tables(QSql::TableType) const Q_DECL_OVERRIDE;
+ const QString & connOpts) override;
+ void close() override;
+ QSqlResult *createResult() const override;
+ bool beginTransaction() override;
+ bool commitTransaction() override;
+ bool rollbackTransaction() override;
+ QStringList tables(QSql::TableType) const override;
- QSqlRecord record(const QString& tablename) const Q_DECL_OVERRIDE;
- QSqlIndex primaryIndex(const QString &table) const Q_DECL_OVERRIDE;
- QVariant handle() const Q_DECL_OVERRIDE;
- QString escapeIdentifier(const QString &identifier, IdentifierType) const Q_DECL_OVERRIDE;
+ QSqlRecord record(const QString& tablename) const override;
+ QSqlIndex primaryIndex(const QString &table) const override;
+ QVariant handle() const override;
+ QString escapeIdentifier(const QString &identifier, IdentifierType) const override;
- bool subscribeToNotification(const QString &name) Q_DECL_OVERRIDE;
- bool unsubscribeFromNotification(const QString &name) Q_DECL_OVERRIDE;
- QStringList subscribedToNotifications() const Q_DECL_OVERRIDE;
+ bool subscribeToNotification(const QString &name) override;
+ bool unsubscribeFromNotification(const QString &name) override;
+ QStringList subscribedToNotifications() const override;
private Q_SLOTS:
void handleNotification(const QString &tableName, qint64 rowid);
};
diff --git a/src/plugins/sqldrivers/sqlite/smain.cpp b/src/plugins/sqldrivers/sqlite/smain.cpp
index 2ad466a61e..092813990c 100644
--- a/src/plugins/sqldrivers/sqlite/smain.cpp
+++ b/src/plugins/sqldrivers/sqlite/smain.cpp
@@ -51,7 +51,7 @@ class QSQLiteDriverPlugin : public QSqlDriverPlugin
public:
QSQLiteDriverPlugin();
- QSqlDriver* create(const QString &) Q_DECL_OVERRIDE;
+ QSqlDriver* create(const QString &) override;
};
QSQLiteDriverPlugin::QSQLiteDriverPlugin()
diff --git a/src/plugins/sqldrivers/sqlite2/qsql_sqlite2.cpp b/src/plugins/sqldrivers/sqlite2/qsql_sqlite2.cpp
index 67c24e4168..390f05c7aa 100644
--- a/src/plugins/sqldrivers/sqlite2/qsql_sqlite2.cpp
+++ b/src/plugins/sqldrivers/sqlite2/qsql_sqlite2.cpp
@@ -107,16 +107,16 @@ class QSQLite2Result : public QSqlCachedResult
public:
explicit QSQLite2Result(const QSQLite2Driver* db);
~QSQLite2Result();
- QVariant handle() const Q_DECL_OVERRIDE;
+ QVariant handle() const override;
protected:
- bool gotoNext(QSqlCachedResult::ValueCache &row, int idx) Q_DECL_OVERRIDE;
- bool reset(const QString &query) Q_DECL_OVERRIDE;
- int size() Q_DECL_OVERRIDE;
- int numRowsAffected() Q_DECL_OVERRIDE;
- QSqlRecord record() const Q_DECL_OVERRIDE;
- void detachFromResultSet() Q_DECL_OVERRIDE;
- void virtual_hook(int id, void *data) Q_DECL_OVERRIDE;
+ bool gotoNext(QSqlCachedResult::ValueCache &row, int idx) override;
+ bool reset(const QString &query) override;
+ int size() override;
+ int numRowsAffected() override;
+ QSqlRecord record() const override;
+ void detachFromResultSet() override;
+ void virtual_hook(int id, void *data) override;
};
class QSQLite2ResultPrivate: public QSqlCachedResultPrivate
@@ -178,7 +178,8 @@ void QSQLite2ResultPrivate::finalize()
if (err) {
q->setLastError(QSqlError(QCoreApplication::translate("QSQLite2Result",
"Unable to fetch results"), QString::fromLatin1(err),
- QSqlError::StatementError, res));
+ QSqlError::StatementError,
+ res != -1 ? QString::number(res) : QString()));
sqlite_freemem(err);
}
currentMachine = 0;
@@ -576,7 +577,7 @@ QSqlIndex QSQLite2Driver::primaryIndex(const QString &tblname) const
QVariant::Type type = QVariant::Invalid;
if (rec.contains(name))
type = rec.field(name).type();
- index.append(QSqlField(name, type));
+ index.append(QSqlField(name, type, tblname));
}
return index;
}
diff --git a/src/plugins/sqldrivers/sqlite2/qsql_sqlite2_p.h b/src/plugins/sqldrivers/sqlite2/qsql_sqlite2_p.h
index 83b248ec6a..48c64536f1 100644
--- a/src/plugins/sqldrivers/sqlite2/qsql_sqlite2_p.h
+++ b/src/plugins/sqldrivers/sqlite2/qsql_sqlite2_p.h
@@ -79,29 +79,29 @@ public:
explicit QSQLite2Driver(QObject *parent = 0);
explicit QSQLite2Driver(sqlite *connection, QObject *parent = 0);
~QSQLite2Driver();
- bool hasFeature(DriverFeature f) const Q_DECL_OVERRIDE;
+ bool hasFeature(DriverFeature f) const override;
bool open(const QString &db,
const QString &user,
const QString &password,
const QString &host,
int port,
- const QString &connOpts) Q_DECL_OVERRIDE;
+ const QString &connOpts) override;
bool open(const QString &db,
const QString &user,
const QString &password,
const QString &host,
int port) { return open(db, user, password, host, port, QString()); }
- void close() Q_DECL_OVERRIDE;
- QSqlResult *createResult() const Q_DECL_OVERRIDE;
- bool beginTransaction() Q_DECL_OVERRIDE;
- bool commitTransaction() Q_DECL_OVERRIDE;
- bool rollbackTransaction() Q_DECL_OVERRIDE;
- QStringList tables(QSql::TableType) const Q_DECL_OVERRIDE;
+ void close() override;
+ QSqlResult *createResult() const override;
+ bool beginTransaction() override;
+ bool commitTransaction() override;
+ bool rollbackTransaction() override;
+ QStringList tables(QSql::TableType) const override;
- QSqlRecord record(const QString &tablename) const Q_DECL_OVERRIDE;
- QSqlIndex primaryIndex(const QString &table) const Q_DECL_OVERRIDE;
- QVariant handle() const Q_DECL_OVERRIDE;
- QString escapeIdentifier(const QString &identifier, IdentifierType) const Q_DECL_OVERRIDE;
+ QSqlRecord record(const QString &tablename) const override;
+ QSqlIndex primaryIndex(const QString &table) const override;
+ QVariant handle() const override;
+ QString escapeIdentifier(const QString &identifier, IdentifierType) const override;
};
QT_END_NAMESPACE
diff --git a/src/plugins/sqldrivers/tds/qsql_tds.cpp b/src/plugins/sqldrivers/tds/qsql_tds.cpp
index 6ebd09a572..ad95b097ef 100644
--- a/src/plugins/sqldrivers/tds/qsql_tds.cpp
+++ b/src/plugins/sqldrivers/tds/qsql_tds.cpp
@@ -132,7 +132,8 @@ QT_BEGIN_NAMESPACE
QSqlError qMakeError(const QString& err, QSqlError::ErrorType type, int errNo = -1)
{
- return QSqlError(QLatin1String("QTDS: ") + err, QString(), type, errNo);
+ return QSqlError(QLatin1String("QTDS: ") + err, QString(), type,
+ errNo != -1 ? QString::number(errNo) : QString());
}
class QTDSDriverPrivate : public QSqlDriverPrivate
@@ -163,15 +164,15 @@ class QTDSResult : public QSqlCachedResult
public:
explicit QTDSResult(const QTDSDriver* db);
~QTDSResult();
- QVariant handle() const Q_DECL_OVERRIDE;
+ QVariant handle() const override;
protected:
void cleanup();
- bool reset(const QString &query) Q_DECL_OVERRIDE;
- int size() Q_DECL_OVERRIDE;
- int numRowsAffected() Q_DECL_OVERRIDE;
- bool gotoNext(QSqlCachedResult::ValueCache &values, int index) Q_DECL_OVERRIDE;
- QSqlRecord record() const Q_DECL_OVERRIDE;
+ bool reset(const QString &query) override;
+ int size() override;
+ int numRowsAffected() override;
+ bool gotoNext(QSqlCachedResult::ValueCache &values, int index) override;
+ QSqlRecord record() const override;
};
class QTDSResultPrivate: public QSqlCachedResultPrivate
@@ -767,7 +768,7 @@ QSqlRecord QTDSDriver::record(const QString& tablename) const
"where id = (select id from sysobjects where name = '%1')"));
t.exec(stmt.arg(table));
while (t.next()) {
- QSqlField f(t.value(0).toString().simplified(), qDecodeTDSType(t.value(1).toInt()));
+ QSqlField f(t.value(0).toString().simplified(), qDecodeTDSType(t.value(1).toInt()), tablename);
f.setLength(t.value(2).toInt());
f.setPrecision(t.value(3).toInt());
f.setSqlType(t.value(1).toInt());
@@ -853,7 +854,7 @@ QSqlIndex QTDSDriver::primaryIndex(const QString& tablename) const
QRegExp regx(QLatin1String("\\s*(\\S+)(?:\\s+(DESC|desc))?\\s*"));
for(QStringList::Iterator it = fNames.begin(); it != fNames.end(); ++it) {
regx.indexIn(*it);
- QSqlField f(regx.cap(1), rec.field(regx.cap(1)).type());
+ QSqlField f(regx.cap(1), rec.field(regx.cap(1)).type(), tablename);
if (regx.cap(2).toLower() == QLatin1String("desc")) {
idx.append(f, true);
} else {
diff --git a/src/plugins/sqldrivers/tds/qsql_tds_p.h b/src/plugins/sqldrivers/tds/qsql_tds_p.h
index d0914455a2..948e3c7024 100644
--- a/src/plugins/sqldrivers/tds/qsql_tds_p.h
+++ b/src/plugins/sqldrivers/tds/qsql_tds_p.h
@@ -88,29 +88,29 @@ public:
explicit QTDSDriver(QObject* parent = 0);
QTDSDriver(LOGINREC* rec, const QString& host, const QString &db, QObject* parent = 0);
~QTDSDriver();
- bool hasFeature(DriverFeature f) const Q_DECL_OVERRIDE;
+ bool hasFeature(DriverFeature f) const override;
bool open(const QString &db,
const QString &user,
const QString &password,
const QString &host,
int port,
- const QString &connOpts) Q_DECL_OVERRIDE;
- void close() Q_DECL_OVERRIDE;
- QStringList tables(QSql::TableType) const Q_DECL_OVERRIDE;
- QSqlResult *createResult() const Q_DECL_OVERRIDE;
- QSqlRecord record(const QString &tablename) const Q_DECL_OVERRIDE;
- QSqlIndex primaryIndex(const QString &tablename) const Q_DECL_OVERRIDE;
+ const QString &connOpts) override;
+ void close() override;
+ QStringList tables(QSql::TableType) const override;
+ QSqlResult *createResult() const override;
+ QSqlRecord record(const QString &tablename) const override;
+ QSqlIndex primaryIndex(const QString &tablename) const override;
QString formatValue(const QSqlField &field,
- bool trimStrings) const Q_DECL_OVERRIDE;
- QVariant handle() const Q_DECL_OVERRIDE;
+ bool trimStrings) const override;
+ QVariant handle() const override;
- QString escapeIdentifier(const QString &identifier, IdentifierType type) const Q_DECL_OVERRIDE;
+ QString escapeIdentifier(const QString &identifier, IdentifierType type) const override;
protected:
- bool beginTransaction() Q_DECL_OVERRIDE;
- bool commitTransaction() Q_DECL_OVERRIDE;
- bool rollbackTransaction() Q_DECL_OVERRIDE;
+ bool beginTransaction() override;
+ bool commitTransaction() override;
+ bool rollbackTransaction() override;
private:
void init();
};
diff --git a/src/plugins/styles/android/android.pro b/src/plugins/styles/android/android.pro
new file mode 100644
index 0000000000..4ca35d8046
--- /dev/null
+++ b/src/plugins/styles/android/android.pro
@@ -0,0 +1,16 @@
+TARGET = qandroidstyle
+
+QT += widgets-private
+
+SOURCES += \
+ main.cpp \
+ qandroidstyle.cpp
+
+HEADERS += \
+ qandroidstyle_p.h
+
+DISTFILES += androidstyle.json
+
+PLUGIN_TYPE = styles
+PLUGIN_CLASS_NAME = QAndroidStylePlugin
+load(qt_plugin)
diff --git a/src/plugins/styles/android/androidstyle.json b/src/plugins/styles/android/androidstyle.json
new file mode 100644
index 0000000000..6843bd3301
--- /dev/null
+++ b/src/plugins/styles/android/androidstyle.json
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "android" ]
+}
diff --git a/src/plugins/styles/android/main.cpp b/src/plugins/styles/android/main.cpp
new file mode 100644
index 0000000000..2121538b0a
--- /dev/null
+++ b/src/plugins/styles/android/main.cpp
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtWidgets/qstyleplugin.h>
+#include "qandroidstyle_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidStylePlugin : public QStylePlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QStyleFactoryInterface" FILE "androidstyle.json")
+public:
+ QStyle *create(const QString &key);
+};
+
+QStyle *QAndroidStylePlugin::create(const QString &key)
+{
+ if (key.compare(QLatin1String("android"), Qt::CaseInsensitive) == 0)
+ return new QAndroidStyle();
+
+ return 0;
+}
+
+QT_END_NAMESPACE
+
+#include "main.moc"
+
diff --git a/src/plugins/styles/android/qandroidstyle.cpp b/src/plugins/styles/android/qandroidstyle.cpp
new file mode 100644
index 0000000000..086df92322
--- /dev/null
+++ b/src/plugins/styles/android/qandroidstyle.cpp
@@ -0,0 +1,1816 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 BogDan Vatra <bogdan@kde.org>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWidgets module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qandroidstyle_p.h"
+
+#include <QFile>
+#include <QFont>
+#include <QApplication>
+#include <qdrawutil.h>
+#include <QPixmapCache>
+#include <QFileInfo>
+#include <QStyleOption>
+#include <QPainter>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QDebug>
+
+#include <QGuiApplication>
+#include <qpa/qplatformnativeinterface.h>
+#include <qpa/qplatformtheme.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace {
+ const quint32 NO_COLOR = 1;
+ const quint32 TRANSPARENT_COLOR = 0;
+}
+
+QAndroidStyle::QAndroidStyle()
+ : QFusionStyle()
+{
+ QPixmapCache::clear();
+ checkBoxControl = NULL;
+ QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface();
+ QPalette *standardPalette = reinterpret_cast<QPalette *>(nativeInterface->nativeResourceForIntegration("AndroidStandardPalette"));
+ if (standardPalette)
+ m_standardPalette = *standardPalette;
+
+ QHash<QByteArray, QFont> *qwidgetsFonts = reinterpret_cast<QHash<QByteArray, QFont> *>(nativeInterface->nativeResourceForIntegration("AndroidQWidgetFonts"));
+ if (qwidgetsFonts) {
+ for (auto it = qwidgetsFonts->constBegin(); it != qwidgetsFonts->constEnd(); ++it)
+ QApplication::setFont(it.value(), it.key());
+ qwidgetsFonts->clear(); // free the memory
+ }
+
+ QJsonObject *object = reinterpret_cast<QJsonObject *>(nativeInterface->nativeResourceForIntegration("AndroidStyleData"));
+ if (!object)
+ return;
+
+ for (QJsonObject::const_iterator objectIterator = object->constBegin();
+ objectIterator != object->constEnd();
+ ++objectIterator) {
+ QString key = objectIterator.key();
+ QJsonValue value = objectIterator.value();
+ if (Q_UNLIKELY(!value.isObject())) {
+ qWarning("Style.json structure is unrecognized.");
+ continue;
+ }
+
+ QJsonObject item = value.toObject();
+ QAndroidStyle::ItemType itemType = qtControl(key);
+ if (QC_UnknownType == itemType)
+ continue;
+
+ switch (itemType) {
+ case QC_Checkbox:
+ checkBoxControl = new AndroidCompoundButtonControl(item.toVariantMap(), itemType);
+ m_androidControlsHash[int(itemType)] = checkBoxControl;
+ break;
+ case QC_RadioButton:
+ m_androidControlsHash[int(itemType)] = new AndroidCompoundButtonControl(item.toVariantMap(),
+ itemType);
+ break;
+
+ case QC_ProgressBar:
+ m_androidControlsHash[int(itemType)] = new AndroidProgressBarControl(item.toVariantMap(),
+ itemType);
+ break;
+
+ case QC_Slider:
+ m_androidControlsHash[int(itemType)] = new AndroidSeekBarControl(item.toVariantMap(),
+ itemType);
+ break;
+
+ case QC_Combobox:
+ m_androidControlsHash[int(itemType)] = new AndroidSpinnerControl(item.toVariantMap(),
+ itemType);
+ break;
+
+ default:
+ m_androidControlsHash[int(itemType)] = new AndroidControl(item.toVariantMap(),
+ itemType);
+ break;
+ }
+ }
+ *object = QJsonObject(); // free memory
+}
+
+QAndroidStyle::~QAndroidStyle()
+{
+ qDeleteAll(m_androidControlsHash);
+}
+
+QAndroidStyle::ItemType QAndroidStyle::qtControl(const QString &android)
+{
+ if (android == QLatin1String("buttonStyle"))
+ return QC_Button;
+ if (android == QLatin1String("editTextStyle"))
+ return QC_EditText;
+ if (android == QLatin1String("radioButtonStyle"))
+ return QC_RadioButton;
+ if (android == QLatin1String("checkboxStyle"))
+ return QC_Checkbox;
+ if (android == QLatin1String("textViewStyle"))
+ return QC_View;
+ if (android == QLatin1String("buttonStyleToggle"))
+ return QC_Switch;
+ if (android == QLatin1String("spinnerStyle"))
+ return QC_Combobox;
+ if (android == QLatin1String("progressBarStyleHorizontal"))
+ return QC_ProgressBar;
+ if (android == QLatin1String("seekBarStyle"))
+ return QC_Slider;
+
+ return QC_UnknownType;
+}
+
+QAndroidStyle::ItemType QAndroidStyle::qtControl(QStyle::ComplexControl control)
+{
+ switch (control) {
+ case CC_ComboBox:
+ return QC_Combobox;
+ case CC_Slider:
+ return QC_Slider;
+ default:
+ return QC_UnknownType;
+ }
+}
+
+QAndroidStyle::ItemType QAndroidStyle::qtControl(QStyle::ContentsType contentsType)
+{
+ switch (contentsType) {
+ case CT_PushButton:
+ return QC_Button;
+ case CT_CheckBox:
+ return QC_Checkbox;
+ case CT_RadioButton:
+ return QC_RadioButton;
+ case CT_ComboBox:
+ return QC_Combobox;
+ case CT_ProgressBar:
+ return QC_ProgressBar;
+ case CT_Slider:
+ return QC_Slider;
+ case CT_ScrollBar:
+ return QC_Slider;
+ case CT_TabWidget:
+ return QC_Tab;
+ case CT_TabBarTab:
+ return QC_TabButton;
+ case CT_LineEdit:
+ return QC_EditText;
+ case CT_GroupBox:
+ return QC_GroupBox;
+ default:
+ return QC_UnknownType;
+ }
+}
+
+QAndroidStyle::ItemType QAndroidStyle::qtControl(QStyle::ControlElement controlElement)
+{
+ switch (controlElement) {
+ case CE_PushButton:
+ case CE_PushButtonBevel:
+ case CE_PushButtonLabel:
+ return QC_Button;
+
+ case CE_CheckBox:
+ case CE_CheckBoxLabel:
+ return QC_Checkbox;
+
+ case CE_RadioButton:
+ case CE_RadioButtonLabel:
+ return QC_RadioButton;
+
+ case CE_TabBarTab:
+ case CE_TabBarTabShape:
+ case CE_TabBarTabLabel:
+ return QC_Tab;
+
+ case CE_ProgressBar:
+ case CE_ProgressBarGroove:
+ case CE_ProgressBarContents:
+ case CE_ProgressBarLabel:
+ return QC_ProgressBar;
+
+ case CE_ComboBoxLabel:
+ return QC_Combobox;
+
+ case CE_ShapedFrame:
+ return QC_View;
+
+ default:
+ return QC_UnknownType;
+ }
+}
+
+QAndroidStyle::ItemType QAndroidStyle::qtControl(QStyle::PrimitiveElement primitiveElement)
+{
+ switch (primitiveElement) {
+ case QStyle::PE_PanelLineEdit:
+ case QStyle::PE_FrameLineEdit:
+ return QC_EditText;
+
+ case QStyle::PE_IndicatorViewItemCheck:
+ case QStyle::PE_IndicatorCheckBox:
+ return QC_Checkbox;
+
+ case QStyle::PE_FrameWindow:
+ case QStyle::PE_Widget:
+ case QStyle::PE_Frame:
+ case QStyle::PE_FrameFocusRect:
+ return QC_View;
+ default:
+ return QC_UnknownType;
+ }
+}
+
+QAndroidStyle::ItemType QAndroidStyle::qtControl(QStyle::SubElement subElement)
+{
+ switch (subElement) {
+ case QStyle::SE_LineEditContents:
+ return QC_EditText;
+
+ case QStyle::SE_PushButtonContents:
+ case QStyle::SE_PushButtonFocusRect:
+ return QC_Button;
+
+ case SE_RadioButtonContents:
+ return QC_RadioButton;
+
+ case SE_CheckBoxContents:
+ return QC_Checkbox;
+
+ default:
+ return QC_UnknownType;
+ }
+}
+
+void QAndroidStyle::drawPrimitive(PrimitiveElement pe,
+ const QStyleOption *opt,
+ QPainter *p,
+ const QWidget *w) const
+{
+ const ItemType itemType = qtControl(pe);
+ AndroidControlsHash::const_iterator it = itemType != QC_UnknownType
+ ? m_androidControlsHash.find(itemType)
+ : m_androidControlsHash.end();
+ if (it != m_androidControlsHash.end()) {
+ if (itemType != QC_EditText) {
+ it.value()->drawControl(opt, p, w);
+ } else {
+ QStyleOption copy(*opt);
+ copy.state &= ~QStyle::State_Sunken;
+ it.value()->drawControl(&copy, p, w);
+ }
+ } else if (pe == PE_FrameGroupBox) {
+ if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
+ if (frame->features & QStyleOptionFrame::Flat) {
+ QRect fr = frame->rect;
+ QPoint p1(fr.x(), fr.y() + 1);
+ QPoint p2(fr.x() + fr.width(), p1.y());
+ qDrawShadeLine(p, p1, p2, frame->palette, true,
+ frame->lineWidth, frame->midLineWidth);
+ } else {
+ qDrawShadeRect(p, frame->rect.x(), frame->rect.y(), frame->rect.width(),
+ frame->rect.height(), frame->palette, true,
+ frame->lineWidth, frame->midLineWidth);
+ }
+ }
+ } else {
+ QFusionStyle::drawPrimitive(pe, opt, p, w);
+ }
+}
+
+
+void QAndroidStyle::drawControl(QStyle::ControlElement element,
+ const QStyleOption *opt,
+ QPainter *p,
+ const QWidget *w) const
+{
+ const ItemType itemType = qtControl(element);
+ AndroidControlsHash::const_iterator it = itemType != QC_UnknownType
+ ? m_androidControlsHash.find(itemType)
+ : m_androidControlsHash.end();
+ if (it != m_androidControlsHash.end()) {
+ AndroidControl *androidControl = it.value();
+
+ if (element != QStyle::CE_CheckBoxLabel
+ && element != QStyle::CE_PushButtonLabel
+ && element != QStyle::CE_RadioButtonLabel
+ && element != QStyle::CE_TabBarTabLabel
+ && element != QStyle::CE_ProgressBarLabel) {
+ androidControl->drawControl(opt, p, w);
+ }
+
+ if (element != QStyle::CE_PushButtonBevel
+ && element != QStyle::CE_TabBarTabShape
+ && element != QStyle::CE_ProgressBarGroove) {
+ switch (itemType) {
+ case QC_Button:
+ if (const QStyleOptionButton *buttonOption =
+ qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ QMargins padding = androidControl->padding();
+ QStyleOptionButton copy(*buttonOption);
+ copy.rect.adjust(padding.left(), padding.top(), -padding.right(), -padding.bottom());
+ QFusionStyle::drawControl(CE_PushButtonLabel, &copy, p, w);
+ }
+ break;
+ case QC_Checkbox:
+ case QC_RadioButton:
+ if (const QStyleOptionButton *btn =
+ qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ const bool isRadio = (element == CE_RadioButton);
+ QStyleOptionButton subopt(*btn);
+ subopt.rect = subElementRect(isRadio ? SE_RadioButtonContents
+ : SE_CheckBoxContents, btn, w);
+ QFusionStyle::drawControl(isRadio ? CE_RadioButtonLabel : CE_CheckBoxLabel, &subopt, p, w);
+ }
+ break;
+ case QC_Combobox:
+ if (const QStyleOptionComboBox *comboboxOption =
+ qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
+ QMargins padding = androidControl->padding();
+ QStyleOptionComboBox copy (*comboboxOption);
+ copy.rect.adjust(padding.left(), padding.top(), -padding.right(), -padding.bottom());
+ QFusionStyle::drawControl(CE_ComboBoxLabel, comboboxOption, p, w);
+ }
+ break;
+ default:
+ QFusionStyle::drawControl(element, opt, p, w);
+ break;
+ }
+ }
+ } else {
+ QFusionStyle::drawControl(element, opt, p, w);
+ }
+}
+
+QRect QAndroidStyle::subElementRect(SubElement subElement,
+ const QStyleOption *option,
+ const QWidget *widget) const
+{
+ const ItemType itemType = qtControl(subElement);
+ AndroidControlsHash::const_iterator it = itemType != QC_UnknownType
+ ? m_androidControlsHash.find(itemType)
+ : m_androidControlsHash.end();
+ if (it != m_androidControlsHash.end())
+ return it.value()->subElementRect(subElement, option, widget);
+ return QFusionStyle::subElementRect(subElement, option, widget);
+}
+
+void QAndroidStyle::drawComplexControl(ComplexControl cc,
+ const QStyleOptionComplex *opt,
+ QPainter *p,
+ const QWidget *widget) const
+{
+ const ItemType itemType = qtControl(cc);
+ AndroidControlsHash::const_iterator it = itemType != QC_UnknownType
+ ? m_androidControlsHash.find(itemType)
+ : m_androidControlsHash.end();
+ if (it != m_androidControlsHash.end()) {
+ it.value()->drawControl(opt, p, widget);
+ return;
+ }
+ if (cc == CC_GroupBox) {
+ if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) {
+ // Draw frame
+ QRect textRect = subControlRect(CC_GroupBox, opt, SC_GroupBoxLabel, widget);
+ QRect checkBoxRect;
+ if (groupBox->subControls & SC_GroupBoxCheckBox)
+ checkBoxRect = subControlRect(CC_GroupBox, opt, SC_GroupBoxCheckBox, widget);
+ if (groupBox->subControls & QStyle::SC_GroupBoxFrame) {
+ QStyleOptionFrame frame;
+ frame.QStyleOption::operator=(*groupBox);
+ frame.features = groupBox->features;
+ frame.lineWidth = groupBox->lineWidth;
+ frame.midLineWidth = groupBox->midLineWidth;
+ frame.rect = subControlRect(CC_GroupBox, opt, SC_GroupBoxFrame, widget);
+ p->save();
+ QRegion region(groupBox->rect);
+ if (!groupBox->text.isEmpty()) {
+ bool ltr = groupBox->direction == Qt::LeftToRight;
+ QRect finalRect;
+ if (groupBox->subControls & QStyle::SC_GroupBoxCheckBox) {
+ finalRect = checkBoxRect.united(textRect);
+ finalRect.adjust(ltr ? -4 : 0, 0, ltr ? 0 : 4, 0);
+ } else {
+ finalRect = textRect;
+ }
+ region -= finalRect;
+ }
+ p->setClipRegion(region);
+ drawPrimitive(PE_FrameGroupBox, &frame, p, widget);
+ p->restore();
+ }
+
+ // Draw title
+ if ((groupBox->subControls & QStyle::SC_GroupBoxLabel) && !groupBox->text.isEmpty()) {
+ QColor textColor = groupBox->textColor;
+ if (textColor.isValid())
+ p->setPen(textColor);
+ int alignment = int(groupBox->textAlignment);
+ if (!styleHint(QStyle::SH_UnderlineShortcut, opt, widget))
+ alignment |= Qt::TextHideMnemonic;
+
+ drawItemText(p, textRect, Qt::TextShowMnemonic | Qt::AlignHCenter | alignment,
+ groupBox->palette, groupBox->state & State_Enabled, groupBox->text,
+ textColor.isValid() ? QPalette::NoRole : QPalette::WindowText);
+
+ if (groupBox->state & State_HasFocus) {
+ QStyleOptionFocusRect fropt;
+ fropt.QStyleOption::operator=(*groupBox);
+ fropt.rect = textRect;
+ drawPrimitive(PE_FrameFocusRect, &fropt, p, widget);
+ }
+ }
+
+ // Draw checkbox
+ if (groupBox->subControls & SC_GroupBoxCheckBox) {
+ QStyleOptionButton box;
+ box.QStyleOption::operator=(*groupBox);
+ box.rect = checkBoxRect;
+ checkBoxControl->drawControl(&box, p, widget);
+ }
+ }
+ return;
+ }
+ QFusionStyle::drawComplexControl(cc, opt, p, widget);
+}
+
+QStyle::SubControl QAndroidStyle::hitTestComplexControl(ComplexControl cc,
+ const QStyleOptionComplex *opt,
+ const QPoint &pt,
+ const QWidget *widget) const
+{
+ const ItemType itemType = qtControl(cc);
+ AndroidControlsHash::const_iterator it = itemType != QC_UnknownType
+ ? m_androidControlsHash.find(itemType)
+ : m_androidControlsHash.end();
+ if (it != m_androidControlsHash.end()) {
+ switch (cc) {
+ case CC_Slider:
+ if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ QRect r = it.value()->subControlRect(slider, SC_SliderHandle, widget);
+ if (r.isValid() && r.contains(pt)) {
+ return SC_SliderHandle;
+ } else {
+ r = it.value()->subControlRect(slider, SC_SliderGroove, widget);
+ if (r.isValid() && r.contains(pt))
+ return SC_SliderGroove;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return QFusionStyle::hitTestComplexControl(cc, opt, pt, widget);
+}
+
+QRect QAndroidStyle::subControlRect(ComplexControl cc,
+ const QStyleOptionComplex *opt,
+ SubControl sc,
+ const QWidget *widget) const
+{
+ const ItemType itemType = qtControl(cc);
+ AndroidControlsHash::const_iterator it = itemType != QC_UnknownType
+ ? m_androidControlsHash.find(itemType)
+ : m_androidControlsHash.end();
+ if (it != m_androidControlsHash.end())
+ return it.value()->subControlRect(opt, sc, widget);
+ QRect rect = opt->rect;
+ switch (cc) {
+ case CC_GroupBox: {
+ if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) {
+ QSize textSize = opt->fontMetrics.boundingRect(groupBox->text).size() + QSize(2, 2);
+ QSize checkBoxSize = checkBoxControl->size(opt);
+ int indicatorWidth = checkBoxSize.width();
+ int indicatorHeight = checkBoxSize.height();
+ QRect checkBoxRect;
+ if (opt->subControls & QStyle::SC_GroupBoxCheckBox) {
+ checkBoxRect.setWidth(indicatorWidth);
+ checkBoxRect.setHeight(indicatorHeight);
+ }
+ checkBoxRect.moveLeft(1);
+ QRect textRect = checkBoxRect;
+ textRect.setSize(textSize);
+ if (opt->subControls & QStyle::SC_GroupBoxCheckBox)
+ textRect.translate(indicatorWidth + 5, (indicatorHeight - textSize.height()) / 2);
+ if (sc == SC_GroupBoxFrame) {
+ rect = opt->rect.adjusted(0, 0, 0, 0);
+ rect.translate(0, textRect.height() / 2);
+ rect.setHeight(rect.height() - textRect.height() / 2);
+ } else if (sc == SC_GroupBoxContents) {
+ QRect frameRect = opt->rect.adjusted(0, 0, 0, -groupBox->lineWidth);
+ int margin = 3;
+ int leftMarginExtension = 0;
+ int topMargin = qMax(pixelMetric(PM_ExclusiveIndicatorHeight), opt->fontMetrics.height()) + groupBox->lineWidth;
+ frameRect.adjust(leftMarginExtension + margin, margin + topMargin, -margin, -margin - groupBox->lineWidth);
+ frameRect.translate(0, textRect.height() / 2);
+ rect = frameRect;
+ rect.setHeight(rect.height() - textRect.height() / 2);
+ } else if (sc == SC_GroupBoxCheckBox) {
+ rect = checkBoxRect;
+ } else if (sc == SC_GroupBoxLabel) {
+ rect = textRect;
+ }
+ return visualRect(opt->direction, opt->rect, rect);
+ }
+
+ return rect;
+ }
+
+ default:
+ break;
+ }
+
+
+ return QFusionStyle::subControlRect(cc, opt, sc, widget);
+}
+
+int QAndroidStyle::pixelMetric(PixelMetric metric, const QStyleOption *option,
+ const QWidget *widget) const
+{
+ switch (metric) {
+ case PM_ButtonMargin:
+ case PM_FocusFrameVMargin:
+ case PM_FocusFrameHMargin:
+ case PM_ComboBoxFrameWidth:
+ case PM_SpinBoxFrameWidth:
+ case PM_ScrollBarExtent:
+ return 0;
+ case PM_IndicatorWidth:
+ return checkBoxControl->size(option).width();
+ case PM_IndicatorHeight:
+ return checkBoxControl->size(option).height();
+ default:
+ return QFusionStyle::pixelMetric(metric, option, widget);
+ }
+
+}
+
+QSize QAndroidStyle::sizeFromContents(ContentsType ct,
+ const QStyleOption *opt,
+ const QSize &contentsSize,
+ const QWidget *w) const
+{
+ QSize sz = QFusionStyle::sizeFromContents(ct, opt, contentsSize, w);
+ if (ct == CT_HeaderSection) {
+ if (const QStyleOptionHeader *hdr = qstyleoption_cast<const QStyleOptionHeader *>(opt)) {
+ bool nullIcon = hdr->icon.isNull();
+ int margin = pixelMetric(QStyle::PM_HeaderMargin, hdr, w);
+ int iconSize = nullIcon ? 0 : checkBoxControl->size(opt).width();
+ QSize txt;
+/*
+ * These next 4 lines are a bad hack to fix a bug in case a QStyleSheet is applied at QApplication level.
+ * In that case, even if the stylesheet does not refer to headers, the header font is changed to application
+ * font, which is wrong. Even worst, hdr->fontMetrics(...) does not report the proper size.
+ */
+ if (qApp->styleSheet().isEmpty())
+ txt = hdr->fontMetrics.size(0, hdr->text);
+ else
+ txt = qApp->fontMetrics().size(0, hdr->text);
+
+ sz.setHeight(margin + qMax(iconSize, txt.height()) + margin);
+ sz.setWidth((nullIcon ? 0 : margin) + iconSize
+ + (hdr->text.isNull() ? 0 : margin) + txt.width() + margin);
+ if (hdr->sortIndicator != QStyleOptionHeader::None) {
+ int margin = pixelMetric(QStyle::PM_HeaderMargin, hdr, w);
+ if (hdr->orientation == Qt::Horizontal)
+ sz.rwidth() += sz.height() + margin;
+ else
+ sz.rheight() += sz.width() + margin;
+ }
+ return sz;
+ }
+ }
+ const ItemType itemType = qtControl(ct);
+ AndroidControlsHash::const_iterator it = itemType != QC_UnknownType
+ ? m_androidControlsHash.find(itemType)
+ : m_androidControlsHash.end();
+ if (it != m_androidControlsHash.end())
+ return it.value()->sizeFromContents(opt, sz, w);
+ if (ct == CT_GroupBox) {
+ if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) {
+ QSize textSize = opt->fontMetrics.boundingRect(groupBox->text).size() + QSize(2, 2);
+ QSize checkBoxSize = checkBoxControl->size(opt);
+ int indicatorWidth = checkBoxSize.width();
+ int indicatorHeight = checkBoxSize.height();
+ QRect checkBoxRect;
+ if (groupBox->subControls & QStyle::SC_GroupBoxCheckBox) {
+ checkBoxRect.setWidth(indicatorWidth);
+ checkBoxRect.setHeight(indicatorHeight);
+ }
+ checkBoxRect.moveLeft(1);
+ QRect textRect = checkBoxRect;
+ textRect.setSize(textSize);
+ if (groupBox->subControls & QStyle::SC_GroupBoxCheckBox)
+ textRect.translate(indicatorWidth + 5, (indicatorHeight - textSize.height()) / 2);
+ QRect u = textRect.united(checkBoxRect);
+ sz = QSize(sz.width(), sz.height() + u.height());
+ }
+ }
+ return sz;
+}
+
+QPixmap QAndroidStyle::standardPixmap(StandardPixmap standardPixmap,
+ const QStyleOption *opt,
+ const QWidget *widget) const
+{
+ return QFusionStyle::standardPixmap(standardPixmap, opt, widget);
+}
+
+QPixmap QAndroidStyle::generatedIconPixmap(QIcon::Mode iconMode,
+ const QPixmap &pixmap,
+ const QStyleOption *opt) const
+{
+ return QFusionStyle::generatedIconPixmap(iconMode, pixmap, opt);
+}
+
+int QAndroidStyle::styleHint(QStyle::StyleHint hint, const QStyleOption *option, const QWidget *widget, QStyleHintReturn *returnData) const
+{
+ switch (hint) {
+ case SH_Slider_AbsoluteSetButtons:
+ return Qt::LeftButton;
+
+ case SH_Slider_PageSetButtons:
+ return 0;
+
+ case SH_RequestSoftwareInputPanel:
+ return RSIP_OnMouseClick;
+
+ default:
+ return QFusionStyle::styleHint(hint, option, widget, returnData);
+ }
+}
+
+QPalette QAndroidStyle::standardPalette() const
+{
+ return m_standardPalette;
+}
+
+void QAndroidStyle::polish(QWidget *widget)
+{
+ widget->setAttribute(Qt::WA_StyledBackground, true);
+}
+
+void QAndroidStyle::unpolish(QWidget *widget)
+{
+ widget->setAttribute(Qt::WA_StyledBackground, false);
+}
+
+QAndroidStyle::AndroidDrawable::AndroidDrawable(const QVariantMap &drawable,
+ QAndroidStyle::ItemType itemType)
+{
+ initPadding(drawable);
+ m_itemType = itemType;
+}
+
+QAndroidStyle::AndroidDrawable::~AndroidDrawable()
+{
+}
+
+void QAndroidStyle::AndroidDrawable::initPadding(const QVariantMap &drawable)
+{
+ QVariantMap::const_iterator it = drawable.find(QLatin1String("padding"));
+ if (it != drawable.end())
+ m_padding = extractMargins(it.value().toMap());
+}
+
+const QMargins &QAndroidStyle::AndroidDrawable::padding() const
+{
+ return m_padding;
+}
+
+QSize QAndroidStyle::AndroidDrawable::size() const
+{
+ if (type() == Image || type() == NinePatch)
+ return static_cast<const QAndroidStyle::AndroidImageDrawable *>(this)->size();
+
+ return QSize();
+}
+
+QAndroidStyle::AndroidDrawable * QAndroidStyle::AndroidDrawable::fromMap(const QVariantMap &drawable,
+ ItemType itemType)
+{
+ const QString type = drawable.value(QLatin1String("type")).toString();
+ if (type == QLatin1String("image"))
+ return new QAndroidStyle::AndroidImageDrawable(drawable, itemType);
+ if (type == QLatin1String("9patch"))
+ return new QAndroidStyle::Android9PatchDrawable(drawable, itemType);
+ if (type == QLatin1String("stateslist"))
+ return new QAndroidStyle::AndroidStateDrawable(drawable, itemType);
+ if (type == QLatin1String("layer"))
+ return new QAndroidStyle::AndroidLayerDrawable(drawable, itemType);
+ if (type == QLatin1String("gradient"))
+ return new QAndroidStyle::AndroidGradientDrawable(drawable, itemType);
+ if (type == QLatin1String("clipDrawable"))
+ return new QAndroidStyle::AndroidClipDrawable(drawable, itemType);
+ if (type == QLatin1String("color"))
+ return new QAndroidStyle::AndroidColorDrawable(drawable, itemType);
+ return 0;
+}
+
+QMargins QAndroidStyle::AndroidDrawable::extractMargins(const QVariantMap &value)
+{
+ QMargins m;
+ m.setLeft(value.value(QLatin1String("left")).toInt());
+ m.setRight(value.value(QLatin1String("right")).toInt());
+ m.setTop(value.value(QLatin1String("top")).toInt());
+ m.setBottom(value.value(QLatin1String("bottom")).toInt());
+ return m;
+}
+
+void QAndroidStyle::AndroidDrawable::setPaddingLeftToSizeWidth()
+{
+ QSize sz = size();
+ if (m_padding.isNull() && !sz.isNull())
+ m_padding.setLeft(sz.width());
+}
+
+
+QAndroidStyle::AndroidImageDrawable::AndroidImageDrawable(const QVariantMap &drawable,
+ QAndroidStyle::ItemType itemType)
+ : AndroidDrawable(drawable, itemType)
+{
+ m_filePath = drawable.value(QLatin1String("path")).toString();
+ m_size.setHeight(drawable.value(QLatin1String("height")).toInt());
+ m_size.setWidth(drawable.value(QLatin1String("width")).toInt());
+}
+
+QAndroidStyle::AndroidDrawableType QAndroidStyle::AndroidImageDrawable::type() const
+{
+ return QAndroidStyle::Image;
+}
+
+void QAndroidStyle::AndroidImageDrawable::draw(QPainter *painter, const QStyleOption *opt) const
+{
+ if (m_hashKey.isEmpty())
+ m_hashKey = QFileInfo(m_filePath).fileName();
+
+ QPixmap pm;
+ if (!QPixmapCache::find(m_hashKey, &pm)) {
+ pm.load(m_filePath);
+ QPixmapCache::insert(m_hashKey, pm);
+ }
+
+ painter->drawPixmap(opt->rect.x(), opt->rect.y() + (opt->rect.height() - pm.height()) / 2, pm);
+}
+
+QSize QAndroidStyle::AndroidImageDrawable::size() const
+{
+ return m_size;
+}
+
+QAndroidStyle::AndroidColorDrawable::AndroidColorDrawable(const QVariantMap &drawable,
+ ItemType itemType)
+ : AndroidDrawable(drawable, itemType)
+{
+ m_color.setRgba(QRgb(drawable.value(QLatin1String("color")).toInt()));
+}
+
+QAndroidStyle::AndroidDrawableType QAndroidStyle::AndroidColorDrawable::type() const
+{
+ return QAndroidStyle::Color;
+}
+
+void QAndroidStyle::AndroidColorDrawable::draw(QPainter *painter, const QStyleOption *opt) const
+{
+ painter->fillRect(opt->rect, m_color);
+}
+
+QAndroidStyle::Android9PatchDrawable::Android9PatchDrawable(const QVariantMap &drawable,
+ QAndroidStyle::ItemType itemType)
+ : AndroidImageDrawable(drawable.value(QLatin1String("drawable")).toMap(), itemType)
+{
+ initPadding(drawable);
+ QVariantMap chunk = drawable.value(QLatin1String("chunkInfo")).toMap();
+ extractIntArray(chunk.value(QLatin1String("xdivs")).toList(), m_chunkData.xDivs);
+ extractIntArray(chunk.value(QLatin1String("ydivs")).toList(), m_chunkData.yDivs);
+ extractIntArray(chunk.value(QLatin1String("colors")).toList(), m_chunkData.colors);
+}
+
+QAndroidStyle::AndroidDrawableType QAndroidStyle::Android9PatchDrawable::type() const
+{
+ return QAndroidStyle::NinePatch;
+}
+
+int QAndroidStyle::Android9PatchDrawable::calculateStretch(int boundsLimit,
+ int startingPoint,
+ int srcSpace,
+ int numStrechyPixelsRemaining,
+ int numFixedPixelsRemaining)
+{
+ int spaceRemaining = boundsLimit - startingPoint;
+ int stretchySpaceRemaining = spaceRemaining - numFixedPixelsRemaining;
+ return (float(srcSpace) * stretchySpaceRemaining / numStrechyPixelsRemaining + .5);
+}
+
+void QAndroidStyle::Android9PatchDrawable::extractIntArray(const QVariantList &values,
+ QVector<int> & array)
+{
+ for (const QVariant &value : values)
+ array << value.toInt();
+}
+
+
+void QAndroidStyle::Android9PatchDrawable::draw(QPainter *painter, const QStyleOption *opt) const
+{
+ if (m_hashKey.isEmpty())
+ m_hashKey = QFileInfo(m_filePath).fileName();
+
+ QPixmap pixmap;
+ if (!QPixmapCache::find(m_hashKey, &pixmap)) {
+ pixmap.load(m_filePath);
+ QPixmapCache::insert(m_hashKey, pixmap);
+ }
+
+ const QRect &bounds = opt->rect;
+
+ // shamelessly stolen from Android's sources (NinepatchImpl.cpp) and adapted for Qt
+ const int pixmapWidth = pixmap.width();
+ const int pixmapHeight = pixmap.height();
+
+ if (bounds.isNull() || !pixmapWidth || !pixmapHeight)
+ return;
+
+ QPainter::RenderHints savedHints = painter->renderHints();
+
+ // The patchs doesn't need smooth transform !
+ painter->setRenderHints(QPainter::SmoothPixmapTransform, false);
+
+ QRectF dst;
+ QRectF src;
+
+ const qint32 x0 = m_chunkData.xDivs[0];
+ const qint32 y0 = m_chunkData.yDivs[0];
+ const quint8 numXDivs = m_chunkData.xDivs.size();
+ const quint8 numYDivs = m_chunkData.yDivs.size();
+ int i;
+ int j;
+ int colorIndex = 0;
+ quint32 color;
+ bool xIsStretchable;
+ const bool initialXIsStretchable = (x0 == 0);
+ bool yIsStretchable = (y0 == 0);
+ const int bitmapWidth = pixmap.width();
+ const int bitmapHeight = pixmap.height();
+
+ int *dstRights = static_cast<int *>(alloca((numXDivs + 1) * sizeof(int)));
+ bool dstRightsHaveBeenCached = false;
+
+ int numStretchyXPixelsRemaining = 0;
+ for (i = 0; i < numXDivs; i += 2)
+ numStretchyXPixelsRemaining += m_chunkData.xDivs[i + 1] - m_chunkData.xDivs[i];
+
+ int numFixedXPixelsRemaining = bitmapWidth - numStretchyXPixelsRemaining;
+ int numStretchyYPixelsRemaining = 0;
+ for (i = 0; i < numYDivs; i += 2)
+ numStretchyYPixelsRemaining += m_chunkData.yDivs[i + 1] - m_chunkData.yDivs[i];
+
+ int numFixedYPixelsRemaining = bitmapHeight - numStretchyYPixelsRemaining;
+ src.setTop(0);
+ dst.setTop(bounds.top());
+ // The first row always starts with the top being at y=0 and the bottom
+ // being either yDivs[1] (if yDivs[0]=0) of yDivs[0]. In the former case
+ // the first row is stretchable along the Y axis, otherwise it is fixed.
+ // The last row always ends with the bottom being bitmap.height and the top
+ // being either yDivs[numYDivs-2] (if yDivs[numYDivs-1]=bitmap.height) or
+ // yDivs[numYDivs-1]. In the former case the last row is stretchable along
+ // the Y axis, otherwise it is fixed.
+ //
+ // The first and last columns are similarly treated with respect to the X
+ // axis.
+ //
+ // The above is to help explain some of the special casing that goes on the
+ // code below.
+
+ // The initial yDiv and whether the first row is considered stretchable or
+ // not depends on whether yDiv[0] was zero or not.
+ for (j = yIsStretchable ? 1 : 0;
+ j <= numYDivs && src.top() < bitmapHeight;
+ j++, yIsStretchable = !yIsStretchable) {
+ src.setLeft(0);
+ dst.setLeft(bounds.left());
+ if (j == numYDivs) {
+ src.setBottom(bitmapHeight);
+ dst.setBottom(bounds.bottom());
+ } else {
+ src.setBottom(m_chunkData.yDivs[j]);
+ const int srcYSize = src.bottom() - src.top();
+ if (yIsStretchable) {
+ dst.setBottom(dst.top() + calculateStretch(bounds.bottom(), dst.top(),
+ srcYSize,
+ numStretchyYPixelsRemaining,
+ numFixedYPixelsRemaining));
+ numStretchyYPixelsRemaining -= srcYSize;
+ } else {
+ dst.setBottom(dst.top() + srcYSize);
+ numFixedYPixelsRemaining -= srcYSize;
+ }
+ }
+
+ xIsStretchable = initialXIsStretchable;
+ // The initial xDiv and whether the first column is considered
+ // stretchable or not depends on whether xDiv[0] was zero or not.
+ for (i = xIsStretchable ? 1 : 0;
+ i <= numXDivs && src.left() < bitmapWidth;
+ i++, xIsStretchable = !xIsStretchable) {
+ color = m_chunkData.colors[colorIndex++];
+ if (color != TRANSPARENT_COLOR)
+ color = NO_COLOR;
+ if (i == numXDivs) {
+ src.setRight(bitmapWidth);
+ dst.setRight(bounds.right());
+ } else {
+ src.setRight(m_chunkData.xDivs[i]);
+ if (dstRightsHaveBeenCached) {
+ dst.setRight(dstRights[i]);
+ } else {
+ const int srcXSize = src.right() - src.left();
+ if (xIsStretchable) {
+ dst.setRight(dst.left() + calculateStretch(bounds.right(), dst.left(),
+ srcXSize,
+ numStretchyXPixelsRemaining,
+ numFixedXPixelsRemaining));
+ numStretchyXPixelsRemaining -= srcXSize;
+ } else {
+ dst.setRight(dst.left() + srcXSize);
+ numFixedXPixelsRemaining -= srcXSize;
+ }
+ dstRights[i] = dst.right();
+ }
+ }
+ // If this horizontal patch is too small to be displayed, leave
+ // the destination left edge where it is and go on to the next patch
+ // in the source.
+ if (src.left() >= src.right()) {
+ src.setLeft(src.right());
+ continue;
+ }
+ // Make sure that we actually have room to draw any bits
+ if (dst.right() <= dst.left() || dst.bottom() <= dst.top()) {
+ goto nextDiv;
+ }
+ // If this patch is transparent, skip and don't draw.
+ if (color == TRANSPARENT_COLOR)
+ goto nextDiv;
+ if (color != NO_COLOR)
+ painter->fillRect(dst, QRgb(color));
+ else
+ painter->drawPixmap(dst, pixmap, src);
+nextDiv:
+ src.setLeft(src.right());
+ dst.setLeft(dst.right());
+ }
+ src.setTop(src.bottom());
+ dst.setTop(dst.bottom());
+ dstRightsHaveBeenCached = true;
+ }
+ painter->setRenderHints(savedHints);
+}
+
+QAndroidStyle::AndroidGradientDrawable::AndroidGradientDrawable(const QVariantMap &drawable,
+ QAndroidStyle::ItemType itemType)
+ : AndroidDrawable(drawable, itemType), m_orientation(TOP_BOTTOM)
+{
+ m_radius = drawable.value(QLatin1String("radius")).toInt();
+ if (m_radius < 0)
+ m_radius = 0;
+
+ QVariantList colors = drawable.value(QLatin1String("colors")).toList();
+ QVariantList positions = drawable.value(QLatin1String("positions")).toList();
+ int min = colors.size() < positions.size() ? colors.size() : positions.size();
+ for (int i = 0; i < min; i++)
+ m_gradient.setColorAt(positions.at(i).toDouble(), QRgb(colors.at(i).toInt()));
+
+ QByteArray orientation = drawable.value(QLatin1String("orientation")).toByteArray();
+ if (orientation == "TOP_BOTTOM") // draw the gradient from the top to the bottom
+ m_orientation = TOP_BOTTOM;
+ else if (orientation == "TR_BL") // draw the gradient from the top-right to the bottom-left
+ m_orientation = TR_BL;
+ else if (orientation == "RIGHT_LEFT") // draw the gradient from the right to the left
+ m_orientation = RIGHT_LEFT;
+ else if (orientation == "BR_TL") // draw the gradient from the bottom-right to the top-left
+ m_orientation = BR_TL;
+ else if (orientation == "BOTTOM_TOP") // draw the gradient from the bottom to the top
+ m_orientation = BOTTOM_TOP;
+ else if (orientation == "BL_TR") // draw the gradient from the bottom-left to the top-right
+ m_orientation = BL_TR;
+ else if (orientation == "LEFT_RIGHT") // draw the gradient from the left to the right
+ m_orientation = LEFT_RIGHT;
+ else if (orientation == "TL_BR") // draw the gradient from the top-left to the bottom-right
+ m_orientation = TL_BR;
+ else
+ qWarning("AndroidGradientDrawable: unknown orientation");
+}
+
+QAndroidStyle::AndroidDrawableType QAndroidStyle::AndroidGradientDrawable::type() const
+{
+ return QAndroidStyle::Gradient;
+}
+
+void QAndroidStyle::AndroidGradientDrawable::draw(QPainter *painter, const QStyleOption *opt) const
+{
+ const int width = opt->rect.width();
+ const int height = opt->rect.height();
+ switch (m_orientation) {
+ case TOP_BOTTOM:
+ // draw the gradient from the top to the bottom
+ m_gradient.setStart(width / 2, 0);
+ m_gradient.setFinalStop(width / 2, height);
+ break;
+ case TR_BL:
+ // draw the gradient from the top-right to the bottom-left
+ m_gradient.setStart(width, 0);
+ m_gradient.setFinalStop(0, height);
+ break;
+ case RIGHT_LEFT:
+ // draw the gradient from the right to the left
+ m_gradient.setStart(width, height / 2);
+ m_gradient.setFinalStop(0, height / 2);
+ break;
+ case BR_TL:
+ // draw the gradient from the bottom-right to the top-left
+ m_gradient.setStart(width, height);
+ m_gradient.setFinalStop(0, 0);
+ break;
+ case BOTTOM_TOP:
+ // draw the gradient from the bottom to the top
+ m_gradient.setStart(width / 2, height);
+ m_gradient.setFinalStop(width / 2, 0);
+ break;
+ case BL_TR:
+ // draw the gradient from the bottom-left to the top-right
+ m_gradient.setStart(0, height);
+ m_gradient.setFinalStop(width, 0);
+ break;
+ case LEFT_RIGHT:
+ // draw the gradient from the left to the right
+ m_gradient.setStart(0, height / 2);
+ m_gradient.setFinalStop(width, height / 2);
+ break;
+ case TL_BR:
+ // draw the gradient from the top-left to the bottom-right
+ m_gradient.setStart(0, 0);
+ m_gradient.setFinalStop(width, height);
+ break;
+ }
+
+ const QBrush &oldBrush = painter->brush();
+ const QPen oldPen = painter->pen();
+ painter->setPen(Qt::NoPen);
+ painter->setBrush(m_gradient);
+ painter->drawRoundedRect(opt->rect, m_radius, m_radius);
+ painter->setBrush(oldBrush);
+ painter->setPen(oldPen);
+}
+
+QSize QAndroidStyle::AndroidGradientDrawable::size() const
+{
+ return QSize(m_radius * 2, m_radius * 2);
+}
+
+QAndroidStyle::AndroidClipDrawable::AndroidClipDrawable(const QVariantMap &drawable,
+ QAndroidStyle::ItemType itemType)
+ : AndroidDrawable(drawable, itemType)
+{
+ m_drawable = fromMap(drawable.value(QLatin1String("drawable")).toMap(), itemType);
+ m_factor = 0;
+ m_orientation = Qt::Horizontal;
+}
+
+QAndroidStyle::AndroidClipDrawable::~AndroidClipDrawable()
+{
+ delete m_drawable;
+}
+
+QAndroidStyle::AndroidDrawableType QAndroidStyle::AndroidClipDrawable::type() const
+{
+ return QAndroidStyle::Clip;
+}
+
+void QAndroidStyle::AndroidClipDrawable::setFactor(double factor, Qt::Orientation orientation)
+{
+ m_factor = factor;
+ m_orientation = orientation;
+}
+
+void QAndroidStyle::AndroidClipDrawable::draw(QPainter *painter, const QStyleOption *opt) const
+{
+ QStyleOption copy(*opt);
+ if (m_orientation == Qt::Horizontal)
+ copy.rect.setWidth(copy.rect.width() * m_factor);
+ else
+ copy.rect.setHeight(copy.rect.height() * m_factor);
+
+ m_drawable->draw(painter, &copy);
+}
+
+QAndroidStyle::AndroidStateDrawable::AndroidStateDrawable(const QVariantMap &drawable,
+ QAndroidStyle::ItemType itemType)
+ : AndroidDrawable(drawable, itemType)
+{
+ const QVariantList states = drawable.value(QLatin1String("stateslist")).toList();
+ for (const QVariant &stateVariant : states) {
+ QVariantMap state = stateVariant.toMap();
+ const int s = extractState(state.value(QLatin1String("states")).toMap());
+ if (-1 == s)
+ continue;
+ const AndroidDrawable *ad = fromMap(state.value(QLatin1String("drawable")).toMap(), itemType);
+ if (!ad)
+ continue;
+ StateType item;
+ item.first = s;
+ item.second = ad;
+ m_states<<item;
+ }
+}
+
+QAndroidStyle::AndroidStateDrawable::~AndroidStateDrawable()
+{
+ for (const StateType &type : qAsConst(m_states))
+ delete type.second;
+}
+
+QAndroidStyle::AndroidDrawableType QAndroidStyle::AndroidStateDrawable::type() const
+{
+ return QAndroidStyle::State;
+}
+
+void QAndroidStyle::AndroidStateDrawable::draw(QPainter *painter, const QStyleOption *opt) const
+{
+ const AndroidDrawable *drawable = bestAndroidStateMatch(opt);
+ if (drawable)
+ drawable->draw(painter, opt);
+}
+QSize QAndroidStyle::AndroidStateDrawable::sizeImage(const QStyleOption *opt) const
+{
+ QSize s;
+ const AndroidDrawable *drawable = bestAndroidStateMatch(opt);
+ if (drawable)
+ s = drawable->size();
+ return s;
+}
+
+const QAndroidStyle::AndroidDrawable * QAndroidStyle::AndroidStateDrawable::bestAndroidStateMatch(const QStyleOption *opt) const
+{
+ const AndroidDrawable *bestMatch = 0;
+ if (!opt) {
+ if (m_states.size())
+ return m_states[0].second;
+ return bestMatch;
+ }
+
+ uint bestCost = 0xffff;
+ for (const StateType & state : m_states) {
+ if (int(opt->state) == state.first)
+ return state.second;
+ uint cost = 1;
+
+ int difference = int(opt->state^state.first);
+
+ if (difference & QStyle::State_Active)
+ cost <<= 1;
+
+ if (difference & QStyle::State_Enabled)
+ cost <<= 1;
+
+ if (difference & QStyle::State_Raised)
+ cost <<= 1;
+
+ if (difference & QStyle::State_Sunken)
+ cost <<= 1;
+
+ if (difference & QStyle::State_Off)
+ cost <<= 1;
+
+ if (difference & QStyle::State_On)
+ cost <<= 1;
+
+ if (difference & QStyle::State_HasFocus)
+ cost <<= 1;
+
+ if (difference & QStyle::State_Selected)
+ cost <<= 1;
+
+ if (cost < bestCost) {
+ bestCost = cost;
+ bestMatch = state.second;
+ }
+ }
+ return bestMatch;
+}
+
+int QAndroidStyle::AndroidStateDrawable::extractState(const QVariantMap &value)
+{
+ QStyle::State state = QStyle::State_Enabled | QStyle::State_Active;;
+ for (auto it = value.cbegin(), end = value.cend(); it != end; ++it) {
+ const QString &key = it.key();
+ bool val = it.value().toString() == QLatin1String("true");
+ if (key == QLatin1String("enabled")) {
+ state.setFlag(QStyle::State_Enabled, val);
+ continue;
+ }
+
+ if (key == QLatin1String("window_focused")) {
+ state.setFlag(QStyle::State_Active, val);
+ continue;
+ }
+
+ if (key == QLatin1String("focused")) {
+ state.setFlag(QStyle::State_HasFocus, val);
+ continue;
+ }
+
+ if (key == QLatin1String("checked")) {
+ state |= val ? QStyle::State_On : QStyle::State_Off;
+ continue;
+ }
+
+ if (key == QLatin1String("pressed")) {
+ state |= val ? QStyle::State_Sunken : QStyle::State_Raised;
+ continue;
+ }
+
+ if (key == QLatin1String("selected")) {
+ state.setFlag(QStyle::State_Selected, val);
+ continue;
+ }
+
+ if (key == QLatin1String("active")) {
+ state.setFlag(QStyle::State_Active, val);
+ continue;
+ }
+
+ if (key == QLatin1String("multiline"))
+ return 0;
+
+ if (key == QLatin1String("background") && val)
+ return -1;
+ }
+ return static_cast<int>(state);
+}
+
+void QAndroidStyle::AndroidStateDrawable::setPaddingLeftToSizeWidth()
+{
+ for (const StateType &type : qAsConst(m_states))
+ const_cast<AndroidDrawable *>(type.second)->setPaddingLeftToSizeWidth();
+}
+
+QAndroidStyle::AndroidLayerDrawable::AndroidLayerDrawable(const QVariantMap &drawable,
+ QAndroidStyle::ItemType itemType)
+ : AndroidDrawable(drawable, itemType)
+{
+ m_id = 0;
+ m_factor = 1;
+ m_orientation = Qt::Horizontal;
+ const QVariantList layers = drawable.value(QLatin1String("layers")).toList();
+ for (const QVariant &layer : layers) {
+ QVariantMap layerMap = layer.toMap();
+ AndroidDrawable *ad = fromMap(layerMap, itemType);
+ if (ad) {
+ LayerType l;
+ l.second = ad;
+ l.first = layerMap.value(QLatin1String("id")).toInt();
+ m_layers << l;
+ }
+ }
+}
+
+QAndroidStyle::AndroidLayerDrawable::~AndroidLayerDrawable()
+{
+ for (const LayerType &layer : qAsConst(m_layers))
+ delete layer.second;
+}
+
+QAndroidStyle::AndroidDrawableType QAndroidStyle::AndroidLayerDrawable::type() const
+{
+ return QAndroidStyle::Layer;
+}
+
+void QAndroidStyle::AndroidLayerDrawable::setFactor(int id, double factor, Qt::Orientation orientation)
+{
+ m_id = id;
+ m_factor = factor;
+ m_orientation = orientation;
+}
+
+void QAndroidStyle::AndroidLayerDrawable::draw(QPainter *painter, const QStyleOption *opt) const
+{
+ for (const LayerType &layer : m_layers) {
+ if (layer.first == m_id) {
+ QStyleOption copy(*opt);
+ if (m_orientation == Qt::Horizontal)
+ copy.rect.setWidth(copy.rect.width() * m_factor);
+ else
+ copy.rect.setHeight(copy.rect.height() * m_factor);
+ layer.second->draw(painter, &copy);
+ } else {
+ layer.second->draw(painter, opt);
+ }
+ }
+}
+
+QAndroidStyle::AndroidDrawable *QAndroidStyle::AndroidLayerDrawable::layer(int id) const
+{
+ for (const LayerType &layer : m_layers)
+ if (layer.first == id)
+ return layer.second;
+ return 0;
+}
+
+QSize QAndroidStyle::AndroidLayerDrawable::size() const
+{
+ QSize sz;
+ for (const LayerType &layer : m_layers)
+ sz = sz.expandedTo(layer.second->size());
+ return sz;
+}
+
+QAndroidStyle::AndroidControl::AndroidControl(const QVariantMap &control,
+ QAndroidStyle::ItemType itemType)
+{
+ QVariantMap::const_iterator it = control.find(QLatin1String("View_background"));
+ if (it != control.end())
+ m_background = AndroidDrawable::fromMap(it.value().toMap(), itemType);
+ else
+ m_background = 0;
+
+ it = control.find(QLatin1String("View_minWidth"));
+ if (it != control.end())
+ m_minSize.setWidth(it.value().toInt());
+
+ it = control.find(QLatin1String("View_minHeight"));
+ if (it != control.end())
+ m_minSize.setHeight(it.value().toInt());
+
+ it = control.find(QLatin1String("View_maxWidth"));
+ if (it != control.end())
+ m_maxSize.setWidth(it.value().toInt());
+
+ it = control.find(QLatin1String("View_maxHeight"));
+ if (it != control.end())
+ m_maxSize.setHeight(it.value().toInt());
+}
+
+QAndroidStyle::AndroidControl::~AndroidControl()
+{
+ delete m_background;
+}
+
+void QAndroidStyle::AndroidControl::drawControl(const QStyleOption *opt, QPainter *p, const QWidget * /* w */)
+{
+ if (m_background) {
+ m_background->draw(p, opt);
+ } else {
+ if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
+ if ((frame->state & State_Sunken) || (frame->state & State_Raised)) {
+ qDrawShadePanel(p, frame->rect, frame->palette, frame->state & State_Sunken,
+ frame->lineWidth);
+ } else {
+ qDrawPlainRect(p, frame->rect, frame->palette.foreground().color(), frame->lineWidth);
+ }
+ } else {
+ if (const QStyleOptionFocusRect *fropt = qstyleoption_cast<const QStyleOptionFocusRect *>(opt)) {
+ QColor bg = fropt->backgroundColor;
+ QPen oldPen = p->pen();
+ if (bg.isValid()) {
+ int h, s, v;
+ bg.getHsv(&h, &s, &v);
+ if (v >= 128)
+ p->setPen(Qt::black);
+ else
+ p->setPen(Qt::white);
+ } else {
+ p->setPen(opt->palette.foreground().color());
+ }
+ QRect focusRect = opt->rect.adjusted(1, 1, -1, -1);
+ p->drawRect(focusRect.adjusted(0, 0, -1, -1)); //draw pen inclusive
+ p->setPen(oldPen);
+ } else {
+ p->fillRect(opt->rect, opt->palette.brush(QPalette::Background));
+ }
+ }
+ }
+}
+
+QRect QAndroidStyle::AndroidControl::subElementRect(QStyle::SubElement /* subElement */,
+ const QStyleOption *option,
+ const QWidget * /* widget */) const
+{
+ if (const AndroidDrawable *drawable = backgroundDrawable()) {
+ if (drawable->type() == State)
+ drawable = static_cast<const AndroidStateDrawable *>(backgroundDrawable())->bestAndroidStateMatch(option);
+
+ const QMargins &padding = drawable->padding();
+
+ QRect r = option->rect.adjusted(padding.left(), padding.top(),
+ -padding.right(), -padding.bottom());
+
+ if (r.width() < m_minSize.width())
+ r.setWidth(m_minSize.width());
+
+ if (r.height() < m_minSize.height())
+ r.setHeight(m_minSize.height());
+
+ return visualRect(option->direction, option->rect, r);
+ }
+ return option->rect;
+}
+
+QRect QAndroidStyle::AndroidControl::subControlRect(const QStyleOptionComplex *option,
+ QStyle::SubControl /*sc*/,
+ const QWidget *widget) const
+{
+ return subElementRect(QStyle::SE_CustomBase, option, widget);
+}
+
+QSize QAndroidStyle::AndroidControl::sizeFromContents(const QStyleOption *opt,
+ const QSize &contentsSize,
+ const QWidget * /* w */) const
+{
+ QSize sz;
+ if (const AndroidDrawable *drawable = backgroundDrawable()) {
+
+ if (drawable->type() == State)
+ drawable = static_cast<const AndroidStateDrawable*>(backgroundDrawable())->bestAndroidStateMatch(opt);
+ const QMargins &padding = drawable->padding();
+ sz.setWidth(padding.left() + padding.right());
+ sz.setHeight(padding.top() + padding.bottom());
+ if (sz.isEmpty())
+ sz = drawable->size();
+ }
+ sz += contentsSize;
+ if (contentsSize.height() < opt->fontMetrics.height())
+ sz.setHeight(sz.height() + (opt->fontMetrics.height() - contentsSize.height()));
+ if (sz.height() < m_minSize.height())
+ sz.setHeight(m_minSize.height());
+ if (sz.width() < m_minSize.width())
+ sz.setWidth(m_minSize.width());
+ return sz;
+}
+
+QMargins QAndroidStyle::AndroidControl::padding()
+{
+ if (const AndroidDrawable *drawable = m_background) {
+ if (drawable->type() == State)
+ drawable = static_cast<const AndroidStateDrawable *>(m_background)->bestAndroidStateMatch(0);
+ return drawable->padding();
+ }
+ return QMargins();
+}
+
+QSize QAndroidStyle::AndroidControl::size(const QStyleOption *option)
+{
+ if (const AndroidDrawable *drawable = backgroundDrawable()) {
+ if (drawable->type() == State)
+ drawable = static_cast<const AndroidStateDrawable *>(backgroundDrawable())->bestAndroidStateMatch(option);
+ return drawable->size();
+ }
+ return QSize();
+}
+
+const QAndroidStyle::AndroidDrawable *QAndroidStyle::AndroidControl::backgroundDrawable() const
+{
+ return m_background;
+}
+
+QAndroidStyle::AndroidCompoundButtonControl::AndroidCompoundButtonControl(const QVariantMap &control,
+ ItemType itemType)
+ : AndroidControl(control, itemType)
+{
+ QVariantMap::const_iterator it = control.find(QLatin1String("CompoundButton_button"));
+ if (it != control.end()) {
+ m_button = AndroidDrawable::fromMap(it.value().toMap(), itemType);
+ const_cast<AndroidDrawable *>(m_button)->setPaddingLeftToSizeWidth();
+ } else {
+ m_button = 0;
+ }
+}
+
+QAndroidStyle::AndroidCompoundButtonControl::~AndroidCompoundButtonControl()
+{
+ delete m_button;
+}
+
+void QAndroidStyle::AndroidCompoundButtonControl::drawControl(const QStyleOption *opt,
+ QPainter *p,
+ const QWidget *w)
+{
+ AndroidControl::drawControl(opt, p, w);
+ if (m_button)
+ m_button->draw(p, opt);
+}
+
+QMargins QAndroidStyle::AndroidCompoundButtonControl::padding()
+{
+ if (m_button)
+ return m_button->padding();
+ return AndroidControl::padding();
+}
+
+QSize QAndroidStyle::AndroidCompoundButtonControl::size(const QStyleOption *option)
+{
+ if (m_button) {
+ if (m_button->type() == State)
+ return static_cast<const AndroidStateDrawable *>(m_button)->bestAndroidStateMatch(option)->size();
+ return m_button->size();
+ }
+ return AndroidControl::size(option);
+}
+
+const QAndroidStyle::AndroidDrawable * QAndroidStyle::AndroidCompoundButtonControl::backgroundDrawable() const
+{
+ return m_background ? m_background : m_button;
+}
+
+QAndroidStyle::AndroidProgressBarControl::AndroidProgressBarControl(const QVariantMap &control,
+ ItemType itemType)
+ : AndroidControl(control, itemType)
+{
+ QVariantMap::const_iterator it = control.find(QLatin1String("ProgressBar_indeterminateDrawable"));
+ if (it != control.end())
+ m_indeterminateDrawable = AndroidDrawable::fromMap(it.value().toMap(), itemType);
+ else
+ m_indeterminateDrawable = 0;
+
+ it = control.find(QLatin1String("ProgressBar_progressDrawable"));
+ if (it != control.end())
+ m_progressDrawable = AndroidDrawable::fromMap(it.value().toMap(), itemType);
+ else
+ m_progressDrawable = 0;
+
+ it = control.find(QLatin1String("ProgressBar_progress_id"));
+ if (it != control.end())
+ m_progressId = it.value().toInt();
+
+ it = control.find(QLatin1String("ProgressBar_secondaryProgress_id"));
+ if (it != control.end())
+ m_secondaryProgress_id = it.value().toInt();
+
+ it = control.find(QLatin1String("ProgressBar_minWidth"));
+ if (it != control.end())
+ m_minSize.setWidth(it.value().toInt());
+
+ it = control.find(QLatin1String("ProgressBar_minHeight"));
+ if (it != control.end())
+ m_minSize.setHeight(it.value().toInt());
+
+ it = control.find(QLatin1String("ProgressBar_maxWidth"));
+ if (it != control.end())
+ m_maxSize.setWidth(it.value().toInt());
+
+ it = control.find(QLatin1String("ProgressBar_maxHeight"));
+ if (it != control.end())
+ m_maxSize.setHeight(it.value().toInt());
+}
+
+QAndroidStyle::AndroidProgressBarControl::~AndroidProgressBarControl()
+{
+ delete m_progressDrawable;
+ delete m_indeterminateDrawable;
+}
+
+void QAndroidStyle::AndroidProgressBarControl::drawControl(const QStyleOption *option, QPainter *p, const QWidget * /* w */)
+{
+ if (!m_progressDrawable)
+ return;
+
+ if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) {
+ if (m_progressDrawable->type() == QAndroidStyle::Layer) {
+ const double fraction = double(qint64(pb->progress) - pb->minimum) / (qint64(pb->maximum) - pb->minimum);
+ QAndroidStyle::AndroidDrawable *clipDrawable = static_cast<QAndroidStyle::AndroidLayerDrawable *>(m_progressDrawable)->layer(m_progressId);
+ if (clipDrawable->type() == QAndroidStyle::Clip)
+ static_cast<AndroidClipDrawable *>(clipDrawable)->setFactor(fraction, pb->orientation);
+ else
+ static_cast<AndroidLayerDrawable *>(m_progressDrawable)->setFactor(m_progressId, fraction, pb->orientation);
+ }
+ m_progressDrawable->draw(p, option);
+ }
+}
+
+QRect QAndroidStyle::AndroidProgressBarControl::subElementRect(QStyle::SubElement subElement,
+ const QStyleOption *option,
+ const QWidget *widget) const
+{
+ if (const QStyleOptionProgressBar *progressBarOption =
+ qstyleoption_cast<const QStyleOptionProgressBar *>(option)) {
+ const bool horizontal = progressBarOption->orientation == Qt::Vertical;
+ if (!m_background)
+ return option->rect;
+
+ QMargins padding = m_background->padding();
+ QRect p(padding.left(), padding.top(), padding.right() - padding.left(), padding.bottom() - padding.top());
+ padding = m_indeterminateDrawable->padding();
+ p |= QRect(padding.left(), padding.top(), padding.right() - padding.left(), padding.bottom() - padding.top());
+ padding = m_progressDrawable->padding();
+ p |= QRect(padding.left(), padding.top(), padding.right() - padding.left(), padding.bottom() - padding.top());
+ QRect r = option->rect.adjusted(p.left(), p.top(), -p.right(), -p.bottom());
+
+ if (horizontal) {
+ if (r.height()<m_minSize.height())
+ r.setHeight(m_minSize.height());
+
+ if (r.height()>m_maxSize.height())
+ r.setHeight(m_maxSize.height());
+ } else {
+ if (r.width()<m_minSize.width())
+ r.setWidth(m_minSize.width());
+
+ if (r.width()>m_maxSize.width())
+ r.setWidth(m_maxSize.width());
+ }
+ return visualRect(option->direction, option->rect, r);
+ }
+ return AndroidControl::subElementRect(subElement, option, widget);
+}
+
+QSize QAndroidStyle::AndroidProgressBarControl::sizeFromContents(const QStyleOption *opt,
+ const QSize &contentsSize,
+ const QWidget * /* w */) const
+{
+ QSize sz(contentsSize);
+ if (sz.height() < m_minSize.height())
+ sz.setHeight(m_minSize.height());
+ if (sz.width() < m_minSize.width())
+ sz.setWidth(m_minSize.width());
+
+ if (const QStyleOptionProgressBar *progressBarOption =
+ qstyleoption_cast<const QStyleOptionProgressBar *>(opt)) {
+ if (progressBarOption->orientation == Qt::Vertical) {
+ if (sz.height() > m_maxSize.height())
+ sz.setHeight(m_maxSize.height());
+ } else {
+ if (sz.width() > m_maxSize.width())
+ sz.setWidth(m_maxSize.width());
+ }
+ }
+ return contentsSize;
+}
+
+QAndroidStyle::AndroidSeekBarControl::AndroidSeekBarControl(const QVariantMap &control,
+ ItemType itemType)
+ : AndroidProgressBarControl(control, itemType)
+{
+ QVariantMap::const_iterator it = control.find(QLatin1String("SeekBar_thumb"));
+ if (it != control.end())
+ m_seekBarThumb = AndroidDrawable::fromMap(it.value().toMap(), itemType);
+ else
+ m_seekBarThumb = 0;
+}
+
+QAndroidStyle::AndroidSeekBarControl::~AndroidSeekBarControl()
+{
+ delete m_seekBarThumb;
+}
+
+void QAndroidStyle::AndroidSeekBarControl::drawControl(const QStyleOption *option,
+ QPainter *p,
+ const QWidget * /* w */)
+{
+ if (!m_seekBarThumb || !m_progressDrawable)
+ return;
+
+ if (const QStyleOptionSlider *styleOption =
+ qstyleoption_cast<const QStyleOptionSlider *>(option)) {
+ double factor = double(styleOption->sliderPosition - styleOption->minimum)
+ / double(styleOption->maximum - styleOption->minimum);
+
+ // Android does not have a vertical slider. To support the vertical orientation, we rotate
+ // the painter and pretend that we are horizontal.
+ if (styleOption->orientation == Qt::Vertical)
+ factor = 1 - factor;
+
+ if (m_progressDrawable->type() == QAndroidStyle::Layer) {
+ QAndroidStyle::AndroidDrawable *clipDrawable = static_cast<QAndroidStyle::AndroidLayerDrawable *>(m_progressDrawable)->layer(m_progressId);
+ if (clipDrawable->type() == QAndroidStyle::Clip)
+ static_cast<QAndroidStyle::AndroidClipDrawable *>(clipDrawable)->setFactor(factor, Qt::Horizontal);
+ else
+ static_cast<QAndroidStyle::AndroidLayerDrawable *>(m_progressDrawable)->setFactor(m_progressId, factor, Qt::Horizontal);
+ }
+ const AndroidDrawable *drawable = m_seekBarThumb;
+ if (drawable->type() == State)
+ drawable = static_cast<const QAndroidStyle::AndroidStateDrawable *>(m_seekBarThumb)->bestAndroidStateMatch(option);
+ QStyleOption copy(*option);
+
+ p->save();
+
+ if (styleOption->orientation == Qt::Vertical) {
+ // rotate the painter, and transform the rectangle to match
+ p->rotate(90);
+ copy.rect = QRect(copy.rect.y(), copy.rect.x() - copy.rect.width(), copy.rect.height(), copy.rect.width());
+ }
+
+ copy.rect.setHeight(m_progressDrawable->size().height());
+ copy.rect.setWidth(copy.rect.width() - drawable->size().width());
+ const int yTranslate = abs(drawable->size().height() - copy.rect.height()) / 2;
+ copy.rect.translate(drawable->size().width() / 2, yTranslate);
+ m_progressDrawable->draw(p, &copy);
+ int pos = copy.rect.width() * factor - drawable->size().width() / 2;
+ copy.rect.translate(pos, -yTranslate);
+ copy.rect.setSize(drawable->size());
+ m_seekBarThumb->draw(p, &copy);
+
+ p->restore();
+ }
+}
+
+QSize QAndroidStyle::AndroidSeekBarControl::sizeFromContents(const QStyleOption *opt,
+ const QSize &contentsSize,
+ const QWidget *w) const
+{
+ QSize sz = AndroidProgressBarControl::sizeFromContents(opt, contentsSize, w);
+ if (!m_seekBarThumb)
+ return sz;
+ const AndroidDrawable *drawable = m_seekBarThumb;
+ if (drawable->type() == State)
+ drawable = static_cast<const QAndroidStyle::AndroidStateDrawable *>(m_seekBarThumb)->bestAndroidStateMatch(opt);
+ return sz.expandedTo(drawable->size());
+}
+
+QRect QAndroidStyle::AndroidSeekBarControl::subControlRect(const QStyleOptionComplex *option,
+ SubControl sc,
+ const QWidget * /* widget */) const
+{
+ const QStyleOptionSlider *styleOption =
+ qstyleoption_cast<const QStyleOptionSlider *>(option);
+
+ if (m_seekBarThumb && sc == SC_SliderHandle && styleOption) {
+ const AndroidDrawable *drawable = m_seekBarThumb;
+ if (drawable->type() == State)
+ drawable = static_cast<const QAndroidStyle::AndroidStateDrawable *>(m_seekBarThumb)->bestAndroidStateMatch(option);
+
+ QRect r(option->rect);
+ double factor = double(styleOption->sliderPosition - styleOption->minimum)
+ / (styleOption->maximum - styleOption->minimum);
+ if (styleOption->orientation == Qt::Vertical) {
+ int pos = option->rect.height() * (1 - factor) - double(drawable->size().height() / 2);
+ r.setY(r.y() + pos);
+ } else {
+ int pos = option->rect.width() * factor - double(drawable->size().width() / 2);
+ r.setX(r.x() + pos);
+ }
+ r.setSize(drawable->size());
+ return r;
+ }
+ return option->rect;
+}
+
+QAndroidStyle::AndroidSpinnerControl::AndroidSpinnerControl(const QVariantMap &control,
+ QAndroidStyle::ItemType itemType)
+ : AndroidControl(control, itemType)
+{}
+
+QRect QAndroidStyle::AndroidSpinnerControl::subControlRect(const QStyleOptionComplex *option,
+ SubControl sc,
+ const QWidget *widget) const
+{
+ if (sc == QStyle::SC_ComboBoxListBoxPopup)
+ return option->rect;
+ if (sc == QStyle::SC_ComboBoxArrow) {
+ const QRect editField = subControlRect(option, QStyle::SC_ComboBoxEditField, widget);
+ return QRect(editField.topRight(), QSize(option->rect.width() - editField.width(), option->rect.height()));
+ }
+ return AndroidControl::subControlRect(option, sc, widget);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/styles/android/qandroidstyle_p.h b/src/plugins/styles/android/qandroidstyle_p.h
new file mode 100644
index 0000000000..3faa08afb9
--- /dev/null
+++ b/src/plugins/styles/android/qandroidstyle_p.h
@@ -0,0 +1,391 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 BogDan Vatra <bogdan@kde.org>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWidgets module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QANDROIDSTYLE_P_H
+#define QANDROIDSTYLE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qstylefactory.cpp. This header may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtWidgets/private/qtwidgetsglobal_p.h>
+#include <QtWidgets/private/qfusionstyle_p.h>
+#include <QtCore/QList>
+#include <QtCore/QMargins>
+#include <QtCore/QHash>
+#include <QtCore/QVariantMap>
+
+QT_BEGIN_NAMESPACE
+
+class Q_WIDGETS_EXPORT QAndroidStyle : public QFusionStyle
+{
+ Q_OBJECT
+
+public:
+ enum ItemType
+ {
+ QC_UnknownType = -1,
+ QC_View,
+ QC_GroupBox,
+ QC_Button,
+ QC_Checkbox,
+ QC_RadioButton,
+ QC_Slider,
+ QC_Switch,
+ QC_EditText,
+ QC_Combobox,
+ QC_BusyIndicator,
+ QC_ProgressBar,
+ QC_Tab,
+ QC_TabButton,
+ QC_RatingIndicator,
+ QC_SearchBox,
+ QC_CustomControl=0xf00,
+ QC_ControlMask=0xfff
+ };
+
+ struct Android9PatchChunk
+ {
+ QVector<int> xDivs;
+ QVector<int> yDivs;
+ QVector<int> colors;
+ };
+
+ struct AndroidItemStateInfo
+ {
+ AndroidItemStateInfo():state(0){}
+ int state;
+ QByteArray filePath;
+ QByteArray hashKey;
+ Android9PatchChunk chunkData;
+ QSize size;
+ QMargins padding;
+ };
+
+ enum AndroidDrawableType
+ {
+ Color,
+ Image,
+ Clip,
+ NinePatch,
+ Gradient,
+ State,
+ Layer
+ };
+
+ class AndroidDrawable
+ {
+ public:
+ AndroidDrawable(const QVariantMap &drawable, ItemType itemType);
+ virtual ~AndroidDrawable();
+ virtual void initPadding(const QVariantMap &drawable);
+ virtual AndroidDrawableType type() const = 0;
+ virtual void draw(QPainter *painter,const QStyleOption *opt) const = 0;
+ const QMargins &padding() const;
+ virtual QSize size() const;
+ static AndroidDrawable *fromMap(const QVariantMap &drawable, ItemType itemType);
+ static QMargins extractMargins(const QVariantMap &value);
+ virtual void setPaddingLeftToSizeWidth();
+ protected:
+ ItemType m_itemType;
+ QMargins m_padding;
+ };
+
+ class AndroidColorDrawable: public AndroidDrawable
+ {
+ public:
+ AndroidColorDrawable(const QVariantMap &drawable, ItemType itemType);
+ virtual AndroidDrawableType type() const;
+ virtual void draw(QPainter *painter,const QStyleOption *opt) const;
+
+ protected:
+ QColor m_color;
+ };
+
+ class AndroidImageDrawable: public AndroidDrawable
+ {
+ public:
+ AndroidImageDrawable(const QVariantMap &drawable, ItemType itemType);
+ virtual AndroidDrawableType type() const;
+ virtual void draw(QPainter *painter,const QStyleOption *opt) const;
+ virtual QSize size() const;
+
+ protected:
+ QString m_filePath;
+ mutable QString m_hashKey;
+ QSize m_size;
+ };
+
+ class Android9PatchDrawable: public AndroidImageDrawable
+ {
+ public:
+ Android9PatchDrawable(const QVariantMap &drawable, ItemType itemType);
+ virtual AndroidDrawableType type() const;
+ virtual void draw(QPainter *painter, const QStyleOption *opt) const;
+ private:
+ static int calculateStretch(int boundsLimit, int startingPoint,
+ int srcSpace, int numStrechyPixelsRemaining,
+ int numFixedPixelsRemaining);
+ void extractIntArray(const QVariantList &values, QVector<int> &array);
+ private:
+ Android9PatchChunk m_chunkData;
+ };
+
+ class AndroidGradientDrawable: public AndroidDrawable
+ {
+ public:
+ enum GradientOrientation
+ {
+ TOP_BOTTOM,
+ TR_BL,
+ RIGHT_LEFT,
+ BR_TL,
+ BOTTOM_TOP,
+ BL_TR,
+ LEFT_RIGHT,
+ TL_BR
+ };
+
+ public:
+ AndroidGradientDrawable(const QVariantMap &drawable, ItemType itemType);
+ virtual AndroidDrawableType type() const;
+ virtual void draw(QPainter *painter, const QStyleOption *opt) const;
+ QSize size() const;
+ private:
+ mutable QLinearGradient m_gradient;
+ GradientOrientation m_orientation;
+ int m_radius;
+ };
+
+ class AndroidClipDrawable: public AndroidDrawable
+ {
+ public:
+ AndroidClipDrawable(const QVariantMap &drawable, ItemType itemType);
+ ~AndroidClipDrawable();
+ virtual AndroidDrawableType type() const;
+ virtual void setFactor(double factor, Qt::Orientation orientation);
+ virtual void draw(QPainter *painter, const QStyleOption *opt) const;
+
+ private:
+ double m_factor;
+ Qt::Orientation m_orientation;
+ const AndroidDrawable *m_drawable;
+ };
+
+ class AndroidStateDrawable: public AndroidDrawable
+ {
+ public:
+ AndroidStateDrawable(const QVariantMap &drawable, ItemType itemType);
+ ~AndroidStateDrawable();
+ virtual AndroidDrawableType type() const;
+ virtual void draw(QPainter *painter, const QStyleOption *opt) const;
+ inline const AndroidDrawable *bestAndroidStateMatch(const QStyleOption *opt) const;
+ static int extractState(const QVariantMap &value);
+ virtual void setPaddingLeftToSizeWidth();
+ QSize sizeImage(const QStyleOption *opt) const;
+ private:
+ typedef QPair<int, const AndroidDrawable *> StateType;
+ QList<StateType> m_states;
+ };
+
+ class AndroidLayerDrawable: public AndroidDrawable
+ {
+ public:
+ AndroidLayerDrawable(const QVariantMap &drawable, QAndroidStyle::ItemType itemType);
+ ~AndroidLayerDrawable();
+ virtual AndroidDrawableType type() const;
+ virtual void setFactor(int id, double factor, Qt::Orientation orientation);
+ virtual void draw(QPainter *painter, const QStyleOption *opt) const;
+ AndroidDrawable *layer(int id) const;
+ QSize size() const;
+ private:
+ typedef QPair<int, AndroidDrawable *> LayerType;
+ QList<LayerType> m_layers;
+ int m_id;
+ double m_factor;
+ Qt::Orientation m_orientation;
+ };
+
+ class AndroidControl
+ {
+ public:
+ AndroidControl(const QVariantMap &control, ItemType itemType);
+ virtual ~AndroidControl();
+ virtual void drawControl(const QStyleOption *opt, QPainter *p, const QWidget *w);
+ virtual QRect subElementRect(SubElement subElement,
+ const QStyleOption *option,
+ const QWidget *widget = 0) const;
+ virtual QRect subControlRect(const QStyleOptionComplex *option,
+ SubControl sc,
+ const QWidget *widget = 0) const;
+ virtual QSize sizeFromContents(const QStyleOption *opt,
+ const QSize &contentsSize,
+ const QWidget *w) const;
+ virtual QMargins padding();
+ virtual QSize size(const QStyleOption *option);
+ protected:
+ virtual const AndroidDrawable * backgroundDrawable() const;
+ const AndroidDrawable *m_background;
+ QSize m_minSize;
+ QSize m_maxSize;
+ };
+
+ class AndroidCompoundButtonControl : public AndroidControl
+ {
+ public:
+ AndroidCompoundButtonControl(const QVariantMap &control, ItemType itemType);
+ virtual ~AndroidCompoundButtonControl();
+ virtual void drawControl(const QStyleOption *opt, QPainter *p, const QWidget *w);
+ virtual QMargins padding();
+ virtual QSize size(const QStyleOption *option);
+ protected:
+ virtual const AndroidDrawable * backgroundDrawable() const;
+ const AndroidDrawable *m_button;
+ };
+
+ class AndroidProgressBarControl : public AndroidControl
+ {
+ public:
+ AndroidProgressBarControl(const QVariantMap &control, ItemType itemType);
+ virtual ~AndroidProgressBarControl();
+ virtual void drawControl(const QStyleOption *option, QPainter *p, const QWidget *w);
+ virtual QRect subElementRect(SubElement subElement,
+ const QStyleOption *option,
+ const QWidget *widget = 0) const;
+
+ QSize sizeFromContents(const QStyleOption *opt,
+ const QSize &contentsSize,
+ const QWidget *w) const;
+ protected:
+ AndroidDrawable *m_progressDrawable;
+ AndroidDrawable *m_indeterminateDrawable;
+ int m_secondaryProgress_id;
+ int m_progressId;
+ };
+
+ class AndroidSeekBarControl : public AndroidProgressBarControl
+ {
+ public:
+ AndroidSeekBarControl(const QVariantMap &control, ItemType itemType);
+ virtual ~AndroidSeekBarControl();
+ virtual void drawControl(const QStyleOption *option, QPainter *p, const QWidget *w);
+ QSize sizeFromContents(const QStyleOption *opt,
+ const QSize &contentsSize, const QWidget *w) const;
+ QRect subControlRect(const QStyleOptionComplex *option, SubControl sc,
+ const QWidget *widget = 0) const;
+ private:
+ AndroidDrawable *m_seekBarThumb;
+ };
+
+ class AndroidSpinnerControl : public AndroidControl
+ {
+ public:
+ AndroidSpinnerControl(const QVariantMap &control, ItemType itemType);
+ virtual ~AndroidSpinnerControl(){}
+ virtual QRect subControlRect(const QStyleOptionComplex *option,
+ SubControl sc,
+ const QWidget *widget = 0) const;
+ };
+
+ typedef QList<AndroidItemStateInfo *> AndroidItemStateInfoList;
+
+public:
+ QAndroidStyle();
+ ~QAndroidStyle();
+
+ virtual void drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p,
+ const QWidget *w = 0) const;
+
+ virtual void drawControl(QStyle::ControlElement element, const QStyleOption *opt, QPainter *p,
+ const QWidget *w = 0) const;
+
+ virtual QRect subElementRect(SubElement subElement, const QStyleOption *option,
+ const QWidget *widget = 0) const;
+ virtual void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *p,
+ const QWidget *widget = 0) const;
+ virtual SubControl hitTestComplexControl(ComplexControl cc, const QStyleOptionComplex *opt,
+ const QPoint &pt, const QWidget *widget = 0) const;
+ virtual QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *opt,
+ SubControl sc, const QWidget *widget = 0) const;
+
+ virtual int pixelMetric(PixelMetric metric, const QStyleOption *option = 0,
+ const QWidget *widget = 0) const;
+
+ virtual QSize sizeFromContents(ContentsType ct, const QStyleOption *opt,
+ const QSize &contentsSize, const QWidget *w = 0) const;
+
+ virtual QPixmap standardPixmap(StandardPixmap standardPixmap, const QStyleOption *opt = 0,
+ const QWidget *widget = 0) const;
+
+ virtual QPixmap generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap,
+ const QStyleOption *opt) const;
+
+ int styleHint(StyleHint hint, const QStyleOption *option = 0, const QWidget *widget = 0,
+ QStyleHintReturn *returnData = 0) const;
+
+ virtual QPalette standardPalette() const;
+ void polish(QWidget *widget);
+ void unpolish(QWidget *widget);
+
+private:
+ Q_DISABLE_COPY(QAndroidStyle)
+ static ItemType qtControl(QStyle::ComplexControl control);
+ static ItemType qtControl(QStyle::ContentsType contentsType);
+ static ItemType qtControl(QStyle::ControlElement controlElement);
+ static ItemType qtControl(QStyle::PrimitiveElement primitiveElement);
+ static ItemType qtControl(QStyle::SubElement subElement);
+ static ItemType qtControl(const QString &android);
+
+private:
+ typedef QHash<int, AndroidControl *> AndroidControlsHash;
+ AndroidControlsHash m_androidControlsHash;
+ QPalette m_standardPalette;
+ AndroidCompoundButtonControl *checkBoxControl;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDSTYLE_P_H
diff --git a/src/plugins/styles/mac/mac.pro b/src/plugins/styles/mac/mac.pro
new file mode 100644
index 0000000000..f3c7c1c067
--- /dev/null
+++ b/src/plugins/styles/mac/mac.pro
@@ -0,0 +1,19 @@
+TARGET = qmacstyle
+
+QT += widgets-private
+
+SOURCES += \
+ main.mm \
+ qmacstyle_mac.mm
+
+HEADERS += \
+ qmacstyle_mac_p.h \
+ qmacstyle_mac_p_p.h
+
+LIBS_PRIVATE += -framework AppKit -framework ApplicationServices -framework Carbon
+
+DISTFILES += macstyle.json
+
+PLUGIN_TYPE = styles
+PLUGIN_CLASS_NAME = QMacStylePlugin
+load(qt_plugin)
diff --git a/src/plugins/styles/mac/macstyle.json b/src/plugins/styles/mac/macstyle.json
new file mode 100644
index 0000000000..5897815eec
--- /dev/null
+++ b/src/plugins/styles/mac/macstyle.json
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "macintosh" ]
+}
diff --git a/src/plugins/styles/mac/main.mm b/src/plugins/styles/mac/main.mm
new file mode 100644
index 0000000000..ae31bb95fb
--- /dev/null
+++ b/src/plugins/styles/mac/main.mm
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtWidgets/qstyleplugin.h>
+#include "qmacstyle_mac_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QMacStylePlugin : public QStylePlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QStyleFactoryInterface" FILE "macstyle.json")
+public:
+ QStyle *create(const QString &key);
+};
+
+QStyle *QMacStylePlugin::create(const QString &key)
+{
+ QMacAutoReleasePool pool;
+ if (key.compare(QLatin1String("macintosh"), Qt::CaseInsensitive) == 0)
+ return new QMacStyle();
+
+ return 0;
+}
+
+QT_END_NAMESPACE
+
+#include "main.moc"
+
diff --git a/src/plugins/styles/mac/qmacstyle.qdoc b/src/plugins/styles/mac/qmacstyle.qdoc
new file mode 100644
index 0000000000..30b3d7775f
--- /dev/null
+++ b/src/plugins/styles/mac/qmacstyle.qdoc
@@ -0,0 +1,207 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+/*!
+ \class QMacStyle
+ \brief The QMacStyle class provides a \macos style using the Apple Appearance Manager.
+
+ \ingroup appearance
+ \inmodule QtWidgets
+ \internal
+
+ This class is implemented as a wrapper to the HITheme
+ APIs, allowing applications to be styled according to the current
+ theme in use on \macos. This is done by having primitives
+ in QStyle implemented in terms of what \macos would normally theme.
+
+ \warning This style is only available on \macos because it relies on the
+ HITheme APIs.
+
+ There are additional issues that should be taken
+ into consideration to make an application compatible with the
+ \l{Apple Human Interface Guidelines}{Apple Human Interface Guidelines}. Some of these issues are outlined
+ below.
+
+ \list
+
+ \li Layout - The restrictions on window layout are such that some
+ aspects of layout that are style-dependent cannot be achieved
+ using QLayout. Changes are being considered (and feedback would be
+ appreciated) to make layouts QStyle-able. Some of the restrictions
+ involve horizontal and vertical widget alignment and widget size
+ (covered below).
+
+ \li Widget size - \macos allows widgets to have specific fixed sizes. Qt
+ does not fully implement this behavior so as to maintain cross-platform
+ compatibility. As a result some widgets sizes may be inappropriate (and
+ subsequently not rendered correctly by the HITheme APIs).The
+ QWidget::sizeHint() will return the appropriate size for many
+ managed widgets (widgets enumerated in \l QStyle::ContentsType).
+
+ \li Effects - QMacStyle uses HITheme for performing most of the drawing, but
+ also uses emulation in a few cases where HITheme does not provide the
+ required functionality (for example, tab bars on Panther, the toolbar
+ separator, etc). We tried to make the emulation as close to the original as
+ possible. Please report any issues you see in effects or non-standard
+ widgets.
+
+ \endlist
+
+ There are other issues that need to be considered in the feel of
+ your application (including the general color scheme to match the
+ Aqua colors). The Guidelines mentioned above will remain current
+ with new advances and design suggestions for \macos.
+
+ Note that the functions provided by QMacStyle are
+ reimplementations of QStyle functions; see QStyle for their
+ documentation.
+
+ \image qmacstyle.png
+ \sa QWindowsVistaStyle, QWindowsStyle, QFusionStyle
+*/
+
+
+/*!
+ \enum QStyleHelper::WidgetSizePolicy
+
+ \value SizeSmall
+ \value SizeLarge
+ \value SizeMini
+ \value SizeDefault
+*/
+
+/*! \fn QMacStyle::QMacStyle()
+ Constructs a QMacStyle object.
+*/
+
+/*! \fn QMacStyle::~QMacStyle()
+ Destructs a QMacStyle object.
+*/
+
+/*! \fn void QMacStyle::polish(QPalette &pal)
+ \reimp
+*/
+
+/*! \fn void QMacStyle::polish(QApplication *)
+ \reimp
+*/
+
+/*! \fn void QMacStyle::unpolish(QApplication *)
+ \reimp
+*/
+
+/*! \fn void QMacStyle::polish(QWidget* w)
+ \reimp
+*/
+
+/*! \fn void QMacStyle::unpolish(QWidget* w)
+ \reimp
+*/
+
+/*! \fn int QMacStyle::pixelMetric(QStyle::PixelMetric metric, const QStyleOption *opt, const QWidget *widget) const
+ \reimp
+*/
+
+/*! \fn QPalette QMacStyle::standardPalette() const
+ \reimp
+*/
+
+/*! \fn int QMacStyle::styleHint(QStyle::StyleHint sh, const QStyleOption *opt, const QWidget *w, QStyleHintReturn *hret) const
+ \reimp
+*/
+
+/*! \fn QPixmap QMacStyle::generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap, const QStyleOption *opt) const
+ \reimp
+*/
+
+/*! \fn QPixmap QMacStyle::standardPixmap(QStyle::StandardPixmap standardPixmap, const QStyleOption *opt, const QWidget *widget) const
+ \reimp
+*/
+
+/*! \fn void QStyleHelper::setWidgetSizePolicy(const QWidget *widget, QStyleHelper::WidgetSizePolicy policy)
+
+ \obsolete
+
+ Call QWidget::setAttribute() with Qt::WA_MacMiniSize, Qt::WA_MacSmallSize,
+ or Qt::WA_MacNormalSize instead.
+*/
+
+/*! \fn QStyleHelper::WidgetSizePolicy QStyleHelper::widgetSizePolicy(const QWidget *widget, const QStyleOption *opt)
+ \obsolete
+
+ Call QWidget::testAttribute() with Qt::WA_MacMiniSize, Qt::WA_MacSmallSize,
+ or Qt::WA_MacNormalSize instead.
+*/
+
+/*! \fn void QMacStyle::drawPrimitive(QStyle::PrimitiveElement pe, const QStyleOption *opt, QPainter *p, const QWidget *w) const
+
+ \reimp
+*/
+
+/*! \fn void QMacStyle::drawControl(QStyle::ControlElement ce, const QStyleOption *opt, QPainter *p, const QWidget *w) const
+
+ \reimp
+*/
+
+/*! \fn QRect QMacStyle::subElementRect(QStyle::SubElement sr, const QStyleOption *opt, const QWidget *widget) const
+
+ \reimp
+*/
+
+/*! \fn void QMacStyle::drawComplexControl(QStyle::ComplexControl cc, const QStyleOptionComplex *opt, QPainter *p, const QWidget *widget) const
+ \reimp
+*/
+
+/*! \fn QStyle::SubControl QMacStyle::hitTestComplexControl(QStyle::ComplexControl cc, const QStyleOptionComplex *opt, const QPoint &pt, const QWidget *widget) const
+ \reimp
+*/
+
+/*! \fn QRect QMacStyle::subControlRect(QStyle::ComplexControl cc, const QStyleOptionComplex *opt, QStyle::SubControl sc, const QWidget *widget) const
+ \reimp
+*/
+
+/*! \fn QSize QMacStyle::sizeFromContents(QStyle::ContentsType ct, const QStyleOption *opt, const QSize &csz, const QWidget *widget) const
+ \reimp
+*/
+
+/*! \fn void QMacStyle::drawItemText(QPainter *p, const QRect &r, int flags, const QPalette &pal, bool enabled, const QString &text, QPalette::ColorRole textRole) const
+ \reimp
+*/
+
+/*! \fn bool QMacStyle::event(QEvent *e)
+ \reimp
+*/
+
+/*! \fn QIcon QMacStyle::standardIcon(QStyle::StandardPixmap standardIcon, const QStyleOption *opt, const QWidget *widget) const
+ \reimp
+*/
+
+/*! \fn int QMacStyle::layoutSpacing(QSizePolicy::ControlType control1, QSizePolicy::ControlType control2, Qt::Orientation orientation, const QStyleOption *option, const QWidget *widget) const
+ \reimp
+*/
+
diff --git a/src/plugins/styles/mac/qmacstyle_mac.mm b/src/plugins/styles/mac/qmacstyle_mac.mm
new file mode 100644
index 0000000000..df2b637630
--- /dev/null
+++ b/src/plugins/styles/mac/qmacstyle_mac.mm
@@ -0,0 +1,6705 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWidgets module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+ Note: The qdoc comments for QMacStyle are contained in
+ .../doc/src/qstyles.qdoc.
+*/
+
+#include <AppKit/AppKit.h>
+#include <ApplicationServices/ApplicationServices.h>
+
+#include "qmacstyle_mac_p.h"
+#include "qmacstyle_mac_p_p.h"
+
+#define QMAC_QAQUASTYLE_SIZE_CONSTRAIN
+//#define DEBUG_SIZE_CONSTRAINT
+
+#include <private/qcore_mac_p.h>
+#if QT_CONFIG(tabbar)
+#include <private/qtabbar_p.h>
+#endif
+#include <private/qpainter_p.h>
+#include <qapplication.h>
+#include <qbitmap.h>
+#if QT_CONFIG(combobox)
+#include <private/qcombobox_p.h>
+#include <qcombobox.h>
+#endif
+#if QT_CONFIG(dialogbuttonbox)
+#include <qdialogbuttonbox.h>
+#endif
+#if QT_CONFIG(dockwidget)
+#include <qdockwidget.h>
+#endif
+#include <qevent.h>
+#include <qfocusframe.h>
+#include <qformlayout.h>
+#include <qgroupbox.h>
+#include <qhash.h>
+#include <qheaderview.h>
+#if QT_CONFIG(lineedit)
+#include <qlineedit.h>
+#endif
+#if QT_CONFIG(mainwindow)
+#include <qmainwindow.h>
+#endif
+#if QT_CONFIG(mdiarea)
+#include <qmdisubwindow.h>
+#endif
+#if QT_CONFIG(menubar)
+#include <qmenubar.h>
+#endif
+#include <qpaintdevice.h>
+#include <qpainter.h>
+#include <qpixmapcache.h>
+#include <qpointer.h>
+#if QT_CONFIG(progressbar)
+#include <qprogressbar.h>
+#endif
+#if QT_CONFIG(pushbutton)
+#include <qpushbutton.h>
+#endif
+#include <qradiobutton.h>
+#if QT_CONFIG(rubberband)
+#include <qrubberband.h>
+#endif
+#if QT_CONFIG(scrollbar)
+#include <qscrollbar.h>
+#endif
+#if QT_CONFIG(sizegrip)
+#include <qsizegrip.h>
+#endif
+#include <qstyleoption.h>
+#include <qtoolbar.h>
+#if QT_CONFIG(toolbutton)
+#include <qtoolbutton.h>
+#endif
+#if QT_CONFIG(treeview)
+#include <qtreeview.h>
+#endif
+#if QT_CONFIG(tableview)
+#include <qtableview.h>
+#endif
+#include <qoperatingsystemversion.h>
+#if QT_CONFIG(wizard)
+#include <qwizard.h>
+#endif
+#include <qdebug.h>
+#include <qlibrary.h>
+#if QT_CONFIG(datetimeedit)
+#include <qdatetimeedit.h>
+#endif
+#include <qmath.h>
+#include <QtWidgets/qgraphicsproxywidget.h>
+#if QT_CONFIG(graphicsview)
+#include <QtWidgets/qgraphicsview.h>
+#endif
+#include <QtCore/qvariant.h>
+#include <private/qstylehelper_p.h>
+#include <private/qstyleanimation_p.h>
+#include <qpa/qplatformfontdatabase.h>
+#include <qpa/qplatformtheme.h>
+#include <QtGui/private/qcoregraphics_p.h>
+
+QT_USE_NAMESPACE
+
+static QWindow *qt_getWindow(const QWidget *widget)
+{
+ return widget ? widget->window()->windowHandle() : 0;
+}
+
+@interface QT_MANGLE_NAMESPACE(NotificationReceiver) : NSObject {
+QMacStylePrivate *mPrivate;
+}
+- (id)initWithPrivate:(QMacStylePrivate *)priv;
+- (void)scrollBarStyleDidChange:(NSNotification *)notification;
+@end
+
+QT_NAMESPACE_ALIAS_OBJC_CLASS(NotificationReceiver);
+
+@implementation NotificationReceiver
+- (id)initWithPrivate:(QMacStylePrivate *)priv
+{
+ self = [super init];
+ mPrivate = priv;
+ return self;
+}
+
+- (void)scrollBarStyleDidChange:(NSNotification *)notification
+{
+ Q_UNUSED(notification);
+
+ // purge destroyed scroll bars:
+ QMacStylePrivate::scrollBars.removeAll(QPointer<QObject>());
+
+ QEvent event(QEvent::StyleChange);
+ for (const auto &o : QMacStylePrivate::scrollBars)
+ QCoreApplication::sendEvent(o, &event);
+}
+@end
+
+@interface QT_MANGLE_NAMESPACE(QIndeterminateProgressIndicator) : NSProgressIndicator
+
+@property (readonly, nonatomic) NSInteger animators;
+
+- (instancetype)init;
+
+- (void)startAnimation;
+- (void)stopAnimation;
+
+- (void)drawWithFrame:(CGRect)rect inView:(NSView *)view;
+
+@end
+
+QT_NAMESPACE_ALIAS_OBJC_CLASS(QIndeterminateProgressIndicator);
+
+@implementation QIndeterminateProgressIndicator
+
+- (instancetype)init
+{
+ if ((self = [super init])) {
+ _animators = 0;
+ self.indeterminate = YES;
+ self.usesThreadedAnimation = NO;
+ self.alphaValue = 0.0;
+ }
+
+ return self;
+}
+
+- (void)startAnimation
+{
+ if (_animators == 0) {
+ self.hidden = NO;
+ [super startAnimation:self];
+ }
+ ++_animators;
+}
+
+- (void)stopAnimation
+{
+ --_animators;
+ if (_animators == 0) {
+ [super stopAnimation:self];
+ self.hidden = YES;
+ [self removeFromSuperviewWithoutNeedingDisplay];
+ }
+}
+
+- (void)drawWithFrame:(CGRect)rect inView:(NSView *)view
+{
+ // The alphaValue change is not strictly necessary, but feels safer.
+ self.alphaValue = 1.0;
+ if (self.superview != view)
+ [view addSubview:self];
+ if (!CGRectEqualToRect(self.frame, rect))
+ self.frame = rect;
+ [self drawRect:rect];
+ self.alphaValue = 0.0;
+}
+
+@end
+
+@interface QT_MANGLE_NAMESPACE(QVerticalSplitView) : NSSplitView
+- (BOOL)isVertical;
+@end
+
+QT_NAMESPACE_ALIAS_OBJC_CLASS(QVerticalSplitView);
+
+@implementation QVerticalSplitView
+- (BOOL)isVertical
+{
+ return YES;
+}
+@end
+
+QT_BEGIN_NAMESPACE
+
+// The following constants are used for adjusting the size
+// of push buttons so that they are drawn inside their bounds.
+const int QMacStylePrivate::PushButtonLeftOffset = 6;
+const int QMacStylePrivate::PushButtonTopOffset = 4;
+const int QMacStylePrivate::PushButtonRightOffset = 12;
+const int QMacStylePrivate::PushButtonBottomOffset = 12;
+const int QMacStylePrivate::MiniButtonH = 26;
+const int QMacStylePrivate::SmallButtonH = 30;
+const int QMacStylePrivate::BevelButtonW = 50;
+const int QMacStylePrivate::BevelButtonH = 22;
+const int QMacStylePrivate::PushButtonContentPadding = 6;
+
+QVector<QPointer<QObject> > QMacStylePrivate::scrollBars;
+
+// Title bar gradient colors for Lion were determined by inspecting PSDs exported
+// using CoreUI's CoreThemeDocument; there is no public API to retrieve them
+
+static QLinearGradient titlebarGradientActive()
+{
+ static QLinearGradient gradient;
+ if (gradient == QLinearGradient()) {
+ gradient.setColorAt(0, QColor(235, 235, 235));
+ gradient.setColorAt(0.5, QColor(210, 210, 210));
+ gradient.setColorAt(0.75, QColor(195, 195, 195));
+ gradient.setColorAt(1, QColor(180, 180, 180));
+ }
+ return gradient;
+}
+
+static QLinearGradient titlebarGradientInactive()
+{
+ static QLinearGradient gradient;
+ if (gradient == QLinearGradient()) {
+ gradient.setColorAt(0, QColor(250, 250, 250));
+ gradient.setColorAt(1, QColor(225, 225, 225));
+ }
+ return gradient;
+}
+
+static const QColor titlebarSeparatorLineActive(111, 111, 111);
+static const QColor titlebarSeparatorLineInactive(131, 131, 131);
+
+// Gradient colors used for the dock widget title bar and
+// non-unifed tool bar bacground.
+static const QColor mainWindowGradientBegin(240, 240, 240);
+static const QColor mainWindowGradientEnd(200, 200, 200);
+
+static const int DisclosureOffset = 4;
+
+static const qreal titleBarIconTitleSpacing = 5;
+static const qreal titleBarTitleRightMargin = 12;
+static const qreal titleBarButtonSpacing = 8;
+
+// Tab bar colors
+// active: window is active
+// selected: tab is selected
+// hovered: tab is hovered
+static const QColor tabBarTabBackgroundActive(190, 190, 190);
+static const QColor tabBarTabBackgroundActiveHovered(178, 178, 178);
+static const QColor tabBarTabBackgroundActiveSelected(211, 211, 211);
+static const QColor tabBarTabBackground(227, 227, 227);
+static const QColor tabBarTabBackgroundSelected(246, 246, 246);
+static const QColor tabBarTabLineActive(160, 160, 160);
+static const QColor tabBarTabLineActiveHovered(150, 150, 150);
+static const QColor tabBarTabLine(210, 210, 210);
+static const QColor tabBarTabLineSelected(189, 189, 189);
+static const QColor tabBarCloseButtonBackgroundHovered(162, 162, 162);
+static const QColor tabBarCloseButtonBackgroundPressed(153, 153, 153);
+static const QColor tabBarCloseButtonBackgroundSelectedHovered(192, 192, 192);
+static const QColor tabBarCloseButtonBackgroundSelectedPressed(181, 181, 181);
+static const QColor tabBarCloseButtonCross(100, 100, 100);
+static const QColor tabBarCloseButtonCrossSelected(115, 115, 115);
+
+static const int closeButtonSize = 14;
+static const qreal closeButtonCornerRadius = 2.0;
+
+static const int headerSectionArrowHeight = 6;
+static const int headerSectionSeparatorInset = 2;
+
+#if QT_CONFIG(tabbar)
+static bool isVerticalTabs(const QTabBar::Shape shape) {
+ return (shape == QTabBar::RoundedEast
+ || shape == QTabBar::TriangularEast
+ || shape == QTabBar::RoundedWest
+ || shape == QTabBar::TriangularWest);
+}
+#endif
+
+static bool setupScroller(NSScroller *scroller, const QStyleOptionSlider *sb)
+{
+ const qreal length = sb->maximum - sb->minimum + sb->pageStep;
+ if (qFuzzyIsNull(length))
+ return false;
+ const qreal proportion = sb->pageStep / length;
+ const qreal range = qreal(sb->maximum - sb->minimum);
+ qreal value = range ? qreal(sb->sliderValue - sb->minimum) / range : 0;
+ if (sb->orientation == Qt::Horizontal && sb->direction == Qt::RightToLeft)
+ value = 1.0 - value;
+
+ scroller.frame = sb->rect.toCGRect();
+ scroller.floatValue = value;
+ scroller.knobProportion = proportion;
+ return true;
+}
+
+static bool setupSlider(NSSlider *slider, const QStyleOptionSlider *sl)
+{
+ if (sl->minimum >= sl->maximum)
+ return false;
+
+ slider.frame = sl->rect.toCGRect();
+ slider.minValue = sl->minimum;
+ slider.maxValue = sl->maximum;
+ slider.intValue = sl->sliderPosition;
+ slider.enabled = sl->state & QStyle::State_Enabled;
+ if (sl->tickPosition != QSlider::NoTicks) {
+ // Set numberOfTickMarks, but TicksBothSides will be treated differently
+ int interval = sl->tickInterval;
+ if (interval == 0) {
+ interval = sl->pageStep;
+ if (interval == 0)
+ interval = sl->singleStep;
+ if (interval == 0)
+ interval = 1; // return false?
+ }
+ slider.numberOfTickMarks = 1 + ((sl->maximum - sl->minimum) / interval);
+
+ const bool ticksAbove = sl->tickPosition == QSlider::TicksAbove;
+ if (sl->orientation == Qt::Horizontal)
+ slider.tickMarkPosition = ticksAbove ? NSTickMarkAbove : NSTickMarkBelow;
+ else
+ slider.tickMarkPosition = ticksAbove ? NSTickMarkLeft : NSTickMarkRight;
+ } else {
+ slider.numberOfTickMarks = 0;
+ }
+
+ return true;
+}
+
+static bool isInMacUnifiedToolbarArea(QWindow *window, int windowY)
+{
+ QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface();
+ QPlatformNativeInterface::NativeResourceForIntegrationFunction function =
+ nativeInterface->nativeResourceFunctionForIntegration("testContentBorderPosition");
+ if (!function)
+ return false; // Not Cocoa platform plugin.
+
+ typedef bool (*TestContentBorderPositionFunction)(QWindow *, int);
+ return (reinterpret_cast<TestContentBorderPositionFunction>(function))(window, windowY);
+}
+
+
+static void drawTabCloseButton(QPainter *p, bool hover, bool selected, bool pressed, bool documentMode)
+{
+ p->setRenderHints(QPainter::Antialiasing);
+ QRect rect(0, 0, closeButtonSize, closeButtonSize);
+ const int width = rect.width();
+ const int height = rect.height();
+
+ if (hover) {
+ // draw background circle
+ QColor background;
+ if (selected) {
+ if (documentMode)
+ background = pressed ? tabBarCloseButtonBackgroundSelectedPressed : tabBarCloseButtonBackgroundSelectedHovered;
+ else
+ background = QColor(255, 255, 255, pressed ? 150 : 100); // Translucent white
+ } else {
+ background = pressed ? tabBarCloseButtonBackgroundPressed : tabBarCloseButtonBackgroundHovered;
+ if (!documentMode)
+ background = background.lighter(pressed ? 135 : 140); // Lighter tab background, lighter color
+ }
+
+ p->setPen(Qt::transparent);
+ p->setBrush(background);
+ p->drawRoundedRect(rect, closeButtonCornerRadius, closeButtonCornerRadius);
+ }
+
+ // draw cross
+ const int margin = 3;
+ QPen crossPen;
+ crossPen.setColor(selected ? (documentMode ? tabBarCloseButtonCrossSelected : Qt::white) : tabBarCloseButtonCross);
+ crossPen.setWidthF(1.1);
+ crossPen.setCapStyle(Qt::FlatCap);
+ p->setPen(crossPen);
+ p->drawLine(margin, margin, width - margin, height - margin);
+ p->drawLine(margin, height - margin, width - margin, margin);
+}
+
+#if QT_CONFIG(tabbar)
+QRect rotateTabPainter(QPainter *p, QTabBar::Shape shape, QRect tabRect)
+{
+ if (isVerticalTabs(shape)) {
+ int newX, newY, newRot;
+ if (shape == QTabBar::RoundedEast
+ || shape == QTabBar::TriangularEast) {
+ newX = tabRect.width();
+ newY = tabRect.y();
+ newRot = 90;
+ } else {
+ newX = 0;
+ newY = tabRect.y() + tabRect.height();
+ newRot = -90;
+ }
+ tabRect.setRect(0, 0, tabRect.height(), tabRect.width());
+ QMatrix m;
+ m.translate(newX, newY);
+ m.rotate(newRot);
+ p->setMatrix(m, true);
+ }
+ return tabRect;
+}
+
+void drawTabShape(QPainter *p, const QStyleOptionTab *tabOpt, bool isUnified, int tabOverlap)
+{
+ QRect rect = tabOpt->rect;
+
+ switch (tabOpt->shape) {
+ case QTabBar::RoundedNorth:
+ case QTabBar::TriangularNorth:
+ case QTabBar::RoundedSouth:
+ case QTabBar::TriangularSouth:
+ rect.adjust(-tabOverlap, 0, 0, 0);
+ break;
+ case QTabBar::RoundedEast:
+ case QTabBar::TriangularEast:
+ case QTabBar::RoundedWest:
+ case QTabBar::TriangularWest:
+ rect.adjust(0, -tabOverlap, 0, 0);
+ break;
+ default:
+ break;
+ }
+
+ p->translate(rect.x(), rect.y());
+ rect.moveLeft(0);
+ rect.moveTop(0);
+ const QRect tabRect = rotateTabPainter(p, tabOpt->shape, rect);
+
+ const int width = tabRect.width();
+ const int height = tabRect.height();
+ const bool active = (tabOpt->state & QStyle::State_Active);
+ const bool selected = (tabOpt->state & QStyle::State_Selected);
+
+ const QRect bodyRect(1, 1, width - 2, height - 2);
+ const QRect topLineRect(1, 0, width - 2, 1);
+ const QRect bottomLineRect(1, height - 1, width - 2, 1);
+ if (selected) {
+ // fill body
+ if (tabOpt->documentMode && isUnified) {
+ p->save();
+ p->setCompositionMode(QPainter::CompositionMode_Source);
+ p->fillRect(tabRect, QColor(Qt::transparent));
+ p->restore();
+ } else if (active) {
+ p->fillRect(bodyRect, tabBarTabBackgroundActiveSelected);
+ // top line
+ p->fillRect(topLineRect, tabBarTabLineSelected);
+ } else {
+ p->fillRect(bodyRect, tabBarTabBackgroundSelected);
+ }
+ } else {
+ // when the mouse is over non selected tabs they get a new color
+ const bool hover = (tabOpt->state & QStyle::State_MouseOver);
+ if (hover) {
+ // fill body
+ p->fillRect(bodyRect, tabBarTabBackgroundActiveHovered);
+ // bottom line
+ p->fillRect(bottomLineRect, tabBarTabLineActiveHovered);
+ }
+ }
+
+ // separator lines between tabs
+ const QRect leftLineRect(0, 1, 1, height - 2);
+ const QRect rightLineRect(width - 1, 1, 1, height - 2);
+ const QColor separatorLineColor = active ? tabBarTabLineActive : tabBarTabLine;
+ p->fillRect(leftLineRect, separatorLineColor);
+ p->fillRect(rightLineRect, separatorLineColor);
+}
+
+void drawTabBase(QPainter *p, const QStyleOptionTabBarBase *tbb, const QWidget *w)
+{
+ QRect r = tbb->rect;
+ if (isVerticalTabs(tbb->shape)) {
+ r.setWidth(w->width());
+ } else {
+ r.setHeight(w->height());
+ }
+ const QRect tabRect = rotateTabPainter(p, tbb->shape, r);
+ const int width = tabRect.width();
+ const int height = tabRect.height();
+ const bool active = (tbb->state & QStyle::State_Active);
+
+ // fill body
+ const QRect bodyRect(0, 1, width, height - 1);
+ const QColor bodyColor = active ? tabBarTabBackgroundActive : tabBarTabBackground;
+ p->fillRect(bodyRect, bodyColor);
+
+ // top line
+ const QRect topLineRect(0, 0, width, 1);
+ const QColor topLineColor = active ? tabBarTabLineActive : tabBarTabLine;
+ p->fillRect(topLineRect, topLineColor);
+
+ // bottom line
+ const QRect bottomLineRect(0, height - 1, width, 1);
+ const QColor bottomLineColor = active ? tabBarTabLineActive : tabBarTabLine;
+ p->fillRect(bottomLineRect, bottomLineColor);
+}
+#endif
+
+static QStyleHelper::WidgetSizePolicy getControlSize(const QStyleOption *option, const QWidget *widget)
+{
+ const auto wsp = QStyleHelper::widgetSizePolicy(widget, option);
+ if (wsp == QStyleHelper::SizeDefault)
+ return QStyleHelper::SizeLarge;
+
+ return wsp;
+}
+
+#if QT_CONFIG(treeview)
+static inline bool isTreeView(const QWidget *widget)
+{
+ return (widget && widget->parentWidget() &&
+ (qobject_cast<const QTreeView *>(widget->parentWidget())
+ ));
+}
+#endif
+
+#if QT_CONFIG(tabbar)
+static inline ThemeTabDirection getTabDirection(QTabBar::Shape shape)
+{
+ ThemeTabDirection ttd;
+ switch (shape) {
+ case QTabBar::RoundedSouth:
+ case QTabBar::TriangularSouth:
+ ttd = kThemeTabSouth;
+ break;
+ case QTabBar::RoundedNorth:
+ case QTabBar::TriangularNorth:
+ ttd = kThemeTabNorth;
+ break;
+ case QTabBar::RoundedWest:
+ case QTabBar::TriangularWest:
+ ttd = kThemeTabWest;
+ break;
+ case QTabBar::RoundedEast:
+ case QTabBar::TriangularEast:
+ ttd = kThemeTabEast;
+ break;
+ }
+ return ttd;
+}
+#endif
+
+static QString qt_mac_removeMnemonics(const QString &original)
+{
+ QString returnText(original.size(), 0);
+ int finalDest = 0;
+ int currPos = 0;
+ int l = original.length();
+ while (l) {
+ if (original.at(currPos) == QLatin1Char('&')) {
+ ++currPos;
+ --l;
+ if (l == 0)
+ break;
+ } else if (original.at(currPos) == QLatin1Char('(') && l >= 4 &&
+ original.at(currPos + 1) == QLatin1Char('&') &&
+ original.at(currPos + 2) != QLatin1Char('&') &&
+ original.at(currPos + 3) == QLatin1Char(')')) {
+ /* remove mnemonics its format is "\s*(&X)" */
+ int n = 0;
+ while (finalDest > n && returnText.at(finalDest - n - 1).isSpace())
+ ++n;
+ finalDest -= n;
+ currPos += 4;
+ l -= 4;
+ continue;
+ }
+ returnText[finalDest] = original.at(currPos);
+ ++currPos;
+ ++finalDest;
+ --l;
+ }
+ returnText.truncate(finalDest);
+ return returnText;
+}
+
+bool qt_macWindowIsTextured(const QWidget *window)
+{
+ if (QWindow *w = window->windowHandle())
+ if (w->handle())
+ if (NSWindow *nswindow = static_cast<NSWindow*>(QGuiApplication::platformNativeInterface()->nativeResourceForWindow(QByteArrayLiteral("NSWindow"), w)))
+ return ([nswindow styleMask] & NSTexturedBackgroundWindowMask) ? true : false;
+ return false;
+}
+
+static bool qt_macWindowMainWindow(const QWidget *window)
+{
+ if (QWindow *w = window->windowHandle()) {
+ if (w->handle()) {
+ if (NSWindow *nswindow = static_cast<NSWindow*>(QGuiApplication::platformNativeInterface()->nativeResourceForWindow(QByteArrayLiteral("nswindow"), w))) {
+ return [nswindow isMainWindow];
+ }
+ }
+ }
+ return false;
+}
+
+/*****************************************************************************
+ QMacCGStyle globals
+ *****************************************************************************/
+const int qt_mac_hitheme_version = 0; //the HITheme version we speak
+const int macItemFrame = 2; // menu item frame width
+const int macItemHMargin = 3; // menu item hor text margin
+const int macRightBorder = 12; // right border on mac
+
+/*****************************************************************************
+ QMacCGStyle utility functions
+ *****************************************************************************/
+static inline int qt_mac_hitheme_tab_version()
+{
+ return 1;
+}
+
+enum QAquaMetric {
+ // Prepend kThemeMetric to get the HIToolBox constant.
+ // Represents the values already used in QMacStyle.
+ CheckBoxHeight = 0,
+ CheckBoxWidth,
+ EditTextFrameOutset,
+ FocusRectOutset,
+ HSliderHeight,
+ HSliderTickHeight,
+ LargeProgressBarThickness,
+ ListHeaderHeight,
+ MenuSeparatorHeight, // GetThemeMenuSeparatorHeight
+ MiniCheckBoxHeight,
+ MiniCheckBoxWidth,
+ MiniHSliderHeight,
+ MiniHSliderTickHeight,
+ MiniPopupButtonHeight,
+ MiniPushButtonHeight,
+ MiniRadioButtonHeight,
+ MiniRadioButtonWidth,
+ MiniVSliderTickWidth,
+ MiniVSliderWidth,
+ NormalProgressBarThickness,
+ PopupButtonHeight,
+ ProgressBarShadowOutset,
+ PushButtonHeight,
+ RadioButtonHeight,
+ RadioButtonWidth,
+ SeparatorSize,
+ SmallCheckBoxHeight,
+ SmallCheckBoxWidth,
+ SmallHSliderHeight,
+ SmallHSliderTickHeight,
+ SmallPopupButtonHeight,
+ SmallProgressBarShadowOutset,
+ SmallPushButtonHeight,
+ SmallRadioButtonHeight,
+ SmallRadioButtonWidth,
+ SmallVSliderTickWidth,
+ SmallVSliderWidth,
+ VSliderTickWidth,
+ VSliderWidth
+};
+
+static const int qt_mac_aqua_metrics[] = {
+ // Values as of macOS 10.12.4 and Xcode 8.3.1
+ 18 /* CheckBoxHeight */,
+ 18 /* CheckBoxWidth */,
+ 1 /* EditTextFrameOutset */,
+ 4 /* FocusRectOutset */,
+ 22 /* HSliderHeight */,
+ 5 /* HSliderTickHeight */,
+ 16 /* LargeProgressBarThickness */,
+ 17 /* ListHeaderHeight */,
+ 12 /* MenuSeparatorHeight, aka GetThemeMenuSeparatorHeight */,
+ 11 /* MiniCheckBoxHeight */,
+ 10 /* MiniCheckBoxWidth */,
+ 12 /* MiniHSliderHeight */,
+ 4 /* MiniHSliderTickHeight */,
+ 15 /* MiniPopupButtonHeight */,
+ 16 /* MiniPushButtonHeight */,
+ 11 /* MiniRadioButtonHeight */,
+ 10 /* MiniRadioButtonWidth */,
+ 4 /* MiniVSliderTickWidth */,
+ 12 /* MiniVSliderWidth */,
+ 12 /* NormalProgressBarThickness */,
+ 20 /* PopupButtonHeight */,
+ 4 /* ProgressBarShadowOutset */,
+ 20 /* PushButtonHeight */,
+ 18 /* RadioButtonHeight */,
+ 18 /* RadioButtonWidth */,
+ 1 /* SeparatorSize */,
+ 16 /* SmallCheckBoxHeight */,
+ 14 /* SmallCheckBoxWidth */,
+ 15 /* SmallHSliderHeight */,
+ 4 /* SmallHSliderTickHeight */,
+ 17 /* SmallPopupButtonHeight */,
+ 2 /* SmallProgressBarShadowOutset */,
+ 17 /* SmallPushButtonHeight */,
+ 15 /* SmallRadioButtonHeight */,
+ 14 /* SmallRadioButtonWidth */,
+ 4 /* SmallVSliderTickWidth */,
+ 15 /* SmallVSliderWidth */,
+ 5 /* VSliderTickWidth */,
+ 22 /* VSliderWidth */
+};
+
+static inline int qt_mac_aqua_get_metric(QAquaMetric m)
+{
+ return qt_mac_aqua_metrics[m];
+}
+
+static QSize qt_aqua_get_known_size(QStyle::ContentsType ct, const QWidget *widg, QSize szHint,
+ QStyleHelper::WidgetSizePolicy sz)
+{
+ QSize ret(-1, -1);
+ if (sz != QStyleHelper::SizeSmall && sz != QStyleHelper::SizeLarge && sz != QStyleHelper::SizeMini) {
+ qDebug("Not sure how to return this...");
+ return ret;
+ }
+ if ((widg && widg->testAttribute(Qt::WA_SetFont)) || !QApplication::desktopSettingsAware()) {
+ // If you're using a custom font and it's bigger than the default font,
+ // then no constraints for you. If you are smaller, we can try to help you out
+ QFont font = qt_app_fonts_hash()->value(widg->metaObject()->className(), QFont());
+ if (widg->font().pointSize() > font.pointSize())
+ return ret;
+ }
+
+ if (ct == QStyle::CT_CustomBase && widg) {
+#if QT_CONFIG(pushbutton)
+ if (qobject_cast<const QPushButton *>(widg))
+ ct = QStyle::CT_PushButton;
+#endif
+ else if (qobject_cast<const QRadioButton *>(widg))
+ ct = QStyle::CT_RadioButton;
+#if QT_CONFIG(checkbox)
+ else if (qobject_cast<const QCheckBox *>(widg))
+ ct = QStyle::CT_CheckBox;
+#endif
+#if QT_CONFIG(combobox)
+ else if (qobject_cast<const QComboBox *>(widg))
+ ct = QStyle::CT_ComboBox;
+#endif
+#if QT_CONFIG(toolbutton)
+ else if (qobject_cast<const QToolButton *>(widg))
+ ct = QStyle::CT_ToolButton;
+#endif
+ else if (qobject_cast<const QSlider *>(widg))
+ ct = QStyle::CT_Slider;
+#if QT_CONFIG(progressbar)
+ else if (qobject_cast<const QProgressBar *>(widg))
+ ct = QStyle::CT_ProgressBar;
+#endif
+#if QT_CONFIG(lineedit)
+ else if (qobject_cast<const QLineEdit *>(widg))
+ ct = QStyle::CT_LineEdit;
+#endif
+ else if (qobject_cast<const QHeaderView *>(widg))
+ ct = QStyle::CT_HeaderSection;
+#if QT_CONFIG(menubar)
+ else if (qobject_cast<const QMenuBar *>(widg))
+ ct = QStyle::CT_MenuBar;
+#endif
+#if QT_CONFIG(sizegrip)
+ else if (qobject_cast<const QSizeGrip *>(widg))
+ ct = QStyle::CT_SizeGrip;
+#endif
+ else
+ return ret;
+ }
+
+ switch (ct) {
+#if QT_CONFIG(pushbutton)
+ case QStyle::CT_PushButton: {
+ const QPushButton *psh = qobject_cast<const QPushButton *>(widg);
+ // If this comparison is false, then the widget was not a push button.
+ // This is bad and there's very little we can do since we were requested to find a
+ // sensible size for a widget that pretends to be a QPushButton but is not.
+ if(psh) {
+ QString buttonText = qt_mac_removeMnemonics(psh->text());
+ if (buttonText.contains(QLatin1Char('\n')))
+ ret = QSize(-1, -1);
+ else if (sz == QStyleHelper::SizeLarge)
+ ret = QSize(-1, qt_mac_aqua_get_metric(PushButtonHeight));
+ else if (sz == QStyleHelper::SizeSmall)
+ ret = QSize(-1, qt_mac_aqua_get_metric(SmallPushButtonHeight));
+ else if (sz == QStyleHelper::SizeMini)
+ ret = QSize(-1, qt_mac_aqua_get_metric(MiniPushButtonHeight));
+
+ if (!psh->icon().isNull()){
+ // If the button got an icon, and the icon is larger than the
+ // button, we can't decide on a default size
+ ret.setWidth(-1);
+ if (ret.height() < psh->iconSize().height())
+ ret.setHeight(-1);
+ }
+ else if (buttonText == QLatin1String("OK") || buttonText == QLatin1String("Cancel")){
+ // Aqua Style guidelines restrict the size of OK and Cancel buttons to 68 pixels.
+ // However, this doesn't work for German, therefore only do it for English,
+ // I suppose it would be better to do some sort of lookups for languages
+ // that like to have really long words.
+ ret.setWidth(77 - 8);
+ }
+ } else {
+ // The only sensible thing to do is to return whatever the style suggests...
+ if (sz == QStyleHelper::SizeLarge)
+ ret = QSize(-1, qt_mac_aqua_get_metric(PushButtonHeight));
+ else if (sz == QStyleHelper::SizeSmall)
+ ret = QSize(-1, qt_mac_aqua_get_metric(SmallPushButtonHeight));
+ else if (sz == QStyleHelper::SizeMini)
+ ret = QSize(-1, qt_mac_aqua_get_metric(MiniPushButtonHeight));
+ else
+ // Since there's no default size we return the large size...
+ ret = QSize(-1, qt_mac_aqua_get_metric(PushButtonHeight));
+ }
+#endif
+#if 0 //Not sure we are applying the rules correctly for RadioButtons/CheckBoxes --Sam
+ } else if (ct == QStyle::CT_RadioButton) {
+ QRadioButton *rdo = static_cast<QRadioButton *>(widg);
+ // Exception for case where multiline radio button text requires no size constrainment
+ if (rdo->text().find('\n') != -1)
+ return ret;
+ if (sz == QStyleHelper::SizeLarge)
+ ret = QSize(-1, qt_mac_aqua_get_metric(RadioButtonHeight));
+ else if (sz == QStyleHelper::SizeSmall)
+ ret = QSize(-1, qt_mac_aqua_get_metric(SmallRadioButtonHeight));
+ else if (sz == QStyleHelper::SizeMini)
+ ret = QSize(-1, qt_mac_aqua_get_metric(MiniRadioButtonHeight));
+ } else if (ct == QStyle::CT_CheckBox) {
+ if (sz == QStyleHelper::SizeLarge)
+ ret = QSize(-1, qt_mac_aqua_get_metric(CheckBoxHeight));
+ else if (sz == QStyleHelper::SizeSmall)
+ ret = QSize(-1, qt_mac_aqua_get_metric(SmallCheckBoxHeight));
+ else if (sz == QStyleHelper::SizeMini)
+ ret = QSize(-1, qt_mac_aqua_get_metric(MiniCheckBoxHeight));
+#endif
+ break;
+ }
+ case QStyle::CT_SizeGrip:
+ // Not HIG kosher: mimic what we were doing earlier until we support 4-edge resizing in MDI subwindows
+ if (sz == QStyleHelper::SizeLarge || sz == QStyleHelper::SizeSmall) {
+ int s = sz == QStyleHelper::SizeSmall ? 16 : 22; // large: pixel measured from HITheme, small: from my hat
+ int width = 0;
+#if QT_CONFIG(mdiarea)
+ if (widg && qobject_cast<QMdiSubWindow *>(widg->parentWidget()))
+ width = s;
+#endif
+ ret = QSize(width, s);
+ }
+ break;
+ case QStyle::CT_ComboBox:
+ switch (sz) {
+ case QStyleHelper::SizeLarge:
+ ret = QSize(-1, qt_mac_aqua_get_metric(PopupButtonHeight));
+ break;
+ case QStyleHelper::SizeSmall:
+ ret = QSize(-1, qt_mac_aqua_get_metric(SmallPopupButtonHeight));
+ break;
+ case QStyleHelper::SizeMini:
+ ret = QSize(-1, qt_mac_aqua_get_metric(MiniPopupButtonHeight));
+ break;
+ default:
+ break;
+ }
+ break;
+ case QStyle::CT_ToolButton:
+ if (sz == QStyleHelper::SizeSmall) {
+ int width = 0, height = 0;
+ if (szHint == QSize(-1, -1)) { //just 'guess'..
+#if QT_CONFIG(toolbutton)
+ const QToolButton *bt = qobject_cast<const QToolButton *>(widg);
+ // If this conversion fails then the widget was not what it claimed to be.
+ if(bt) {
+ if (!bt->icon().isNull()) {
+ QSize iconSize = bt->iconSize();
+ QSize pmSize = bt->icon().actualSize(QSize(32, 32), QIcon::Normal);
+ width = qMax(width, qMax(iconSize.width(), pmSize.width()));
+ height = qMax(height, qMax(iconSize.height(), pmSize.height()));
+ }
+ if (!bt->text().isNull() && bt->toolButtonStyle() != Qt::ToolButtonIconOnly) {
+ int text_width = bt->fontMetrics().horizontalAdvance(bt->text()),
+ text_height = bt->fontMetrics().height();
+ if (bt->toolButtonStyle() == Qt::ToolButtonTextUnderIcon) {
+ width = qMax(width, text_width);
+ height += text_height;
+ } else {
+ width += text_width;
+ width = qMax(height, text_height);
+ }
+ }
+ } else
+#endif
+ {
+ // Let's return the size hint...
+ width = szHint.width();
+ height = szHint.height();
+ }
+ } else {
+ width = szHint.width();
+ height = szHint.height();
+ }
+ width = qMax(20, width + 5); //border
+ height = qMax(20, height + 5); //border
+ ret = QSize(width, height);
+ }
+ break;
+ case QStyle::CT_Slider: {
+ int w = -1;
+ const QSlider *sld = qobject_cast<const QSlider *>(widg);
+ // If this conversion fails then the widget was not what it claimed to be.
+ if(sld) {
+ if (sz == QStyleHelper::SizeLarge) {
+ if (sld->orientation() == Qt::Horizontal) {
+ w = qt_mac_aqua_get_metric(HSliderHeight);
+ if (sld->tickPosition() != QSlider::NoTicks)
+ w += qt_mac_aqua_get_metric(HSliderTickHeight);
+ } else {
+ w = qt_mac_aqua_get_metric(VSliderWidth);
+ if (sld->tickPosition() != QSlider::NoTicks)
+ w += qt_mac_aqua_get_metric(VSliderTickWidth);
+ }
+ } else if (sz == QStyleHelper::SizeSmall) {
+ if (sld->orientation() == Qt::Horizontal) {
+ w = qt_mac_aqua_get_metric(SmallHSliderHeight);
+ if (sld->tickPosition() != QSlider::NoTicks)
+ w += qt_mac_aqua_get_metric(SmallHSliderTickHeight);
+ } else {
+ w = qt_mac_aqua_get_metric(SmallVSliderWidth);
+ if (sld->tickPosition() != QSlider::NoTicks)
+ w += qt_mac_aqua_get_metric(SmallVSliderTickWidth);
+ }
+ } else if (sz == QStyleHelper::SizeMini) {
+ if (sld->orientation() == Qt::Horizontal) {
+ w = qt_mac_aqua_get_metric(MiniHSliderHeight);
+ if (sld->tickPosition() != QSlider::NoTicks)
+ w += qt_mac_aqua_get_metric(MiniHSliderTickHeight);
+ } else {
+ w = qt_mac_aqua_get_metric(MiniVSliderWidth);
+ if (sld->tickPosition() != QSlider::NoTicks)
+ w += qt_mac_aqua_get_metric(MiniVSliderTickWidth);
+ }
+ }
+ } else {
+ // This is tricky, we were requested to find a size for a slider which is not
+ // a slider. We don't know if this is vertical or horizontal or if we need to
+ // have tick marks or not.
+ // For this case we will return an horizontal slider without tick marks.
+ w = qt_mac_aqua_get_metric(HSliderHeight);
+ w += qt_mac_aqua_get_metric(HSliderTickHeight);
+ }
+ if (sld->orientation() == Qt::Horizontal)
+ ret.setHeight(w);
+ else
+ ret.setWidth(w);
+ break;
+ }
+#if QT_CONFIG(progressbar)
+ case QStyle::CT_ProgressBar: {
+ int finalValue = -1;
+ Qt::Orientation orient = Qt::Horizontal;
+ if (const QProgressBar *pb = qobject_cast<const QProgressBar *>(widg))
+ orient = pb->orientation();
+
+ if (sz == QStyleHelper::SizeLarge)
+ finalValue = qt_mac_aqua_get_metric(LargeProgressBarThickness)
+ + qt_mac_aqua_get_metric(ProgressBarShadowOutset);
+ else
+ finalValue = qt_mac_aqua_get_metric(NormalProgressBarThickness)
+ + qt_mac_aqua_get_metric(SmallProgressBarShadowOutset);
+ if (orient == Qt::Horizontal)
+ ret.setHeight(finalValue);
+ else
+ ret.setWidth(finalValue);
+ break;
+ }
+#endif
+#if QT_CONFIG(combobox)
+ case QStyle::CT_LineEdit:
+ if (!widg || !qobject_cast<QComboBox *>(widg->parentWidget())) {
+ //should I take into account the font dimentions of the lineedit? -Sam
+ if (sz == QStyleHelper::SizeLarge)
+ ret = QSize(-1, 21);
+ else
+ ret = QSize(-1, 19);
+ }
+ break;
+#endif
+ case QStyle::CT_HeaderSection:
+#if QT_CONFIG(treeview)
+ if (isTreeView(widg))
+ ret = QSize(-1, qt_mac_aqua_get_metric(ListHeaderHeight));
+#endif
+ break;
+ case QStyle::CT_MenuBar:
+ if (sz == QStyleHelper::SizeLarge) {
+ ret = QSize(-1, [[NSApp mainMenu] menuBarHeight]);
+ // In the qt_mac_set_native_menubar(false) case,
+ // we come it here with a zero-height main menu,
+ // preventing the in-window menu from displaying.
+ // Use 22 pixels for the height, by observation.
+ if (ret.height() <= 0)
+ ret.setHeight(22);
+ }
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+
+#if defined(QMAC_QAQUASTYLE_SIZE_CONSTRAIN) || defined(DEBUG_SIZE_CONSTRAINT)
+static QStyleHelper::WidgetSizePolicy qt_aqua_guess_size(const QWidget *widg, QSize large, QSize small, QSize mini)
+{
+ Q_UNUSED(widg);
+
+ if (large == QSize(-1, -1)) {
+ if (small != QSize(-1, -1))
+ return QStyleHelper::SizeSmall;
+ if (mini != QSize(-1, -1))
+ return QStyleHelper::SizeMini;
+ return QStyleHelper::SizeDefault;
+ } else if (small == QSize(-1, -1)) {
+ if (mini != QSize(-1, -1))
+ return QStyleHelper::SizeMini;
+ return QStyleHelper::SizeLarge;
+ } else if (mini == QSize(-1, -1)) {
+ return QStyleHelper::SizeLarge;
+ }
+
+#if QT_CONFIG(mainwindow)
+ if (qEnvironmentVariableIsSet("QWIDGET_ALL_SMALL")) {
+ //if (small.width() != -1 || small.height() != -1)
+ return QStyleHelper::SizeSmall;
+ } else if (qEnvironmentVariableIsSet("QWIDGET_ALL_MINI")) {
+ return QStyleHelper::SizeMini;
+ }
+#endif
+
+#if 0
+ /* Figure out which size we're closer to, I just hacked this in, I haven't
+ tested it as it would probably look pretty strange to have some widgets
+ big and some widgets small in the same window?? -Sam */
+ int large_delta=0;
+ if (large.width() != -1) {
+ int delta = large.width() - widg->width();
+ large_delta += delta * delta;
+ }
+ if (large.height() != -1) {
+ int delta = large.height() - widg->height();
+ large_delta += delta * delta;
+ }
+ int small_delta=0;
+ if (small.width() != -1) {
+ int delta = small.width() - widg->width();
+ small_delta += delta * delta;
+ }
+ if (small.height() != -1) {
+ int delta = small.height() - widg->height();
+ small_delta += delta * delta;
+ }
+ int mini_delta=0;
+ if (mini.width() != -1) {
+ int delta = mini.width() - widg->width();
+ mini_delta += delta * delta;
+ }
+ if (mini.height() != -1) {
+ int delta = mini.height() - widg->height();
+ mini_delta += delta * delta;
+ }
+ if (mini_delta < small_delta && mini_delta < large_delta)
+ return QStyleHelper::SizeMini;
+ else if (small_delta < large_delta)
+ return QStyleHelper::SizeSmall;
+#endif
+ return QStyleHelper::SizeLarge;
+}
+#endif
+
+void QMacStylePrivate::drawFocusRing(QPainter *p, const QRect &targetRect, int hMargin, int vMargin, qreal radius) const
+{
+ const qreal pixelRatio = p->device()->devicePixelRatioF();
+ static const QString keyFormat = QLatin1String("$qt_focusring%1-%2-%3-%4");
+ const QString &key = keyFormat.arg(hMargin).arg(vMargin).arg(radius).arg(pixelRatio);
+ QPixmap focusRingPixmap;
+
+ if (!QPixmapCache::find(key, focusRingPixmap)) {
+ const qreal size = radius * 2 + 5;
+ focusRingPixmap = QPixmap((QSize(size, size) + 2 * QSize(hMargin, vMargin)) * pixelRatio);
+ focusRingPixmap.fill(Qt::transparent);
+ focusRingPixmap.setDevicePixelRatio(pixelRatio);
+
+ static const qreal focusRingWidth = 3.5;
+ const auto focusRingRect = QRectF(hMargin, vMargin, size, size);
+ QPainterPath focusRingPath;
+ focusRingPath.setFillRule(Qt::OddEvenFill);
+ const auto innerRect = radius > 0 ? focusRingRect : focusRingRect.adjusted(0.5, 0.5, -0.5, -0.5);
+ if (radius > 0)
+ focusRingPath.addRoundedRect(innerRect, radius, radius);
+ else
+ focusRingPath.addRect(innerRect);
+
+ const auto outterRect = innerRect.adjusted(-focusRingWidth, -focusRingWidth, focusRingWidth, focusRingWidth);
+ const auto outterRadius = radius + focusRingWidth;
+ focusRingPath.addRoundedRect(outterRect, outterRadius, outterRadius);
+
+ QPainter pp(&focusRingPixmap);
+ pp.setOpacity(0.5);
+ pp.setRenderHint(QPainter::Antialiasing);
+ const auto focusRingColor = qt_mac_toQColor(NSColor.keyboardFocusIndicatorColor.CGColor);
+ pp.fillPath(focusRingPath, focusRingColor);
+
+ QPixmapCache::insert(key, focusRingPixmap);
+ }
+
+ // Add 2 for the actual ring tickness going inwards
+ const qreal hCornerSize = 2 + hMargin + radius;
+ const qreal vCornerSize = 2 + vMargin + radius;
+ const qreal shCornerSize = hCornerSize * pixelRatio;
+ const qreal svCornerSize = vCornerSize * pixelRatio;
+ // top-left corner
+ p->drawPixmap(QPointF(targetRect.left(), targetRect.top()), focusRingPixmap,
+ QRectF(0, 0, shCornerSize, svCornerSize));
+ // top-right corner
+ p->drawPixmap(QPointF(targetRect.right() - hCornerSize + 1, targetRect.top()), focusRingPixmap,
+ QRectF(focusRingPixmap.width() - shCornerSize, 0, shCornerSize, svCornerSize));
+ // bottom-left corner
+ p->drawPixmap(QPointF(targetRect.left(), targetRect.bottom() - vCornerSize + 1), focusRingPixmap,
+ QRectF(0, focusRingPixmap.height() - svCornerSize, shCornerSize, svCornerSize));
+ // bottom-right corner
+ p->drawPixmap(QPointF(targetRect.right() - hCornerSize + 1, targetRect.bottom() - vCornerSize + 1), focusRingPixmap,
+ QRect(focusRingPixmap.width() - shCornerSize, focusRingPixmap.height() - svCornerSize, shCornerSize, svCornerSize));
+ // top edge
+ p->drawPixmap(QRectF(targetRect.left() + hCornerSize, targetRect.top(), targetRect.width() - 2 * hCornerSize, vCornerSize), focusRingPixmap,
+ QRect(shCornerSize, 0, focusRingPixmap.width() - 2 * shCornerSize, svCornerSize));
+ // bottom edge
+ p->drawPixmap(QRectF(targetRect.left() + hCornerSize, targetRect.bottom() - vCornerSize + 1, targetRect.width() - 2 * hCornerSize, vCornerSize), focusRingPixmap,
+ QRect(shCornerSize, focusRingPixmap.height() - svCornerSize, focusRingPixmap.width() - 2 * shCornerSize, svCornerSize));
+ // left edge
+ p->drawPixmap(QRectF(targetRect.left(), targetRect.top() + vCornerSize, hCornerSize, targetRect.height() - 2 * vCornerSize), focusRingPixmap,
+ QRect(0, svCornerSize, shCornerSize, focusRingPixmap.width() - 2 * svCornerSize));
+ // right edge
+ p->drawPixmap(QRectF(targetRect.right() - hCornerSize + 1, targetRect.top() + vCornerSize, hCornerSize, targetRect.height() - 2 * vCornerSize), focusRingPixmap,
+ QRect(focusRingPixmap.width() - shCornerSize, svCornerSize, shCornerSize, focusRingPixmap.width() - 2 * svCornerSize));
+}
+
+void QMacStylePrivate::drawFocusRing(QPainter *p, const QRect &targetRect, int hMargin, int vMargin, const CocoaControl &cw) const
+{
+ static const auto focusRingWidth = 3.5;
+
+ QPainterPath focusRingPath;
+ qreal hOffset = 0.0;
+ qreal vOffset = 0.0;
+ switch (cw.first) {
+ case Button_CheckBox: {
+ const auto cbInnerRadius = (cw.second == QStyleHelper::SizeMini ? 2.0 : 3.0);
+ const auto cbSize = cw.second == QStyleHelper::SizeLarge ? 13 :
+ cw.second == QStyleHelper::SizeSmall ? 11 : 9; // As measured
+ hOffset = hMargin + (cw.second == QStyleHelper::SizeLarge ? 2.5 :
+ cw.second == QStyleHelper::SizeSmall ? 2.0 : 1.0); // As measured
+ vOffset = 0.5 * qreal(targetRect.height() - cbSize);
+ const auto cbInnerRect = QRectF(0, 0, cbSize, cbSize);
+ const auto cbOutterRadius = cbInnerRadius + focusRingWidth;
+ const auto cbOutterRect = cbInnerRect.adjusted(-focusRingWidth, -focusRingWidth, focusRingWidth, focusRingWidth);
+ focusRingPath.setFillRule(Qt::OddEvenFill);
+ focusRingPath.addRoundedRect(cbOutterRect, cbOutterRadius, cbOutterRadius);
+ focusRingPath.addRoundedRect(cbInnerRect, cbInnerRadius, cbInnerRadius);
+ break;
+ }
+ case Button_RadioButton: {
+ const auto rbSize = cw.second == QStyleHelper::SizeLarge ? 15 :
+ cw.second == QStyleHelper::SizeSmall ? 13 : 9; // As measured
+ hOffset = hMargin + (cw.second == QStyleHelper::SizeLarge ? 1.5 :
+ cw.second == QStyleHelper::SizeSmall ? 1.0 : 1.0); // As measured
+ vOffset = 0.5 * qreal(targetRect.height() - rbSize);
+ const auto rbInnerRect = QRectF(0, 0, rbSize, rbSize);
+ const auto rbOutterRect = rbInnerRect.adjusted(-focusRingWidth, -focusRingWidth, focusRingWidth, focusRingWidth);
+ focusRingPath.setFillRule(Qt::OddEvenFill);
+ focusRingPath.addEllipse(rbInnerRect);
+ focusRingPath.addEllipse(rbOutterRect);
+ break;
+ }
+ default:
+ Q_UNUSED(vMargin);
+ Q_UNREACHABLE();
+ }
+
+ const auto focusRingColor = qt_mac_toQColor(NSColor.keyboardFocusIndicatorColor.CGColor);
+
+ p->save();
+ p->setRenderHint(QPainter::Antialiasing);
+ p->setOpacity(0.5);
+ p->translate(hOffset, vOffset);
+ p->fillPath(focusRingPath, focusRingColor);
+ p->restore();
+}
+
+QPainterPath QMacStylePrivate::windowPanelPath(const QRectF &r) const
+{
+ static const qreal CornerPointOffset = 5.5;
+ static const qreal CornerControlOffset = 2.1;
+
+ QPainterPath path;
+ // Top-left corner
+ path.moveTo(r.left(), r.top() + CornerPointOffset);
+ path.cubicTo(r.left(), r.top() + CornerControlOffset,
+ r.left() + CornerControlOffset, r.top(),
+ r.left() + CornerPointOffset, r.top());
+ // Top-right corner
+ path.lineTo(r.right() - CornerPointOffset, r.top());
+ path.cubicTo(r.right() - CornerControlOffset, r.top(),
+ r.right(), r.top() + CornerControlOffset,
+ r.right(), r.top() + CornerPointOffset);
+ // Bottom-right corner
+ path.lineTo(r.right(), r.bottom() - CornerPointOffset);
+ path.cubicTo(r.right(), r.bottom() - CornerControlOffset,
+ r.right() - CornerControlOffset, r.bottom(),
+ r.right() - CornerPointOffset, r.bottom());
+ // Bottom-right corner
+ path.lineTo(r.left() + CornerPointOffset, r.bottom());
+ path.cubicTo(r.left() + CornerControlOffset, r.bottom(),
+ r.left(), r.bottom() - CornerControlOffset,
+ r.left(), r.bottom() - CornerPointOffset);
+ path.lineTo(r.left(), r.top() + CornerPointOffset);
+
+ return path;
+}
+
+QMacStylePrivate::CocoaControlType QMacStylePrivate::windowButtonCocoaControl(QStyle::SubControl sc) const
+{
+ struct WindowButtons {
+ QStyle::SubControl sc;
+ QMacStylePrivate::CocoaControlType ct;
+ };
+
+ static const WindowButtons buttons[] = {
+ { QStyle::SC_TitleBarCloseButton, QMacStylePrivate::Button_WindowClose },
+ { QStyle::SC_TitleBarMinButton, QMacStylePrivate::Button_WindowMiniaturize },
+ { QStyle::SC_TitleBarMaxButton, QMacStylePrivate::Button_WindowZoom }
+ };
+
+ for (const auto &wb : buttons)
+ if (wb.sc == sc)
+ return wb.ct;
+
+ return NoControl;
+}
+
+
+#if QT_CONFIG(tabbar)
+void QMacStylePrivate::tabLayout(const QStyleOptionTab *opt, const QWidget *widget, QRect *textRect, QRect *iconRect) const
+{
+ Q_ASSERT(textRect);
+ Q_ASSERT(iconRect);
+ QRect tr = opt->rect;
+ const bool verticalTabs = opt->shape == QTabBar::RoundedEast
+ || opt->shape == QTabBar::RoundedWest
+ || opt->shape == QTabBar::TriangularEast
+ || opt->shape == QTabBar::TriangularWest;
+ if (verticalTabs)
+ tr.setRect(0, 0, tr.height(), tr.width()); // 0, 0 as we will have a translate transform
+
+ int verticalShift = proxyStyle->pixelMetric(QStyle::PM_TabBarTabShiftVertical, opt, widget);
+ int horizontalShift = proxyStyle->pixelMetric(QStyle::PM_TabBarTabShiftHorizontal, opt, widget);
+ const int hpadding = 4;
+ const int vpadding = proxyStyle->pixelMetric(QStyle::PM_TabBarTabVSpace, opt, widget) / 2;
+ if (opt->shape == QTabBar::RoundedSouth || opt->shape == QTabBar::TriangularSouth)
+ verticalShift = -verticalShift;
+ tr.adjust(hpadding, verticalShift - vpadding, horizontalShift - hpadding, vpadding);
+
+ // left widget
+ if (!opt->leftButtonSize.isEmpty()) {
+ const int buttonSize = verticalTabs ? opt->leftButtonSize.height() : opt->leftButtonSize.width();
+ tr.setLeft(tr.left() + 4 + buttonSize);
+ // make text aligned to center
+ if (opt->rightButtonSize.isEmpty())
+ tr.setRight(tr.right() - 4 - buttonSize);
+ }
+ // right widget
+ if (!opt->rightButtonSize.isEmpty()) {
+ const int buttonSize = verticalTabs ? opt->rightButtonSize.height() : opt->rightButtonSize.width();
+ tr.setRight(tr.right() - 4 - buttonSize);
+ // make text aligned to center
+ if (opt->leftButtonSize.isEmpty())
+ tr.setLeft(tr.left() + 4 + buttonSize);
+ }
+
+ // icon
+ if (!opt->icon.isNull()) {
+ QSize iconSize = opt->iconSize;
+ if (!iconSize.isValid()) {
+ int iconExtent = proxyStyle->pixelMetric(QStyle::PM_SmallIconSize);
+ iconSize = QSize(iconExtent, iconExtent);
+ }
+ QSize tabIconSize = opt->icon.actualSize(iconSize,
+ (opt->state & QStyle::State_Enabled) ? QIcon::Normal : QIcon::Disabled,
+ (opt->state & QStyle::State_Selected) ? QIcon::On : QIcon::Off);
+ // High-dpi icons do not need adjustment; make sure tabIconSize is not larger than iconSize
+ tabIconSize = QSize(qMin(tabIconSize.width(), iconSize.width()), qMin(tabIconSize.height(), iconSize.height()));
+
+ *iconRect = QRect(tr.left(), tr.center().y() - tabIconSize.height() / 2,
+ tabIconSize.width(), tabIconSize.height());
+ if (!verticalTabs)
+ *iconRect = proxyStyle->visualRect(opt->direction, opt->rect, *iconRect);
+
+ int stylePadding = proxyStyle->pixelMetric(QStyle::PM_TabBarTabHSpace, opt, widget) / 2;
+ stylePadding -= hpadding;
+
+ tr.setLeft(tr.left() + stylePadding + tabIconSize.width() + 4);
+ tr.setRight(tr.right() - stylePadding - tabIconSize.width() - 4);
+ }
+
+ if (!verticalTabs)
+ tr = proxyStyle->visualRect(opt->direction, opt->rect, tr);
+
+ *textRect = tr;
+}
+#endif // QT_CONFIG(tabbar)
+
+QStyleHelper::WidgetSizePolicy QMacStylePrivate::effectiveAquaSizeConstrain(const QStyleOption *option,
+ const QWidget *widg,
+ QStyle::ContentsType ct,
+ QSize szHint, QSize *insz) const
+{
+ QStyleHelper::WidgetSizePolicy sz = aquaSizeConstrain(option, widg, ct, szHint, insz);
+ if (sz == QStyleHelper::SizeDefault)
+ return QStyleHelper::SizeLarge;
+ return sz;
+}
+
+QStyleHelper::WidgetSizePolicy QMacStylePrivate::aquaSizeConstrain(const QStyleOption *option, const QWidget *widg,
+ QStyle::ContentsType ct, QSize szHint, QSize *insz) const
+{
+#if defined(QMAC_QAQUASTYLE_SIZE_CONSTRAIN) || defined(DEBUG_SIZE_CONSTRAINT)
+ if (option) {
+ if (option->state & QStyle::State_Small)
+ return QStyleHelper::SizeSmall;
+ if (option->state & QStyle::State_Mini)
+ return QStyleHelper::SizeMini;
+ }
+
+ if (!widg) {
+ if (insz)
+ *insz = QSize();
+ if (qEnvironmentVariableIsSet("QWIDGET_ALL_SMALL"))
+ return QStyleHelper::SizeSmall;
+ if (qEnvironmentVariableIsSet("QWIDGET_ALL_MINI"))
+ return QStyleHelper::SizeMini;
+ return QStyleHelper::SizeDefault;
+ }
+
+ QSize large = qt_aqua_get_known_size(ct, widg, szHint, QStyleHelper::SizeLarge),
+ small = qt_aqua_get_known_size(ct, widg, szHint, QStyleHelper::SizeSmall),
+ mini = qt_aqua_get_known_size(ct, widg, szHint, QStyleHelper::SizeMini);
+ bool guess_size = false;
+ QStyleHelper::WidgetSizePolicy ret = QStyleHelper::SizeDefault;
+ QStyleHelper::WidgetSizePolicy wsp = QStyleHelper::widgetSizePolicy(widg);
+ if (wsp == QStyleHelper::SizeDefault)
+ guess_size = true;
+ else if (wsp == QStyleHelper::SizeMini)
+ ret = QStyleHelper::SizeMini;
+ else if (wsp == QStyleHelper::SizeSmall)
+ ret = QStyleHelper::SizeSmall;
+ else if (wsp == QStyleHelper::SizeLarge)
+ ret = QStyleHelper::SizeLarge;
+ if (guess_size)
+ ret = qt_aqua_guess_size(widg, large, small, mini);
+
+ QSize *sz = 0;
+ if (ret == QStyleHelper::SizeSmall)
+ sz = &small;
+ else if (ret == QStyleHelper::SizeLarge)
+ sz = &large;
+ else if (ret == QStyleHelper::SizeMini)
+ sz = &mini;
+ if (insz)
+ *insz = sz ? *sz : QSize(-1, -1);
+#ifdef DEBUG_SIZE_CONSTRAINT
+ if (sz) {
+ const char *size_desc = "Unknown";
+ if (sz == &small)
+ size_desc = "Small";
+ else if (sz == &large)
+ size_desc = "Large";
+ else if (sz == &mini)
+ size_desc = "Mini";
+ qDebug("%s - %s: %s taken (%d, %d) [%d, %d]",
+ widg ? widg->objectName().toLatin1().constData() : "*Unknown*",
+ widg ? widg->metaObject()->className() : "*Unknown*", size_desc, widg->width(), widg->height(),
+ sz->width(), sz->height());
+ }
+#endif
+ return ret;
+#else
+ if (insz)
+ *insz = QSize();
+ Q_UNUSED(widg);
+ Q_UNUSED(ct);
+ Q_UNUSED(szHint);
+ return QStyleHelper::SizeDefault;
+#endif
+}
+
+/**
+ Returns the free space awailable for contents inside the
+ button (and not the size of the contents itself)
+*/
+CGRect QMacStylePrivate::pushButtonContentBounds(const QStyleOptionButton *btn,
+ const HIThemeButtonDrawInfo *bdi) const
+{
+ CGRect outerBounds = btn->rect.toCGRect();
+ // Adjust the bounds to correct for
+ // carbon not calculating the content bounds fully correct
+ if (bdi->kind == kThemePushButton || bdi->kind == kThemePushButtonSmall){
+ outerBounds.origin.y += QMacStylePrivate::PushButtonTopOffset;
+ outerBounds.size.height -= QMacStylePrivate::PushButtonBottomOffset;
+ } else if (bdi->kind == kThemePushButtonMini) {
+ outerBounds.origin.y += QMacStylePrivate::PushButtonTopOffset;
+ }
+
+ CGRect contentBounds;
+ HIThemeGetButtonContentBounds(&outerBounds, bdi, &contentBounds);
+ return contentBounds;
+}
+
+/**
+ Calculates the size of the button contents.
+ This includes both the text and the icon.
+*/
+QSize QMacStylePrivate::pushButtonSizeFromContents(const QStyleOptionButton *btn) const
+{
+ Q_Q(const QMacStyle);
+ QSize csz(0, 0);
+ QSize iconSize = btn->icon.isNull() ? QSize(0, 0)
+ : (btn->iconSize + QSize(QMacStylePrivate::PushButtonContentPadding, 0));
+ QRect textRect = btn->text.isEmpty() ? QRect(0, 0, 1, 1)
+ : btn->fontMetrics.boundingRect(QRect(), Qt::AlignCenter, btn->text);
+ csz.setWidth(iconSize.width() + textRect.width()
+ + ((btn->features & QStyleOptionButton::HasMenu)
+ ? q->proxy()->pixelMetric(QStyle::PM_MenuButtonIndicator, btn, 0) : 0));
+ csz.setHeight(qMax(iconSize.height(), textRect.height()));
+ return csz;
+}
+
+/**
+ Checks if the actual contents of btn fits inside the free content bounds of
+ 'buttonKindToCheck'. Meant as a helper function for 'initHIThemePushButton'
+ for determining which button kind to use for drawing.
+*/
+bool QMacStylePrivate::contentFitsInPushButton(const QStyleOptionButton *btn,
+ HIThemeButtonDrawInfo *bdi,
+ ThemeButtonKind buttonKindToCheck) const
+{
+ ThemeButtonKind tmp = bdi->kind;
+ bdi->kind = buttonKindToCheck;
+ QSize contentSize = pushButtonSizeFromContents(btn);
+ QRect freeContentRect = QRectF::fromCGRect(pushButtonContentBounds(btn, bdi)).toRect();
+ bdi->kind = tmp;
+ return freeContentRect.contains(QRect(freeContentRect.x(), freeContentRect.y(),
+ contentSize.width(), contentSize.height()));
+}
+
+/**
+ Creates a HIThemeButtonDrawInfo structure that specifies the correct button
+ kind and other details to use for drawing the given push button. Which
+ button kind depends on the size of the button, the size of the contents,
+ explicit user style settings, etc.
+*/
+void QMacStylePrivate::initHIThemePushButton(const QStyleOptionButton *btn,
+ const QWidget *widget,
+ const ThemeDrawState tds,
+ HIThemeButtonDrawInfo *bdi) const
+{
+ ThemeDrawState tdsModified = tds;
+ if (btn->state & QStyle::State_On)
+ tdsModified = kThemeStatePressed;
+ bdi->version = qt_mac_hitheme_version;
+ bdi->state = tdsModified;
+ bdi->value = kThemeButtonOff;
+
+ if (tds == kThemeStateInactive)
+ bdi->state = kThemeStateActive;
+ if (btn->state & QStyle::State_HasFocus)
+ bdi->adornment = kThemeAdornmentFocus;
+ else
+ bdi->adornment = kThemeAdornmentNone;
+
+
+ if (btn->features & (QStyleOptionButton::Flat)) {
+ bdi->kind = kThemeBevelButton;
+ } else {
+ switch (aquaSizeConstrain(btn, widget)) {
+ case QStyleHelper::SizeSmall:
+ bdi->kind = kThemePushButtonSmall;
+ break;
+ case QStyleHelper::SizeMini:
+ bdi->kind = kThemePushButtonMini;
+ break;
+ case QStyleHelper::SizeLarge:
+ // ... We should honor if the user is explicit about using the
+ // large button. But right now Qt will specify the large button
+ // as default rather than QStyleHelper::SizeDefault.
+ // So we treat it like QStyleHelper::SizeDefault
+ // to get the dynamic choosing of button kind.
+ case QStyleHelper::SizeDefault:
+ // Choose the button kind that closest match the button rect, but at the
+ // same time displays the button contents without clipping.
+ bdi->kind = kThemeBevelButton;
+ if (btn->rect.width() >= QMacStylePrivate::BevelButtonW && btn->rect.height() >= QMacStylePrivate::BevelButtonH){
+ if (widget && widget->testAttribute(Qt::WA_MacVariableSize)) {
+ if (btn->rect.height() <= QMacStylePrivate::MiniButtonH){
+ if (contentFitsInPushButton(btn, bdi, kThemePushButtonMini))
+ bdi->kind = kThemePushButtonMini;
+ } else if (btn->rect.height() <= QMacStylePrivate::SmallButtonH){
+ if (contentFitsInPushButton(btn, bdi, kThemePushButtonSmall))
+ bdi->kind = kThemePushButtonSmall;
+ } else if (contentFitsInPushButton(btn, bdi, kThemePushButton)) {
+ bdi->kind = kThemePushButton;
+ }
+ } else {
+ bdi->kind = kThemePushButton;
+ }
+ }
+ }
+ }
+}
+
+/**
+ Creates a HIThemeButtonDrawInfo structure that specifies the correct button
+ kind and other details to use for drawing the given combobox. Which button
+ kind depends on the size of the combo, wether or not it is editable,
+ explicit user style settings, etc.
+*/
+void QMacStylePrivate::initComboboxBdi(const QStyleOptionComboBox *combo, HIThemeButtonDrawInfo *bdi,
+ CocoaControl *cw,
+ const QWidget *widget, const ThemeDrawState &tds) const
+{
+ bdi->version = qt_mac_hitheme_version;
+ bdi->adornment = kThemeAdornmentArrowLeftArrow;
+ bdi->value = kThemeButtonOff;
+ if (combo->state & QStyle::State_HasFocus)
+ bdi->adornment = kThemeAdornmentFocus;
+ if (combo->activeSubControls & QStyle::SC_ComboBoxArrow)
+ bdi->state = kThemeStatePressed;
+ else
+ bdi->state = tds;
+
+ QStyleHelper::WidgetSizePolicy aSize = aquaSizeConstrain(combo, widget);
+ cw->first = combo->editable ? ComboBox : Button_PopupButton;
+ cw->second = aSize;
+ switch (aSize) {
+ case QStyleHelper::SizeMini:
+ bdi->kind = combo->editable ? ThemeButtonKind(kThemeComboBoxMini)
+ : ThemeButtonKind(kThemePopupButtonMini);
+ break;
+ case QStyleHelper::SizeSmall:
+ bdi->kind = combo->editable ? ThemeButtonKind(kThemeComboBoxSmall)
+ : ThemeButtonKind(kThemePopupButtonSmall);
+ break;
+ case QStyleHelper::SizeLarge:
+ case QStyleHelper::SizeDefault:
+ // Unless the user explicitly specified large buttons, determine the
+ // kind by looking at the combox size.
+ // ... specifying small and mini-buttons it not a current feature of
+ // Qt (e.g. QWidget::getAttribute(WA_ButtonSize)). But when it is, add
+ // an extra check here before using the mini and small buttons.
+ int h = combo->rect.size().height();
+ if (combo->editable){
+#if QT_CONFIG(datetimeedit)
+ if (qobject_cast<const QDateTimeEdit *>(widget)) {
+ // Except when, you know, we get a QDateTimeEdit with calendarPopup
+ // enabled. And then things get weird, basically because it's a
+ // transvestite spinbox with editable combobox tendencies. Meaning
+ // that it wants to look a combobox, except that it isn't one, so it
+ // doesn't get all those extra free margins around. (Don't know whose
+ // idea those margins were, but now it looks like we're stuck with
+ // them forever). So anyway, the height threshold should be smaller
+ // in this case, or the style gets confused when it needs to render
+ // or return any subcontrol size of the poor thing.
+ if (h < 9) {
+ bdi->kind = kThemeComboBoxMini;
+ cw->second = QStyleHelper::SizeMini;
+ } else if (h < 22) {
+ bdi->kind = kThemeComboBoxSmall;
+ cw->second = QStyleHelper::SizeSmall;
+ } else {
+ bdi->kind = kThemeComboBox;
+ cw->second = QStyleHelper::SizeLarge;
+ }
+ } else
+#endif
+ {
+ if (h < 21) {
+ bdi->kind = kThemeComboBoxMini;
+ cw->second = QStyleHelper::SizeMini;
+ } else if (h < 26) {
+ bdi->kind = kThemeComboBoxSmall;
+ cw->second = QStyleHelper::SizeSmall;
+ } else {
+ bdi->kind = kThemeComboBox;
+ cw->second = QStyleHelper::SizeLarge;
+ }
+ }
+ } else {
+ // Even if we specify that we want the kThemePopupButton, Carbon
+ // will use the kThemePopupButtonSmall if the size matches. So we
+ // do the same size check explicit to have the size of the inner
+ // text field be correct. Therefore, do this even if the user specifies
+ // the use of LargeButtons explicit.
+ if (h < 21) {
+ bdi->kind = kThemePopupButtonMini;
+ cw->second = QStyleHelper::SizeMini;
+ } else if (h < 26) {
+ bdi->kind = kThemePopupButtonSmall;
+ cw->second = QStyleHelper::SizeSmall;
+ } else {
+ bdi->kind = kThemePopupButton;
+ cw->second = QStyleHelper::SizeLarge;
+ }
+ }
+ break;
+ }
+}
+
+/**
+ Carbon draws comboboxes (and other views) outside the rect given as argument. Use this function to obtain
+ the corresponding inner rect for drawing the same combobox so that it stays inside the given outerBounds.
+*/
+CGRect QMacStylePrivate::comboboxInnerBounds(const CGRect &outerBounds, const CocoaControl &cocoaWidget)
+{
+ CGRect innerBounds = outerBounds;
+ // Carbon draw parts of the view outside the rect.
+ // So make the rect a bit smaller to compensate
+ // (I wish HIThemeGetButtonBackgroundBounds worked)
+ if (cocoaWidget.first == Button_PopupButton) {
+ switch (cocoaWidget.second) {
+ case QStyleHelper::SizeSmall:
+ innerBounds.origin.x += 3;
+ innerBounds.origin.y += 3;
+ innerBounds.size.width -= 6;
+ innerBounds.size.height -= 7;
+ break;
+ case QStyleHelper::SizeMini:
+ innerBounds.origin.x += 2;
+ innerBounds.origin.y += 2;
+ innerBounds.size.width -= 5;
+ innerBounds.size.height -= 6;
+ break;
+ case QStyleHelper::SizeLarge:
+ case QStyleHelper::SizeDefault:
+ innerBounds.origin.x += 2;
+ innerBounds.origin.y += 2;
+ innerBounds.size.width -= 5;
+ innerBounds.size.height -= 6;
+ }
+ } else if (cocoaWidget.first == ComboBox) {
+ switch (cocoaWidget.second) {
+ case QStyleHelper::SizeSmall:
+ innerBounds.origin.x += 3;
+ innerBounds.origin.y += 3;
+ innerBounds.size.width -= 7;
+ innerBounds.size.height -= 8;
+ break;
+ case QStyleHelper::SizeMini:
+ innerBounds.origin.x += 3;
+ innerBounds.origin.y += 3;
+ innerBounds.size.width -= 4;
+ innerBounds.size.height -= 8;
+ break;
+ case QStyleHelper::SizeLarge:
+ case QStyleHelper::SizeDefault:
+ innerBounds.origin.x += 3;
+ innerBounds.origin.y += 2;
+ innerBounds.size.width -= 6;
+ innerBounds.size.height -= 8;
+ }
+ }
+
+ return innerBounds;
+}
+
+/**
+ Inside a combobox Qt places a line edit widget. The size of this widget should depend on the kind
+ of combobox we choose to draw. This function calculates and returns this size.
+*/
+QRect QMacStylePrivate::comboboxEditBounds(const QRect &outerBounds, const HIThemeButtonDrawInfo &bdi)
+{
+ QRect ret = outerBounds;
+ switch (bdi.kind){
+ case kThemeComboBox:
+ ret.adjust(5, 5, -22, -5);
+ break;
+ case kThemeComboBoxSmall:
+ ret.adjust(4, 5, -18, 0);
+ ret.setHeight(16);
+ break;
+ case kThemeComboBoxMini:
+ ret.adjust(4, 5, -16, 0);
+ ret.setHeight(13);
+ break;
+ case kThemePopupButton:
+ ret.adjust(10, 2, -23, -4);
+ break;
+ case kThemePopupButtonSmall:
+ ret.adjust(9, 3, -20, -3);
+ break;
+ case kThemePopupButtonMini:
+ ret.adjust(8, 3, -19, 0);
+ ret.setHeight(13);
+ break;
+ }
+ return ret;
+}
+
+/**
+ Carbon comboboxes don't scale (sight). If the size of the combo suggest a scaled version,
+ create it manually by drawing a small Carbon combo onto a pixmap (use pixmap cache), chop
+ it up, and copy it back onto the widget. Othervise, draw the combobox supplied by Carbon directly.
+*/
+void QMacStylePrivate::drawCombobox(const CGRect &outerBounds, const HIThemeButtonDrawInfo &bdi, const CocoaControl &cw, QPainter *p)
+{
+ if (!(bdi.kind == kThemeComboBox && outerBounds.size.height > 28)){
+ // We have an unscaled combobox, or popup-button; use Carbon directly.
+ const CGRect innerBounds = QMacStylePrivate::comboboxInnerBounds(outerBounds, cw);
+ HIThemeDrawButton(&innerBounds, &bdi, QMacCGContext(p), kHIThemeOrientationNormal, 0);
+ } else {
+ QPixmap buffer;
+ QString key = QString(QLatin1String("$qt_cbox%1-%2")).arg(int(bdi.state)).arg(int(bdi.adornment));
+ if (!QPixmapCache::find(key, buffer)) {
+ CGRect innerBoundsSmallCombo = {{3, 3}, {29, 25}};
+ buffer = QPixmap(35, 28);
+ buffer.fill(Qt::transparent);
+ QPainter buffPainter(&buffer);
+ HIThemeDrawButton(&innerBoundsSmallCombo, &bdi, QMacCGContext(&buffPainter), kHIThemeOrientationNormal, 0);
+ buffPainter.end();
+ QPixmapCache::insert(key, buffer);
+ }
+
+ const int bwidth = 20;
+ const int fwidth = 10;
+ const int fheight = 10;
+ int w = qRound(outerBounds.size.width);
+ int h = qRound(outerBounds.size.height);
+ int bstart = w - bwidth;
+ int blower = fheight + 1;
+ int flower = h - fheight;
+ int sheight = flower - fheight;
+ int center = qRound(outerBounds.size.height + outerBounds.origin.y) / 2;
+
+ // Draw upper and lower gap
+ p->drawPixmap(fwidth, 0, bstart - fwidth, fheight, buffer, fwidth, 0, 1, fheight);
+ p->drawPixmap(fwidth, flower, bstart - fwidth, fheight, buffer, fwidth, buffer.height() - fheight, 1, fheight);
+ // Draw left and right gap. Right gap is drawn top and bottom separatly
+ p->drawPixmap(0, fheight, fwidth, sheight, buffer, 0, fheight, fwidth, 1);
+ p->drawPixmap(bstart, fheight, bwidth, center - fheight, buffer, buffer.width() - bwidth, fheight - 1, bwidth, 1);
+ p->drawPixmap(bstart, center, bwidth, sheight / 2, buffer, buffer.width() - bwidth, fheight + 6, bwidth, 1);
+ // Draw arrow
+ p->drawPixmap(bstart, center - 4, bwidth - 3, 6, buffer, buffer.width() - bwidth, fheight, bwidth - 3, 6);
+ // Draw corners
+ p->drawPixmap(0, 0, fwidth, fheight, buffer, 0, 0, fwidth, fheight);
+ p->drawPixmap(bstart, 0, bwidth, fheight, buffer, buffer.width() - bwidth, 0, bwidth, fheight);
+ p->drawPixmap(0, flower, fwidth, fheight, buffer, 0, buffer.height() - fheight, fwidth, fheight);
+ p->drawPixmap(bstart, h - blower, bwidth, blower, buffer, buffer.width() - bwidth, buffer.height() - blower, bwidth, blower);
+ }
+}
+
+QMacStylePrivate::QMacStylePrivate()
+ : backingStoreNSView(nil)
+{
+ if (auto *ssf = QGuiApplicationPrivate::platformTheme()->font(QPlatformTheme::SmallFont))
+ smallSystemFont = *ssf;
+ if (auto *msf = QGuiApplicationPrivate::platformTheme()->font(QPlatformTheme::MiniFont))
+ miniSystemFont = *msf;
+}
+
+QMacStylePrivate::~QMacStylePrivate()
+{
+ QMacAutoReleasePool pool;
+ for (NSView *b : cocoaControls)
+ [b release];
+ for (NSCell *cell : cocoaCells)
+ [cell release];
+}
+
+ThemeDrawState QMacStylePrivate::getDrawState(QStyle::State flags)
+{
+ ThemeDrawState tds = kThemeStateActive;
+ if (flags & QStyle::State_Sunken) {
+ tds = kThemeStatePressed;
+ } else if (flags & QStyle::State_Active) {
+ if (!(flags & QStyle::State_Enabled))
+ tds = kThemeStateUnavailable;
+ } else {
+ if (flags & QStyle::State_Enabled)
+ tds = kThemeStateInactive;
+ else
+ tds = kThemeStateUnavailableInactive;
+ }
+ return tds;
+}
+
+ QMacStylePrivate::CocoaControl QMacStylePrivate::cocoaControlFromHIThemeButtonKind(ThemeButtonKind kind)
+{
+ CocoaControl w;
+
+ switch (kind) {
+ case kThemePopupButton:
+ case kThemePopupButtonSmall:
+ case kThemePopupButtonMini:
+ w.first = Button_PopupButton;
+ break;
+ case kThemeComboBox:
+ w.first = ComboBox;
+ break;
+ case kThemeArrowButton:
+ w.first = Button_Disclosure;
+ break;
+ case kThemeCheckBox:
+ case kThemeCheckBoxSmall:
+ case kThemeCheckBoxMini:
+ w.first = Button_CheckBox;
+ break;
+ case kThemeRadioButton:
+ case kThemeRadioButtonSmall:
+ case kThemeRadioButtonMini:
+ w.first = Button_RadioButton;
+ break;
+ case kThemePushButton:
+ case kThemePushButtonSmall:
+ case kThemePushButtonMini:
+ w.first = Button_PushButton;
+ break;
+ default:
+ break;
+ }
+
+ switch (kind) {
+ case kThemePushButtonSmall:
+ case kThemePopupButtonSmall:
+ case kThemeCheckBoxSmall:
+ case kThemeRadioButtonSmall:
+ w.second = QStyleHelper::SizeSmall;
+ break;
+ case kThemePushButtonMini:
+ case kThemePopupButtonMini:
+ case kThemeCheckBoxMini:
+ case kThemeRadioButtonMini:
+ w.second = QStyleHelper::SizeMini;
+ break;
+ default:
+ w.second = QStyleHelper::SizeLarge;
+ break;
+ }
+
+ return w;
+}
+
+static NSButton *makeButton(NSButtonType type, NSBezelStyle style)
+{
+ NSButton *b = [[NSButton alloc] init];
+ b.title = @"";
+ b.buttonType = type;
+ b.bezelStyle = style;
+ return b;
+}
+
+NSView *QMacStylePrivate::cocoaControl(CocoaControl widget) const
+{
+ NSView *bv = cocoaControls.value(widget, nil);
+
+ if (!bv) {
+ switch (widget.first) {
+ case Box: {
+ NSBox *bc = [[NSBox alloc] init];
+ bc.title = @"";
+ bc.titlePosition = NSNoTitle;
+ bc.boxType = NSBoxPrimary;
+ bc.borderType = NSBezelBorder;
+ bv = bc;
+ break;
+ }
+ case Button_CheckBox:
+ bv = makeButton(NSSwitchButton, NSRegularSquareBezelStyle);
+ break;
+ case Button_Disclosure:
+ bv = makeButton(NSOnOffButton, NSDisclosureBezelStyle);
+ break;
+ case Button_PopupButton:
+ case Button_PullDown: {
+ NSPopUpButton *bc = [[NSPopUpButton alloc] init];
+ bc.title = @"";
+ if (widget.first == Button_PullDown)
+ bc.pullsDown = YES;
+ bv = bc;
+ break;
+ }
+ case Button_PushButton:
+ bv = makeButton(NSMomentaryLightButton, NSRoundedBezelStyle);
+ break;
+ case Button_RadioButton:
+ bv = makeButton(NSRadioButton, NSRegularSquareBezelStyle);
+ break;
+ case Button_WindowClose:
+ case Button_WindowMiniaturize:
+ case Button_WindowZoom: {
+ const NSWindowButton button = [=] {
+ switch (widget.first) {
+ case Button_WindowClose:
+ return NSWindowCloseButton;
+ case Button_WindowMiniaturize:
+ return NSWindowMiniaturizeButton;
+ case Button_WindowZoom:
+ return NSWindowZoomButton;
+ default:
+ break;
+ }
+ Q_UNREACHABLE();
+ } ();
+#if QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_12)
+ const auto styleMask = NSWindowStyleMaskTitled
+ | NSWindowStyleMaskClosable
+ | NSWindowStyleMaskMiniaturizable
+ | NSWindowStyleMaskResizable;
+#else
+ const auto styleMask = NSTitledWindowMask
+ | NSClosableWindowMask
+ | NSMiniaturizableWindowMask
+ | NSResizableWindowMask;
+#endif
+ bv = [NSWindow standardWindowButton:button forStyleMask:styleMask];
+ [bv retain];
+ break;
+ }
+ case ComboBox:
+ bv = [[NSComboBox alloc] init];
+ break;
+ case ProgressIndicator_Determinate:
+ bv = [[NSProgressIndicator alloc] init];
+ break;
+ case ProgressIndicator_Indeterminate:
+ bv = [[QIndeterminateProgressIndicator alloc] init];
+ break;
+ case Scroller_Horizontal:
+ bv = [[NSScroller alloc] initWithFrame:NSMakeRect(0, 0, 200, 20)];
+ break;
+ case Scroller_Vertical:
+ // Cocoa sets the orientation from the view's frame
+ // at construction time, and it cannot be changed later.
+ bv = [[NSScroller alloc] initWithFrame:NSMakeRect(0, 0, 20, 200)];
+ break;
+ case Slider_Horizontal:
+ bv = [[NSSlider alloc] initWithFrame:NSMakeRect(0, 0, 200, 20)];
+ break;
+ case Slider_Vertical:
+ // Cocoa sets the orientation from the view's frame
+ // at construction time, and it cannot be changed later.
+ bv = [[NSSlider alloc] initWithFrame:NSMakeRect(0, 0, 20, 200)];
+ break;
+ case SplitView_Horizontal:
+ bv = [[NSSplitView alloc] init];
+ break;
+ case SplitView_Vertical:
+ bv = [[QVerticalSplitView alloc] init];
+ break;
+ case TextField:
+ bv = [[NSTextField alloc] init];
+ break;
+ default:
+ break;
+ }
+
+ if ([bv isKindOfClass:[NSControl class]]) {
+ auto *ctrl = static_cast<NSControl *>(bv);
+ switch (widget.second) {
+ case QStyleHelper::SizeSmall:
+ ctrl.controlSize = NSSmallControlSize;
+ break;
+ case QStyleHelper::SizeMini:
+ ctrl.controlSize = NSMiniControlSize;
+ break;
+ default:
+ break;
+ }
+ } else if (widget.first == ProgressIndicator_Determinate ||
+ widget.first == ProgressIndicator_Indeterminate) {
+ auto *pi = static_cast<NSProgressIndicator *>(bv);
+ pi.indeterminate = (widget.first == ProgressIndicator_Indeterminate);
+ switch (widget.second) {
+ case QStyleHelper::SizeSmall:
+ pi.controlSize = NSSmallControlSize;
+ break;
+ case QStyleHelper::SizeMini:
+ pi.controlSize = NSMiniControlSize;
+ break;
+ default:
+ break;
+ }
+ }
+
+ cocoaControls.insert(widget, bv);
+ }
+
+ return bv;
+}
+
+NSCell *QMacStylePrivate::cocoaCell(CocoaControl widget) const
+{
+ NSCell *cell = cocoaCells[widget];
+ if (!cell) {
+ switch (widget.first) {
+ case Stepper:
+ cell = [[NSStepperCell alloc] init];
+ break;
+ case Button_Disclosure: {
+ NSButtonCell *bc = [[NSButtonCell alloc] init];
+ bc.buttonType = NSOnOffButton;
+ bc.bezelStyle = NSDisclosureBezelStyle;
+ cell = bc;
+ break;
+ }
+ default:
+ break;
+ }
+
+ switch (widget.second) {
+ case QStyleHelper::SizeSmall:
+ cell.controlSize = NSSmallControlSize;
+ break;
+ case QStyleHelper::SizeMini:
+ cell.controlSize = NSMiniControlSize;
+ break;
+ default:
+ break;
+ }
+
+ cocoaCells.insert(widget, cell);
+ }
+
+ return cell;
+}
+
+void QMacStylePrivate::drawNSViewInRect(CocoaControl widget, NSView *view, const QRect &qtRect, QPainter *p, bool isQWidget, __attribute__((noescape)) DrawRectBlock drawRectBlock) const
+{
+ QPoint offset;
+ if (widget == CocoaControl(Button_PopupButton, QStyleHelper::SizeSmall))
+ offset.setY(1);
+ else if (widget == CocoaControl(Button_PopupButton, QStyleHelper::SizeMini))
+ offset = QPoint(2, -1);
+ else if (widget == CocoaControl(Button_PullDown, QStyleHelper::SizeLarge))
+ offset = isQWidget ? QPoint(3, -1) : QPoint(-1, -3);
+ else if (widget == CocoaControl(Button_PullDown, QStyleHelper::SizeSmall))
+ offset = QPoint(2, 1);
+ else if (widget == CocoaControl(Button_PullDown, QStyleHelper::SizeMini))
+ offset = QPoint(5, 0);
+ else if (widget == CocoaControl(ComboBox, QStyleHelper::SizeLarge))
+ offset = QPoint(3, 0);
+
+ QMacCGContext ctx(p);
+ setupNSGraphicsContext(ctx, YES);
+
+ CGContextTranslateCTM(ctx, offset.x(), offset.y());
+
+ const CGRect rect = CGRectMake(qtRect.x(), qtRect.y(), qtRect.width(), qtRect.height());
+
+ [backingStoreNSView addSubview:view];
+ view.frame = rect;
+ if (drawRectBlock)
+ drawRectBlock(ctx, rect);
+ else
+ [view drawRect:rect];
+ [view removeFromSuperviewWithoutNeedingDisplay];
+
+ restoreNSGraphicsContext(ctx);
+}
+
+void QMacStylePrivate::resolveCurrentNSView(QWindow *window) const
+{
+ backingStoreNSView = window ? (NSView *)window->winId() : nil;
+}
+
+void QMacStylePrivate::drawColorlessButton(const CGRect &macRect, HIThemeButtonDrawInfo *bdi, const CocoaControl &cw,
+ QPainter *p, const QStyleOption *opt) const
+{
+ int xoff = 0,
+ yoff = 0,
+ extraWidth = 0,
+ extraHeight = 0,
+ finalyoff = 0;
+
+ const bool combo = opt->type == QStyleOption::SO_ComboBox;
+ const bool editableCombo = bdi->kind == kThemeComboBox
+ || bdi->kind == kThemeComboBoxSmall
+ || bdi->kind == kThemeComboBoxMini;
+ const bool button = opt->type == QStyleOption::SO_Button;
+ const bool viewItem = opt->type == QStyleOption::SO_ViewItem;
+ const bool pressed = bdi->state == kThemeStatePressed;
+
+ if (button && pressed) {
+ if (bdi->kind == kThemePushButton) {
+ extraHeight = 2;
+ } else if (bdi->kind == kThemePushButtonSmall) {
+ xoff = 1;
+ extraWidth = 2;
+ extraHeight = 5;
+ }
+ }
+
+ int devicePixelRatio = p->device()->devicePixelRatioF();
+ int width = devicePixelRatio * (int(macRect.size.width) + extraWidth);
+ int height = devicePixelRatio * (int(macRect.size.height) + extraHeight);
+
+ if (width <= 0 || height <= 0)
+ return; // nothing to draw
+
+ QString key = QLatin1String("$qt_mac_style_ctb_") + QString::number(bdi->kind) + QLatin1Char('_')
+ + QString::number(bdi->value) + QLatin1Char('_')
+ + (button ? QString::number(bdi->state) + QLatin1Char('_') : QString())
+ + QLatin1Char('_') + QString::number(width) + QLatin1Char('_') + QString::number(height);
+ QPixmap pm;
+ if (!QPixmapCache::find(key, pm)) {
+ QPixmap activePixmap(width, height);
+ activePixmap.setDevicePixelRatio(devicePixelRatio);
+ activePixmap.fill(Qt::transparent);
+ {
+ if (combo){
+ // Carbon combos don't scale. Therefore we draw it
+ // ourselves, if a scaled version is needed.
+ QPainter tmpPainter(&activePixmap);
+ QMacStylePrivate::drawCombobox(macRect, *bdi, cw, &tmpPainter);
+ } else {
+ QMacCGContext cg(&activePixmap);
+ CGRect newRect = CGRectMake(xoff, yoff, macRect.size.width, macRect.size.height);
+ if (button && pressed)
+ bdi->state = kThemeStateActive;
+ else if (viewItem)
+ bdi->state = kThemeStateInactive;
+ HIThemeDrawButton(&newRect, bdi, cg, kHIThemeOrientationNormal, 0);
+ }
+ }
+
+ if (!combo && !button && bdi->value == kThemeButtonOff) {
+ pm = activePixmap;
+ } else if ((combo && !editableCombo) || button) {
+ CocoaControl cw = cocoaControlFromHIThemeButtonKind(bdi->kind);
+ NSButton *bc = (NSButton *)cocoaControl(cw);
+ [bc highlight:pressed];
+ bc.enabled = bdi->state != kThemeStateUnavailable && bdi->state != kThemeStateUnavailableInactive;
+ bc.allowsMixedState = YES;
+ bc.state = bdi->value == kThemeButtonOn ? NSOnState :
+ bdi->value == kThemeButtonMixed ? NSMixedState : NSOffState;
+ // The view frame may differ from what we pass to HITheme
+ QRect rect = opt->rect;
+ if (bdi->kind == kThemePopupButtonMini)
+ rect.adjust(0, 0, -5, 0);
+ drawNSViewInRect(cw, bc, rect, p);
+ return;
+ } else if (editableCombo || viewItem) {
+ QImage image = activePixmap.toImage();
+
+ for (int y = 0; y < height; ++y) {
+ QRgb *scanLine = reinterpret_cast<QRgb *>(image.scanLine(y));
+
+ for (int x = 0; x < width; ++x) {
+ QRgb &pixel = scanLine[x];
+ int gray = qRed(pixel); // We know the image is grayscale
+ int alpha = qAlpha(pixel);
+
+ if (gray == 128 && alpha == 128) {
+ pixel = qRgba(255, 255, 255, 255);
+ } else if (alpha == 0) {
+ pixel = 0;
+ } else {
+ bool belowThreshold = (alpha * gray) / 255 + 255 - alpha < 128;
+ gray = belowThreshold ? 0 : 2 * gray - 255;
+ alpha = belowThreshold ? 0 : 2 * alpha - 255;
+ pixel = qRgba(gray, gray, gray, alpha);
+ }
+ }
+ }
+ pm = QPixmap::fromImage(image);
+ } else {
+ QImage activeImage = activePixmap.toImage();
+ QImage colorlessImage;
+ {
+ QPixmap colorlessPixmap(width, height);
+ colorlessPixmap.setDevicePixelRatio(devicePixelRatio);
+ colorlessPixmap.fill(Qt::transparent);
+
+ QMacCGContext cg(&colorlessPixmap);
+ CGRect newRect = CGRectMake(xoff, yoff, macRect.size.width, macRect.size.height);
+ int oldValue = bdi->value;
+ bdi->value = kThemeButtonOff;
+ HIThemeDrawButton(&newRect, bdi, cg, kHIThemeOrientationNormal, 0);
+ bdi->value = oldValue;
+ colorlessImage = colorlessPixmap.toImage();
+ }
+
+ for (int y = 0; y < height; ++y) {
+ QRgb *colorlessScanLine = reinterpret_cast<QRgb *>(colorlessImage.scanLine(y));
+ const QRgb *activeScanLine = reinterpret_cast<const QRgb *>(activeImage.scanLine(y));
+
+ for (int x = 0; x < width; ++x) {
+ QRgb &colorlessPixel = colorlessScanLine[x];
+ QRgb activePixel = activeScanLine[x];
+
+ if (activePixel != colorlessPixel) {
+ int max = qMax(qMax(qRed(activePixel), qGreen(activePixel)),
+ qBlue(activePixel));
+ QRgb newPixel = qRgba(max, max, max, qAlpha(activePixel));
+ if (qGray(newPixel) < qGray(colorlessPixel)
+ || qAlpha(newPixel) > qAlpha(colorlessPixel))
+ colorlessPixel = newPixel;
+ }
+ }
+ }
+ pm = QPixmap::fromImage(colorlessImage);
+ }
+ QPixmapCache::insert(key, pm);
+ }
+ p->drawPixmap(int(macRect.origin.x) - xoff, int(macRect.origin.y) + finalyoff, width / devicePixelRatio, height / devicePixelRatio , pm);
+}
+
+QMacStyle::QMacStyle()
+ : QCommonStyle(*new QMacStylePrivate)
+{
+ Q_D(QMacStyle);
+ QMacAutoReleasePool pool;
+
+ d->receiver = [[NotificationReceiver alloc] initWithPrivate:d];
+ [[NSNotificationCenter defaultCenter] addObserver:d->receiver
+ selector:@selector(scrollBarStyleDidChange:)
+ name:NSPreferredScrollerStyleDidChangeNotification
+ object:nil];
+}
+
+QMacStyle::~QMacStyle()
+{
+ Q_D(QMacStyle);
+ QMacAutoReleasePool pool;
+
+ [[NSNotificationCenter defaultCenter] removeObserver:d->receiver];
+ [d->receiver release];
+}
+
+void QMacStyle::polish(QPalette &)
+{
+}
+
+void QMacStyle::polish(QApplication *)
+{
+}
+
+void QMacStyle::unpolish(QApplication *)
+{
+}
+
+void QMacStyle::polish(QWidget* w)
+{
+ if (false
+#if QT_CONFIG(menu)
+ || qobject_cast<QMenu*>(w)
+# if QT_CONFIG(combobox)
+ || qobject_cast<QComboBoxPrivateContainer *>(w)
+# endif
+#endif
+#if QT_CONFIG(mdiarea)
+ || qobject_cast<QMdiSubWindow *>(w)
+#endif
+ ) {
+ w->setAttribute(Qt::WA_TranslucentBackground, true);
+ w->setAutoFillBackground(false);
+ }
+
+#if QT_CONFIG(tabbar)
+ if (QTabBar *tb = qobject_cast<QTabBar*>(w)) {
+ if (tb->documentMode()) {
+ w->setAttribute(Qt::WA_Hover);
+ w->setFont(qt_app_fonts_hash()->value("QSmallFont", QFont()));
+ QPalette p = w->palette();
+ p.setColor(QPalette::WindowText, QColor(17, 17, 17));
+ w->setPalette(p);
+ w->setAttribute(Qt::WA_SetPalette, false);
+ w->setAttribute(Qt::WA_SetFont, false);
+ }
+ }
+#endif
+
+ QCommonStyle::polish(w);
+
+ if (QRubberBand *rubber = qobject_cast<QRubberBand*>(w)) {
+ rubber->setWindowOpacity(0.25);
+ rubber->setAttribute(Qt::WA_PaintOnScreen, false);
+ rubber->setAttribute(Qt::WA_NoSystemBackground, false);
+ }
+
+ if (qobject_cast<QScrollBar*>(w)) {
+ w->setAttribute(Qt::WA_OpaquePaintEvent, false);
+ w->setAttribute(Qt::WA_Hover, true);
+ w->setMouseTracking(true);
+ }
+}
+
+void QMacStyle::unpolish(QWidget* w)
+{
+ if (
+#if QT_CONFIG(menu)
+ qobject_cast<QMenu*>(w) &&
+#endif
+ !w->testAttribute(Qt::WA_SetPalette)) {
+ QPalette pal = qApp->palette(w);
+ w->setPalette(pal);
+ w->setAttribute(Qt::WA_SetPalette, false);
+ w->setWindowOpacity(1.0);
+ }
+
+#if QT_CONFIG(combobox)
+ if (QComboBox *combo = qobject_cast<QComboBox *>(w)) {
+ if (!combo->isEditable()) {
+ if (QWidget *widget = combo->findChild<QComboBoxPrivateContainer *>())
+ widget->setWindowOpacity(1.0);
+ }
+ }
+#endif
+
+#if QT_CONFIG(tabbar)
+ if (qobject_cast<QTabBar*>(w)) {
+ if (!w->testAttribute(Qt::WA_SetFont))
+ w->setFont(qApp->font(w));
+ if (!w->testAttribute(Qt::WA_SetPalette))
+ w->setPalette(qApp->palette(w));
+ }
+#endif
+
+ if (QRubberBand *rubber = qobject_cast<QRubberBand*>(w)) {
+ rubber->setWindowOpacity(1.0);
+ rubber->setAttribute(Qt::WA_PaintOnScreen, true);
+ rubber->setAttribute(Qt::WA_NoSystemBackground, true);
+ }
+
+ if (QFocusFrame *frame = qobject_cast<QFocusFrame *>(w))
+ frame->setAttribute(Qt::WA_NoSystemBackground, true);
+
+ QCommonStyle::unpolish(w);
+
+ if (qobject_cast<QScrollBar*>(w)) {
+ w->setAttribute(Qt::WA_OpaquePaintEvent, true);
+ w->setAttribute(Qt::WA_Hover, false);
+ w->setMouseTracking(false);
+ }
+}
+
+int QMacStyle::pixelMetric(PixelMetric metric, const QStyleOption *opt, const QWidget *widget) const
+{
+ Q_D(const QMacStyle);
+ const int controlSize = getControlSize(opt, widget);
+ int ret = 0;
+
+ switch (metric) {
+ case PM_TabCloseIndicatorWidth:
+ case PM_TabCloseIndicatorHeight:
+ ret = closeButtonSize;
+ break;
+ case PM_ToolBarIconSize:
+ ret = proxy()->pixelMetric(PM_LargeIconSize);
+ break;
+ case PM_FocusFrameVMargin:
+ case PM_FocusFrameHMargin:
+ ret = qt_mac_aqua_get_metric(FocusRectOutset);
+ break;
+ case PM_DialogButtonsSeparator:
+ ret = -5;
+ break;
+ case PM_DialogButtonsButtonHeight: {
+ QSize sz;
+ ret = d->aquaSizeConstrain(opt, 0, QStyle::CT_PushButton, QSize(-1, -1), &sz);
+ if (sz == QSize(-1, -1))
+ ret = 32;
+ else
+ ret = sz.height();
+ break; }
+ case PM_DialogButtonsButtonWidth: {
+ QSize sz;
+ ret = d->aquaSizeConstrain(opt, 0, QStyle::CT_PushButton, QSize(-1, -1), &sz);
+ if (sz == QSize(-1, -1))
+ ret = 70;
+ else
+ ret = sz.width();
+ break; }
+
+ case PM_MenuBarHMargin:
+ ret = 8;
+ break;
+
+ case PM_MenuBarVMargin:
+ ret = 0;
+ break;
+
+ case PM_MenuBarPanelWidth:
+ ret = 0;
+ break;
+
+ case QStyle::PM_MenuDesktopFrameWidth:
+ ret = 5;
+ break;
+
+ case PM_CheckBoxLabelSpacing:
+ case PM_RadioButtonLabelSpacing:
+ ret = [=] {
+ if (opt) {
+ if (opt->state & State_Mini)
+ return 4;
+ if (opt->state & State_Small)
+ return 3;
+ }
+ return 2;
+ } ();
+ break;
+ case PM_MenuScrollerHeight:
+ ret = 15; // I hate having magic numbers in here...
+ break;
+ case PM_DefaultFrameWidth:
+#if QT_CONFIG(mainwindow)
+ if (widget && (widget->isWindow() || !widget->parentWidget()
+ || (qobject_cast<const QMainWindow*>(widget->parentWidget())
+ && static_cast<QMainWindow *>(widget->parentWidget())->centralWidget() == widget))
+ && qobject_cast<const QAbstractScrollArea *>(widget))
+ ret = 0;
+ else
+#endif
+ // The combo box popup has no frame.
+ if (qstyleoption_cast<const QStyleOptionComboBox *>(opt) != 0)
+ ret = 0;
+ else
+ ret = 1;
+ break;
+ case PM_MaximumDragDistance:
+ ret = -1;
+ break;
+ case PM_ScrollBarSliderMin:
+ ret = 24;
+ break;
+ case PM_SpinBoxFrameWidth:
+ ret = qt_mac_aqua_get_metric(EditTextFrameOutset);
+ break;
+ case PM_ButtonShiftHorizontal:
+ case PM_ButtonShiftVertical:
+ ret = 0;
+ break;
+ case PM_SliderLength:
+ ret = 17;
+ break;
+ // Returns the number of pixels to use for the business part of the
+ // slider (i.e., the non-tickmark portion). The remaining space is shared
+ // equally between the tickmark regions.
+ case PM_SliderControlThickness:
+ if (const QStyleOptionSlider *sl = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ int space = (sl->orientation == Qt::Horizontal) ? sl->rect.height() : sl->rect.width();
+ int ticks = sl->tickPosition;
+ int n = 0;
+ if (ticks & QSlider::TicksAbove)
+ ++n;
+ if (ticks & QSlider::TicksBelow)
+ ++n;
+ if (!n) {
+ ret = space;
+ break;
+ }
+
+ int thick = 6; // Magic constant to get 5 + 16 + 5
+ if (ticks != QSlider::TicksBothSides && ticks != QSlider::NoTicks)
+ thick += proxy()->pixelMetric(PM_SliderLength, sl, widget) / 4;
+
+ space -= thick;
+ if (space > 0)
+ thick += (space * 2) / (n + 2);
+ ret = thick;
+ } else {
+ ret = 0;
+ }
+ break;
+ case PM_SmallIconSize:
+ ret = int(QStyleHelper::dpiScaled(16.));
+ break;
+
+ case PM_LargeIconSize:
+ ret = int(QStyleHelper::dpiScaled(32.));
+ break;
+
+ case PM_IconViewIconSize:
+ ret = proxy()->pixelMetric(PM_LargeIconSize, opt, widget);
+ break;
+
+ case PM_ButtonDefaultIndicator:
+ ret = 0;
+ break;
+ case PM_TitleBarHeight: {
+ NSUInteger style = NSTitledWindowMask;
+ if (widget && ((widget->windowFlags() & Qt::Tool) == Qt::Tool))
+ style |= NSUtilityWindowMask;
+ ret = int([NSWindow frameRectForContentRect:NSZeroRect
+ styleMask:style].size.height);
+ break; }
+ case QStyle::PM_TabBarTabHSpace:
+ switch (d->aquaSizeConstrain(opt, widget)) {
+ case QStyleHelper::SizeLarge:
+ ret = QCommonStyle::pixelMetric(metric, opt, widget);
+ break;
+ case QStyleHelper::SizeSmall:
+ ret = 20;
+ break;
+ case QStyleHelper::SizeMini:
+ ret = 16;
+ break;
+ case QStyleHelper::SizeDefault:
+ const QStyleOptionTab *tb = qstyleoption_cast<const QStyleOptionTab *>(opt);
+ if (tb && tb->documentMode)
+ ret = 30;
+ else
+ ret = QCommonStyle::pixelMetric(metric, opt, widget);
+ break;
+ }
+ break;
+ case PM_TabBarTabVSpace:
+ ret = 4;
+ break;
+ case PM_TabBarTabShiftHorizontal:
+ case PM_TabBarTabShiftVertical:
+ ret = 0;
+ break;
+ case PM_TabBarBaseHeight:
+ ret = 0;
+ break;
+ case PM_TabBarTabOverlap:
+ ret = 1;
+ break;
+ case PM_TabBarBaseOverlap:
+ switch (d->aquaSizeConstrain(opt, widget)) {
+ case QStyleHelper::SizeDefault:
+ case QStyleHelper::SizeLarge:
+ ret = 11;
+ break;
+ case QStyleHelper::SizeSmall:
+ ret = 8;
+ break;
+ case QStyleHelper::SizeMini:
+ ret = 7;
+ break;
+ }
+ break;
+ case PM_ScrollBarExtent: {
+ const QStyleHelper::WidgetSizePolicy size = d->effectiveAquaSizeConstrain(opt, widget);
+ ret = static_cast<int>([NSScroller
+ scrollerWidthForControlSize:static_cast<NSControlSize>(size)
+ scrollerStyle:[NSScroller preferredScrollerStyle]]);
+ break; }
+ case PM_IndicatorHeight: {
+ switch (d->aquaSizeConstrain(opt, widget)) {
+ case QStyleHelper::SizeDefault:
+ case QStyleHelper::SizeLarge:
+ ret = qt_mac_aqua_get_metric(CheckBoxHeight);
+ break;
+ case QStyleHelper::SizeMini:
+ ret = qt_mac_aqua_get_metric(MiniCheckBoxHeight);
+ break;
+ case QStyleHelper::SizeSmall:
+ ret = qt_mac_aqua_get_metric(SmallCheckBoxHeight);
+ break;
+ }
+ break; }
+ case PM_IndicatorWidth: {
+ switch (d->aquaSizeConstrain(opt, widget)) {
+ case QStyleHelper::SizeDefault:
+ case QStyleHelper::SizeLarge:
+ ret = qt_mac_aqua_get_metric(CheckBoxWidth);
+ break;
+ case QStyleHelper::SizeMini:
+ ret = qt_mac_aqua_get_metric(MiniCheckBoxWidth);
+ break;
+ case QStyleHelper::SizeSmall:
+ ret = qt_mac_aqua_get_metric(SmallCheckBoxWidth);
+ break;
+ }
+ ++ret;
+ break; }
+ case PM_ExclusiveIndicatorHeight: {
+ switch (d->aquaSizeConstrain(opt, widget)) {
+ case QStyleHelper::SizeDefault:
+ case QStyleHelper::SizeLarge:
+ ret = qt_mac_aqua_get_metric(RadioButtonHeight);
+ break;
+ case QStyleHelper::SizeMini:
+ ret = qt_mac_aqua_get_metric(MiniRadioButtonHeight);
+ break;
+ case QStyleHelper::SizeSmall:
+ ret = qt_mac_aqua_get_metric(SmallRadioButtonHeight);
+ break;
+ }
+ break; }
+ case PM_ExclusiveIndicatorWidth: {
+ switch (d->aquaSizeConstrain(opt, widget)) {
+ case QStyleHelper::SizeDefault:
+ case QStyleHelper::SizeLarge:
+ ret = qt_mac_aqua_get_metric(RadioButtonWidth);
+ break;
+ case QStyleHelper::SizeMini:
+ ret = qt_mac_aqua_get_metric(MiniRadioButtonWidth);
+ break;
+ case QStyleHelper::SizeSmall:
+ ret = qt_mac_aqua_get_metric(SmallRadioButtonWidth);
+ break;
+ }
+ ++ret;
+ break; }
+ case PM_MenuVMargin:
+ ret = 4;
+ break;
+ case PM_MenuPanelWidth:
+ ret = 0;
+ break;
+ case PM_ToolTipLabelFrameWidth:
+ ret = 0;
+ break;
+ case PM_SizeGripSize: {
+ QStyleHelper::WidgetSizePolicy aSize;
+ if (widget && widget->window()->windowType() == Qt::Tool)
+ aSize = QStyleHelper::SizeSmall;
+ else
+ aSize = QStyleHelper::SizeLarge;
+ const QSize size = qt_aqua_get_known_size(CT_SizeGrip, widget, QSize(), aSize);
+ ret = size.width();
+ break; }
+ case PM_MdiSubWindowFrameWidth:
+ ret = 1;
+ break;
+ case PM_DockWidgetFrameWidth:
+ ret = 0;
+ break;
+ case PM_DockWidgetTitleMargin:
+ ret = 0;
+ break;
+ case PM_DockWidgetSeparatorExtent:
+ ret = 1;
+ break;
+ case PM_ToolBarHandleExtent:
+ ret = 11;
+ break;
+ case PM_ToolBarItemMargin:
+ ret = 0;
+ break;
+ case PM_ToolBarItemSpacing:
+ ret = 4;
+ break;
+ case PM_SplitterWidth:
+ ret = qMax(7, QApplication::globalStrut().width());
+ break;
+ case PM_LayoutLeftMargin:
+ case PM_LayoutTopMargin:
+ case PM_LayoutRightMargin:
+ case PM_LayoutBottomMargin:
+ {
+ bool isWindow = false;
+ if (opt) {
+ isWindow = (opt->state & State_Window);
+ } else if (widget) {
+ isWindow = widget->isWindow();
+ }
+
+ if (isWindow) {
+ /*
+ AHIG would have (20, 8, 10) here but that makes
+ no sense. It would also have 14 for the top margin
+ but this contradicts both Builder and most
+ applications.
+ */
+ return_SIZE(20, 10, 10); // AHIG
+ } else {
+ // hack to detect QTabWidget
+ if (widget && widget->parentWidget()
+ && widget->parentWidget()->sizePolicy().controlType() == QSizePolicy::TabWidget) {
+ if (metric == PM_LayoutTopMargin) {
+ /*
+ Builder would have 14 (= 20 - 6) instead of 12,
+ but that makes the tab look disproportionate.
+ */
+ return_SIZE(12, 6, 6); // guess
+ } else {
+ return_SIZE(20 /* Builder */, 8 /* guess */, 8 /* guess */);
+ }
+ } else {
+ /*
+ Child margins are highly inconsistent in AHIG and Builder.
+ */
+ return_SIZE(12, 8, 6); // guess
+ }
+ }
+ }
+ case PM_LayoutHorizontalSpacing:
+ case PM_LayoutVerticalSpacing:
+ return -1;
+ case PM_MenuHMargin:
+ ret = 0;
+ break;
+ case PM_ToolBarExtensionExtent:
+ ret = 21;
+ break;
+ case PM_ToolBarFrameWidth:
+ ret = 1;
+ break;
+ case PM_ScrollView_ScrollBarOverlap:
+ ret = [NSScroller preferredScrollerStyle] == NSScrollerStyleOverlay ?
+ pixelMetric(PM_ScrollBarExtent, opt, widget) : 0;
+ break;
+ default:
+ ret = QCommonStyle::pixelMetric(metric, opt, widget);
+ break;
+ }
+ return ret;
+}
+
+QPalette QMacStyle::standardPalette() const
+{
+ QPalette pal = QCommonStyle::standardPalette();
+ pal.setColor(QPalette::Disabled, QPalette::Dark, QColor(191, 191, 191));
+ pal.setColor(QPalette::Active, QPalette::Dark, QColor(191, 191, 191));
+ pal.setColor(QPalette::Inactive, QPalette::Dark, QColor(191, 191, 191));
+ return pal;
+}
+
+int QMacStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWidget *w,
+ QStyleHintReturn *hret) const
+{
+ QMacAutoReleasePool pool;
+
+ int ret = 0;
+ switch (sh) {
+ case SH_Slider_SnapToValue:
+ case SH_PrintDialog_RightAlignButtons:
+ case SH_FontDialog_SelectAssociatedText:
+ case SH_MenuBar_MouseTracking:
+ case SH_Menu_MouseTracking:
+ case SH_ComboBox_ListMouseTracking:
+ case SH_MainWindow_SpaceBelowMenuBar:
+ case SH_ItemView_ChangeHighlightOnFocus:
+ ret = 1;
+ break;
+ case SH_ToolBox_SelectedPageTitleBold:
+ ret = 0;
+ break;
+ case SH_DialogButtonBox_ButtonsHaveIcons:
+ ret = 0;
+ break;
+ case SH_Menu_SelectionWrap:
+ ret = false;
+ break;
+ case SH_Menu_KeyboardSearch:
+ ret = true;
+ break;
+ case SH_Menu_SpaceActivatesItem:
+ ret = true;
+ break;
+ case SH_Slider_AbsoluteSetButtons:
+ ret = Qt::LeftButton|Qt::MidButton;
+ break;
+ case SH_Slider_PageSetButtons:
+ ret = 0;
+ break;
+ case SH_ScrollBar_ContextMenu:
+ ret = false;
+ break;
+ case SH_TitleBar_AutoRaise:
+ ret = true;
+ break;
+ case SH_Menu_AllowActiveAndDisabled:
+ ret = false;
+ break;
+ case SH_Menu_SubMenuPopupDelay:
+ ret = 100;
+ break;
+ case SH_Menu_SubMenuUniDirection:
+ ret = true;
+ break;
+ case SH_Menu_SubMenuSloppySelectOtherActions:
+ ret = false;
+ break;
+ case SH_Menu_SubMenuResetWhenReenteringParent:
+ ret = true;
+ break;
+ case SH_Menu_SubMenuDontStartSloppyOnLeave:
+ ret = true;
+ break;
+
+ case SH_ScrollBar_LeftClickAbsolutePosition: {
+ NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
+ bool result = [defaults boolForKey:@"AppleScrollerPagingBehavior"];
+ if(QApplication::keyboardModifiers() & Qt::AltModifier)
+ ret = !result;
+ else
+ ret = result;
+ break; }
+ case SH_TabBar_PreferNoArrows:
+ ret = true;
+ break;
+ /*
+ case SH_DialogButtons_DefaultButton:
+ ret = QDialogButtons::Reject;
+ break;
+ */
+ case SH_GroupBox_TextLabelVerticalAlignment:
+ ret = Qt::AlignTop;
+ break;
+ case SH_ScrollView_FrameOnlyAroundContents:
+ ret = QCommonStyle::styleHint(sh, opt, w, hret);
+ break;
+ case SH_Menu_FillScreenWithScroll:
+ ret = false;
+ break;
+ case SH_Menu_Scrollable:
+ ret = true;
+ break;
+ case SH_RichText_FullWidthSelection:
+ ret = true;
+ break;
+ case SH_BlinkCursorWhenTextSelected:
+ ret = false;
+ break;
+ case SH_ScrollBar_StopMouseOverSlider:
+ ret = true;
+ break;
+ case SH_ListViewExpand_SelectMouseType:
+ ret = QEvent::MouseButtonRelease;
+ break;
+ case SH_TabBar_SelectMouseType:
+#if QT_CONFIG(tabbar)
+ if (const QStyleOptionTabBarBase *opt2 = qstyleoption_cast<const QStyleOptionTabBarBase *>(opt)) {
+ ret = opt2->documentMode ? QEvent::MouseButtonPress : QEvent::MouseButtonRelease;
+ } else
+#endif
+ {
+ ret = QEvent::MouseButtonRelease;
+ }
+ break;
+ case SH_ComboBox_Popup:
+ if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(opt))
+ ret = !cmb->editable;
+ else
+ ret = 0;
+ break;
+ case SH_Workspace_FillSpaceOnMaximize:
+ ret = true;
+ break;
+ case SH_Widget_ShareActivation:
+ ret = true;
+ break;
+ case SH_Header_ArrowAlignment:
+ ret = Qt::AlignRight;
+ break;
+ case SH_TabBar_Alignment: {
+#if QT_CONFIG(tabwidget)
+ if (const QTabWidget *tab = qobject_cast<const QTabWidget*>(w)) {
+ if (tab->documentMode()) {
+ ret = Qt::AlignLeft;
+ break;
+ }
+ }
+#endif
+#if QT_CONFIG(tabbar)
+ if (const QTabBar *tab = qobject_cast<const QTabBar*>(w)) {
+ if (tab->documentMode()) {
+ ret = Qt::AlignLeft;
+ break;
+ }
+ }
+#endif
+ ret = Qt::AlignCenter;
+ } break;
+ case SH_UnderlineShortcut:
+ ret = false;
+ break;
+ case SH_ToolTipLabel_Opacity:
+ ret = 242; // About 95%
+ break;
+ case SH_Button_FocusPolicy:
+ ret = Qt::TabFocus;
+ break;
+ case SH_EtchDisabledText:
+ ret = false;
+ break;
+ case SH_FocusFrame_Mask: {
+ ret = true;
+ if(QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask*>(hret)) {
+ const uchar fillR = 192, fillG = 191, fillB = 190;
+ QImage img;
+
+ QSize pixmapSize = opt->rect.size();
+ if (!pixmapSize.isEmpty()) {
+ QPixmap pix(pixmapSize);
+ pix.fill(QColor(fillR, fillG, fillB));
+ QPainter pix_paint(&pix);
+ proxy()->drawControl(CE_FocusFrame, opt, &pix_paint, w);
+ pix_paint.end();
+ img = pix.toImage();
+ }
+
+ const QRgb *sptr = (QRgb*)img.bits(), *srow;
+ const int sbpl = img.bytesPerLine();
+ const int w = sbpl/4, h = img.height();
+
+ QImage img_mask(img.width(), img.height(), QImage::Format_ARGB32);
+ QRgb *dptr = (QRgb*)img_mask.bits(), *drow;
+ const int dbpl = img_mask.bytesPerLine();
+
+ for (int y = 0; y < h; ++y) {
+ srow = sptr+((y*sbpl)/4);
+ drow = dptr+((y*dbpl)/4);
+ for (int x = 0; x < w; ++x) {
+ const int redDiff = qRed(*srow) - fillR;
+ const int greenDiff = qGreen(*srow) - fillG;
+ const int blueDiff = qBlue(*srow) - fillB;
+ const int diff = (redDiff * redDiff) + (greenDiff * greenDiff) + (blueDiff * blueDiff);
+ (*drow++) = (diff < 10) ? 0xffffffff : 0xff000000;
+ ++srow;
+ }
+ }
+ QBitmap qmask = QBitmap::fromImage(img_mask);
+ mask->region = QRegion(qmask);
+ }
+ break; }
+ case SH_TitleBar_NoBorder:
+ ret = 1;
+ break;
+ case SH_RubberBand_Mask:
+ ret = 0;
+ break;
+ case SH_ComboBox_LayoutDirection:
+ ret = Qt::LeftToRight;
+ break;
+ case SH_ItemView_EllipsisLocation:
+ ret = Qt::AlignHCenter;
+ break;
+ case SH_ItemView_ShowDecorationSelected:
+ ret = true;
+ break;
+ case SH_TitleBar_ModifyNotification:
+ ret = false;
+ break;
+ case SH_ScrollBar_RollBetweenButtons:
+ ret = true;
+ break;
+ case SH_WindowFrame_Mask:
+ ret = false;
+ break;
+ case SH_TabBar_ElideMode:
+ ret = Qt::ElideRight;
+ break;
+#if QT_CONFIG(dialogbuttonbox)
+ case SH_DialogButtonLayout:
+ ret = QDialogButtonBox::MacLayout;
+ break;
+#endif
+ case SH_FormLayoutWrapPolicy:
+ ret = QFormLayout::DontWrapRows;
+ break;
+ case SH_FormLayoutFieldGrowthPolicy:
+ ret = QFormLayout::FieldsStayAtSizeHint;
+ break;
+ case SH_FormLayoutFormAlignment:
+ ret = Qt::AlignHCenter | Qt::AlignTop;
+ break;
+ case SH_FormLayoutLabelAlignment:
+ ret = Qt::AlignRight;
+ break;
+ case SH_ComboBox_PopupFrameStyle:
+ ret = QFrame::NoFrame;
+ break;
+ case SH_MessageBox_TextInteractionFlags:
+ ret = Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse | Qt::TextSelectableByKeyboard;
+ break;
+ case SH_SpellCheckUnderlineStyle:
+ ret = QTextCharFormat::DashUnderline;
+ break;
+ case SH_MessageBox_CenterButtons:
+ ret = false;
+ break;
+ case SH_MenuBar_AltKeyNavigation:
+ ret = false;
+ break;
+ case SH_ItemView_MovementWithoutUpdatingSelection:
+ ret = false;
+ break;
+ case SH_FocusFrame_AboveWidget:
+ ret = true;
+ break;
+#if QT_CONFIG(wizard)
+ case SH_WizardStyle:
+ ret = QWizard::MacStyle;
+ break;
+#endif
+ case SH_ItemView_ArrowKeysNavigateIntoChildren:
+ ret = false;
+ break;
+ case SH_Menu_FlashTriggeredItem:
+ ret = true;
+ break;
+ case SH_Menu_FadeOutOnHide:
+ ret = true;
+ break;
+ case SH_ItemView_PaintAlternatingRowColorsForEmptyArea:
+ ret = true;
+ break;
+#if QT_CONFIG(tabbar)
+ case SH_TabBar_CloseButtonPosition:
+ ret = QTabBar::LeftSide;
+ break;
+#endif
+ case SH_DockWidget_ButtonsHaveFrame:
+ ret = false;
+ break;
+ case SH_ScrollBar_Transient:
+ if ((qobject_cast<const QScrollBar *>(w) && w->parent() &&
+ qobject_cast<QAbstractScrollArea*>(w->parent()->parent()))
+#ifndef QT_NO_ACCESSIBILITY
+ || (opt && QStyleHelper::hasAncestor(opt->styleObject, QAccessible::ScrollBar))
+#endif
+ ) {
+ ret = [NSScroller preferredScrollerStyle] == NSScrollerStyleOverlay;
+ }
+ break;
+ case SH_ItemView_ScrollMode:
+ ret = QAbstractItemView::ScrollPerPixel;
+ break;
+ case SH_TitleBar_ShowToolTipsOnButtons:
+ // min/max/close buttons on windows don't show tool tips
+ ret = false;
+ break;
+ case SH_ComboBox_AllowWheelScrolling:
+ ret = false;
+ break;
+ case SH_SpinBox_ButtonsInsideFrame:
+ ret = false;
+ break;
+ default:
+ ret = QCommonStyle::styleHint(sh, opt, w, hret);
+ break;
+ }
+ return ret;
+}
+
+QPixmap QMacStyle::generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap,
+ const QStyleOption *opt) const
+{
+ switch (iconMode) {
+ case QIcon::Disabled: {
+ QImage img = pixmap.toImage().convertToFormat(QImage::Format_ARGB32);
+ int imgh = img.height();
+ int imgw = img.width();
+ QRgb pixel;
+ for (int y = 0; y < imgh; ++y) {
+ for (int x = 0; x < imgw; ++x) {
+ pixel = img.pixel(x, y);
+ img.setPixel(x, y, qRgba(qRed(pixel), qGreen(pixel), qBlue(pixel),
+ qAlpha(pixel) / 2));
+ }
+ }
+ return QPixmap::fromImage(img);
+ }
+ default:
+ ;
+ }
+ return QCommonStyle::generatedIconPixmap(iconMode, pixmap, opt);
+}
+
+
+QPixmap QMacStyle::standardPixmap(StandardPixmap standardPixmap, const QStyleOption *opt,
+ const QWidget *widget) const
+{
+ // The default implementation of QStyle::standardIconImplementation() is to call standardPixmap()
+ // I don't want infinite recursion so if we do get in that situation, just return the Window's
+ // standard pixmap instead (since there is no mac-specific icon then). This should be fine until
+ // someone changes how Windows standard
+ // pixmap works.
+ static bool recursionGuard = false;
+
+ if (recursionGuard)
+ return QCommonStyle::standardPixmap(standardPixmap, opt, widget);
+
+ recursionGuard = true;
+ QIcon icon = proxy()->standardIcon(standardPixmap, opt, widget);
+ recursionGuard = false;
+ int size;
+ switch (standardPixmap) {
+ default:
+ size = 32;
+ break;
+ case SP_MessageBoxCritical:
+ case SP_MessageBoxQuestion:
+ case SP_MessageBoxInformation:
+ case SP_MessageBoxWarning:
+ size = 64;
+ break;
+ }
+ return icon.pixmap(qt_getWindow(widget), QSize(size, size));
+}
+
+void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p,
+ const QWidget *w) const
+{
+ Q_D(const QMacStyle);
+ ThemeDrawState tds = d->getDrawState(opt->state);
+ QMacCGContext cg(p);
+ QWindow *window = w && w->window() ? w->window()->windowHandle() :
+ QStyleHelper::styleObjectWindow(opt->styleObject);
+ d->resolveCurrentNSView(window);
+ switch (pe) {
+ case PE_IndicatorArrowUp:
+ case PE_IndicatorArrowDown:
+ case PE_IndicatorArrowRight:
+ case PE_IndicatorArrowLeft: {
+ p->save();
+ p->setRenderHint(QPainter::Antialiasing);
+ const int xOffset = 1; // FIXME: opt->direction == Qt::LeftToRight ? 2 : -1;
+ qreal halfSize = 0.5 * qMin(opt->rect.width(), opt->rect.height());
+ const qreal penWidth = qMax(halfSize / 3.0, 1.25);
+#if QT_CONFIG(toolbutton)
+ if (const QToolButton *tb = qobject_cast<const QToolButton *>(w)) {
+ // When stroking the arrow, make sure it fits in the tool button
+ if (tb->arrowType() != Qt::NoArrow)
+ halfSize -= penWidth;
+ }
+#endif
+
+ QMatrix matrix;
+ matrix.translate(opt->rect.center().x() + xOffset, opt->rect.center().y() + 2);
+ QPainterPath path;
+ switch(pe) {
+ default:
+ case PE_IndicatorArrowDown:
+ break;
+ case PE_IndicatorArrowUp:
+ matrix.rotate(180);
+ break;
+ case PE_IndicatorArrowLeft:
+ matrix.rotate(90);
+ break;
+ case PE_IndicatorArrowRight:
+ matrix.rotate(-90);
+ break;
+ }
+ p->setMatrix(matrix);
+
+ path.moveTo(-halfSize, -halfSize * 0.5);
+ path.lineTo(0.0, halfSize * 0.5);
+ path.lineTo(halfSize, -halfSize * 0.5);
+
+ const QPen arrowPen(opt->palette.text(), penWidth,
+ Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
+ p->strokePath(path, arrowPen);
+ p->restore();
+ break; }
+#if QT_CONFIG(tabbar)
+ case PE_FrameTabBarBase:
+ if (const QStyleOptionTabBarBase *tbb
+ = qstyleoption_cast<const QStyleOptionTabBarBase *>(opt)) {
+ if (tbb->documentMode) {
+ p->save();
+ drawTabBase(p, tbb, w);
+ p->restore();
+ return;
+ }
+
+ QRegion region(tbb->rect);
+ region -= tbb->tabBarRect;
+ p->save();
+ p->setClipRegion(region);
+ QStyleOptionTabWidgetFrame twf;
+ twf.QStyleOption::operator=(*tbb);
+ twf.shape = tbb->shape;
+ switch (getTabDirection(twf.shape)) {
+ case kThemeTabNorth:
+ twf.rect = twf.rect.adjusted(0, 0, 0, 10);
+ break;
+ case kThemeTabSouth:
+ twf.rect = twf.rect.adjusted(0, -10, 0, 0);
+ break;
+ case kThemeTabWest:
+ twf.rect = twf.rect.adjusted(0, 0, 10, 0);
+ break;
+ case kThemeTabEast:
+ twf.rect = twf.rect.adjusted(0, -10, 0, 0);
+ break;
+ }
+ proxy()->drawPrimitive(PE_FrameTabWidget, &twf, p, w);
+ p->restore();
+ }
+ break;
+#endif
+ case PE_PanelTipLabel:
+ p->fillRect(opt->rect, opt->palette.brush(QPalette::ToolTipBase));
+ break;
+ case PE_FrameGroupBox:
+ if (const QStyleOptionFrame *groupBox = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
+ if (groupBox->features & QStyleOptionFrame::Flat) {
+ QCommonStyle::drawPrimitive(pe, groupBox, p, w);
+ } else {
+ const auto cw = QMacStylePrivate::CocoaControl(QMacStylePrivate::Box, QStyleHelper::SizeDefault);
+ auto *box = static_cast<NSBox *>(d->cocoaControl(cw));
+ d->drawNSViewInRect(cw, box, groupBox->rect, p, w != nullptr, ^(CGContextRef ctx, const CGRect &rect) {
+ CGContextTranslateCTM(ctx, 0, rect.origin.y + rect.size.height);
+ CGContextScaleCTM(ctx, 1, -1);
+ [box drawRect:rect];
+ });
+ }
+ }
+ break;
+ case PE_IndicatorToolBarSeparator: {
+ QPainterPath path;
+ if (opt->state & State_Horizontal) {
+ int xpoint = opt->rect.center().x();
+ path.moveTo(xpoint + 0.5, opt->rect.top() + 1);
+ path.lineTo(xpoint + 0.5, opt->rect.bottom());
+ } else {
+ int ypoint = opt->rect.center().y();
+ path.moveTo(opt->rect.left() + 2 , ypoint + 0.5);
+ path.lineTo(opt->rect.right() + 1, ypoint + 0.5);
+ }
+ QPainterPathStroker theStroker;
+ theStroker.setCapStyle(Qt::FlatCap);
+ theStroker.setDashPattern(QVector<qreal>() << 1 << 2);
+ path = theStroker.createStroke(path);
+ p->fillPath(path, QColor(0, 0, 0, 119));
+ }
+ break;
+ case PE_FrameWindow:
+ if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
+ if (w && w->inherits("QMdiSubWindow")) {
+ p->save();
+ p->setPen(QPen(frame->palette.dark().color(), frame->lineWidth));
+ p->setBrush(frame->palette.window());
+ p->drawRect(frame->rect);
+ p->restore();
+ }
+ }
+ break;
+ case PE_IndicatorDockWidgetResizeHandle: {
+ // The docwidget resize handle is drawn as a one-pixel wide line.
+ p->save();
+ if (opt->state & State_Horizontal) {
+ p->setPen(QColor(160, 160, 160));
+ p->drawLine(opt->rect.topLeft(), opt->rect.topRight());
+ } else {
+ p->setPen(QColor(145, 145, 145));
+ p->drawLine(opt->rect.topRight(), opt->rect.bottomRight());
+ }
+ p->restore();
+ } break;
+ case PE_IndicatorToolBarHandle: {
+ p->save();
+ QPainterPath path;
+ int x = opt->rect.x() + 6;
+ int y = opt->rect.y() + 7;
+ static const int RectHeight = 2;
+ if (opt->state & State_Horizontal) {
+ while (y < opt->rect.height() - RectHeight - 5) {
+ path.moveTo(x, y);
+ path.addEllipse(x, y, RectHeight, RectHeight);
+ y += 6;
+ }
+ } else {
+ while (x < opt->rect.width() - RectHeight - 5) {
+ path.moveTo(x, y);
+ path.addEllipse(x, y, RectHeight, RectHeight);
+ x += 6;
+ }
+ }
+ p->setPen(Qt::NoPen);
+ QColor dark = opt->palette.dark().color().darker();
+ dark.setAlphaF(0.50);
+ p->fillPath(path, dark);
+ p->restore();
+
+ break;
+ }
+ case PE_IndicatorHeaderArrow:
+ if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt)) {
+ // In HITheme, up is down, down is up and hamburgers eat people.
+ if (header->sortIndicator != QStyleOptionHeader::None)
+ proxy()->drawPrimitive(
+ (header->sortIndicator == QStyleOptionHeader::SortDown) ?
+ PE_IndicatorArrowUp : PE_IndicatorArrowDown, header, p, w);
+ }
+ break;
+ case PE_IndicatorMenuCheckMark: {
+ if (!(opt->state & State_On))
+ break;
+ QColor pc;
+ if (opt->state & State_Selected)
+ pc = opt->palette.highlightedText().color();
+ else
+ pc = opt->palette.text().color();
+ QCFType<CGColorRef> checkmarkColor = CGColorCreateGenericRGB(static_cast<CGFloat>(pc.redF()),
+ static_cast<CGFloat>(pc.greenF()),
+ static_cast<CGFloat>(pc.blueF()),
+ static_cast<CGFloat>(pc.alphaF()));
+ // kCTFontUIFontSystem and others give the same result
+ // as kCTFontUIFontMenuItemMark. However, the latter is
+ // more reminiscent to HITheme's kThemeMenuItemMarkFont.
+ // See also the font for small- and mini-sized widgets,
+ // where we end up using the generic system font type.
+ const CTFontUIFontType fontType = (opt->state & State_Mini) ? kCTFontUIFontMiniSystem :
+ (opt->state & State_Small) ? kCTFontUIFontSmallSystem :
+ kCTFontUIFontMenuItemMark;
+ // Similarly for the font size, where there is a small difference
+ // between regular combobox and item view items, and and menu items.
+ // However, we ignore any difference for small- and mini-sized widgets.
+ const CGFloat fontSize = fontType == kCTFontUIFontMenuItemMark ? opt->fontMetrics.height() : 0.0;
+ QCFType<CTFontRef> checkmarkFont = CTFontCreateUIFontForLanguage(fontType, fontSize, NULL);
+
+ CGContextSaveGState(cg);
+ CGContextSetShouldSmoothFonts(cg, NO); // Same as HITheme and Cocoa menu checkmarks
+
+ // Baseline alignment tweaks for QComboBox and QMenu
+ const CGFloat vOffset = (opt->state & State_Mini) ? 0.0 :
+ (opt->state & State_Small) ? 1.0 :
+ 0.75;
+
+ CGContextTranslateCTM(cg, 0, opt->rect.bottom());
+ CGContextScaleCTM(cg, 1, -1);
+ // Translate back to the original position and add rect origin and offset
+ CGContextTranslateCTM(cg, opt->rect.x(), vOffset);
+
+ // CTFont has severe difficulties finding the checkmark character among its
+ // glyphs. Fortunately, CTLine knows its ways inside the Cocoa labyrinth.
+ static const CFStringRef keys[] = { kCTFontAttributeName, kCTForegroundColorAttributeName };
+ static const int numValues = sizeof(keys) / sizeof(keys[0]);
+ const CFTypeRef values[] = { (CFTypeRef)checkmarkFont, (CFTypeRef)checkmarkColor };
+ Q_STATIC_ASSERT((sizeof(values) / sizeof(values[0])) == numValues);
+ QCFType<CFDictionaryRef> attributes = CFDictionaryCreate(kCFAllocatorDefault, (const void **)keys, (const void **)values,
+ numValues, NULL, NULL);
+ // U+2713: CHECK MARK
+ QCFType<CFAttributedStringRef> checkmarkString = CFAttributedStringCreate(kCFAllocatorDefault, (CFStringRef)@"\u2713", attributes);
+ QCFType<CTLineRef> line = CTLineCreateWithAttributedString(checkmarkString);
+
+ CTLineDraw((CTLineRef)line, cg);
+ CGContextFlush(cg); // CTLineDraw's documentation says it doesn't flush
+
+ CGContextRestoreGState(cg);
+ break; }
+ case PE_IndicatorViewItemCheck:
+ case PE_IndicatorRadioButton:
+ case PE_IndicatorCheckBox: {
+ const bool isEnabled = opt->state & State_Enabled;
+ const bool isPressed = opt->state & State_Sunken;
+ const bool isRadioButton = (pe == PE_IndicatorRadioButton);
+ const auto ct = isRadioButton ? QMacStylePrivate::Button_RadioButton : QMacStylePrivate::Button_CheckBox;
+ const auto cs = d->effectiveAquaSizeConstrain(opt, w);
+ const auto cw = QMacStylePrivate::CocoaControl(ct, cs);
+ auto *tb = static_cast<NSButton *>(d->cocoaControl(cw));
+ tb.enabled = isEnabled;
+ tb.state = (opt->state & State_NoChange) ? NSMixedState :
+ (opt->state & State_On) ? NSOnState : NSOffState;
+ [tb highlight:isPressed];
+ const auto vOffset = [=] {
+ // As measured
+ if (cs == QStyleHelper::SizeMini)
+ return ct == QMacStylePrivate::Button_CheckBox ? -0.5 : 0.5;
+
+ return cs == QStyleHelper::SizeSmall ? 0.5 : 0.0;
+ } ();
+ d->drawNSViewInRect(cw, tb, opt->rect, p, w != nullptr, ^(CGContextRef ctx, const CGRect &rect) {
+ CGContextTranslateCTM(ctx, 0, vOffset);
+ [tb.cell drawInteriorWithFrame:rect inView:tb];
+ });
+ break; }
+ case PE_FrameFocusRect:
+ // Use the our own focus widget stuff.
+ break;
+ case PE_IndicatorBranch: {
+ if (!(opt->state & State_Children))
+ break;
+ const auto cw = QMacStylePrivate::CocoaControl(QMacStylePrivate::Button_Disclosure, QStyleHelper::SizeLarge);
+ NSButtonCell *triangleCell = static_cast<NSButtonCell *>(d->cocoaCell(cw));
+ [triangleCell setState:(opt->state & State_Open) ? NSOnState : NSOffState];
+ bool viewHasFocus = (w && w->hasFocus()) || (opt->state & State_HasFocus);
+ [triangleCell setBackgroundStyle:((opt->state & State_Selected) && viewHasFocus) ? NSBackgroundStyleDark : NSBackgroundStyleLight];
+
+ d->setupNSGraphicsContext(cg, NO);
+
+ QRect qtRect = opt->rect.adjusted(DisclosureOffset, 0, -DisclosureOffset, 0);
+ CGRect rect = CGRectMake(qtRect.x() + 1, qtRect.y(), qtRect.width(), qtRect.height());
+ CGContextTranslateCTM(cg, rect.origin.x, rect.origin.y + rect.size.height);
+ CGContextScaleCTM(cg, 1, -1);
+ CGContextTranslateCTM(cg, -rect.origin.x, -rect.origin.y);
+
+ [triangleCell drawBezelWithFrame:NSRectFromCGRect(rect) inView:[triangleCell controlView]];
+
+ d->restoreNSGraphicsContext(cg);
+ break; }
+
+ case PE_Frame: {
+ QPen oldPen = p->pen();
+ p->setPen(opt->palette.base().color().darker(140));
+ p->drawRect(opt->rect.adjusted(0, 0, -1, -1));
+ p->setPen(opt->palette.base().color().darker(180));
+ p->drawLine(opt->rect.topLeft(), opt->rect.topRight());
+ p->setPen(oldPen);
+ break; }
+
+ case PE_FrameLineEdit:
+ if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
+ if (frame->state & State_Sunken) {
+ const bool isEnabled = opt->state & State_Enabled;
+ const bool isReadOnly = opt->state & State_ReadOnly;
+ const bool isRounded = frame->features & QStyleOptionFrame::Rounded;
+ const auto cs = d->effectiveAquaSizeConstrain(opt, w, CT_LineEdit);
+ const auto cw = QMacStylePrivate::CocoaControl(QMacStylePrivate::TextField, cs);
+ auto *tf = static_cast<NSTextField *>(d->cocoaControl(cw));
+ tf.enabled = isEnabled;
+ tf.editable = !isReadOnly;
+ tf.bezeled = YES;
+ static_cast<NSTextFieldCell *>(tf.cell).bezelStyle = isRounded ? NSTextFieldRoundedBezel : NSTextFieldSquareBezel;
+ tf.frame = opt->rect.toCGRect();
+ d->drawNSViewInRect(cw, tf, opt->rect, p, w != nullptr, ^(CGContextRef ctx, const CGRect &rect) {
+ Q_UNUSED(ctx);
+ [tf.cell drawWithFrame:rect inView:tf];
+ });
+ } else {
+ QCommonStyle::drawPrimitive(pe, opt, p, w);
+ }
+ }
+ break;
+ case PE_PanelLineEdit:
+ QCommonStyle::drawPrimitive(pe, opt, p, w);
+ // Draw the focus frame for widgets other than QLineEdit (e.g. for line edits in Webkit).
+ // Focus frame is drawn outside the rectangle passed in the option-rect.
+ if (const QStyleOptionFrame *panel = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
+#if QT_CONFIG(lineedit)
+ if ((opt->state & State_HasFocus) && !qobject_cast<const QLineEdit*>(w)) {
+ int vmargin = pixelMetric(QStyle::PM_FocusFrameVMargin);
+ int hmargin = pixelMetric(QStyle::PM_FocusFrameHMargin);
+ QStyleOptionFrame focusFrame = *panel;
+ focusFrame.rect = panel->rect.adjusted(-hmargin, -vmargin, hmargin, vmargin);
+ drawControl(CE_FocusFrame, &focusFrame, p, w);
+ }
+#endif
+ }
+
+ break;
+#if QT_CONFIG(tabwidget)
+ case PE_FrameTabWidget:
+ if (const QStyleOptionTabWidgetFrame *twf
+ = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) {
+ CGRect cgRect = twf->rect.toCGRect();
+ HIThemeTabPaneDrawInfo tpdi;
+ tpdi.version = qt_mac_hitheme_tab_version();
+ tpdi.state = tds;
+ tpdi.direction = getTabDirection(twf->shape);
+ tpdi.size = kHIThemeTabSizeNormal;
+ tpdi.kind = kHIThemeTabKindNormal;
+ tpdi.adornment = kHIThemeTabPaneAdornmentNormal;
+ HIThemeDrawTabPane(&cgRect, &tpdi, cg, kHIThemeOrientationNormal);
+ }
+ break;
+#endif
+ case PE_PanelScrollAreaCorner: {
+ const QBrush brush(opt->palette.brush(QPalette::Base));
+ p->fillRect(opt->rect, brush);
+ p->setPen(QPen(QColor(217, 217, 217)));
+ p->drawLine(opt->rect.topLeft(), opt->rect.topRight());
+ p->drawLine(opt->rect.topLeft(), opt->rect.bottomLeft());
+ } break;
+ case PE_FrameStatusBarItem:
+ break;
+ case PE_IndicatorTabClose: {
+ // Make close button visible only on the hovered tab.
+ if (QTabBar *tabBar = qobject_cast<QTabBar*>(w->parentWidget())) {
+ const bool documentMode = tabBar->documentMode();
+ const QTabBarPrivate *tabBarPrivate = static_cast<QTabBarPrivate *>(QObjectPrivate::get(tabBar));
+ const int hoveredTabIndex = tabBarPrivate->hoveredTabIndex();
+ if (!documentMode ||
+ (hoveredTabIndex != -1 && ((w == tabBar->tabButton(hoveredTabIndex, QTabBar::LeftSide)) ||
+ (w == tabBar->tabButton(hoveredTabIndex, QTabBar::RightSide))))) {
+ const bool hover = (opt->state & State_MouseOver);
+ const bool selected = (opt->state & State_Selected);
+ const bool pressed = (opt->state & State_Sunken);
+ drawTabCloseButton(p, hover, selected, pressed, documentMode);
+ }
+ }
+ } break;
+ case PE_PanelStatusBar: {
+ // Fill the status bar with the titlebar gradient.
+ QLinearGradient linearGrad;
+ if (w ? qt_macWindowMainWindow(w->window()) : (opt->state & QStyle::State_Active)) {
+ linearGrad = titlebarGradientActive();
+ } else {
+ linearGrad = titlebarGradientInactive();
+ }
+
+ linearGrad.setStart(0, opt->rect.top());
+ linearGrad.setFinalStop(0, opt->rect.bottom());
+ p->fillRect(opt->rect, linearGrad);
+
+ // Draw the black separator line at the top of the status bar.
+ if (w ? qt_macWindowMainWindow(w->window()) : (opt->state & QStyle::State_Active))
+ p->setPen(titlebarSeparatorLineActive);
+ else
+ p->setPen(titlebarSeparatorLineInactive);
+ p->drawLine(opt->rect.left(), opt->rect.top(), opt->rect.right(), opt->rect.top());
+
+ break;
+ }
+ case PE_PanelMenu: {
+ p->save();
+ p->fillRect(opt->rect, Qt::transparent);
+ p->setPen(Qt::transparent);
+ p->setBrush(opt->palette.window());
+ p->setRenderHint(QPainter::Antialiasing, true);
+ const QPainterPath path = d->windowPanelPath(opt->rect);
+ p->drawPath(path);
+ p->restore();
+ } break;
+
+ default:
+ QCommonStyle::drawPrimitive(pe, opt, p, w);
+ break;
+ }
+}
+
+static inline QPixmap darkenPixmap(const QPixmap &pixmap)
+{
+ QImage img = pixmap.toImage().convertToFormat(QImage::Format_ARGB32);
+ int imgh = img.height();
+ int imgw = img.width();
+ int h, s, v, a;
+ QRgb pixel;
+ for (int y = 0; y < imgh; ++y) {
+ for (int x = 0; x < imgw; ++x) {
+ pixel = img.pixel(x, y);
+ a = qAlpha(pixel);
+ QColor hsvColor(pixel);
+ hsvColor.getHsv(&h, &s, &v);
+ s = qMin(100, s * 2);
+ v = v / 2;
+ hsvColor.setHsv(h, s, v);
+ pixel = hsvColor.rgb();
+ img.setPixel(x, y, qRgba(qRed(pixel), qGreen(pixel), qBlue(pixel), a));
+ }
+ }
+ return QPixmap::fromImage(img);
+}
+
+
+
+void QMacStylePrivate::setupVerticalInvertedXform(CGContextRef cg, bool reverse, bool vertical, const CGRect &rect) const
+{
+ if (vertical) {
+ CGContextTranslateCTM(cg, rect.size.height, 0);
+ CGContextRotateCTM(cg, M_PI_2);
+ }
+ if (vertical != reverse) {
+ CGContextTranslateCTM(cg, rect.size.width, 0);
+ CGContextScaleCTM(cg, -1, 1);
+ }
+}
+
+void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter *p,
+ const QWidget *w) const
+{
+ Q_D(const QMacStyle);
+ ThemeDrawState tds = d->getDrawState(opt->state);
+ QMacCGContext cg(p);
+ QWindow *window = w && w->window() ? w->window()->windowHandle() :
+ QStyleHelper::styleObjectWindow(opt->styleObject);
+ d->resolveCurrentNSView(window);
+ switch (ce) {
+ case CE_HeaderSection:
+ if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt)) {
+ State flags = header->state;
+ QRect ir = header->rect;
+
+
+#if 0 // FIXME: What's this solving exactly?
+ bool noVerticalHeader = true;
+#if QT_CONFIG(tableview)
+ if (w)
+ if (const QTableView *table = qobject_cast<const QTableView *>(w->parentWidget()))
+ noVerticalHeader = !table->verticalHeader()->isVisible();
+#endif
+
+ const bool drawLeftBorder = header->orientation == Qt::Vertical
+ || header->position == QStyleOptionHeader::OnlyOneSection
+ || (header->position == QStyleOptionHeader::Beginning && noVerticalHeader);
+#endif
+
+ const bool pressed = (flags & State_Sunken) && !(flags & State_On);
+ p->fillRect(ir, pressed ? header->palette.dark() : header->palette.button());
+ p->setPen(QPen(header->palette.dark(), 1.0));
+ if (header->orientation == Qt::Horizontal)
+ p->drawLine(QLineF(ir.right() + 0.5, ir.top() + headerSectionSeparatorInset,
+ ir.right() + 0.5, ir.bottom() - headerSectionSeparatorInset));
+ else
+ p->drawLine(QLineF(ir.left() + headerSectionSeparatorInset, ir.bottom(),
+ ir.right() - headerSectionSeparatorInset, ir.bottom()));
+ }
+
+ break;
+ case CE_HeaderLabel:
+ if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt)) {
+ p->save();
+ QRect textr = header->rect;
+ if (!header->icon.isNull()) {
+ QIcon::Mode mode = QIcon::Disabled;
+ if (opt->state & State_Enabled)
+ mode = QIcon::Normal;
+ int iconExtent = proxy()->pixelMetric(PM_SmallIconSize);
+ QPixmap pixmap = header->icon.pixmap(window, QSize(iconExtent, iconExtent), mode);
+
+ QRect pixr = header->rect;
+ pixr.setY(header->rect.center().y() - (pixmap.height() / pixmap.devicePixelRatio() - 1) / 2);
+ proxy()->drawItemPixmap(p, pixr, Qt::AlignVCenter, pixmap);
+ textr.translate(pixmap.width() / pixmap.devicePixelRatio() + 2, 0);
+ }
+
+ proxy()->drawItemText(p, textr, header->textAlignment | Qt::AlignVCenter, header->palette,
+ header->state & State_Enabled, header->text, QPalette::ButtonText);
+ p->restore();
+ }
+ break;
+ case CE_ToolButtonLabel:
+ if (const QStyleOptionToolButton *tb = qstyleoption_cast<const QStyleOptionToolButton *>(opt)) {
+ QStyleOptionToolButton myTb = *tb;
+ myTb.state &= ~State_AutoRaise;
+#ifndef QT_NO_ACCESSIBILITY
+ if (QStyleHelper::hasAncestor(opt->styleObject, QAccessible::ToolBar)) {
+ QRect cr = tb->rect;
+ int shiftX = 0;
+ int shiftY = 0;
+ bool needText = false;
+ int alignment = 0;
+ bool down = tb->state & (State_Sunken | State_On);
+ if (down) {
+ shiftX = proxy()->pixelMetric(PM_ButtonShiftHorizontal, tb, w);
+ shiftY = proxy()->pixelMetric(PM_ButtonShiftVertical, tb, w);
+ }
+ // The down state is special for QToolButtons in a toolbar on the Mac
+ // The text is a bit bolder and gets a drop shadow and the icons are also darkened.
+ // This doesn't really fit into any particular case in QIcon, so we
+ // do the majority of the work ourselves.
+ if (!(tb->features & QStyleOptionToolButton::Arrow)) {
+ Qt::ToolButtonStyle tbstyle = tb->toolButtonStyle;
+ if (tb->icon.isNull() && !tb->text.isEmpty())
+ tbstyle = Qt::ToolButtonTextOnly;
+
+ switch (tbstyle) {
+ case Qt::ToolButtonTextOnly: {
+ needText = true;
+ alignment = Qt::AlignCenter;
+ break; }
+ case Qt::ToolButtonIconOnly:
+ case Qt::ToolButtonTextBesideIcon:
+ case Qt::ToolButtonTextUnderIcon: {
+ QRect pr = cr;
+ QIcon::Mode iconMode = (tb->state & State_Enabled) ? QIcon::Normal
+ : QIcon::Disabled;
+ QIcon::State iconState = (tb->state & State_On) ? QIcon::On
+ : QIcon::Off;
+ QPixmap pixmap = tb->icon.pixmap(window,
+ tb->rect.size().boundedTo(tb->iconSize),
+ iconMode, iconState);
+
+ // Draw the text if it's needed.
+ if (tb->toolButtonStyle != Qt::ToolButtonIconOnly) {
+ needText = true;
+ if (tb->toolButtonStyle == Qt::ToolButtonTextUnderIcon) {
+ pr.setHeight(pixmap.size().height() / pixmap.devicePixelRatio() + 6);
+ cr.adjust(0, pr.bottom(), 0, -3);
+ alignment |= Qt::AlignCenter;
+ } else {
+ pr.setWidth(pixmap.width() / pixmap.devicePixelRatio() + 8);
+ cr.adjust(pr.right(), 0, 0, 0);
+ alignment |= Qt::AlignLeft | Qt::AlignVCenter;
+ }
+ }
+ if (opt->state & State_Sunken) {
+ pr.translate(shiftX, shiftY);
+ pixmap = darkenPixmap(pixmap);
+ }
+ proxy()->drawItemPixmap(p, pr, Qt::AlignCenter, pixmap);
+ break; }
+ default:
+ Q_ASSERT(false);
+ break;
+ }
+
+ if (needText) {
+ QPalette pal = tb->palette;
+ QPalette::ColorRole role = QPalette::NoRole;
+ if (!proxy()->styleHint(SH_UnderlineShortcut, tb, w))
+ alignment |= Qt::TextHideMnemonic;
+ if (down)
+ cr.translate(shiftX, shiftY);
+ if (tbstyle == Qt::ToolButtonTextOnly
+ || (tbstyle != Qt::ToolButtonTextOnly && !down)) {
+ QPen pen = p->pen();
+ QColor light = down ? Qt::black : Qt::white;
+ light.setAlphaF(0.375f);
+ p->setPen(light);
+ p->drawText(cr.adjusted(0, 1, 0, 1), alignment, tb->text);
+ p->setPen(pen);
+ if (down && tbstyle == Qt::ToolButtonTextOnly) {
+ pal = QApplication::palette("QMenu");
+ pal.setCurrentColorGroup(tb->palette.currentColorGroup());
+ role = QPalette::HighlightedText;
+ }
+ }
+ proxy()->drawItemText(p, cr, alignment, pal,
+ tb->state & State_Enabled, tb->text, role);
+ }
+ } else {
+ QCommonStyle::drawControl(ce, &myTb, p, w);
+ }
+ } else
+#endif // QT_NO_ACCESSIBILITY
+ {
+ QCommonStyle::drawControl(ce, &myTb, p, w);
+ }
+ }
+ break;
+ case CE_ToolBoxTabShape:
+ QCommonStyle::drawControl(ce, opt, p, w);
+ break;
+ case CE_PushButtonBevel:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ if (!(btn->state & (State_Raised | State_Sunken | State_On)))
+ break;
+
+ if (btn->features & QStyleOptionButton::CommandLinkButton) {
+ QCommonStyle::drawControl(ce, opt, p, w);
+ break;
+ }
+
+ // a focused auto-default button within an active window
+ // takes precedence over a normal default button
+ if ((btn->features & QStyleOptionButton::AutoDefaultButton)
+ && (opt->state & State_Active)
+ && (opt->state & State_HasFocus))
+ d->autoDefaultButton = opt->styleObject;
+ else if (d->autoDefaultButton == opt->styleObject)
+ d->autoDefaultButton = nullptr;
+
+ bool hasMenu = btn->features & QStyleOptionButton::HasMenu;
+ HIThemeButtonDrawInfo bdi;
+ d->initHIThemePushButton(btn, w, tds, &bdi);
+
+ if (!hasMenu) {
+ // HITheme is not drawing a nice focus frame around buttons.
+ // We'll do it ourselves further down.
+ bdi.adornment &= ~kThemeAdornmentFocus;
+
+ // We can't rely on an animation existing to test for the default look. That means a bit
+ // more logic (notice that the logic is slightly different for the bevel and the label).
+ if (tds == kThemeStateActive
+ && (btn->features & QStyleOptionButton::DefaultButton
+ || (btn->features & QStyleOptionButton::AutoDefaultButton
+ && d->autoDefaultButton == btn->styleObject)))
+ bdi.adornment |= kThemeAdornmentDefault;
+ }
+
+ // Unlike Carbon, we want the button to always be drawn inside its bounds.
+ // Therefore, make the button a bit smaller, so that even if it got focus,
+ // the focus 'shadow' will be inside.
+ CGRect newRect = btn->rect.toCGRect();
+ if (bdi.kind == kThemePushButton || bdi.kind == kThemePushButtonSmall) {
+ newRect.origin.x += QMacStylePrivate::PushButtonLeftOffset;
+ newRect.origin.y += QMacStylePrivate::PushButtonTopOffset;
+ newRect.size.width -= QMacStylePrivate::PushButtonRightOffset;
+ newRect.size.height -= QMacStylePrivate::PushButtonBottomOffset;
+ } else if (bdi.kind == kThemePushButtonMini) {
+ newRect.origin.x += QMacStylePrivate::PushButtonLeftOffset - 2;
+ newRect.origin.y += QMacStylePrivate::PushButtonTopOffset;
+ newRect.size.width -= QMacStylePrivate::PushButtonRightOffset - 4;
+ }
+
+ QMacStylePrivate::CocoaControl cw = QMacStylePrivate::cocoaControlFromHIThemeButtonKind(bdi.kind);
+ if (hasMenu)
+ cw.first = QMacStylePrivate::Button_PullDown;
+ if (hasMenu && bdi.kind != kThemeBevelButton) {
+ NSPopUpButton *pdb = (NSPopUpButton *)d->cocoaControl(cw);
+ [pdb highlight:(bdi.state == kThemeStatePressed)];
+ pdb.enabled = bdi.state != kThemeStateUnavailable && bdi.state != kThemeStateUnavailableInactive;
+ QRect rect = opt->rect;
+ rect.adjust(0, 0, cw.second == QStyleHelper::SizeSmall ? -4 : cw.second == QStyleHelper::SizeMini ? -9 : -6, 0);
+ d->drawNSViewInRect(cw, pdb, rect, p, w != 0);
+ } else if (hasMenu && bdi.state == kThemeStatePressed)
+ d->drawColorlessButton(newRect, &bdi, cw, p, opt);
+ else
+ HIThemeDrawButton(&newRect, &bdi, cg, kHIThemeOrientationNormal, 0);
+
+ if (btn->state & State_HasFocus) {
+ CGRect focusRect = newRect;
+ if (bdi.kind == kThemePushButton)
+ focusRect.size.height += 1; // Another thing HITheme and Cocoa seem to disagree about.
+ else if (bdi.kind == kThemePushButtonMini)
+ focusRect.size.height = 15; // Our QPushButton sizes are really weird
+
+ if (bdi.adornment & kThemeAdornmentDefault || bdi.state == kThemeStatePressed) {
+ if (bdi.kind == kThemePushButtonSmall) {
+ focusRect = CGRectInset(focusRect, -1, 0);
+ } else if (bdi.kind == kThemePushButtonMini) {
+ focusRect = CGRectInset(focusRect, 1, 0);
+ }
+ } else {
+ if (bdi.kind == kThemePushButton) {
+ focusRect = CGRectInset(focusRect, 1, 1);
+ } else if (bdi.kind == kThemePushButtonSmall) {
+ focusRect = CGRectInset(focusRect, 0, 2);
+ } else if (bdi.kind == kThemePushButtonMini) {
+ focusRect = CGRectInset(focusRect, 2, 1);
+ }
+ }
+
+ const qreal radius = bdi.kind == kThemeBevelButton ? 0 : 3;
+ const int hMargin = proxy()->pixelMetric(QStyle::PM_FocusFrameHMargin, btn, w);
+ const int vMargin = proxy()->pixelMetric(QStyle::PM_FocusFrameVMargin, btn, w);
+ const QRect focusTargetRect(focusRect.origin.x, focusRect.origin.y, focusRect.size.width, focusRect.size.height);
+ d->drawFocusRing(p, focusTargetRect.adjusted(-hMargin, -vMargin, hMargin, vMargin), hMargin, vMargin, radius);
+ }
+
+ if (hasMenu && bdi.kind == kThemeBevelButton) {
+ int mbi = proxy()->pixelMetric(QStyle::PM_MenuButtonIndicator, btn, w);
+ QRect ir = btn->rect;
+ int arrowXOffset = bdi.kind == kThemePushButton ? 6 :
+ bdi.kind == kThemePushButtonSmall ? 7 : 8;
+ int arrowYOffset = bdi.kind == kThemePushButton ? 3 :
+ bdi.kind == kThemePushButtonSmall ? 1 : 2;
+ if (!w) {
+ // adjustment for Qt Quick Controls
+ arrowYOffset -= ir.top();
+ if (bdi.kind == kThemePushButtonSmall)
+ arrowYOffset += 1;
+ }
+ QRect ar = QRect(ir.right() - mbi - QMacStylePrivate::PushButtonRightOffset,
+ ir.height() / 2 - arrowYOffset, mbi, ir.height() / 2);
+ ar = visualRect(btn->direction, ir, ar);
+ CGRect arrowRect = CGRectMake(ar.x() + arrowXOffset, ar.y(), ar.width(), ar.height());
+
+ HIThemePopupArrowDrawInfo pdi;
+ pdi.version = qt_mac_hitheme_version;
+ pdi.state = tds == kThemeStateInactive ? kThemeStateActive : tds;
+ pdi.orientation = kThemeArrowDown;
+ if (bdi.kind == kThemePushButtonMini)
+ pdi.size = kThemeArrow5pt;
+ else if (bdi.kind == kThemePushButton || bdi.kind == kThemePushButtonSmall)
+ pdi.size = kThemeArrow7pt;
+ HIThemeDrawPopupArrow(&arrowRect, &pdi, cg, kHIThemeOrientationNormal);
+ }
+ }
+ break;
+ case CE_PushButtonLabel:
+ if (const QStyleOptionButton *b = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ QStyleOptionButton btn(*b);
+ // We really don't want the label to be drawn the same as on
+ // windows style if it has an icon and text, then it should be more like a
+ // tab. So, cheat a little here. However, if it *is* only an icon
+ // the windows style works great, so just use that implementation.
+ const bool hasMenu = btn.features & QStyleOptionButton::HasMenu;
+ const bool hasIcon = !btn.icon.isNull();
+ const bool hasText = !btn.text.isEmpty();
+
+ if (!hasMenu) {
+ if (tds == kThemeStatePressed
+ || (tds == kThemeStateActive
+ && ((btn.features & QStyleOptionButton::DefaultButton && !d->autoDefaultButton)
+ || d->autoDefaultButton == btn.styleObject)))
+ btn.palette.setColor(QPalette::ButtonText, Qt::white);
+ }
+
+ if ((!hasIcon && !hasMenu) || (hasIcon && !hasText)) {
+ QCommonStyle::drawControl(ce, &btn, p, w);
+ } else {
+ QRect freeContentRect = btn.rect;
+ QRect textRect = itemTextRect(
+ btn.fontMetrics, freeContentRect, Qt::AlignCenter, btn.state & State_Enabled, btn.text);
+ if (hasMenu) {
+ textRect.moveTo(w ? 15 : 11, textRect.top()); // Supports Qt Quick Controls
+ }
+ // Draw the icon:
+ if (hasIcon) {
+ int contentW = textRect.width();
+ if (hasMenu)
+ contentW += proxy()->pixelMetric(PM_MenuButtonIndicator) + 4;
+ QIcon::Mode mode = btn.state & State_Enabled ? QIcon::Normal : QIcon::Disabled;
+ if (mode == QIcon::Normal && btn.state & State_HasFocus)
+ mode = QIcon::Active;
+ // Decide if the icon is should be on or off:
+ QIcon::State state = QIcon::Off;
+ if (btn.state & State_On)
+ state = QIcon::On;
+ QPixmap pixmap = btn.icon.pixmap(window, btn.iconSize, mode, state);
+ int pixmapWidth = pixmap.width() / pixmap.devicePixelRatio();
+ int pixmapHeight = pixmap.height() / pixmap.devicePixelRatio();
+ contentW += pixmapWidth + QMacStylePrivate::PushButtonContentPadding;
+ int iconLeftOffset = freeContentRect.x() + (freeContentRect.width() - contentW) / 2;
+ int iconTopOffset = freeContentRect.y() + (freeContentRect.height() - pixmapHeight) / 2;
+ QRect iconDestRect(iconLeftOffset, iconTopOffset, pixmapWidth, pixmapHeight);
+ QRect visualIconDestRect = visualRect(btn.direction, freeContentRect, iconDestRect);
+ proxy()->drawItemPixmap(p, visualIconDestRect, Qt::AlignLeft | Qt::AlignVCenter, pixmap);
+ int newOffset = iconDestRect.x() + iconDestRect.width()
+ + QMacStylePrivate::PushButtonContentPadding - textRect.x();
+ textRect.adjust(newOffset, 0, newOffset, 0);
+ }
+ // Draw the text:
+ if (hasText) {
+ textRect = visualRect(btn.direction, freeContentRect, textRect);
+ proxy()->drawItemText(p, textRect, Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic, btn.palette,
+ (btn.state & State_Enabled), btn.text, QPalette::ButtonText);
+ }
+ }
+ }
+ break;
+ case CE_ComboBoxLabel:
+ if (const QStyleOptionComboBox *cb = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
+ QStyleOptionComboBox comboCopy = *cb;
+ comboCopy.direction = Qt::LeftToRight;
+ QCommonStyle::drawControl(CE_ComboBoxLabel, &comboCopy, p, w);
+ }
+ break;
+#if QT_CONFIG(tabbar)
+ case CE_TabBarTabShape:
+ if (const QStyleOptionTab *tabOpt = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
+ if (tabOpt->documentMode) {
+ p->save();
+ bool isUnified = false;
+ if (w) {
+ QRect tabRect = tabOpt->rect;
+ QPoint windowTabStart = w->mapTo(w->window(), tabRect.topLeft());
+ isUnified = isInMacUnifiedToolbarArea(w->window()->windowHandle(), windowTabStart.y());
+ }
+
+ const int tabOverlap = proxy()->pixelMetric(PM_TabBarTabOverlap, opt, w);
+ drawTabShape(p, tabOpt, isUnified, tabOverlap);
+
+ p->restore();
+ return;
+ }
+
+ HIThemeTabDrawInfo tdi;
+ tdi.version = 1;
+ tdi.style = kThemeTabNonFront;
+ tdi.direction = getTabDirection(tabOpt->shape);
+ switch (d->aquaSizeConstrain(opt, w)) {
+ case QStyleHelper::SizeDefault:
+ case QStyleHelper::SizeLarge:
+ tdi.size = kHIThemeTabSizeNormal;
+ break;
+ case QStyleHelper::SizeSmall:
+ tdi.size = kHIThemeTabSizeSmall;
+ break;
+ case QStyleHelper::SizeMini:
+ tdi.size = kHIThemeTabSizeMini;
+ break;
+ }
+ bool verticalTabs = tdi.direction == kThemeTabWest || tdi.direction == kThemeTabEast;
+ QRect tabRect = tabOpt->rect;
+
+ bool selected = tabOpt->state & State_Selected;
+ if (selected) {
+ if (!(tabOpt->state & State_Active))
+ tdi.style = kThemeTabFrontUnavailable;
+ else if (!(tabOpt->state & State_Enabled))
+ tdi.style = kThemeTabFrontInactive;
+ else
+ tdi.style = kThemeTabFront;
+ } else if (!(tabOpt->state & State_Active)) {
+ tdi.style = kThemeTabNonFrontUnavailable;
+ } else if (!(tabOpt->state & State_Enabled)) {
+ tdi.style = kThemeTabNonFrontInactive;
+ } else if (tabOpt->state & State_Sunken) {
+ tdi.style = kThemeTabNonFrontPressed;
+ }
+ if (tabOpt->state & State_HasFocus)
+ tdi.adornment = kHIThemeTabAdornmentFocus;
+ else
+ tdi.adornment = kHIThemeTabAdornmentNone;
+ tdi.kind = kHIThemeTabKindNormal;
+
+ QStyleOptionTab::TabPosition tp = tabOpt->position;
+ QStyleOptionTab::SelectedPosition sp = tabOpt->selectedPosition;
+ if (tabOpt->direction == Qt::RightToLeft && !verticalTabs) {
+ if (sp == QStyleOptionTab::NextIsSelected)
+ sp = QStyleOptionTab::PreviousIsSelected;
+ else if (sp == QStyleOptionTab::PreviousIsSelected)
+ sp = QStyleOptionTab::NextIsSelected;
+ switch (tp) {
+ case QStyleOptionTab::Beginning:
+ tp = QStyleOptionTab::End;
+ break;
+ case QStyleOptionTab::End:
+ tp = QStyleOptionTab::Beginning;
+ break;
+ default:
+ break;
+ }
+ }
+ bool stretchTabs = (!verticalTabs && tabRect.height() > 22) || (verticalTabs && tabRect.width() > 22);
+
+ switch (tp) {
+ case QStyleOptionTab::Beginning:
+ tdi.position = kHIThemeTabPositionFirst;
+ if (sp != QStyleOptionTab::NextIsSelected || stretchTabs)
+ tdi.adornment |= kHIThemeTabAdornmentTrailingSeparator;
+ break;
+ case QStyleOptionTab::Middle:
+ tdi.position = kHIThemeTabPositionMiddle;
+ if (selected)
+ tdi.adornment |= kHIThemeTabAdornmentLeadingSeparator;
+ if (sp != QStyleOptionTab::NextIsSelected || stretchTabs) // Also when we're selected.
+ tdi.adornment |= kHIThemeTabAdornmentTrailingSeparator;
+ break;
+ case QStyleOptionTab::End:
+ tdi.position = kHIThemeTabPositionLast;
+ if (selected)
+ tdi.adornment |= kHIThemeTabAdornmentLeadingSeparator;
+ break;
+ case QStyleOptionTab::OnlyOneTab:
+ tdi.position = kHIThemeTabPositionOnly;
+ break;
+ }
+ // HITheme doesn't stretch its tabs. Therefore we have to cheat and do the job ourselves.
+ if (stretchTabs) {
+ CGRect cgRect = CGRectMake(0, 0, 23, 23);
+ QPixmap pm(23, 23);
+ pm.fill(Qt::transparent);
+ {
+ QMacCGContext pmcg(&pm);
+ HIThemeDrawTab(&cgRect, &tdi, pmcg, kHIThemeOrientationNormal, 0);
+ }
+ QStyleHelper::drawBorderPixmap(pm, p, tabRect, 7, 7, 7, 7);
+ } else {
+ CGRect cgRect = tabRect.toCGRect();
+ HIThemeDrawTab(&cgRect, &tdi, cg, kHIThemeOrientationNormal, 0);
+ }
+ }
+ break;
+ case CE_TabBarTabLabel:
+ if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
+ QStyleOptionTab myTab = *tab;
+ const bool verticalTabs = tab->shape == QTabBar::RoundedWest
+ || tab->shape == QTabBar::RoundedEast
+ || tab->shape == QTabBar::TriangularWest
+ || tab->shape == QTabBar::TriangularEast;
+
+ // Check to see if we use have the same as the system font
+ // (QComboMenuItem is internal and should never be seen by the
+ // outside world, unless they read the source, in which case, it's
+ // their own fault).
+ const bool nonDefaultFont = p->font() != qt_app_fonts_hash()->value("QComboMenuItem");
+
+ if (!myTab.documentMode && (myTab.state & State_Selected) && (myTab.state & State_Active))
+ if (const auto *tabBar = qobject_cast<const QTabBar *>(w))
+ if (!tabBar->tabTextColor(tabBar->currentIndex()).isValid())
+ myTab.palette.setColor(QPalette::WindowText, Qt::white);
+
+ int heightOffset = 0;
+ if (verticalTabs) {
+ heightOffset = -1;
+ } else if (nonDefaultFont) {
+ if (p->fontMetrics().height() == myTab.rect.height())
+ heightOffset = 2;
+ }
+ myTab.rect.setHeight(myTab.rect.height() + heightOffset);
+
+ QCommonStyle::drawControl(ce, &myTab, p, w);
+ }
+ break;
+#endif
+#if QT_CONFIG(dockwidget)
+ case CE_DockWidgetTitle:
+ if (const auto *dwOpt = qstyleoption_cast<const QStyleOptionDockWidget *>(opt)) {
+ const bool isVertical = dwOpt->verticalTitleBar;
+ const auto effectiveRect = isVertical ? opt->rect.transposed() : opt->rect;
+ p->save();
+ if (isVertical) {
+ p->translate(effectiveRect.left(), effectiveRect.top() + effectiveRect.width());
+ p->rotate(-90);
+ p->translate(-effectiveRect.left(), -effectiveRect.top());
+ }
+
+ // fill title bar background
+ QLinearGradient linearGrad;
+ linearGrad.setStart(QPointF(0, 0));
+ linearGrad.setFinalStop(QPointF(0, 2 * effectiveRect.height()));
+ linearGrad.setColorAt(0, opt->palette.button().color());
+ linearGrad.setColorAt(1, opt->palette.dark().color());
+ p->fillRect(effectiveRect, linearGrad);
+
+ // draw horizontal line at bottom
+ p->setPen(opt->palette.dark().color());
+ p->drawLine(effectiveRect.bottomLeft(), effectiveRect.bottomRight());
+
+ if (!dwOpt->title.isEmpty()) {
+ auto titleRect = proxy()->subElementRect(SE_DockWidgetTitleBarText, opt, w);
+ if (isVertical)
+ titleRect = QRect(effectiveRect.left() + opt->rect.bottom() - titleRect.bottom(),
+ effectiveRect.top() + titleRect.left() - opt->rect.left(),
+ titleRect.height(),
+ titleRect.width());
+
+ const auto text = p->fontMetrics().elidedText(dwOpt->title, Qt::ElideRight, titleRect.width());
+ proxy()->drawItemText(p, titleRect, Qt::AlignCenter | Qt::TextShowMnemonic, dwOpt->palette,
+ dwOpt->state & State_Enabled, text, QPalette::WindowText);
+ }
+ p->restore();
+ }
+ break;
+#endif
+ case CE_FocusFrame: {
+ const auto *ff = qobject_cast<const QFocusFrame *>(w);
+ const auto *ffw = ff ? ff->widget() : nullptr;
+ const auto ct = [=] {
+ if (ffw) {
+ if (ffw->inherits("QCheckBox"))
+ return QMacStylePrivate::Button_CheckBox;
+ if (ffw->inherits("QRadioButton"))
+ return QMacStylePrivate::Button_RadioButton;
+ }
+
+ return QMacStylePrivate::Box; // Not really, just make it the default
+ } ();
+ const int hMargin = proxy()->pixelMetric(QStyle::PM_FocusFrameHMargin, opt, w);
+ const int vMargin = proxy()->pixelMetric(QStyle::PM_FocusFrameVMargin, opt, w);
+ if (ct == QMacStylePrivate::Box) {
+ d->drawFocusRing(p, opt->rect, hMargin, vMargin);
+ } else if (ffw) {
+ const auto cs = ffw->testAttribute(Qt::WA_MacMiniSize) ? QStyleHelper::SizeMini :
+ ffw->testAttribute(Qt::WA_MacSmallSize) ? QStyleHelper::SizeSmall :
+ QStyleHelper::SizeLarge;
+ d->drawFocusRing(p, opt->rect, hMargin, vMargin, QMacStylePrivate::CocoaControl(ct, cs));
+ }
+ break; }
+ case CE_MenuEmptyArea:
+ // Skip: PE_PanelMenu fills in everything
+ break;
+ case CE_MenuItem:
+ case CE_MenuHMargin:
+ case CE_MenuVMargin:
+ case CE_MenuTearoff:
+ case CE_MenuScroller:
+ if (const QStyleOptionMenuItem *mi = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
+ const bool active = mi->state & State_Selected;
+ if (active)
+ p->fillRect(mi->rect, mi->palette.highlight());
+
+ const QStyleHelper::WidgetSizePolicy widgetSize = d->aquaSizeConstrain(opt, w);
+
+ if (ce == CE_MenuTearoff) {
+ p->setPen(QPen(mi->palette.dark().color(), 1, Qt::DashLine));
+ p->drawLine(mi->rect.x() + 2, mi->rect.y() + mi->rect.height() / 2 - 1,
+ mi->rect.x() + mi->rect.width() - 4,
+ mi->rect.y() + mi->rect.height() / 2 - 1);
+ p->setPen(QPen(mi->palette.light().color(), 1, Qt::DashLine));
+ p->drawLine(mi->rect.x() + 2, mi->rect.y() + mi->rect.height() / 2,
+ mi->rect.x() + mi->rect.width() - 4,
+ mi->rect.y() + mi->rect.height() / 2);
+ } else if (ce == CE_MenuScroller) {
+ const QSize scrollerSize = QSize(10, 8);
+ const int scrollerVOffset = 5;
+ const int left = mi->rect.x() + (mi->rect.width() - scrollerSize.width()) / 2;
+ const int right = left + scrollerSize.width();
+ int top;
+ int bottom;
+ if (opt->state & State_DownArrow) {
+ bottom = mi->rect.y() + scrollerVOffset;
+ top = bottom + scrollerSize.height();
+ } else {
+ bottom = mi->rect.bottom() - scrollerVOffset;
+ top = bottom - scrollerSize.height();
+ }
+ p->save();
+ p->setRenderHint(QPainter::Antialiasing);
+ QPainterPath path;
+ path.moveTo(left, bottom);
+ path.lineTo(right, bottom);
+ path.lineTo((left + right) / 2, top);
+ p->fillPath(path, opt->palette.buttonText());
+ p->restore();
+ } else if (ce != CE_MenuItem) {
+ break;
+ }
+
+ if (mi->menuItemType == QStyleOptionMenuItem::Separator) {
+ CGColorRef separatorColor = [NSColor quaternaryLabelColor].CGColor;
+ const QRect separatorRect = QRect(mi->rect.left(), mi->rect.center().y(), mi->rect.width(), 2);
+ p->fillRect(separatorRect, qt_mac_toQColor(separatorColor));
+ break;
+ }
+
+ const int maxpmw = mi->maxIconWidth;
+ const bool enabled = mi->state & State_Enabled;
+
+ int xpos = mi->rect.x() + 18;
+ int checkcol = maxpmw;
+ if (!enabled)
+ p->setPen(mi->palette.text().color());
+ else if (active)
+ p->setPen(mi->palette.highlightedText().color());
+ else
+ p->setPen(mi->palette.buttonText().color());
+
+ if (mi->checked) {
+ QStyleOption checkmarkOpt;
+ checkmarkOpt.initFrom(w);
+
+ const int mw = checkcol + macItemFrame;
+ const int mh = mi->rect.height() + macItemFrame;
+ const int xp = mi->rect.x() + macItemFrame;
+ checkmarkOpt.rect = QRect(xp, mi->rect.y() - checkmarkOpt.fontMetrics.descent(), mw, mh);
+
+ checkmarkOpt.state |= State_On; // Always on. Never rendered when off.
+ checkmarkOpt.state.setFlag(State_Selected, active);
+ checkmarkOpt.state.setFlag(State_Enabled, enabled);
+ if (widgetSize == QStyleHelper::SizeMini)
+ checkmarkOpt.state |= State_Mini;
+ else if (widgetSize == QStyleHelper::SizeSmall)
+ checkmarkOpt.state |= State_Small;
+
+ // We let drawPrimitive(PE_IndicatorMenuCheckMark) pick the right color
+ checkmarkOpt.palette.setColor(QPalette::HighlightedText, p->pen().color());
+ checkmarkOpt.palette.setColor(QPalette::Text, p->pen().color());
+
+ proxy()->drawPrimitive(PE_IndicatorMenuCheckMark, &checkmarkOpt, p, w);
+ }
+ if (!mi->icon.isNull()) {
+ QIcon::Mode mode = (mi->state & State_Enabled) ? QIcon::Normal
+ : QIcon::Disabled;
+ // Always be normal or disabled to follow the Mac style.
+ int smallIconSize = proxy()->pixelMetric(PM_SmallIconSize);
+ QSize iconSize(smallIconSize, smallIconSize);
+#if QT_CONFIG(combobox)
+ if (const QComboBox *comboBox = qobject_cast<const QComboBox *>(w)) {
+ iconSize = comboBox->iconSize();
+ }
+#endif
+ QPixmap pixmap = mi->icon.pixmap(window, iconSize, mode);
+ int pixw = pixmap.width() / pixmap.devicePixelRatio();
+ int pixh = pixmap.height() / pixmap.devicePixelRatio();
+ QRect cr(xpos, mi->rect.y(), checkcol, mi->rect.height());
+ QRect pmr(0, 0, pixw, pixh);
+ pmr.moveCenter(cr.center());
+ p->drawPixmap(pmr.topLeft(), pixmap);
+ xpos += pixw + 6;
+ }
+
+ QString s = mi->text;
+ const auto text_flags = Qt::AlignVCenter | Qt::TextHideMnemonic
+ | Qt::TextSingleLine | Qt::AlignAbsolute;
+ int yPos = mi->rect.y();
+ if (widgetSize == QStyleHelper::SizeMini)
+ yPos += 1;
+
+ const bool isSubMenu = mi->menuItemType == QStyleOptionMenuItem::SubMenu;
+ const int tabwidth = isSubMenu ? 9 : mi->tabWidth;
+
+ QString rightMarginText;
+ if (isSubMenu)
+ rightMarginText = QStringLiteral("\u25b6\ufe0e"); // U+25B6 U+FE0E: BLACK RIGHT-POINTING TRIANGLE
+
+ // If present, save and remove embedded shorcut from text
+ const int tabIndex = s.indexOf(QLatin1Char('\t'));
+ if (tabIndex >= 0) {
+ if (!isSubMenu) // ... but ignore it if it's a submenu.
+ rightMarginText = s.mid(tabIndex + 1);
+ s = s.left(tabIndex);
+ }
+
+ p->save();
+ if (!rightMarginText.isEmpty()) {
+ p->setFont(qt_app_fonts_hash()->value("QMenuItem", p->font()));
+ int xp = mi->rect.right() - tabwidth - macRightBorder + 2;
+ if (!isSubMenu)
+ xp -= macItemHMargin + macItemFrame + 3; // Adjust for shortcut
+ p->drawText(xp, yPos, tabwidth, mi->rect.height(), text_flags | Qt::AlignRight, rightMarginText);
+ }
+
+ if (!s.isEmpty()) {
+ const int xm = macItemFrame + maxpmw + macItemHMargin;
+ QFont myFont = mi->font;
+ // myFont may not have any "hard" flags set. We override
+ // the point size so that when it is resolved against the device, this font will win.
+ // This is mainly to handle cases where someone sets the font on the window
+ // and then the combo inherits it and passes it onward. At that point the resolve mask
+ // is very, very weak. This makes it stonger.
+ myFont.setPointSizeF(QFontInfo(mi->font).pointSizeF());
+
+ // QTBUG-65653: Our own text rendering doesn't look good enough, especially on non-retina
+ // displays. Worked around here while waiting for a proper fix in QCoreTextFontEngine.
+ // Only if we're not using QCoreTextFontEngine we do fallback to our own text rendering.
+ const auto *fontEngine = QFontPrivate::get(myFont)->engineForScript(QChar::Script_Common);
+ Q_ASSERT(fontEngine);
+ if (fontEngine->type() == QFontEngine::Multi) {
+ fontEngine = static_cast<const QFontEngineMulti *>(fontEngine)->engine(0);
+ Q_ASSERT(fontEngine);
+ }
+ if (fontEngine->type() == QFontEngine::Mac) {
+ NSFont *f = (NSFont *)(CTFontRef)fontEngine->handle();
+
+ // Respect the menu item palette as set in the style option.
+ const auto pc = p->pen().color();
+ NSColor *c = [NSColor colorWithSRGBRed:pc.redF()
+ green:pc.greenF()
+ blue:pc.blueF()
+ alpha:pc.alphaF()];
+
+ s = qt_mac_removeMnemonics(s);
+ const auto textRect = CGRectMake(xpos, yPos, mi->rect.width() - xm - tabwidth + 1, mi->rect.height());
+
+ QMacCGContext cgCtx(p);
+ d->setupNSGraphicsContext(cgCtx, YES);
+
+ [s.toNSString() drawInRect:textRect
+ withAttributes:@{ NSFontAttributeName:f, NSForegroundColorAttributeName:c }];
+
+ d->restoreNSGraphicsContext(cgCtx);
+ } else {
+ p->setFont(myFont);
+ p->drawText(xpos, yPos, mi->rect.width() - xm - tabwidth + 1,
+ mi->rect.height(), text_flags, s);
+ }
+ }
+ p->restore();
+ }
+ break;
+ case CE_MenuBarItem:
+ case CE_MenuBarEmptyArea:
+ if (const QStyleOptionMenuItem *mi = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
+ const bool selected = (opt->state & State_Selected) && (opt->state & State_Enabled) && (opt->state & State_Sunken);
+ const QBrush bg = selected ? mi->palette.highlight() : mi->palette.background();
+ p->fillRect(mi->rect, bg);
+
+ if (ce != CE_MenuBarItem)
+ break;
+
+ if (!mi->icon.isNull()) {
+ int iconExtent = proxy()->pixelMetric(PM_SmallIconSize);
+ drawItemPixmap(p, mi->rect,
+ Qt::AlignCenter | Qt::TextHideMnemonic | Qt::TextDontClip
+ | Qt::TextSingleLine,
+ mi->icon.pixmap(window, QSize(iconExtent, iconExtent),
+ (mi->state & State_Enabled) ? QIcon::Normal : QIcon::Disabled));
+ } else {
+ drawItemText(p, mi->rect,
+ Qt::AlignCenter | Qt::TextHideMnemonic | Qt::TextDontClip
+ | Qt::TextSingleLine,
+ mi->palette, mi->state & State_Enabled,
+ mi->text, selected ? QPalette::HighlightedText : QPalette::ButtonText);
+ }
+ }
+ break;
+ case CE_ProgressBarLabel:
+ case CE_ProgressBarGroove:
+ // Do nothing. All done in CE_ProgressBarContents. Only keep these for proxy style overrides.
+ break;
+ case CE_ProgressBarContents:
+ if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(opt)) {
+ QMacAutoReleasePool pool;
+ const bool isIndeterminate = (pb->minimum == 0 && pb->maximum == 0);
+ const bool vertical = pb->orientation == Qt::Vertical;
+ const bool inverted = pb->invertedAppearance;
+ bool reverse = (!vertical && (pb->direction == Qt::RightToLeft));
+ if (inverted)
+ reverse = !reverse;
+
+ QRect rect = pb->rect;
+ if (vertical)
+ rect = rect.transposed();
+ const CGRect cgRect = rect.toCGRect();
+
+ const auto aquaSize = d->effectiveAquaSizeConstrain(opt, w);
+ const QProgressStyleAnimation *animation = qobject_cast<QProgressStyleAnimation*>(d->animation(opt->styleObject));
+ QIndeterminateProgressIndicator *ipi = nil;
+ if (isIndeterminate || animation)
+ ipi = static_cast<QIndeterminateProgressIndicator *>(d->cocoaControl({ QMacStylePrivate::ProgressIndicator_Indeterminate, aquaSize }));
+ if (isIndeterminate) {
+ // QIndeterminateProgressIndicator derives from NSProgressIndicator. We use a single
+ // instance that we start animating as soon as one of the progress bars is indeterminate.
+ // Since they will be in sync (as it's the case in Cocoa), we just need to draw it with
+ // the right geometry when the animation triggers an update. However, we can't hide it
+ // entirely between frames since that would stop the animation, so we just set its alpha
+ // value to 0. Same if we remove it from its superview. See QIndeterminateProgressIndicator
+ // implementation for details.
+ if (!animation && opt->styleObject) {
+ auto *animation = new QProgressStyleAnimation(d->animateSpeed(QMacStylePrivate::AquaProgressBar), opt->styleObject);
+ // NSProgressIndicator is heavier to draw than the HITheme API, so we reduce the frame rate a couple notches.
+ animation->setFrameRate(QStyleAnimation::FifteenFps);
+ d->startAnimation(animation);
+ [ipi startAnimation];
+ }
+
+ d->setupNSGraphicsContext(cg, NO);
+ d->setupVerticalInvertedXform(cg, reverse, vertical, cgRect);
+ [ipi drawWithFrame:cgRect inView:d->backingStoreNSView];
+ d->restoreNSGraphicsContext(cg);
+ } else {
+ if (animation) {
+ d->stopAnimation(opt->styleObject);
+ [ipi stopAnimation];
+ }
+
+ const auto cw = QMacStylePrivate::CocoaControl(QMacStylePrivate::ProgressIndicator_Determinate, aquaSize);
+ auto *pi = static_cast<NSProgressIndicator *>(d->cocoaControl(cw));
+ d->drawNSViewInRect(cw, pi, rect, p, w != nullptr, ^(CGContextRef ctx, const CGRect &rect) {
+ d->setupVerticalInvertedXform(ctx, reverse, vertical, rect);
+ pi.minValue = pb->minimum;
+ pi.maxValue = pb->maximum;
+ pi.doubleValue = pb->progress;
+ [pi drawRect:rect];
+ });
+ }
+ }
+ break;
+ case CE_SizeGrip: {
+ // This is not HIG kosher: Fall back to the old stuff until we decide what to do.
+#ifndef QT_NO_MDIAREA
+ if (!w || !qobject_cast<QMdiSubWindow *>(w->parentWidget()))
+#endif
+ break;
+
+ if (w->testAttribute(Qt::WA_MacOpaqueSizeGrip))
+ p->fillRect(opt->rect, opt->palette.window());
+
+ QPen lineColor = QColor(82, 82, 82, 192);
+ lineColor.setWidth(1);
+ p->save();
+ p->setRenderHint(QPainter::Antialiasing);
+ p->setPen(lineColor);
+ const Qt::LayoutDirection layoutDirection = w ? w->layoutDirection() : qApp->layoutDirection();
+ const int NumLines = 3;
+ for (int l = 0; l < NumLines; ++l) {
+ const int offset = (l * 4 + 3);
+ QPoint start, end;
+ if (layoutDirection == Qt::LeftToRight) {
+ start = QPoint(opt->rect.width() - offset, opt->rect.height() - 1);
+ end = QPoint(opt->rect.width() - 1, opt->rect.height() - offset);
+ } else {
+ start = QPoint(offset, opt->rect.height() - 1);
+ end = QPoint(1, opt->rect.height() - offset);
+ }
+ p->drawLine(start, end);
+ }
+ p->restore();
+ break;
+ }
+ case CE_Splitter:
+ if (opt->rect.width() > 1 && opt->rect.height() > 1) {
+ const bool isVertical = !(opt->state & QStyle::State_Horizontal);
+ // Qt refers to the layout orientation, while Cocoa refers to the divider's.
+ const auto ct = isVertical ? QMacStylePrivate::SplitView_Horizontal : QMacStylePrivate::SplitView_Vertical;
+ const auto cw = QMacStylePrivate::CocoaControl(ct, QStyleHelper::SizeLarge);
+ auto *sv = static_cast<NSSplitView *>(d->cocoaControl(cw));
+ sv.frame = opt->rect.toCGRect();
+ d->drawNSViewInRect(cw, sv, opt->rect, p, w != nullptr, ^(CGContextRef ctx, const CGRect &rect) {
+ [sv drawDividerInRect:rect];
+ });
+ } else {
+ QPen oldPen = p->pen();
+ p->setPen(opt->palette.dark().color());
+ if (opt->state & QStyle::State_Horizontal)
+ p->drawLine(opt->rect.topLeft(), opt->rect.bottomLeft());
+ else
+ p->drawLine(opt->rect.topLeft(), opt->rect.topRight());
+ p->setPen(oldPen);
+ }
+ break;
+ case CE_RubberBand:
+ if (const QStyleOptionRubberBand *rubber = qstyleoption_cast<const QStyleOptionRubberBand *>(opt)) {
+ QColor fillColor(opt->palette.color(QPalette::Disabled, QPalette::Highlight));
+ if (!rubber->opaque) {
+ QColor strokeColor;
+ // I retrieved these colors from the Carbon-Dev mailing list
+ strokeColor.setHsvF(0, 0, 0.86, 1.0);
+ fillColor.setHsvF(0, 0, 0.53, 0.25);
+ if (opt->rect.width() * opt->rect.height() <= 3) {
+ p->fillRect(opt->rect, strokeColor);
+ } else {
+ QPen oldPen = p->pen();
+ QBrush oldBrush = p->brush();
+ QPen pen(strokeColor);
+ p->setPen(pen);
+ p->setBrush(fillColor);
+ QRect adjusted = opt->rect.adjusted(1, 1, -1, -1);
+ if (adjusted.isValid())
+ p->drawRect(adjusted);
+ p->setPen(oldPen);
+ p->setBrush(oldBrush);
+ }
+ } else {
+ p->fillRect(opt->rect, fillColor);
+ }
+ }
+ break;
+#ifndef QT_NO_TOOLBAR
+ case CE_ToolBar: {
+ const QStyleOptionToolBar *toolBar = qstyleoption_cast<const QStyleOptionToolBar *>(opt);
+
+ // Unified title and toolbar drawing. In this mode the cocoa platform plugin will
+ // fill the top toolbar area part with a background gradient that "unifies" with
+ // the title bar. The following code fills the toolBar area with transparent pixels
+ // to make that gradient visible.
+ if (w) {
+#if QT_CONFIG(mainwindow)
+ if (QMainWindow * mainWindow = qobject_cast<QMainWindow *>(w->window())) {
+ if (toolBar && toolBar->toolBarArea == Qt::TopToolBarArea && mainWindow->unifiedTitleAndToolBarOnMac()) {
+ // fill with transparent pixels.
+ p->save();
+ p->setCompositionMode(QPainter::CompositionMode_Source);
+ p->fillRect(opt->rect, Qt::transparent);
+ p->restore();
+
+ // Drow a horizontal separator line at the toolBar bottom if the "unified" area ends here.
+ // There might be additional toolbars or other widgets such as tab bars in document
+ // mode below. Determine this by making a unified toolbar area test for the row below
+ // this toolbar.
+ const QPoint windowToolbarEnd = w->mapTo(w->window(), opt->rect.bottomLeft());
+ const bool isEndOfUnifiedArea = !isInMacUnifiedToolbarArea(w->window()->windowHandle(), windowToolbarEnd.y() + 1);
+ if (isEndOfUnifiedArea) {
+ const int margin = qt_mac_aqua_get_metric(SeparatorSize);
+ const auto separatorRect = QRect(opt->rect.left(), opt->rect.bottom(), opt->rect.width(), margin);
+ p->fillRect(separatorRect, opt->palette.dark().color());
+ }
+ break;
+ }
+ }
+#endif
+ }
+
+ // draw background gradient
+ QLinearGradient linearGrad;
+ if (opt->state & State_Horizontal)
+ linearGrad = QLinearGradient(0, opt->rect.top(), 0, opt->rect.bottom());
+ else
+ linearGrad = QLinearGradient(opt->rect.left(), 0, opt->rect.right(), 0);
+
+ linearGrad.setColorAt(0, mainWindowGradientBegin);
+ linearGrad.setColorAt(1, mainWindowGradientEnd);
+ p->fillRect(opt->rect, linearGrad);
+
+ p->save();
+ if (opt->state & State_Horizontal) {
+ p->setPen(mainWindowGradientBegin.lighter(114));
+ p->drawLine(opt->rect.topLeft(), opt->rect.topRight());
+ p->setPen(mainWindowGradientEnd.darker(114));
+ p->drawLine(opt->rect.bottomLeft(), opt->rect.bottomRight());
+
+ } else {
+ p->setPen(mainWindowGradientBegin.lighter(114));
+ p->drawLine(opt->rect.topLeft(), opt->rect.bottomLeft());
+ p->setPen(mainWindowGradientEnd.darker(114));
+ p->drawLine(opt->rect.topRight(), opt->rect.bottomRight());
+ }
+ p->restore();
+
+
+ } break;
+#endif
+ default:
+ QCommonStyle::drawControl(ce, opt, p, w);
+ break;
+ }
+}
+
+static void setLayoutItemMargins(int left, int top, int right, int bottom, QRect *rect, Qt::LayoutDirection dir)
+{
+ if (dir == Qt::RightToLeft) {
+ rect->adjust(-right, top, -left, bottom);
+ } else {
+ rect->adjust(left, top, right, bottom);
+ }
+}
+
+QRect QMacStyle::subElementRect(SubElement sr, const QStyleOption *opt,
+ const QWidget *widget) const
+{
+ Q_D(const QMacStyle);
+ QRect rect;
+ const int controlSize = getControlSize(opt, widget);
+
+ switch (sr) {
+ case SE_ItemViewItemText:
+ if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(opt)) {
+ int fw = proxy()->pixelMetric(PM_FocusFrameHMargin, opt, widget);
+ // We add the focusframeargin between icon and text in commonstyle
+ rect = QCommonStyle::subElementRect(sr, opt, widget);
+ if (vopt->features & QStyleOptionViewItem::HasDecoration)
+ rect.adjust(-fw, 0, 0, 0);
+ }
+ break;
+ case SE_ToolBoxTabContents:
+ rect = QCommonStyle::subElementRect(sr, opt, widget);
+ break;
+ case SE_PushButtonContents:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ // Unlike Carbon, we want the button to always be drawn inside its bounds.
+ // Therefore, the button is a bit smaller, so that even if it got focus,
+ // the focus 'shadow' will be inside. Adjust the content rect likewise.
+ HIThemeButtonDrawInfo bdi;
+ d->initHIThemePushButton(btn, widget, d->getDrawState(opt->state), &bdi);
+ CGRect contentRect = d->pushButtonContentBounds(btn, &bdi);
+ rect = QRectF::fromCGRect(contentRect).toRect();
+ }
+ break;
+ case SE_HeaderLabel: {
+ int margin = proxy()->pixelMetric(QStyle::PM_HeaderMargin, opt, widget);
+ rect.setRect(opt->rect.x() + margin, opt->rect.y(),
+ opt->rect.width() - margin * 2, opt->rect.height() - 2);
+ if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt)) {
+ // Subtract width needed for arrow, if there is one
+ if (header->sortIndicator != QStyleOptionHeader::None) {
+ if (opt->state & State_Horizontal)
+ rect.setWidth(rect.width() - (headerSectionArrowHeight) - (margin * 2));
+ else
+ rect.setHeight(rect.height() - (headerSectionArrowHeight) - (margin * 2));
+ }
+ }
+ rect = visualRect(opt->direction, opt->rect, rect);
+ break;
+ }
+ case SE_HeaderArrow: {
+ int h = opt->rect.height();
+ int w = opt->rect.width();
+ int x = opt->rect.x();
+ int y = opt->rect.y();
+ int margin = proxy()->pixelMetric(QStyle::PM_HeaderMargin, opt, widget);
+
+ if (opt->state & State_Horizontal) {
+ rect.setRect(x + w - margin * 2 - headerSectionArrowHeight, y + 5,
+ headerSectionArrowHeight, h - margin * 2 - 5);
+ } else {
+ rect.setRect(x + 5, y + h - margin * 2 - headerSectionArrowHeight,
+ w - margin * 2 - 5, headerSectionArrowHeight);
+ }
+ rect = visualRect(opt->direction, opt->rect, rect);
+ break;
+ }
+ case SE_ProgressBarGroove:
+ // Wrong in the secondary dimension, but accurate enough in the main dimension.
+ rect = opt->rect;
+ break;
+ case SE_ProgressBarLabel:
+ break;
+ case SE_ProgressBarContents:
+ rect = opt->rect;
+ break;
+ case SE_TreeViewDisclosureItem: {
+ CGRect inRect = CGRectMake(opt->rect.x(), opt->rect.y(),
+ opt->rect.width(), opt->rect.height());
+ HIThemeButtonDrawInfo bdi;
+ bdi.version = qt_mac_hitheme_version;
+ bdi.state = kThemeStateActive;
+ bdi.kind = kThemeDisclosureButton;
+ bdi.value = kThemeDisclosureRight;
+ bdi.adornment = kThemeAdornmentNone;
+ CGRect contentRect;
+ HIThemeGetButtonContentBounds(&inRect, &bdi, &contentRect);
+ QCFType<HIShapeRef> shape;
+ CGRect outRect;
+ HIThemeGetButtonShape(&inRect, &bdi, &shape);
+ HIShapeGetBounds(shape, &outRect);
+ rect = QRect(int(outRect.origin.x + DisclosureOffset), int(outRect.origin.y),
+ int(contentRect.origin.x - outRect.origin.x + DisclosureOffset),
+ int(outRect.size.height));
+ break;
+ }
+#if QT_CONFIG(tabwidget)
+ case SE_TabWidgetLeftCorner:
+ if (const QStyleOptionTabWidgetFrame *twf
+ = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) {
+ switch (twf->shape) {
+ case QTabBar::RoundedNorth:
+ case QTabBar::TriangularNorth:
+ rect = QRect(QPoint(0, 0), twf->leftCornerWidgetSize);
+ break;
+ case QTabBar::RoundedSouth:
+ case QTabBar::TriangularSouth:
+ rect = QRect(QPoint(0, twf->rect.height() - twf->leftCornerWidgetSize.height()),
+ twf->leftCornerWidgetSize);
+ break;
+ default:
+ break;
+ }
+ rect = visualRect(twf->direction, twf->rect, rect);
+ }
+ break;
+ case SE_TabWidgetRightCorner:
+ if (const QStyleOptionTabWidgetFrame *twf
+ = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) {
+ switch (twf->shape) {
+ case QTabBar::RoundedNorth:
+ case QTabBar::TriangularNorth:
+ rect = QRect(QPoint(twf->rect.width() - twf->rightCornerWidgetSize.width(), 0),
+ twf->rightCornerWidgetSize);
+ break;
+ case QTabBar::RoundedSouth:
+ case QTabBar::TriangularSouth:
+ rect = QRect(QPoint(twf->rect.width() - twf->rightCornerWidgetSize.width(),
+ twf->rect.height() - twf->rightCornerWidgetSize.height()),
+ twf->rightCornerWidgetSize);
+ break;
+ default:
+ break;
+ }
+ rect = visualRect(twf->direction, twf->rect, rect);
+ }
+ break;
+ case SE_TabWidgetTabContents:
+ rect = QCommonStyle::subElementRect(sr, opt, widget);
+ if (const QStyleOptionTabWidgetFrame *twf
+ = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) {
+ if (twf->lineWidth != 0) {
+ switch (getTabDirection(twf->shape)) {
+ case kThemeTabNorth:
+ rect.adjust(+1, +14, -1, -1);
+ break;
+ case kThemeTabSouth:
+ rect.adjust(+1, +1, -1, -14);
+ break;
+ case kThemeTabWest:
+ rect.adjust(+14, +1, -1, -1);
+ break;
+ case kThemeTabEast:
+ rect.adjust(+1, +1, -14, -1);
+ }
+ }
+ }
+ break;
+ case SE_TabBarTabText:
+ if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
+ QRect dummyIconRect;
+ d->tabLayout(tab, widget, &rect, &dummyIconRect);
+ }
+ break;
+ case SE_TabBarTabLeftButton:
+ case SE_TabBarTabRightButton:
+ if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
+ bool selected = tab->state & State_Selected;
+ int verticalShift = proxy()->pixelMetric(QStyle::PM_TabBarTabShiftVertical, tab, widget);
+ int horizontalShift = proxy()->pixelMetric(QStyle::PM_TabBarTabShiftHorizontal, tab, widget);
+ int hpadding = 5;
+
+ bool verticalTabs = tab->shape == QTabBar::RoundedEast
+ || tab->shape == QTabBar::RoundedWest
+ || tab->shape == QTabBar::TriangularEast
+ || tab->shape == QTabBar::TriangularWest;
+
+ QRect tr = tab->rect;
+ if (tab->shape == QTabBar::RoundedSouth || tab->shape == QTabBar::TriangularSouth)
+ verticalShift = -verticalShift;
+ if (verticalTabs) {
+ qSwap(horizontalShift, verticalShift);
+ horizontalShift *= -1;
+ verticalShift *= -1;
+ }
+ if (tab->shape == QTabBar::RoundedWest || tab->shape == QTabBar::TriangularWest)
+ horizontalShift = -horizontalShift;
+
+ tr.adjust(0, 0, horizontalShift, verticalShift);
+ if (selected)
+ {
+ tr.setBottom(tr.bottom() - verticalShift);
+ tr.setRight(tr.right() - horizontalShift);
+ }
+
+ QSize size = (sr == SE_TabBarTabLeftButton) ? tab->leftButtonSize : tab->rightButtonSize;
+ int w = size.width();
+ int h = size.height();
+ int midHeight = static_cast<int>(qCeil(float(tr.height() - h) / 2));
+ int midWidth = ((tr.width() - w) / 2);
+
+ bool atTheTop = true;
+ switch (tab->shape) {
+ case QTabBar::RoundedWest:
+ case QTabBar::TriangularWest:
+ atTheTop = (sr == SE_TabBarTabLeftButton);
+ break;
+ case QTabBar::RoundedEast:
+ case QTabBar::TriangularEast:
+ atTheTop = (sr == SE_TabBarTabRightButton);
+ break;
+ default:
+ if (sr == SE_TabBarTabLeftButton)
+ rect = QRect(tab->rect.x() + hpadding, midHeight, w, h);
+ else
+ rect = QRect(tab->rect.right() - w - hpadding, midHeight, w, h);
+ rect = visualRect(tab->direction, tab->rect, rect);
+ }
+ if (verticalTabs) {
+ if (atTheTop)
+ rect = QRect(midWidth, tr.y() + tab->rect.height() - hpadding - h, w, h);
+ else
+ rect = QRect(midWidth, tr.y() + hpadding, w, h);
+ }
+ }
+ break;
+#endif
+ case SE_LineEditContents:
+ rect = QCommonStyle::subElementRect(sr, opt, widget);
+#if QT_CONFIG(combobox)
+ if (widget && qobject_cast<const QComboBox*>(widget->parentWidget()))
+ rect.adjust(-1, -2, 0, 0);
+ else
+#endif
+ rect.adjust(-1, -1, 0, +1);
+ break;
+ case SE_CheckBoxLayoutItem:
+ rect = opt->rect;
+ if (controlSize == QStyleHelper::SizeLarge) {
+ setLayoutItemMargins(+2, +3, -9, -4, &rect, opt->direction);
+ } else if (controlSize == QStyleHelper::SizeSmall) {
+ setLayoutItemMargins(+1, +5, 0 /* fix */, -6, &rect, opt->direction);
+ } else {
+ setLayoutItemMargins(0, +7, 0 /* fix */, -6, &rect, opt->direction);
+ }
+ break;
+ case SE_ComboBoxLayoutItem:
+#ifndef QT_NO_TOOLBAR
+ if (widget && qobject_cast<QToolBar *>(widget->parentWidget())) {
+ // Do nothing, because QToolbar needs the entire widget rect.
+ // Otherwise it will be clipped. Equivalent to
+ // widget->setAttribute(Qt::WA_LayoutUsesWidgetRect), but without
+ // all the hassle.
+ } else
+#endif
+ {
+ rect = opt->rect;
+ if (controlSize == QStyleHelper::SizeLarge) {
+ rect.adjust(+3, +2, -3, -4);
+ } else if (controlSize == QStyleHelper::SizeSmall) {
+ setLayoutItemMargins(+2, +1, -3, -4, &rect, opt->direction);
+ } else {
+ setLayoutItemMargins(+1, 0, -2, 0, &rect, opt->direction);
+ }
+ }
+ break;
+ case SE_LabelLayoutItem:
+ rect = opt->rect;
+ setLayoutItemMargins(+1, 0 /* SHOULD be -1, done for alignment */, 0, 0 /* SHOULD be -1, done for alignment */, &rect, opt->direction);
+ break;
+ case SE_ProgressBarLayoutItem: {
+ rect = opt->rect;
+ int bottom = SIZE(3, 8, 8);
+ if (opt->state & State_Horizontal) {
+ rect.adjust(0, +1, 0, -bottom);
+ } else {
+ setLayoutItemMargins(+1, 0, -bottom, 0, &rect, opt->direction);
+ }
+ break;
+ }
+ case SE_PushButtonLayoutItem:
+ if (const QStyleOptionButton *buttonOpt
+ = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ if ((buttonOpt->features & QStyleOptionButton::Flat))
+ break; // leave rect alone
+ }
+ rect = opt->rect;
+ if (controlSize == QStyleHelper::SizeLarge) {
+ rect.adjust(+6, +4, -6, -8);
+ } else if (controlSize == QStyleHelper::SizeSmall) {
+ rect.adjust(+5, +4, -5, -6);
+ } else {
+ rect.adjust(+1, 0, -1, -2);
+ }
+ break;
+ case SE_RadioButtonLayoutItem:
+ rect = opt->rect;
+ if (controlSize == QStyleHelper::SizeLarge) {
+ setLayoutItemMargins(+2, +2 /* SHOULD BE +3, done for alignment */,
+ 0, -4 /* SHOULD BE -3, done for alignment */, &rect, opt->direction);
+ } else if (controlSize == QStyleHelper::SizeSmall) {
+ rect.adjust(0, +6, 0 /* fix */, -5);
+ } else {
+ rect.adjust(0, +6, 0 /* fix */, -7);
+ }
+ break;
+ case SE_SliderLayoutItem:
+ if (const QStyleOptionSlider *sliderOpt
+ = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ rect = opt->rect;
+ if (sliderOpt->tickPosition == QSlider::NoTicks) {
+ int above = SIZE(3, 0, 2);
+ int below = SIZE(4, 3, 0);
+ if (sliderOpt->orientation == Qt::Horizontal) {
+ rect.adjust(0, +above, 0, -below);
+ } else {
+ rect.adjust(+above, 0, -below, 0); //### Seems that QSlider flip the position of the ticks in reverse mode.
+ }
+ } else if (sliderOpt->tickPosition == QSlider::TicksAbove) {
+ int below = SIZE(3, 2, 0);
+ if (sliderOpt->orientation == Qt::Horizontal) {
+ rect.setHeight(rect.height() - below);
+ } else {
+ rect.setWidth(rect.width() - below);
+ }
+ } else if (sliderOpt->tickPosition == QSlider::TicksBelow) {
+ int above = SIZE(3, 2, 0);
+ if (sliderOpt->orientation == Qt::Horizontal) {
+ rect.setTop(rect.top() + above);
+ } else {
+ rect.setLeft(rect.left() + above);
+ }
+ }
+ }
+ break;
+ case SE_FrameLayoutItem:
+ // hack because QStyleOptionFrame doesn't have a frameStyle member
+ if (const QFrame *frame = qobject_cast<const QFrame *>(widget)) {
+ rect = opt->rect;
+ switch (frame->frameStyle() & QFrame::Shape_Mask) {
+ case QFrame::HLine:
+ rect.adjust(0, +1, 0, -1);
+ break;
+ case QFrame::VLine:
+ rect.adjust(+1, 0, -1, 0);
+ break;
+ default:
+ ;
+ }
+ }
+ break;
+ case SE_GroupBoxLayoutItem:
+ rect = opt->rect;
+ if (const QStyleOptionGroupBox *groupBoxOpt =
+ qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) {
+ /*
+ AHIG is very inconsistent when it comes to group boxes.
+ Basically, we make sure that (non-checkable) group boxes
+ and tab widgets look good when laid out side by side.
+ */
+ if (groupBoxOpt->subControls & (QStyle::SC_GroupBoxCheckBox
+ | QStyle::SC_GroupBoxLabel)) {
+ int delta;
+ if (groupBoxOpt->subControls & QStyle::SC_GroupBoxCheckBox) {
+ delta = SIZE(8, 4, 4); // guess
+ } else {
+ delta = SIZE(15, 12, 12); // guess
+ }
+ rect.setTop(rect.top() + delta);
+ }
+ }
+ rect.setBottom(rect.bottom() - 1);
+ break;
+#if QT_CONFIG(tabwidget)
+ case SE_TabWidgetLayoutItem:
+ if (const QStyleOptionTabWidgetFrame *tabWidgetOpt =
+ qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) {
+ /*
+ AHIG specifies "12 or 14" as the distance from the window
+ edge. We choose 14 and since the default top margin is 20,
+ the overlap is 6.
+ */
+ rect = tabWidgetOpt->rect;
+ if (tabWidgetOpt->shape == QTabBar::RoundedNorth)
+ rect.setTop(rect.top() + SIZE(6 /* AHIG */, 3 /* guess */, 2 /* AHIG */));
+ }
+ break;
+#endif
+#if QT_CONFIG(dockwidget)
+ case SE_DockWidgetCloseButton:
+ case SE_DockWidgetFloatButton:
+ case SE_DockWidgetTitleBarText:
+ case SE_DockWidgetIcon: {
+ int iconSize = proxy()->pixelMetric(PM_SmallIconSize, opt, widget);
+ int buttonMargin = proxy()->pixelMetric(PM_DockWidgetTitleBarButtonMargin, opt, widget);
+ QRect srect = opt->rect;
+
+ const QStyleOptionDockWidget *dwOpt
+ = qstyleoption_cast<const QStyleOptionDockWidget*>(opt);
+ bool canClose = dwOpt == 0 ? true : dwOpt->closable;
+ bool canFloat = dwOpt == 0 ? false : dwOpt->floatable;
+
+ const bool verticalTitleBar = dwOpt->verticalTitleBar;
+
+ // If this is a vertical titlebar, we transpose and work as if it was
+ // horizontal, then transpose again.
+ if (verticalTitleBar)
+ srect = srect.transposed();
+
+ do {
+ int right = srect.right();
+ int left = srect.left();
+
+ QRect closeRect;
+ if (canClose) {
+ QSize sz = proxy()->standardIcon(QStyle::SP_TitleBarCloseButton,
+ opt, widget).actualSize(QSize(iconSize, iconSize));
+ sz += QSize(buttonMargin, buttonMargin);
+ if (verticalTitleBar)
+ sz = sz.transposed();
+ closeRect = QRect(left,
+ srect.center().y() - sz.height()/2,
+ sz.width(), sz.height());
+ left = closeRect.right() + 1;
+ }
+ if (sr == SE_DockWidgetCloseButton) {
+ rect = closeRect;
+ break;
+ }
+
+ QRect floatRect;
+ if (canFloat) {
+ QSize sz = proxy()->standardIcon(QStyle::SP_TitleBarNormalButton,
+ opt, widget).actualSize(QSize(iconSize, iconSize));
+ sz += QSize(buttonMargin, buttonMargin);
+ if (verticalTitleBar)
+ sz = sz.transposed();
+ floatRect = QRect(left,
+ srect.center().y() - sz.height()/2,
+ sz.width(), sz.height());
+ left = floatRect.right() + 1;
+ }
+ if (sr == SE_DockWidgetFloatButton) {
+ rect = floatRect;
+ break;
+ }
+
+ QRect iconRect;
+ if (const QDockWidget *dw = qobject_cast<const QDockWidget*>(widget)) {
+ QIcon icon;
+ if (dw->isFloating())
+ icon = dw->windowIcon();
+ if (!icon.isNull()
+ && icon.cacheKey() != QApplication::windowIcon().cacheKey()) {
+ QSize sz = icon.actualSize(QSize(rect.height(), rect.height()));
+ if (verticalTitleBar)
+ sz = sz.transposed();
+ iconRect = QRect(right - sz.width(), srect.center().y() - sz.height()/2,
+ sz.width(), sz.height());
+ right = iconRect.left() - 1;
+ }
+ }
+ if (sr == SE_DockWidgetIcon) {
+ rect = iconRect;
+ break;
+ }
+
+ QRect textRect = QRect(left, srect.top(),
+ right - left, srect.height());
+ if (sr == SE_DockWidgetTitleBarText) {
+ rect = textRect;
+ break;
+ }
+ } while (false);
+
+ if (verticalTitleBar) {
+ rect = QRect(srect.left() + rect.top() - srect.top(),
+ srect.top() + srect.right() - rect.right(),
+ rect.height(), rect.width());
+ } else {
+ rect = visualRect(opt->direction, srect, rect);
+ }
+ break;
+ }
+#endif
+ default:
+ rect = QCommonStyle::subElementRect(sr, opt, widget);
+ break;
+ }
+ return rect;
+}
+
+static inline void drawToolbarButtonArrow(const QRect &toolButtonRect, ThemeDrawState tds, CGContextRef cg)
+{
+ QRect arrowRect = QRect(toolButtonRect.right() - 9, toolButtonRect.bottom() - 9, 7, 5);
+ HIThemePopupArrowDrawInfo padi;
+ padi.version = qt_mac_hitheme_version;
+ padi.state = tds;
+ padi.orientation = kThemeArrowDown;
+ padi.size = kThemeArrow7pt;
+ CGRect cgRect = arrowRect.toCGRect();
+ HIThemeDrawPopupArrow(&cgRect, &padi, cg, kHIThemeOrientationNormal);
+}
+
+void QMacStylePrivate::setupNSGraphicsContext(CGContextRef cg, bool flipped) const
+{
+ CGContextSaveGState(cg);
+ [NSGraphicsContext saveGraphicsState];
+
+ [NSGraphicsContext setCurrentContext:
+ [NSGraphicsContext graphicsContextWithCGContext:cg flipped:flipped]];
+}
+
+void QMacStylePrivate::restoreNSGraphicsContext(CGContextRef cg) const
+{
+ [NSGraphicsContext restoreGraphicsState];
+ CGContextRestoreGState(cg);
+}
+
+void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *p,
+ const QWidget *widget) const
+{
+ Q_D(const QMacStyle);
+ ThemeDrawState tds = d->getDrawState(opt->state);
+ QMacCGContext cg(p);
+ QWindow *window = widget && widget->window() ? widget->window()->windowHandle() :
+ QStyleHelper::styleObjectWindow(opt->styleObject);
+ d->resolveCurrentNSView(window);
+ switch (cc) {
+ case CC_ScrollBar:
+ if (const QStyleOptionSlider *sb = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+
+ const bool drawTrack = sb->subControls & SC_ScrollBarGroove;
+ const bool drawKnob = sb->subControls & SC_ScrollBarSlider;
+ if (!drawTrack && !drawKnob)
+ break;
+
+ const bool isHorizontal = sb->orientation == Qt::Horizontal;
+
+ if (opt && opt->styleObject && !QMacStylePrivate::scrollBars.contains(opt->styleObject))
+ QMacStylePrivate::scrollBars.append(QPointer<QObject>(opt->styleObject));
+
+ static const CGFloat knobWidths[] = { 7.0, 5.0, 5.0 };
+ static const CGFloat expandedKnobWidths[] = { 11.0, 9.0, 9.0 };
+ const auto cocoaSize = d->effectiveAquaSizeConstrain(opt, widget);
+ const CGFloat maxExpandScale = expandedKnobWidths[cocoaSize] / knobWidths[cocoaSize];
+
+ const bool isTransient = proxy()->styleHint(SH_ScrollBar_Transient, opt, widget);
+ if (!isTransient)
+ d->stopAnimation(opt->styleObject);
+ bool wasActive = false;
+ CGFloat opacity = 0.0;
+ CGFloat expandScale = 1.0;
+ CGFloat expandOffset = 0.0;
+ bool shouldExpand = false;
+
+ if (QObject *styleObject = opt->styleObject) {
+ const int oldPos = styleObject->property("_q_stylepos").toInt();
+ const int oldMin = styleObject->property("_q_stylemin").toInt();
+ const int oldMax = styleObject->property("_q_stylemax").toInt();
+ const QRect oldRect = styleObject->property("_q_stylerect").toRect();
+ const QStyle::State oldState = static_cast<QStyle::State>(styleObject->property("_q_stylestate").value<QStyle::State::Int>());
+ const uint oldActiveControls = styleObject->property("_q_stylecontrols").toUInt();
+
+ // a scrollbar is transient when the scrollbar itself and
+ // its sibling are both inactive (ie. not pressed/hovered/moved)
+ const bool transient = isTransient && !opt->activeSubControls && !(sb->state & State_On);
+
+ if (!transient ||
+ oldPos != sb->sliderPosition ||
+ oldMin != sb->minimum ||
+ oldMax != sb->maximum ||
+ oldRect != sb->rect ||
+ oldState != sb->state ||
+ oldActiveControls != sb->activeSubControls) {
+
+ // if the scrollbar is transient or its attributes, geometry or
+ // state has changed, the opacity is reset back to 100% opaque
+ opacity = 1.0;
+
+ styleObject->setProperty("_q_stylepos", sb->sliderPosition);
+ styleObject->setProperty("_q_stylemin", sb->minimum);
+ styleObject->setProperty("_q_stylemax", sb->maximum);
+ styleObject->setProperty("_q_stylerect", sb->rect);
+ styleObject->setProperty("_q_stylestate", static_cast<QStyle::State::Int>(sb->state));
+ styleObject->setProperty("_q_stylecontrols", static_cast<uint>(sb->activeSubControls));
+
+ QScrollbarStyleAnimation *anim = qobject_cast<QScrollbarStyleAnimation *>(d->animation(styleObject));
+ if (transient) {
+ if (!anim) {
+ anim = new QScrollbarStyleAnimation(QScrollbarStyleAnimation::Deactivating, styleObject);
+ d->startAnimation(anim);
+ } else if (anim->mode() == QScrollbarStyleAnimation::Deactivating) {
+ // the scrollbar was already fading out while the
+ // state changed -> restart the fade out animation
+ anim->setCurrentTime(0);
+ }
+ } else if (anim && anim->mode() == QScrollbarStyleAnimation::Deactivating) {
+ d->stopAnimation(styleObject);
+ }
+ }
+
+ QScrollbarStyleAnimation *anim = qobject_cast<QScrollbarStyleAnimation *>(d->animation(styleObject));
+ if (anim && anim->mode() == QScrollbarStyleAnimation::Deactivating) {
+ // once a scrollbar was active (hovered/pressed), it retains
+ // the active look even if it's no longer active while fading out
+ if (oldActiveControls)
+ anim->setActive(true);
+
+ wasActive = anim->wasActive();
+ opacity = anim->currentValue();
+ }
+
+ shouldExpand = isTransient && (opt->activeSubControls || wasActive);
+ if (shouldExpand) {
+ if (!anim && !oldActiveControls) {
+ // Start expand animation only once and when entering
+ anim = new QScrollbarStyleAnimation(QScrollbarStyleAnimation::Activating, styleObject);
+ d->startAnimation(anim);
+ }
+ if (anim && anim->mode() == QScrollbarStyleAnimation::Activating) {
+ expandScale = 1.0 + (maxExpandScale - 1.0) * anim->currentValue();
+ expandOffset = 5.5 * (1.0 - anim->currentValue());
+ } else {
+ // Keep expanded state after the animation ends, and when fading out
+ expandScale = maxExpandScale;
+ expandOffset = 0.0;
+ }
+ }
+ }
+
+ d->setupNSGraphicsContext(cg, NO /* flipped */);
+
+ const auto controlType = isHorizontal ? QMacStylePrivate::Scroller_Horizontal : QMacStylePrivate::Scroller_Vertical;
+ const auto cw = QMacStylePrivate::CocoaControl(controlType, cocoaSize);
+ NSScroller *scroller = static_cast<NSScroller *>(d->cocoaControl(cw));
+
+ const QColor bgColor = QStyleHelper::backgroundColor(opt->palette, widget);
+ const bool hasDarkBg = bgColor.red() < 128 && bgColor.green() < 128 && bgColor.blue() < 128;
+ if (isTransient) {
+ // macOS behavior: as soon as one color channel is >= 128,
+ // the background is considered bright, scroller is dark.
+ scroller.knobStyle = hasDarkBg? NSScrollerKnobStyleLight : NSScrollerKnobStyleDark;
+ } else {
+ scroller.knobStyle = NSScrollerKnobStyleDefault;
+ }
+
+ scroller.scrollerStyle = isTransient ? NSScrollerStyleOverlay : NSScrollerStyleLegacy;
+
+ if (!setupScroller(scroller, sb))
+ break;
+
+ if (isTransient) {
+ CGContextBeginTransparencyLayerWithRect(cg, scroller.frame, nullptr);
+ CGContextSetAlpha(cg, opacity);
+ }
+
+ if (drawTrack) {
+ // Draw the track when hovering. Expand by shifting the track rect.
+ if (!isTransient || opt->activeSubControls || wasActive) {
+ CGRect trackRect = scroller.bounds;
+ if (isHorizontal)
+ trackRect.origin.y += expandOffset;
+ else
+ trackRect.origin.x += expandOffset;
+ [scroller drawKnobSlotInRect:trackRect highlight:NO];
+ }
+ }
+
+ if (drawKnob) {
+ if (shouldExpand) {
+ // -[NSScroller drawKnob] is not useful here because any scaling applied
+ // will only be used to draw the hi-DPI artwork. And even if did scale,
+ // the stretched knob would look wrong, actually. So we need to draw the
+ // scroller manually when it's being hovered.
+ const CGFloat scrollerWidth = [NSScroller scrollerWidthForControlSize:scroller.controlSize scrollerStyle:scroller.scrollerStyle];
+ const CGFloat knobWidth = knobWidths[cocoaSize] * expandScale;
+ // Cocoa can help get the exact knob length in the current orientation
+ const CGRect scrollerKnobRect = CGRectInset([scroller rectForPart:NSScrollerKnob], 1, 1);
+ const CGFloat knobLength = isHorizontal ? scrollerKnobRect.size.width : scrollerKnobRect.size.height;
+ const CGFloat knobPos = isHorizontal ? scrollerKnobRect.origin.x : scrollerKnobRect.origin.y;
+ const CGFloat knobOffset = qRound((scrollerWidth + expandOffset - knobWidth) / 2.0);
+ const CGFloat knobRadius = knobWidth / 2.0;
+ CGRect knobRect;
+ if (isHorizontal)
+ knobRect = CGRectMake(knobPos, knobOffset, knobLength, knobWidth);
+ else
+ knobRect = CGRectMake(knobOffset, knobPos, knobWidth, knobLength);
+ QCFType<CGPathRef> knobPath = CGPathCreateWithRoundedRect(knobRect, knobRadius, knobRadius, nullptr);
+ CGContextAddPath(cg, knobPath);
+ CGContextSetAlpha(cg, 0.5);
+ CGColorRef knobColor = hasDarkBg ? NSColor.whiteColor.CGColor : NSColor.blackColor.CGColor;
+ CGContextSetFillColorWithColor(cg, knobColor);
+ CGContextFillPath(cg);
+ } else {
+ [scroller drawKnob];
+
+ if (!isTransient && opt->activeSubControls) {
+ // The knob should appear darker (going from 0.76 down to 0.49).
+ // But no blending mode can help darken enough in a single pass,
+ // so we resort to drawing the knob twice with a small help from
+ // blending. This brings the gray level to a close enough 0.53.
+ CGContextSetBlendMode(cg, kCGBlendModePlusDarker);
+ [scroller drawKnob];
+ }
+ }
+ }
+
+ if (isTransient)
+ CGContextEndTransparencyLayer(cg);
+
+ d->restoreNSGraphicsContext(cg);
+ }
+ break;
+ case CC_Slider:
+ if (const QStyleOptionSlider *sl = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ const bool isHorizontal = sl->orientation == Qt::Horizontal;
+ const auto ct = isHorizontal ? QMacStylePrivate::Slider_Horizontal : QMacStylePrivate::Slider_Vertical;
+ const auto cs = d->effectiveAquaSizeConstrain(opt, widget);
+ const auto cw = QMacStylePrivate::CocoaControl(ct, cs);
+ auto *slider = static_cast<NSSlider *>(d->cocoaControl(cw));
+ if (!setupSlider(slider, sl))
+ break;
+
+ const bool hasTicks = sl->tickPosition != QSlider::NoTicks;
+ const bool hasDoubleTicks = sl->tickPosition == QSlider::TicksBothSides;
+ const bool drawKnob = sl->subControls & SC_SliderHandle;
+ const bool drawBar = sl->subControls & SC_SliderGroove;
+ const bool drawTicks = sl->subControls & SC_SliderTickmarks;
+ const bool isPressed = sl->state & State_Sunken;
+
+ CGPoint pressPoint;
+ if (isPressed) {
+ const CGRect knobRect = [slider.cell knobRectFlipped:NO];
+ pressPoint.x = CGRectGetMidX(knobRect);
+ pressPoint.y = CGRectGetMidY(knobRect);
+ [slider.cell startTrackingAt:pressPoint inView:slider];
+ }
+
+ d->drawNSViewInRect(cw, slider, opt->rect, p, widget != 0, ^(CGContextRef ctx, const CGRect &rect) {
+ if (isHorizontal && sl->upsideDown) {
+ CGContextTranslateCTM(ctx, rect.size.width, 0);
+ CGContextScaleCTM(ctx, -1, 1);
+ }
+
+ if (hasDoubleTicks) {
+ // This ain't HIG kosher: eye-proved constants
+ if (isHorizontal)
+ CGContextTranslateCTM(ctx, 0, 4);
+ else
+ CGContextTranslateCTM(ctx, 1, 0);
+ }
+
+ // Since the GC is flipped, upsideDown means *not* inverted when vertical.
+ const bool verticalFlip = !isHorizontal && !sl->upsideDown; // FIXME: && !isSierraOrLater
+
+#if 0
+ // FIXME: Sadly, this part doesn't work. It seems to somehow polute the
+ // NSSlider's internal state and, when we need to use the "else" part,
+ // the slider's frame is not in sync with its cell dimensions.
+ const bool drawAllParts = drawKnob && drawBar && (!hasTicks || drawTicks);
+ if (drawAllParts && !hasDoubleTicks && (!verticalFlip || drawTicks)) {
+ // Draw eveything at once if we're going to, except for inverted vertical
+ // sliders which need to be drawn part by part because of the shadow below
+ // the knob. Same for two-sided tickmarks.
+ if (verticalFlip && drawTicks) {
+ // Since tickmarks are always rendered symmetrically, a vertically
+ // flipped slider with tickmarks only needs to get its value flipped.
+ slider.intValue = slider.maxValue - slider.intValue + slider.minValue;
+ }
+ [slider drawRect:CGRectZero];
+ } else
+#endif
+ {
+ [slider calcSize];
+ NSSliderCell *cell = slider.cell;
+
+ const int numberOfTickMarks = slider.numberOfTickMarks;
+ // This ain't HIG kosher: force tick-less bar position.
+ if (hasDoubleTicks)
+ slider.numberOfTickMarks = 0;
+
+ const CGRect barRect = [cell barRectFlipped:hasTicks];
+ if (drawBar) {
+ // This ain't HIG kosher: force unfilled bar look.
+ if (hasDoubleTicks)
+ slider.numberOfTickMarks = numberOfTickMarks;
+ [cell drawBarInside:barRect flipped:!verticalFlip];
+ }
+
+ if (hasTicks && drawTicks) {
+ if (!drawBar && hasDoubleTicks)
+ slider.numberOfTickMarks = numberOfTickMarks;
+
+ [cell drawTickMarks];
+
+ if (hasDoubleTicks) {
+ // This ain't HIG kosher: just slap a set of tickmarks on each side, like we used to.
+ CGAffineTransform tickMarksFlip;
+ const CGRect tickMarkRect = [cell rectOfTickMarkAtIndex:0];
+ if (isHorizontal) {
+ tickMarksFlip = CGAffineTransformMakeTranslation(0, rect.size.height - tickMarkRect.size.height - 3);
+ tickMarksFlip = CGAffineTransformScale(tickMarksFlip, 1, -1);
+ } else {
+ tickMarksFlip = CGAffineTransformMakeTranslation(rect.size.width - tickMarkRect.size.width / 2, 0);
+ tickMarksFlip = CGAffineTransformScale(tickMarksFlip, -1, 1);
+ }
+ CGContextConcatCTM(ctx, tickMarksFlip);
+ [cell drawTickMarks];
+ CGContextConcatCTM(ctx, CGAffineTransformInvert(tickMarksFlip));
+ }
+ }
+
+ if (drawKnob) {
+ // This ain't HIG kosher: force round knob look.
+ if (hasDoubleTicks)
+ slider.numberOfTickMarks = 0;
+ // Draw the knob in the symmetrical position instead of flipping.
+ if (verticalFlip)
+ slider.intValue = slider.maxValue - slider.intValue + slider.minValue;
+ [cell drawKnob];
+ }
+ }
+ });
+
+ if (isPressed)
+ [slider.cell stopTracking:pressPoint at:pressPoint inView:slider mouseIsUp:NO];
+ }
+ break;
+#if QT_CONFIG(spinbox)
+ case CC_SpinBox:
+ if (const QStyleOptionSpinBox *sb = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) {
+ if (sb->frame && (sb->subControls & SC_SpinBoxFrame)) {
+ const auto lineEditRect = proxy()->subControlRect(CC_SpinBox, sb, SC_SpinBoxEditField, widget);
+ QStyleOptionFrame frame;
+ static_cast<QStyleOption &>(frame) = *opt;
+ frame.rect = lineEditRect;
+ frame.state |= State_Sunken;
+ frame.lineWidth = 1;
+ frame.midLineWidth = 0;
+ frame.features = QStyleOptionFrame::None;
+ frame.frameShape = QFrame::Box;
+ drawPrimitive(PE_FrameLineEdit, &frame, p, widget);
+ }
+ if (sb->subControls & (SC_SpinBoxUp | SC_SpinBoxDown)) {
+ const QRect updown = proxy()->subControlRect(CC_SpinBox, sb, SC_SpinBoxUp, widget)
+ | proxy()->subControlRect(CC_SpinBox, sb, SC_SpinBoxDown, widget);
+
+ d->setupNSGraphicsContext(cg, NO);
+
+ const auto aquaSize = d->effectiveAquaSizeConstrain(opt, widget);
+ const auto cw = QMacStylePrivate::CocoaControl(QMacStylePrivate::Stepper, aquaSize);
+ NSStepperCell *cell = static_cast<NSStepperCell *>(d->cocoaCell(cw));
+ cell.enabled = (sb->state & State_Enabled);
+
+ const CGRect newRect = [cell drawingRectForBounds:updown.toCGRect()];
+
+ const bool upPressed = sb->activeSubControls == SC_SpinBoxUp && (sb->state & State_Sunken);
+ const bool downPressed = sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_Sunken);
+ const CGFloat x = CGRectGetMidX(newRect);
+ const CGFloat y = upPressed ? -3 : 3; // Weird coordinate shift going on. Verified with Hopper
+ const CGPoint pressPoint = CGPointMake(x, y);
+ // Pretend we're pressing the mouse on the right button. Unfortunately, NSStepperCell has no
+ // API to highlight a specific button. The highlighted property works only on the down button.
+ if (upPressed || downPressed)
+ [cell startTrackingAt:pressPoint inView:d->backingStoreNSView];
+
+ [cell drawWithFrame:newRect inView:d->backingStoreNSView];
+
+ if (upPressed || downPressed)
+ [cell stopTracking:pressPoint at:pressPoint inView:d->backingStoreNSView mouseIsUp:NO];
+
+ d->restoreNSGraphicsContext(cg);
+ }
+ }
+ break;
+#endif
+ case CC_ComboBox:
+ if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox *>(opt)){
+ HIThemeButtonDrawInfo bdi;
+ QMacStylePrivate::CocoaControl cw;
+ d->initComboboxBdi(combo, &bdi, &cw, widget, tds);
+ CGRect rect = combo->rect.toCGRect();
+ if (combo->editable)
+ rect.origin.y += tds == kThemeStateInactive ? 1 : 2;
+ if (tds != kThemeStateInactive)
+ QMacStylePrivate::drawCombobox(rect, bdi, cw, p);
+ else if (!widget && combo->editable) {
+ const auto cw = QMacStylePrivate::cocoaControlFromHIThemeButtonKind(bdi.kind);
+ NSView *cb = d->cocoaControl(cw);
+ QRect r = combo->rect.adjusted(3, 0, 0, 0);
+ d->drawNSViewInRect(cw, cb, r, p, widget != 0);
+ } else
+ d->drawColorlessButton(rect, &bdi, cw, p, opt);
+ }
+ break;
+ case CC_TitleBar:
+ if (const auto *titlebar = qstyleoption_cast<const QStyleOptionTitleBar *>(opt)) {
+ const bool isActive = (titlebar->state & State_Active)
+ && (titlebar->titleBarState & State_Active);
+
+ p->fillRect(opt->rect, Qt::transparent);
+ p->setRenderHint(QPainter::Antialiasing);
+ p->setClipRect(opt->rect, Qt::IntersectClip);
+
+ // FIXME A single drawPath() with 0-sized pen
+ // doesn't look as good as this double fillPath().
+ const auto outterFrameRect = QRectF(opt->rect.adjusted(0, 0, 0, opt->rect.height()));
+ QPainterPath outterFramePath = d->windowPanelPath(outterFrameRect);
+ p->fillPath(outterFramePath, opt->palette.dark());
+
+ const auto frameAdjust = 1.0 / p->device()->devicePixelRatioF();
+ const auto innerFrameRect = outterFrameRect.adjusted(frameAdjust, frameAdjust, -frameAdjust, 0);
+ QPainterPath innerFramePath = d->windowPanelPath(innerFrameRect);
+ if (isActive) {
+ QLinearGradient g;
+ g.setStart(QPointF(0, 0));
+ g.setFinalStop(QPointF(0, 2 * opt->rect.height()));
+ g.setColorAt(0, opt->palette.button().color());
+ g.setColorAt(1, opt->palette.dark().color());
+ p->fillPath(innerFramePath, g);
+ } else {
+ p->fillPath(innerFramePath, opt->palette.button());
+ }
+
+ if (titlebar->subControls & (SC_TitleBarCloseButton
+ | SC_TitleBarMaxButton
+ | SC_TitleBarMinButton
+ | SC_TitleBarNormalButton)) {
+ const bool isHovered = (titlebar->state & State_MouseOver);
+ static const SubControl buttons[] = {
+ SC_TitleBarCloseButton, SC_TitleBarMinButton, SC_TitleBarMaxButton
+ };
+ for (const auto sc : buttons) {
+ const auto ct = d->windowButtonCocoaControl(sc);
+ const auto cw = QMacStylePrivate::CocoaControl(ct, QStyleHelper::SizeLarge);
+ auto *wb = static_cast<NSButton *>(d->cocoaControl(cw));
+ wb.enabled = (sc & titlebar->subControls);
+ [wb highlight:(titlebar->state & State_Sunken) && (sc & titlebar->activeSubControls)];
+ Q_UNUSED(isHovered); // FIXME No public API for this
+
+ const auto buttonRect = proxy()->subControlRect(CC_TitleBar, titlebar, sc, widget);
+ const auto drawBlock = ^ (CGContextRef ctx, const CGRect &rect) {
+ Q_UNUSED(ctx);
+ Q_UNUSED(rect);
+ auto *wbCell = static_cast<NSButtonCell *>(wb.cell);
+ [wbCell drawWithFrame:rect inView:wb];
+ };
+ d->drawNSViewInRect(cw, wb, buttonRect, p, widget != nullptr, drawBlock);
+ }
+ }
+
+ if (titlebar->subControls & SC_TitleBarLabel) {
+ const auto tr = proxy()->subControlRect(CC_TitleBar, titlebar, SC_TitleBarLabel, widget);
+ if (!titlebar->icon.isNull()) {
+ const auto iconExtent = proxy()->pixelMetric(PM_SmallIconSize);
+ const auto iconSize = QSize(iconExtent, iconExtent);
+ const auto iconPos = tr.x() - titlebar->icon.actualSize(iconSize).width() - qRound(titleBarIconTitleSpacing);
+ // Only render the icon if it'll be fully visible
+ if (iconPos < tr.right() - titleBarIconTitleSpacing)
+ p->drawPixmap(iconPos, tr.y(), titlebar->icon.pixmap(window, iconSize, QIcon::Normal));
+ }
+
+ if (!titlebar->text.isEmpty())
+ drawItemText(p, tr, Qt::AlignCenter, opt->palette, isActive, titlebar->text, QPalette::Text);
+ }
+ }
+ break;
+ case CC_GroupBox:
+ if (const QStyleOptionGroupBox *gb
+ = qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) {
+
+ QStyleOptionGroupBox groupBox(*gb);
+ const bool flat = groupBox.features & QStyleOptionFrame::Flat;
+ if (!flat)
+ groupBox.state |= QStyle::State_Mini; // Force mini-sized checkbox to go with small-sized label
+ else
+ groupBox.subControls = groupBox.subControls & ~SC_GroupBoxFrame; // We don't like frames and ugly lines
+
+ const bool didSetFont = widget && widget->testAttribute(Qt::WA_SetFont);
+ const bool didModifySubControls = !didSetFont && QApplication::desktopSettingsAware();
+ if (didModifySubControls)
+ groupBox.subControls = groupBox.subControls & ~SC_GroupBoxLabel;
+ QCommonStyle::drawComplexControl(cc, &groupBox, p, widget);
+ if (didModifySubControls) {
+ const QRect rect = proxy()->subControlRect(CC_GroupBox, &groupBox, SC_GroupBoxLabel, widget);
+ const bool rtl = groupBox.direction == Qt::RightToLeft;
+ const int alignment = Qt::TextHideMnemonic | (rtl ? Qt::AlignRight : Qt::AlignLeft);
+ const QFont savedFont = p->font();
+ if (!flat)
+ p->setFont(d->smallSystemFont);
+ proxy()->drawItemText(p, rect, alignment, groupBox.palette, groupBox.state & State_Enabled, groupBox.text, QPalette::WindowText);
+ if (!flat)
+ p->setFont(savedFont);
+ }
+ }
+ break;
+ case CC_ToolButton:
+ if (const QStyleOptionToolButton *tb
+ = qstyleoption_cast<const QStyleOptionToolButton *>(opt)) {
+#ifndef QT_NO_ACCESSIBILITY
+ if (QStyleHelper::hasAncestor(opt->styleObject, QAccessible::ToolBar)) {
+ if (tb->subControls & SC_ToolButtonMenu) {
+ QStyleOption arrowOpt = *tb;
+ arrowOpt.rect = proxy()->subControlRect(cc, tb, SC_ToolButtonMenu, widget);
+ arrowOpt.rect.setY(arrowOpt.rect.y() + arrowOpt.rect.height() / 2);
+ arrowOpt.rect.setHeight(arrowOpt.rect.height() / 2);
+ proxy()->drawPrimitive(PE_IndicatorArrowDown, &arrowOpt, p, widget);
+ } else if ((tb->features & QStyleOptionToolButton::HasMenu)
+ && (tb->toolButtonStyle != Qt::ToolButtonTextOnly && !tb->icon.isNull())) {
+ drawToolbarButtonArrow(tb->rect, tds, cg);
+ }
+ if (tb->state & State_On) {
+ QWindow *window = 0;
+ if (widget && widget->window())
+ window = widget->window()->windowHandle();
+ else if (opt->styleObject)
+ window = opt->styleObject->property("_q_styleObjectWindow").value<QWindow *>();
+
+ NSView *view = window ? (NSView *)window->winId() : nil;
+ bool isKey = false;
+ if (view)
+ isKey = [view.window isKeyWindow];
+
+ QBrush brush(isKey ? QColor(0, 0, 0, 28)
+ : QColor(0, 0, 0, 21));
+ QPainterPath path;
+ path.addRoundedRect(QRectF(tb->rect.x(), tb->rect.y(), tb->rect.width(), tb->rect.height() + 4), 4, 4);
+ p->setRenderHint(QPainter::Antialiasing);
+ p->fillPath(path, brush);
+ }
+ proxy()->drawControl(CE_ToolButtonLabel, opt, p, widget);
+ } else
+#endif // QT_NO_ACCESSIBILITY
+ {
+ ThemeButtonKind bkind = kThemeBevelButton;
+ switch (d->aquaSizeConstrain(opt, widget)) {
+ case QStyleHelper::SizeDefault:
+ case QStyleHelper::SizeLarge:
+ bkind = kThemeBevelButton;
+ break;
+ case QStyleHelper::SizeMini:
+ case QStyleHelper::SizeSmall:
+ bkind = kThemeSmallBevelButton;
+ break;
+ }
+
+ QRect button, menuarea;
+ button = proxy()->subControlRect(cc, tb, SC_ToolButton, widget);
+ menuarea = proxy()->subControlRect(cc, tb, SC_ToolButtonMenu, widget);
+ State bflags = tb->state,
+ mflags = tb->state;
+ if (tb->subControls & SC_ToolButton)
+ bflags |= State_Sunken;
+ if (tb->subControls & SC_ToolButtonMenu)
+ mflags |= State_Sunken;
+
+ if (tb->subControls & SC_ToolButton) {
+ if (bflags & (State_Sunken | State_On | State_Raised)) {
+ HIThemeButtonDrawInfo bdi;
+ bdi.version = qt_mac_hitheme_version;
+ bdi.state = tds;
+ bdi.adornment = kThemeAdornmentNone;
+ bdi.kind = bkind;
+ bdi.value = kThemeButtonOff;
+ if (tb->state & State_HasFocus)
+ bdi.adornment = kThemeAdornmentFocus;
+ if (tb->state & State_Sunken)
+ bdi.state = kThemeStatePressed;
+ if (tb->state & State_On)
+ bdi.value = kThemeButtonOn;
+
+ CGRect myRect, macRect;
+ myRect = CGRectMake(tb->rect.x(), tb->rect.y(),
+ tb->rect.width(), tb->rect.height());
+ HIThemeGetButtonBackgroundBounds(&myRect, &bdi, &macRect);
+
+ const auto offMargins = QMargins(int(myRect.origin.x - macRect.origin.x),
+ int(myRect.origin.y - macRect.origin.y),
+ int(macRect.size.width - myRect.size.width),
+ int(macRect.size.height - myRect.size.height));
+ myRect = button.marginsRemoved(offMargins).toCGRect();
+ HIThemeDrawButton(&myRect, &bdi, cg, kHIThemeOrientationNormal, 0);
+ }
+ }
+
+ if (tb->subControls & SC_ToolButtonMenu) {
+ HIThemeButtonDrawInfo bdi;
+ bdi.version = qt_mac_hitheme_version;
+ bdi.state = tds;
+ bdi.value = kThemeButtonOff;
+ bdi.adornment = kThemeAdornmentNone;
+ bdi.kind = bkind;
+ if (tb->state & State_HasFocus)
+ bdi.adornment = kThemeAdornmentFocus;
+ if (tb->state & (State_On | State_Sunken)
+ || (tb->activeSubControls & SC_ToolButtonMenu))
+ bdi.state = kThemeStatePressed;
+ CGRect cgRect = menuarea.toCGRect();
+ HIThemeDrawButton(&cgRect, &bdi, cg, kHIThemeOrientationNormal, 0);
+ QRect r(menuarea.x() + ((menuarea.width() / 2) - 3), menuarea.height() - 8, 8, 8);
+ HIThemePopupArrowDrawInfo padi;
+ padi.version = qt_mac_hitheme_version;
+ padi.state = tds;
+ padi.orientation = kThemeArrowDown;
+ padi.size = kThemeArrow7pt;
+ cgRect = r.toCGRect();
+ HIThemeDrawPopupArrow(&cgRect, &padi, cg, kHIThemeOrientationNormal);
+ } else if (tb->features & QStyleOptionToolButton::HasMenu) {
+ drawToolbarButtonArrow(tb->rect, tds, cg);
+ }
+ QRect buttonRect = proxy()->subControlRect(CC_ToolButton, tb, SC_ToolButton, widget);
+ int fw = proxy()->pixelMetric(PM_DefaultFrameWidth, opt, widget);
+ QStyleOptionToolButton label = *tb;
+ label.rect = buttonRect.adjusted(fw, fw, -fw, -fw);
+ proxy()->drawControl(CE_ToolButtonLabel, &label, p, widget);
+ }
+ }
+ break;
+#if QT_CONFIG(dial)
+ case CC_Dial:
+ if (const QStyleOptionSlider *dial = qstyleoption_cast<const QStyleOptionSlider *>(opt))
+ QStyleHelper::drawDial(dial, p);
+ break;
+#endif
+ default:
+ QCommonStyle::drawComplexControl(cc, opt, p, widget);
+ break;
+ }
+}
+
+QStyle::SubControl QMacStyle::hitTestComplexControl(ComplexControl cc,
+ const QStyleOptionComplex *opt,
+ const QPoint &pt, const QWidget *widget) const
+{
+ Q_D(const QMacStyle);
+ SubControl sc = QStyle::SC_None;
+ switch (cc) {
+ case CC_ComboBox:
+ if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
+ sc = QCommonStyle::hitTestComplexControl(cc, cmb, pt, widget);
+ if (!cmb->editable && sc != QStyle::SC_None)
+ sc = SC_ComboBoxArrow; // A bit of a lie, but what we want
+ }
+ break;
+ case CC_Slider:
+ if (const QStyleOptionSlider *sl = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ if (!sl->rect.contains(pt))
+ break;
+
+ const bool hasTicks = sl->tickPosition != QSlider::NoTicks;
+ const bool isHorizontal = sl->orientation == Qt::Horizontal;
+ const auto ct = isHorizontal ? QMacStylePrivate::Slider_Horizontal : QMacStylePrivate::Slider_Vertical;
+ const auto cs = d->effectiveAquaSizeConstrain(opt, widget);
+ const auto cw = QMacStylePrivate::CocoaControl(ct, cs);
+ auto *slider = static_cast<NSSlider *>(d->cocoaControl(cw));
+ if (!setupSlider(slider, sl))
+ break;
+
+ [slider calcSize];
+ NSSliderCell *cell = slider.cell;
+ const auto barRect = QRectF::fromCGRect([cell barRectFlipped:hasTicks]);
+ const auto knobRect = QRectF::fromCGRect([cell knobRectFlipped:NO]);
+ if (knobRect.contains(pt)) {
+ sc = SC_SliderHandle;
+ } else if (barRect.contains(pt)) {
+ sc = SC_SliderGroove;
+ } else if (hasTicks) {
+ sc = SC_SliderTickmarks;
+ }
+ }
+ break;
+ case CC_ScrollBar:
+ if (const QStyleOptionSlider *sb = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ if (!sb->rect.contains(pt)) {
+ sc = SC_None;
+ break;
+ }
+
+ const bool isHorizontal = sb->orientation == Qt::Horizontal;
+ const auto ct = isHorizontal ? QMacStylePrivate::Scroller_Horizontal : QMacStylePrivate::Scroller_Vertical;
+ const auto cs = d->effectiveAquaSizeConstrain(opt, widget);
+ const auto cw = QMacStylePrivate::CocoaControl(ct, cs);
+ auto *scroller = static_cast<NSScroller *>(d->cocoaControl(cw));
+ if (!setupScroller(scroller, sb)) {
+ sc = SC_None;
+ break;
+ }
+
+ // Since -[NSScroller testPart:] doesn't want to cooperate, we do it the
+ // straightforward way. In any case, macOS doesn't return line-sized changes
+ // with NSScroller since 10.7, according to the aforementioned method's doc.
+ const auto knobRect = QRectF::fromCGRect([scroller rectForPart:NSScrollerKnob]);
+ if (isHorizontal) {
+ const bool isReverse = sb->direction == Qt::RightToLeft;
+ if (pt.x() < knobRect.left())
+ sc = isReverse ? SC_ScrollBarAddPage : SC_ScrollBarSubPage;
+ else if (pt.x() > knobRect.right())
+ sc = isReverse ? SC_ScrollBarSubPage : SC_ScrollBarAddPage;
+ else
+ sc = SC_ScrollBarSlider;
+ } else {
+ if (pt.y() < knobRect.top())
+ sc = SC_ScrollBarSubPage;
+ else if (pt.y() > knobRect.bottom())
+ sc = SC_ScrollBarAddPage;
+ else
+ sc = SC_ScrollBarSlider;
+ }
+ }
+ break;
+ default:
+ sc = QCommonStyle::hitTestComplexControl(cc, opt, pt, widget);
+ break;
+ }
+ return sc;
+}
+
+QRect QMacStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex *opt, SubControl sc,
+ const QWidget *widget) const
+{
+ Q_D(const QMacStyle);
+ QRect ret;
+ switch (cc) {
+ case CC_ScrollBar:
+ if (const QStyleOptionSlider *sb = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ const bool isHorizontal = sb->orientation == Qt::Horizontal;
+ const bool isReverseHorizontal = isHorizontal && (sb->direction == Qt::RightToLeft);
+
+ NSScrollerPart part = NSScrollerNoPart;
+ if (sc == SC_ScrollBarSlider) {
+ part = NSScrollerKnob;
+ } else if (sc == SC_ScrollBarGroove) {
+ part = NSScrollerKnobSlot;
+ } else if (sc == SC_ScrollBarSubPage || sc == SC_ScrollBarAddPage) {
+ if ((!isReverseHorizontal && sc == SC_ScrollBarSubPage)
+ || (isReverseHorizontal && sc == SC_ScrollBarAddPage))
+ part = NSScrollerDecrementPage;
+ else
+ part = NSScrollerIncrementPage;
+ }
+ // And nothing else since 10.7
+
+ if (part != NSScrollerNoPart) {
+ const auto ct = isHorizontal ? QMacStylePrivate::Scroller_Horizontal : QMacStylePrivate::Scroller_Vertical;
+ const auto cs = d->effectiveAquaSizeConstrain(opt, widget);
+ const auto cw = QMacStylePrivate::CocoaControl(ct, cs);
+ auto *scroller = static_cast<NSScroller *>(d->cocoaControl(cw));
+ if (setupScroller(scroller, sb))
+ ret = QRectF::fromCGRect([scroller rectForPart:part]).toRect();
+ }
+ }
+ break;
+ case CC_Slider:
+ if (const QStyleOptionSlider *sl = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ const bool hasTicks = sl->tickPosition != QSlider::NoTicks;
+ const bool isHorizontal = sl->orientation == Qt::Horizontal;
+ const auto ct = isHorizontal ? QMacStylePrivate::Slider_Horizontal : QMacStylePrivate::Slider_Vertical;
+ const auto cs = d->effectiveAquaSizeConstrain(opt, widget);
+ const auto cw = QMacStylePrivate::CocoaControl(ct, cs);
+ auto *slider = static_cast<NSSlider *>(d->cocoaControl(cw));
+ if (!setupSlider(slider, sl))
+ break;
+
+ [slider calcSize];
+ NSSliderCell *cell = slider.cell;
+ if (sc == SC_SliderHandle) {
+ ret = QRectF::fromCGRect([cell knobRectFlipped:NO]).toRect();
+ if (isHorizontal) {
+ ret.setTop(sl->rect.top());
+ ret.setBottom(sl->rect.bottom());
+ } else {
+ ret.setLeft(sl->rect.left());
+ ret.setRight(sl->rect.right());
+ }
+ } else if (sc == SC_SliderGroove) {
+ ret = QRectF::fromCGRect([cell barRectFlipped:hasTicks]).toRect();
+ } else if (hasTicks && sc == SC_SliderTickmarks) {
+ const auto tickMarkRect = QRectF::fromCGRect([cell rectOfTickMarkAtIndex:0]);
+ if (isHorizontal)
+ ret = QRect(sl->rect.left(), tickMarkRect.top(), sl->rect.width(), tickMarkRect.height());
+ else
+ ret = QRect(tickMarkRect.left(), sl->rect.top(), tickMarkRect.width(), sl->rect.height());
+ }
+
+ // Invert if needed and extend to the actual bounds of the slider
+ if (isHorizontal) {
+ if (sl->upsideDown) {
+ ret = QRect(sl->rect.right() - ret.right(), sl->rect.top(), ret.width(), sl->rect.height());
+ } else {
+ ret.setTop(sl->rect.top());
+ ret.setBottom(sl->rect.bottom());
+ }
+ } else {
+ if (!sl->upsideDown) {
+ ret = QRect(sl->rect.left(), sl->rect.bottom() - ret.bottom(), sl->rect.width(), ret.height());
+ } else {
+ ret.setLeft(sl->rect.left());
+ ret.setRight(sl->rect.right());
+ }
+ }
+ }
+ break;
+ case CC_TitleBar:
+ if (const auto *titlebar = qstyleoption_cast<const QStyleOptionTitleBar *>(opt)) {
+ // The title bar layout is as follows: close, min, zoom, icon, title
+ // [ x _ + @ Window Title ]
+ // Center the icon and title until it starts to overlap with the buttons.
+ // The icon doesn't count towards SC_TitleBarLabel, but it's still rendered
+ // next to the title text. See drawComplexControl().
+ if (sc == SC_TitleBarLabel) {
+ qreal labelWidth = titlebar->fontMetrics.horizontalAdvance(titlebar->text) + 1; // FIXME Rounding error?
+ qreal labelHeight = titlebar->fontMetrics.height();
+
+ const auto lastButtonRect = proxy()->subControlRect(CC_TitleBar, titlebar, SC_TitleBarMaxButton, widget);
+ qreal controlsSpacing = lastButtonRect.right() + titleBarButtonSpacing;
+ if (!titlebar->icon.isNull()) {
+ const auto iconSize = proxy()->pixelMetric(PM_SmallIconSize);
+ const auto actualIconSize = titlebar->icon.actualSize(QSize(iconSize, iconSize)).width();;
+ controlsSpacing += actualIconSize + titleBarIconTitleSpacing;
+ }
+
+ const qreal labelPos = qMax(controlsSpacing, (opt->rect.width() - labelWidth) / 2.0);
+ labelWidth = qMin(labelWidth, opt->rect.width() - (labelPos + titleBarTitleRightMargin));
+ ret = QRect(labelPos, (opt->rect.height() - labelHeight) / 2,
+ labelWidth, labelHeight);
+ } else {
+ const auto currentButton = d->windowButtonCocoaControl(sc);
+ if (currentButton == QMacStylePrivate::NoControl)
+ break;
+
+ QPointF buttonPos = titlebar->rect.topLeft() + QPointF(titleBarButtonSpacing, 0);
+ QSizeF buttonSize;
+ for (int ct = QMacStylePrivate::Button_WindowClose; ct <= currentButton; ct++) {
+ const auto cw = QMacStylePrivate::CocoaControl(QMacStylePrivate::CocoaControlType(ct),
+ QStyleHelper::SizeLarge);
+ auto *wb = static_cast<NSButton *>(d->cocoaControl(cw));
+ if (ct == currentButton)
+ buttonSize = QSizeF::fromCGSize(wb.frame.size);
+ else
+ buttonPos.rx() += wb.frame.size.width + titleBarButtonSpacing;
+ }
+
+ const auto vOffset = (opt->rect.height() - buttonSize.height()) / 2.0;
+ ret = QRectF(buttonPos, buttonSize).translated(0, vOffset).toRect();
+ }
+ }
+ break;
+ case CC_ComboBox:
+ if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
+ HIThemeButtonDrawInfo bdi;
+ QMacStylePrivate::CocoaControl cw;
+ d->initComboboxBdi(combo, &bdi, &cw, widget, d->getDrawState(opt->state));
+
+ switch (sc) {
+ case SC_ComboBoxEditField:{
+ ret = QMacStylePrivate::comboboxEditBounds(combo->rect, bdi);
+ // 10.10 and above need a slight shift
+ ret.setHeight(ret.height() - 1);
+ break; }
+ case SC_ComboBoxArrow:{
+ ret = QMacStylePrivate::comboboxEditBounds(combo->rect, bdi);
+ ret.setX(ret.x() + ret.width());
+ ret.setWidth(combo->rect.right() - ret.right());
+ break; }
+ case SC_ComboBoxListBoxPopup:{
+ if (combo->editable) {
+ const CGRect inner = QMacStylePrivate::comboboxInnerBounds(combo->rect.toCGRect(), cw);
+ QRect editRect = QMacStylePrivate::comboboxEditBounds(combo->rect, bdi);
+ const int comboTop = combo->rect.top();
+ ret = QRect(qRound(inner.origin.x),
+ comboTop,
+ qRound(inner.origin.x - combo->rect.left() + inner.size.width),
+ editRect.bottom() - comboTop + 2);
+ } else {
+ QRect editRect = QMacStylePrivate::comboboxEditBounds(combo->rect, bdi);
+ ret = QRect(combo->rect.x() + 4 - 11,
+ combo->rect.y() + 1,
+ editRect.width() + 10 + 11,
+ 1);
+ }
+ break; }
+ default:
+ break;
+ }
+ }
+ break;
+ case CC_GroupBox:
+ if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) {
+ bool checkable = groupBox->subControls & SC_GroupBoxCheckBox;
+ const bool flat = groupBox->features & QStyleOptionFrame::Flat;
+ bool hasNoText = !checkable && groupBox->text.isEmpty();
+ switch (sc) {
+ case SC_GroupBoxLabel:
+ case SC_GroupBoxCheckBox: {
+ // Cheat and use the smaller font if we need to
+ const bool checkable = groupBox->subControls & SC_GroupBoxCheckBox;
+ const bool fontIsSet = (widget && widget->testAttribute(Qt::WA_SetFont))
+ || !QApplication::desktopSettingsAware();
+ const int margin = flat || hasNoText ? 0 : 9;
+ ret = groupBox->rect.adjusted(margin, 0, -margin, 0);
+
+ const QFontMetricsF fm = flat || fontIsSet ? QFontMetricsF(groupBox->fontMetrics) : QFontMetricsF(d->smallSystemFont);
+ const QSizeF s = fm.size(Qt::AlignHCenter | Qt::AlignVCenter, qt_mac_removeMnemonics(groupBox->text), 0, nullptr);
+ const int tw = qCeil(s.width());
+ const int h = qCeil(fm.height());
+ ret.setHeight(h);
+
+ QRect labelRect = alignedRect(groupBox->direction, groupBox->textAlignment,
+ QSize(tw, h), ret);
+ if (flat && checkable)
+ labelRect.moveLeft(labelRect.left() + 4);
+ int indicatorWidth = proxy()->pixelMetric(PM_IndicatorWidth, opt, widget);
+ bool rtl = groupBox->direction == Qt::RightToLeft;
+ if (sc == SC_GroupBoxLabel) {
+ if (checkable) {
+ int newSum = indicatorWidth + 1;
+ int newLeft = labelRect.left() + (rtl ? -newSum : newSum);
+ labelRect.moveLeft(newLeft);
+ if (flat)
+ labelRect.moveTop(labelRect.top() + 3);
+ else
+ labelRect.moveTop(labelRect.top() + 4);
+ } else if (flat) {
+ int newLeft = labelRect.left() - (rtl ? 3 : -3);
+ labelRect.moveLeft(newLeft);
+ labelRect.moveTop(labelRect.top() + 3);
+ } else {
+ int newLeft = labelRect.left() - (rtl ? 3 : 2);
+ labelRect.moveLeft(newLeft);
+ labelRect.moveTop(labelRect.top() + 4);
+ }
+ ret = labelRect;
+ }
+
+ if (sc == SC_GroupBoxCheckBox) {
+ int left = rtl ? labelRect.right() - indicatorWidth : labelRect.left() - 1;
+ int top = flat ? ret.top() + 1 : ret.top() + 5;
+ ret.setRect(left, top,
+ indicatorWidth, proxy()->pixelMetric(PM_IndicatorHeight, opt, widget));
+ }
+ break;
+ }
+ case SC_GroupBoxContents:
+ case SC_GroupBoxFrame: {
+ QFontMetrics fm = groupBox->fontMetrics;
+ int yOffset = 3;
+ if (!flat) {
+ if (widget && !widget->testAttribute(Qt::WA_SetFont)
+ && QApplication::desktopSettingsAware())
+ fm = QFontMetrics(qt_app_fonts_hash()->value("QSmallFont", QFont()));
+ yOffset = 5;
+ }
+
+ if (hasNoText)
+ yOffset = -qCeil(QFontMetricsF(fm).height());
+ ret = opt->rect.adjusted(0, qCeil(QFontMetricsF(fm).height()) + yOffset, 0, 0);
+ if (sc == SC_GroupBoxContents) {
+ if (flat)
+ ret.adjust(3, -5, -3, -4); // guess too
+ else
+ ret.adjust(3, 3, -3, -4); // guess
+ }
+ }
+ break;
+ default:
+ ret = QCommonStyle::subControlRect(cc, groupBox, sc, widget);
+ break;
+ }
+ }
+ break;
+#if QT_CONFIG(spinbox)
+ case CC_SpinBox:
+ if (const QStyleOptionSpinBox *spin = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) {
+ QStyleHelper::WidgetSizePolicy aquaSize = d->effectiveAquaSizeConstrain(spin, widget);
+ const auto fw = proxy()->pixelMetric(PM_SpinBoxFrameWidth, spin, widget);
+ int spinner_w;
+ int spinBoxSep;
+ switch (aquaSize) {
+ case QStyleHelper::SizeLarge:
+ spinner_w = 14;
+ spinBoxSep = 2;
+ break;
+ case QStyleHelper::SizeSmall:
+ spinner_w = 12;
+ spinBoxSep = 2;
+ break;
+ case QStyleHelper::SizeMini:
+ spinner_w = 10;
+ spinBoxSep = 1;
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+
+ switch (sc) {
+ case SC_SpinBoxUp:
+ case SC_SpinBoxDown: {
+ if (spin->buttonSymbols == QAbstractSpinBox::NoButtons)
+ break;
+
+ const int y = fw;
+ const int x = spin->rect.width() - spinner_w;
+ ret.setRect(x + spin->rect.x(), y + spin->rect.y(), spinner_w, spin->rect.height() - y * 2);
+ int hackTranslateX;
+ switch (aquaSize) {
+ case QStyleHelper::SizeLarge:
+ hackTranslateX = 0;
+ break;
+ case QStyleHelper::SizeSmall:
+ hackTranslateX = -2;
+ break;
+ case QStyleHelper::SizeMini:
+ hackTranslateX = -1;
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+
+ const auto cw = QMacStylePrivate::CocoaControl(QMacStylePrivate::Stepper, aquaSize);
+ NSStepperCell *cell = static_cast<NSStepperCell *>(d->cocoaCell(cw));
+ const CGRect outRect = [cell drawingRectForBounds:ret.toCGRect()];
+ ret = QRectF::fromCGRect(outRect).toRect();
+
+ switch (sc) {
+ case SC_SpinBoxUp:
+ ret.setHeight(ret.height() / 2);
+ break;
+ case SC_SpinBoxDown:
+ ret.setY(ret.y() + ret.height() / 2);
+ break;
+ default:
+ Q_ASSERT(0);
+ break;
+ }
+ ret.translate(hackTranslateX, 0); // hack: position the buttons correctly (weird that we need this)
+ ret = visualRect(spin->direction, spin->rect, ret);
+ break;
+ }
+ case SC_SpinBoxEditField:
+ ret = spin->rect.adjusted(fw, fw, -fw, -fw);
+ if (spin->buttonSymbols != QAbstractSpinBox::NoButtons) {
+ ret.setWidth(spin->rect.width() - spinBoxSep - spinner_w);
+ ret = visualRect(spin->direction, spin->rect, ret);
+ }
+ break;
+ default:
+ ret = QCommonStyle::subControlRect(cc, spin, sc, widget);
+ break;
+ }
+ }
+ break;
+#endif
+ case CC_ToolButton:
+ ret = QCommonStyle::subControlRect(cc, opt, sc, widget);
+ if (sc == SC_ToolButtonMenu
+#ifndef QT_NO_ACCESSIBILITY
+ && !QStyleHelper::hasAncestor(opt->styleObject, QAccessible::ToolBar)
+#endif
+ ) {
+ ret.adjust(-1, 0, 0, 0);
+ }
+ break;
+ default:
+ ret = QCommonStyle::subControlRect(cc, opt, sc, widget);
+ break;
+ }
+ return ret;
+}
+
+QSize QMacStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt,
+ const QSize &csz, const QWidget *widget) const
+{
+ Q_D(const QMacStyle);
+ QSize sz(csz);
+ bool useAquaGuideline = true;
+
+ switch (ct) {
+#if QT_CONFIG(spinbox)
+ case CT_SpinBox:
+ if (const QStyleOptionSpinBox *vopt = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) {
+ const int buttonWidth = 20; // FIXME Use subControlRect()
+ sz += QSize(buttonWidth, -3);
+ }
+ break;
+#endif
+ case QStyle::CT_TabWidget:
+ // the size between the pane and the "contentsRect" (+4,+4)
+ // (the "contentsRect" is on the inside of the pane)
+ sz = QCommonStyle::sizeFromContents(ct, opt, csz, widget);
+ /**
+ This is supposed to show the relationship between the tabBar and
+ the stack widget of a QTabWidget.
+ Unfortunately ascii is not a good way of representing graphics.....
+ PS: The '=' line is the painted frame.
+
+ top ---+
+ |
+ |
+ |
+ | vvv just outside the painted frame is the "pane"
+ - -|- - - - - - - - - - <-+
+ TAB BAR +=====^============ | +2 pixels
+ - - -|- - -|- - - - - - - <-+
+ | | ^ ^^^ just inside the painted frame is the "contentsRect"
+ | | |
+ | overlap |
+ | | |
+ bottom ------+ <-+ +14 pixels
+ |
+ v
+ ------------------------------ <- top of stack widget
+
+
+ To summarize:
+ * 2 is the distance between the pane and the contentsRect
+ * The 14 and the 1's are the distance from the contentsRect to the stack widget.
+ (same value as used in SE_TabWidgetTabContents)
+ * overlap is how much the pane should overlap the tab bar
+ */
+ // then add the size between the stackwidget and the "contentsRect"
+#if QT_CONFIG(tabwidget)
+ if (const QStyleOptionTabWidgetFrame *twf
+ = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) {
+ QSize extra(0,0);
+ const int overlap = pixelMetric(PM_TabBarBaseOverlap, opt, widget);
+ const int gapBetweenTabbarAndStackWidget = 2 + 14 - overlap;
+
+ if (getTabDirection(twf->shape) == kThemeTabNorth || getTabDirection(twf->shape) == kThemeTabSouth) {
+ extra = QSize(2, gapBetweenTabbarAndStackWidget + 1);
+ } else {
+ extra = QSize(gapBetweenTabbarAndStackWidget + 1, 2);
+ }
+ sz+= extra;
+ }
+#endif
+ break;
+#if QT_CONFIG(tabbar)
+ case QStyle::CT_TabBarTab:
+ if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
+ const QStyleHelper::WidgetSizePolicy AquaSize = d->aquaSizeConstrain(opt, widget);
+ const bool differentFont = (widget && widget->testAttribute(Qt::WA_SetFont))
+ || !QApplication::desktopSettingsAware();
+ ThemeTabDirection ttd = getTabDirection(tab->shape);
+ bool vertTabs = ttd == kThemeTabWest || ttd == kThemeTabEast;
+ if (vertTabs)
+ sz = sz.transposed();
+ int defaultTabHeight;
+ int extraHSpace = proxy()->pixelMetric(PM_TabBarTabHSpace, tab, widget);
+ QFontMetrics fm = opt->fontMetrics;
+ switch (AquaSize) {
+ case QStyleHelper::SizeDefault:
+ case QStyleHelper::SizeLarge:
+ if (tab->documentMode)
+ defaultTabHeight = 24;
+ else
+ defaultTabHeight = 21;
+ break;
+ case QStyleHelper::SizeSmall:
+ defaultTabHeight = 18;
+ break;
+ case QStyleHelper::SizeMini:
+ defaultTabHeight = 16;
+ break;
+ }
+ bool setWidth = false;
+ if (differentFont || !tab->icon.isNull()) {
+ sz.rheight() = qMax(defaultTabHeight, sz.height());
+ } else {
+ QSize textSize = fm.size(Qt::TextShowMnemonic, tab->text);
+ sz.rheight() = qMax(defaultTabHeight, textSize.height());
+ sz.rwidth() = textSize.width();
+ setWidth = true;
+ }
+ sz.rwidth() += extraHSpace;
+
+ if (vertTabs)
+ sz = sz.transposed();
+
+ int maxWidgetHeight = qMax(tab->leftButtonSize.height(), tab->rightButtonSize.height());
+ int maxWidgetWidth = qMax(tab->leftButtonSize.width(), tab->rightButtonSize.width());
+
+ int widgetWidth = 0;
+ int widgetHeight = 0;
+ int padding = 0;
+ if (tab->leftButtonSize.isValid()) {
+ padding += 8;
+ widgetWidth += tab->leftButtonSize.width();
+ widgetHeight += tab->leftButtonSize.height();
+ }
+ if (tab->rightButtonSize.isValid()) {
+ padding += 8;
+ widgetWidth += tab->rightButtonSize.width();
+ widgetHeight += tab->rightButtonSize.height();
+ }
+
+ if (vertTabs) {
+ sz.setHeight(sz.height() + widgetHeight + padding);
+ sz.setWidth(qMax(sz.width(), maxWidgetWidth));
+ } else {
+ if (setWidth)
+ sz.setWidth(sz.width() + widgetWidth + padding);
+ sz.setHeight(qMax(sz.height(), maxWidgetHeight));
+ }
+ }
+ break;
+#endif
+ case QStyle::CT_PushButton:
+ // By default, we fit the contents inside a normal rounded push button.
+ // Do this by add enough space around the contents so that rounded
+ // borders (including highlighting when active) will show.
+ sz.rwidth() += QMacStylePrivate::PushButtonLeftOffset + QMacStylePrivate::PushButtonRightOffset + 12;
+ if (opt->state & QStyle::State_Small)
+ sz.rheight() += 14;
+ else
+ sz.rheight() += 4;
+ break;
+ case QStyle::CT_MenuItem:
+ if (const QStyleOptionMenuItem *mi = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
+ int maxpmw = mi->maxIconWidth;
+#if QT_CONFIG(combobox)
+ const QComboBox *comboBox = qobject_cast<const QComboBox *>(widget);
+#endif
+ int w = sz.width(),
+ h = sz.height();
+ if (mi->menuItemType == QStyleOptionMenuItem::Separator) {
+ w = 10;
+ h = qt_mac_aqua_get_metric(MenuSeparatorHeight);
+ } else {
+ h = mi->fontMetrics.height() + 2;
+ if (!mi->icon.isNull()) {
+#if QT_CONFIG(combobox)
+ if (comboBox) {
+ const QSize &iconSize = comboBox->iconSize();
+ h = qMax(h, iconSize.height() + 4);
+ maxpmw = qMax(maxpmw, iconSize.width());
+ } else
+#endif
+ {
+ int iconExtent = proxy()->pixelMetric(PM_SmallIconSize);
+ h = qMax(h, mi->icon.actualSize(QSize(iconExtent, iconExtent)).height() + 4);
+ }
+ }
+ }
+ if (mi->text.contains(QLatin1Char('\t')))
+ w += 12;
+ else if (mi->menuItemType == QStyleOptionMenuItem::SubMenu)
+ w += 35; // Not quite exactly as it seems to depend on other factors
+ if (maxpmw)
+ w += maxpmw + 6;
+ // add space for a check. All items have place for a check too.
+ w += 20;
+#if QT_CONFIG(combobox)
+ if (comboBox && comboBox->isVisible()) {
+ QStyleOptionComboBox cmb;
+ cmb.initFrom(comboBox);
+ cmb.editable = false;
+ cmb.subControls = QStyle::SC_ComboBoxEditField;
+ cmb.activeSubControls = QStyle::SC_None;
+ w = qMax(w, subControlRect(QStyle::CC_ComboBox, &cmb,
+ QStyle::SC_ComboBoxEditField,
+ comboBox).width());
+ } else
+#endif
+ {
+ w += 12;
+ }
+ sz = QSize(w, h);
+ }
+ break;
+ case CT_MenuBarItem:
+ if (!sz.isEmpty())
+ sz += QSize(12, 4); // Constants from QWindowsStyle
+ break;
+ case CT_ToolButton:
+ sz.rwidth() += 10;
+ sz.rheight() += 10;
+ return sz;
+ case CT_ComboBox: {
+ sz.rwidth() += 50;
+ const QStyleOptionComboBox *cb = qstyleoption_cast<const QStyleOptionComboBox *>(opt);
+ if (cb && !cb->editable)
+ sz.rheight() += 2;
+ break;
+ }
+ case CT_Menu: {
+ if (proxy() == this) {
+ sz = csz;
+ } else {
+ QStyleHintReturnMask menuMask;
+ QStyleOption myOption = *opt;
+ myOption.rect.setSize(sz);
+ if (proxy()->styleHint(SH_Menu_Mask, &myOption, widget, &menuMask))
+ sz = menuMask.region.boundingRect().size();
+ }
+ break; }
+ case CT_HeaderSection:{
+ const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt);
+ sz = QCommonStyle::sizeFromContents(ct, opt, csz, widget);
+ if (header->text.contains(QLatin1Char('\n')))
+ useAquaGuideline = false;
+ break; }
+ case CT_ScrollBar :
+ // Make sure that the scroll bar is large enough to display the thumb indicator.
+ if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ const int minimumSize = 24; // Smallest knob size, but Cocoa doesn't seem to care
+ if (slider->orientation == Qt::Horizontal)
+ sz = sz.expandedTo(QSize(minimumSize, sz.height()));
+ else
+ sz = sz.expandedTo(QSize(sz.width(), minimumSize));
+ }
+ break;
+ case CT_ItemViewItem:
+ if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(opt)) {
+ sz = QCommonStyle::sizeFromContents(ct, vopt, csz, widget);
+ sz.setHeight(sz.height() + 2);
+ }
+ break;
+
+ default:
+ sz = QCommonStyle::sizeFromContents(ct, opt, csz, widget);
+ }
+
+ if (useAquaGuideline){
+ QSize macsz;
+ if (d->aquaSizeConstrain(opt, widget, ct, sz, &macsz) != QStyleHelper::SizeDefault) {
+ if (macsz.width() != -1)
+ sz.setWidth(macsz.width());
+ if (macsz.height() != -1)
+ sz.setHeight(macsz.height());
+ }
+ }
+
+ // The sizes that Carbon and the guidelines gives us excludes the focus frame.
+ // We compensate for this by adding some extra space here to make room for the frame when drawing:
+ if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox *>(opt)){
+ const auto widgetSize = d->aquaSizeConstrain(opt, widget);
+ QMacStylePrivate::CocoaControl cw;
+ cw.first = combo->editable ? QMacStylePrivate::ComboBox : QMacStylePrivate::Button_PopupButton;
+ cw.second = widgetSize;
+ const CGRect diffRect = QMacStylePrivate::comboboxInnerBounds(CGRectZero, cw);
+ sz.rwidth() -= qRound(diffRect.size.width);
+ sz.rheight() -= qRound(diffRect.size.height);
+ } else if (ct == CT_PushButton || ct == CT_ToolButton){
+ ThemeButtonKind bkind;
+ QStyleHelper::WidgetSizePolicy widgetSize = d->aquaSizeConstrain(opt, widget);
+ switch (ct) {
+ default:
+ case CT_PushButton:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ if (btn->features & QStyleOptionButton::CommandLinkButton) {
+ return QCommonStyle::sizeFromContents(ct, opt, sz, widget);
+ }
+ }
+
+ switch (widgetSize) {
+ case QStyleHelper::SizeDefault:
+ case QStyleHelper::SizeLarge:
+ bkind = kThemePushButton;
+ break;
+ case QStyleHelper::SizeSmall:
+ bkind = kThemePushButtonSmall;
+ break;
+ case QStyleHelper::SizeMini:
+ bkind = kThemePushButtonMini;
+ break;
+ }
+ break;
+ case CT_ToolButton:
+ switch (widgetSize) {
+ case QStyleHelper::SizeDefault:
+ case QStyleHelper::SizeLarge:
+ bkind = kThemeLargeBevelButton;
+ break;
+ case QStyleHelper::SizeMini:
+ case QStyleHelper::SizeSmall:
+ bkind = kThemeSmallBevelButton;
+ }
+ break;
+ }
+
+ HIThemeButtonDrawInfo bdi;
+ bdi.version = qt_mac_hitheme_version;
+ bdi.state = kThemeStateActive;
+ bdi.kind = bkind;
+ bdi.value = kThemeButtonOff;
+ bdi.adornment = kThemeAdornmentNone;
+ CGRect macRect, myRect;
+ myRect = CGRectMake(0, 0, sz.width(), sz.height());
+ HIThemeGetButtonBackgroundBounds(&myRect, &bdi, &macRect);
+ // Mini buttons only return their actual size in HIThemeGetButtonBackgroundBounds, so help them out a bit (guess),
+ if (bkind == kThemePushButtonMini)
+ macRect.size.height += 8.;
+ else if (bkind == kThemePushButtonSmall)
+ macRect.size.height -= 10;
+ sz.setWidth(sz.width() + int(macRect.size.width - myRect.size.width));
+ sz.setHeight(sz.height() + int(macRect.size.height - myRect.size.height));
+ }
+ return sz;
+}
+
+void QMacStyle::drawItemText(QPainter *p, const QRect &r, int flags, const QPalette &pal,
+ bool enabled, const QString &text, QPalette::ColorRole textRole) const
+{
+ if(flags & Qt::TextShowMnemonic)
+ flags |= Qt::TextHideMnemonic;
+ QCommonStyle::drawItemText(p, r, flags, pal, enabled, text, textRole);
+}
+
+bool QMacStyle::event(QEvent *e)
+{
+ Q_D(QMacStyle);
+ if(e->type() == QEvent::FocusIn) {
+ QWidget *f = 0;
+ QWidget *focusWidget = QApplication::focusWidget();
+#if QT_CONFIG(graphicsview)
+ if (QGraphicsView *graphicsView = qobject_cast<QGraphicsView *>(focusWidget)) {
+ QGraphicsItem *focusItem = graphicsView->scene() ? graphicsView->scene()->focusItem() : 0;
+ if (focusItem && focusItem->type() == QGraphicsProxyWidget::Type) {
+ QGraphicsProxyWidget *proxy = static_cast<QGraphicsProxyWidget *>(focusItem);
+ if (proxy->widget())
+ focusWidget = proxy->widget()->focusWidget();
+ }
+ }
+#endif
+
+ if (focusWidget && focusWidget->testAttribute(Qt::WA_MacShowFocusRect)) {
+#if QT_CONFIG(spinbox)
+ if (const auto sb = qobject_cast<QAbstractSpinBox *>(focusWidget))
+ f = sb->property("_q_spinbox_lineedit").value<QWidget *>();
+ else
+#endif
+ f = focusWidget;
+ }
+ if (f) {
+ if(!d->focusWidget)
+ d->focusWidget = new QFocusFrame(f);
+ d->focusWidget->setWidget(f);
+ } else if(d->focusWidget) {
+ d->focusWidget->setWidget(0);
+ }
+ } else if(e->type() == QEvent::FocusOut) {
+ if(d->focusWidget)
+ d->focusWidget->setWidget(0);
+ }
+ return false;
+}
+
+QIcon QMacStyle::standardIcon(StandardPixmap standardIcon, const QStyleOption *opt,
+ const QWidget *widget) const
+{
+ switch (standardIcon) {
+ default:
+ return QCommonStyle::standardIcon(standardIcon, opt, widget);
+ case SP_ToolBarHorizontalExtensionButton:
+ case SP_ToolBarVerticalExtensionButton: {
+ QPixmap pixmap(QLatin1String(":/qt-project.org/styles/macstyle/images/toolbar-ext.png"));
+ if (standardIcon == SP_ToolBarVerticalExtensionButton) {
+ QPixmap pix2(pixmap.height(), pixmap.width());
+ pix2.setDevicePixelRatio(pixmap.devicePixelRatio());
+ pix2.fill(Qt::transparent);
+ QPainter p(&pix2);
+ p.translate(pix2.width(), 0);
+ p.rotate(90);
+ p.drawPixmap(0, 0, pixmap);
+ return pix2;
+ }
+ return pixmap;
+ }
+ }
+}
+
+int QMacStyle::layoutSpacing(QSizePolicy::ControlType control1,
+ QSizePolicy::ControlType control2,
+ Qt::Orientation orientation,
+ const QStyleOption *option,
+ const QWidget *widget) const
+{
+ const int ButtonMask = QSizePolicy::ButtonBox | QSizePolicy::PushButton;
+ const int controlSize = getControlSize(option, widget);
+
+ if (control2 == QSizePolicy::ButtonBox) {
+ /*
+ AHIG seems to prefer a 12-pixel margin between group
+ boxes and the row of buttons. The 20 pixel comes from
+ Builder.
+ */
+ if (control1 & (QSizePolicy::Frame // guess
+ | QSizePolicy::GroupBox // (AHIG, guess, guess)
+ | QSizePolicy::TabWidget // guess
+ | ButtonMask)) { // AHIG
+ return_SIZE(14, 8, 8);
+ } else if (control1 == QSizePolicy::LineEdit) {
+ return_SIZE(8, 8, 8); // Interface Builder
+ } else {
+ return_SIZE(20, 7, 7); // Interface Builder
+ }
+ }
+
+ if ((control1 | control2) & ButtonMask) {
+ if (control1 == QSizePolicy::LineEdit)
+ return_SIZE(8, 8, 8); // Interface Builder
+ else if (control2 == QSizePolicy::LineEdit) {
+ if (orientation == Qt::Vertical)
+ return_SIZE(20, 7, 7); // Interface Builder
+ else
+ return_SIZE(20, 8, 8);
+ }
+ return_SIZE(14, 8, 8); // Interface Builder
+ }
+
+ switch (CT2(control1, control2)) {
+ case CT1(QSizePolicy::Label): // guess
+ case CT2(QSizePolicy::Label, QSizePolicy::DefaultType): // guess
+ case CT2(QSizePolicy::Label, QSizePolicy::CheckBox): // AHIG
+ case CT2(QSizePolicy::Label, QSizePolicy::ComboBox): // AHIG
+ case CT2(QSizePolicy::Label, QSizePolicy::LineEdit): // guess
+ case CT2(QSizePolicy::Label, QSizePolicy::RadioButton): // AHIG
+ case CT2(QSizePolicy::Label, QSizePolicy::Slider): // guess
+ case CT2(QSizePolicy::Label, QSizePolicy::SpinBox): // guess
+ case CT2(QSizePolicy::Label, QSizePolicy::ToolButton): // guess
+ return_SIZE(8, 6, 5);
+ case CT1(QSizePolicy::ToolButton):
+ return 8; // AHIG
+ case CT1(QSizePolicy::CheckBox):
+ case CT2(QSizePolicy::CheckBox, QSizePolicy::RadioButton):
+ case CT2(QSizePolicy::RadioButton, QSizePolicy::CheckBox):
+ if (orientation == Qt::Vertical)
+ return_SIZE(8, 8, 7); // AHIG and Builder
+ break;
+ case CT1(QSizePolicy::RadioButton):
+ if (orientation == Qt::Vertical)
+ return 5; // (Builder, guess, AHIG)
+ }
+
+ if (orientation == Qt::Horizontal
+ && (control2 & (QSizePolicy::CheckBox | QSizePolicy::RadioButton)))
+ return_SIZE(12, 10, 8); // guess
+
+ if ((control1 | control2) & (QSizePolicy::Frame
+ | QSizePolicy::GroupBox
+ | QSizePolicy::TabWidget)) {
+ /*
+ These values were chosen so that nested container widgets
+ look good side by side. Builder uses 8, which looks way
+ too small, and AHIG doesn't say anything.
+ */
+ return_SIZE(16, 10, 10); // guess
+ }
+
+ if ((control1 | control2) & (QSizePolicy::Line | QSizePolicy::Slider))
+ return_SIZE(12, 10, 8); // AHIG
+
+ if ((control1 | control2) & QSizePolicy::LineEdit)
+ return_SIZE(10, 8, 8); // AHIG
+
+ /*
+ AHIG and Builder differ by up to 4 pixels for stacked editable
+ comboboxes. We use some values that work fairly well in all
+ cases.
+ */
+ if ((control1 | control2) & QSizePolicy::ComboBox)
+ return_SIZE(10, 8, 7); // guess
+
+ /*
+ Builder defaults to 8, 6, 5 in lots of cases, but most of the time the
+ result looks too cramped.
+ */
+ return_SIZE(10, 8, 6); // guess
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/styles/mac/qmacstyle_mac_p.h b/src/plugins/styles/mac/qmacstyle_mac_p.h
new file mode 100644
index 0000000000..d6874001d3
--- /dev/null
+++ b/src/plugins/styles/mac/qmacstyle_mac_p.h
@@ -0,0 +1,124 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWidgets module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMACSTYLE_MAC_P_H
+#define QMACSTYLE_MAC_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtWidgets/private/qtwidgetsglobal_p.h>
+#include <QtWidgets/qcommonstyle.h>
+
+QT_BEGIN_NAMESPACE
+
+class QPalette;
+
+class QPushButton;
+class QStyleOptionButton;
+class QMacStylePrivate;
+class QMacStyle : public QCommonStyle
+{
+ Q_OBJECT
+public:
+ QMacStyle();
+ virtual ~QMacStyle();
+
+ void polish(QWidget *w);
+ void unpolish(QWidget *w);
+
+ void polish(QApplication*);
+ void unpolish(QApplication*);
+
+ void polish(QPalette &pal);
+
+ void drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p,
+ const QWidget *w = 0) const;
+ void drawControl(ControlElement element, const QStyleOption *opt, QPainter *p,
+ const QWidget *w = 0) const;
+ QRect subElementRect(SubElement r, const QStyleOption *opt, const QWidget *widget = 0) const;
+ void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *p,
+ const QWidget *w = 0) const;
+ SubControl hitTestComplexControl(ComplexControl cc, const QStyleOptionComplex *opt,
+ const QPoint &pt, const QWidget *w = 0) const;
+ QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *opt, SubControl sc,
+ const QWidget *w = 0) const;
+ QSize sizeFromContents(ContentsType ct, const QStyleOption *opt,
+ const QSize &contentsSize, const QWidget *w = 0) const;
+
+ int pixelMetric(PixelMetric pm, const QStyleOption *opt = 0, const QWidget *widget = 0) const;
+
+ QPalette standardPalette() const;
+
+ virtual int styleHint(StyleHint sh, const QStyleOption *opt = 0, const QWidget *w = 0,
+ QStyleHintReturn *shret = 0) const;
+
+ QPixmap standardPixmap(StandardPixmap sp, const QStyleOption *opt,
+ const QWidget *widget = 0) const;
+
+ QPixmap generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap,
+ const QStyleOption *opt) const;
+
+ virtual void drawItemText(QPainter *p, const QRect &r, int flags, const QPalette &pal,
+ bool enabled, const QString &text, QPalette::ColorRole textRole = QPalette::NoRole) const;
+
+ bool event(QEvent *e);
+
+ QIcon standardIcon(StandardPixmap standardIcon, const QStyleOption *opt = 0,
+ const QWidget *widget = 0) const;
+ int layoutSpacing(QSizePolicy::ControlType control1, QSizePolicy::ControlType control2,
+ Qt::Orientation orientation, const QStyleOption *option = 0,
+ const QWidget *widget = 0) const;
+
+private:
+ Q_DISABLE_COPY(QMacStyle)
+ Q_DECLARE_PRIVATE(QMacStyle)
+};
+
+QT_END_NAMESPACE
+
+#endif // QMACSTYLE_MAC_P_H
diff --git a/src/plugins/styles/mac/qmacstyle_mac_p_p.h b/src/plugins/styles/mac/qmacstyle_mac_p_p.h
new file mode 100644
index 0000000000..00dc8a9b53
--- /dev/null
+++ b/src/plugins/styles/mac/qmacstyle_mac_p_p.h
@@ -0,0 +1,306 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWidgets module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#ifndef QMACSTYLE_MAC_P_P_H
+#define QMACSTYLE_MAC_P_P_H
+
+#include <Carbon/Carbon.h>
+#undef check
+
+#include <QtWidgets/private/qtwidgetsglobal_p.h>
+#include <QtWidgets/private/qcommonstyle_p.h>
+#include "qmacstyle_mac_p.h"
+#include <private/qapplication_p.h>
+#if QT_CONFIG(combobox)
+#include <private/qcombobox_p.h>
+#endif
+#include <private/qpainter_p.h>
+#include <private/qstylehelper_p.h>
+#include <qapplication.h>
+#include <qbitmap.h>
+#if QT_CONFIG(checkbox)
+#include <qcheckbox.h>
+#endif
+#include <qcombobox.h>
+#if QT_CONFIG(dialogbuttonbox)
+#include <qdialogbuttonbox.h>
+#endif
+#if QT_CONFIG(dockwidget)
+#include <qdockwidget.h>
+#endif
+#include <qevent.h>
+#include <qfocusframe.h>
+#include <qformlayout.h>
+#if QT_CONFIG(groupbox)
+#include <qgroupbox.h>
+#endif
+#include <qhash.h>
+#include <qheaderview.h>
+#include <qlayout.h>
+#if QT_CONFIG(lineedit)
+#include <qlineedit.h>
+#endif
+#if QT_CONFIG(listview)
+#include <qlistview.h>
+#endif
+#if QT_CONFIG(mainwindow)
+#include <qmainwindow.h>
+#endif
+#include <qmap.h>
+#if QT_CONFIG(menubar)
+#include <qmenubar.h>
+#endif
+#include <qpaintdevice.h>
+#include <qpainter.h>
+#include <qpixmapcache.h>
+#include <qpointer.h>
+#if QT_CONFIG(progressbar)
+#include <qprogressbar.h>
+#endif
+#if QT_CONFIG(pushbutton)
+#include <qpushbutton.h>
+#endif
+#include <qradiobutton.h>
+#if QT_CONFIG(rubberband)
+#include <qrubberband.h>
+#endif
+#if QT_CONFIG(sizegrip)
+#include <qsizegrip.h>
+#endif
+#if QT_CONFIG(spinbox)
+#include <qspinbox.h>
+#endif
+#if QT_CONFIG(splitter)
+#include <qsplitter.h>
+#endif
+#include <qstyleoption.h>
+#include <qtextedit.h>
+#include <qtextstream.h>
+#include <qtoolbar.h>
+#if QT_CONFIG(toolbutton)
+#include <qtoolbutton.h>
+#endif
+#if QT_CONFIG(treeview)
+#include <qtreeview.h>
+#endif
+#if QT_CONFIG(tableview)
+#include <qtableview.h>
+#endif
+#include <qdebug.h>
+#if QT_CONFIG(datetimeedit)
+#include <qdatetimeedit.h>
+#endif
+#include <qmath.h>
+#include <qpair.h>
+#include <qvector.h>
+#include <QtWidgets/qgraphicsproxywidget.h>
+#include <QtWidgets/qgraphicsview.h>
+
+
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+Q_FORWARD_DECLARE_MUTABLE_CG_TYPE(CGContext);
+
+Q_FORWARD_DECLARE_OBJC_CLASS(NSView);
+Q_FORWARD_DECLARE_OBJC_CLASS(NSCell);
+Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(NotificationReceiver));
+
+QT_BEGIN_NAMESPACE
+
+/*
+ AHIG:
+ macOS Human Interface Guidelines
+ https://developer.apple.com/macos/human-interface-guidelines/overview/themes/
+
+ Builder:
+ Interface Builder in Xcode 8 or later
+*/
+
+// this works as long as we have at most 16 different control types
+#define CT1(c) CT2(c, c)
+#define CT2(c1, c2) ((uint(c1) << 16) | uint(c2))
+
+#define SIZE(large, small, mini) \
+ (controlSize == QStyleHelper::SizeLarge ? (large) : controlSize == QStyleHelper::SizeSmall ? (small) : (mini))
+
+// same as return SIZE(...) but optimized
+#define return_SIZE(large, small, mini) \
+ do { \
+ static const int sizes[] = { (large), (small), (mini) }; \
+ return sizes[controlSize]; \
+ } while (false)
+
+class QMacStylePrivate : public QCommonStylePrivate
+{
+ Q_DECLARE_PUBLIC(QMacStyle)
+public:
+ enum CocoaControlType {
+ NoControl, // For when there's no such a control in Cocoa
+ Box, // QGroupBox
+ Button_CheckBox,
+ Button_Disclosure, // Disclosure triangle, like in QTreeView
+ Button_PopupButton, // Non-editable QComboBox
+ Button_PullDown, // QPushButton with menu
+ Button_PushButton,
+ Button_RadioButton,
+ Button_WindowClose,
+ Button_WindowMiniaturize,
+ Button_WindowZoom,
+ ComboBox, // Editable QComboBox
+ ProgressIndicator_Determinate,
+ ProgressIndicator_Indeterminate,
+ Scroller_Horizontal,
+ Scroller_Vertical,
+ Slider_Horizontal,
+ Slider_Vertical,
+ SplitView_Horizontal,
+ SplitView_Vertical,
+ Stepper, // QSpinBox buttons
+ TextField
+ };
+
+ typedef QPair<CocoaControlType, QStyleHelper::WidgetSizePolicy> CocoaControl;
+
+ typedef void (^DrawRectBlock)(CGContextRef, const CGRect &);
+
+ QMacStylePrivate();
+ ~QMacStylePrivate();
+
+ // Ideally these wouldn't exist, but since they already exist we need some accessors.
+ static const int PushButtonLeftOffset;
+ static const int PushButtonTopOffset;
+ static const int PushButtonRightOffset;
+ static const int PushButtonBottomOffset;
+ static const int MiniButtonH;
+ static const int SmallButtonH;
+ static const int BevelButtonW;
+ static const int BevelButtonH;
+ static const int PushButtonContentPadding;
+
+ enum Animates { AquaPushButton, AquaProgressBar, AquaListViewItemOpen, AquaScrollBar };
+ static ThemeDrawState getDrawState(QStyle::State flags);
+ QStyleHelper::WidgetSizePolicy aquaSizeConstrain(const QStyleOption *option, const QWidget *widg,
+ QStyle::ContentsType ct = QStyle::CT_CustomBase,
+ QSize szHint=QSize(-1, -1), QSize *insz = 0) const;
+ QStyleHelper::WidgetSizePolicy effectiveAquaSizeConstrain(const QStyleOption *option, const QWidget *widg,
+ QStyle::ContentsType ct = QStyle::CT_CustomBase,
+ QSize szHint=QSize(-1, -1), QSize *insz = 0) const;
+ void getSliderInfo(QStyle::ComplexControl cc, const QStyleOptionSlider *slider,
+ HIThemeTrackDrawInfo *tdi, const QWidget *needToRemoveMe) const;
+ inline int animateSpeed(Animates) const { return 33; }
+
+ // Utility functions
+ void drawColorlessButton(const CGRect &macRect, HIThemeButtonDrawInfo *bdi,
+ const CocoaControl &cw,
+ QPainter *p, const QStyleOption *opt) const;
+
+ QSize pushButtonSizeFromContents(const QStyleOptionButton *btn) const;
+
+ CGRect pushButtonContentBounds(const QStyleOptionButton *btn,
+ const HIThemeButtonDrawInfo *bdi) const;
+
+ void initComboboxBdi(const QStyleOptionComboBox *combo, HIThemeButtonDrawInfo *bdi,
+ CocoaControl *cw,
+ const QWidget *widget, const ThemeDrawState &tds) const;
+
+ static CGRect comboboxInnerBounds(const CGRect &outerBounds, const CocoaControl &cocoaWidget);
+
+ static QRect comboboxEditBounds(const QRect &outerBounds, const HIThemeButtonDrawInfo &bdi);
+
+ static void drawCombobox(const CGRect &outerBounds, const HIThemeButtonDrawInfo &bdi, const CocoaControl &cw, QPainter *p);
+ bool contentFitsInPushButton(const QStyleOptionButton *btn, HIThemeButtonDrawInfo *bdi,
+ ThemeButtonKind buttonKindToCheck) const;
+ void initHIThemePushButton(const QStyleOptionButton *btn, const QWidget *widget,
+ const ThemeDrawState tds,
+ HIThemeButtonDrawInfo *bdi) const;
+
+ void setAutoDefaultButton(QObject *button) const;
+
+ NSView *cocoaControl(CocoaControl widget) const;
+ NSCell *cocoaCell(CocoaControl widget) const;
+
+ static CocoaControl cocoaControlFromHIThemeButtonKind(ThemeButtonKind kind);
+
+ void setupNSGraphicsContext(CGContextRef cg, bool flipped) const;
+ void restoreNSGraphicsContext(CGContextRef cg) const;
+
+ void setupVerticalInvertedXform(CGContextRef cg, bool reverse, bool vertical, const CGRect &rect) const;
+
+ void drawNSViewInRect(CocoaControl widget, NSView *view, const QRect &rect, QPainter *p, bool isQWidget = true, __attribute__((noescape)) DrawRectBlock drawRectBlock = nil) const;
+ void resolveCurrentNSView(QWindow *window) const;
+
+ void drawFocusRing(QPainter *p, const QRect &targetRect, int hMargin, int vMargin, qreal radius = 0) const;
+ void drawFocusRing(QPainter *p, const QRect &targetRect, int hMargin, int vMargin, const CocoaControl &cw) const;
+
+ QPainterPath windowPanelPath(const QRectF &r) const;
+
+ CocoaControlType windowButtonCocoaControl(QStyle::SubControl sc) const;
+
+#if QT_CONFIG(tabbar)
+ void tabLayout(const QStyleOptionTab *opt, const QWidget *widget, QRect *textRect, QRect *iconRect) const;
+#endif
+
+public:
+ mutable QPointer<QObject> autoDefaultButton;
+ static QVector<QPointer<QObject> > scrollBars;
+
+ mutable QPointer<QFocusFrame> focusWidget;
+ QT_MANGLE_NAMESPACE(NotificationReceiver) *receiver;
+ mutable NSView *backingStoreNSView;
+ mutable QHash<CocoaControl, NSView *> cocoaControls;
+ mutable QHash<CocoaControl, NSCell *> cocoaCells;
+
+ QFont smallSystemFont;
+ QFont miniSystemFont;
+};
+
+QT_END_NAMESPACE
+
+#endif // QMACSTYLE_MAC_P_P_H
diff --git a/src/plugins/styles/styles.pro b/src/plugins/styles/styles.pro
new file mode 100644
index 0000000000..542ad1329a
--- /dev/null
+++ b/src/plugins/styles/styles.pro
@@ -0,0 +1,8 @@
+TEMPLATE = subdirs
+QT_FOR_CONFIG += widgets-private
+
+qtConfig(style-android): SUBDIRS += android
+
+qtConfig(style-mac): SUBDIRS += mac
+
+qtConfig(style-windowsvista): SUBDIRS += windowsvista
diff --git a/src/plugins/styles/windowsvista/main.cpp b/src/plugins/styles/windowsvista/main.cpp
new file mode 100644
index 0000000000..d5048e45b7
--- /dev/null
+++ b/src/plugins/styles/windowsvista/main.cpp
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtWidgets/private/qtwidgetsglobal_p.h>
+#include <QtWidgets/qstyleplugin.h>
+#include "qwindowsvistastyle_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QWindowsVistaStylePlugin : public QStylePlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QStyleFactoryInterface" FILE "windowsvistastyle.json")
+public:
+ QStyle *create(const QString &key);
+};
+
+QStyle *QWindowsVistaStylePlugin::create(const QString &key)
+{
+ if (key.compare(QLatin1String("windowsvista"), Qt::CaseInsensitive) == 0)
+ return new QWindowsVistaStyle();
+
+ return 0;
+}
+
+QT_END_NAMESPACE
+
+#include "main.moc"
diff --git a/src/plugins/styles/windowsvista/qwindowsvistastyle.cpp b/src/plugins/styles/windowsvista/qwindowsvistastyle.cpp
new file mode 100644
index 0000000000..6add110249
--- /dev/null
+++ b/src/plugins/styles/windowsvista/qwindowsvistastyle.cpp
@@ -0,0 +1,2502 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWidgets module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwindowsvistastyle_p.h"
+#include "qwindowsvistastyle_p_p.h"
+#include <qoperatingsystemversion.h>
+#include <qscreen.h>
+#include <qwindow.h>
+#include <private/qstyleanimation_p.h>
+#include <private/qstylehelper_p.h>
+#include <private/qapplication_p.h>
+#include <qpa/qplatformnativeinterface.h>
+
+QT_BEGIN_NAMESPACE
+
+static const int windowsItemFrame = 2; // menu item frame width
+static const int windowsItemHMargin = 3; // menu item hor text margin
+static const int windowsItemVMargin = 4; // menu item ver text margin
+static const int windowsArrowHMargin = 6; // arrow horizontal margin
+static const int windowsRightBorder = 15; // right border on windows
+
+#ifndef TMT_CONTENTMARGINS
+# define TMT_CONTENTMARGINS 3602
+#endif
+#ifndef TMT_SIZINGMARGINS
+# define TMT_SIZINGMARGINS 3601
+#endif
+#ifndef LISS_NORMAL
+# define LISS_NORMAL 1
+# define LISS_HOT 2
+# define LISS_SELECTED 3
+# define LISS_DISABLED 4
+# define LISS_SELECTEDNOTFOCUS 5
+# define LISS_HOTSELECTED 6
+#endif
+#ifndef BP_COMMANDLINK
+# define BP_COMMANDLINK 6
+# define BP_COMMANDLINKGLYPH 7
+# define CMDLGS_NORMAL 1
+# define CMDLGS_HOT 2
+# define CMDLGS_PRESSED 3
+# define CMDLGS_DISABLED 4
+#endif
+
+/* \internal
+ Checks if we should use Vista style , or if we should
+ fall back to Windows style.
+*/
+bool QWindowsVistaStylePrivate::useVista()
+{
+ return (QSysInfo::WindowsVersion >= QSysInfo::WV_VISTA
+ && (QSysInfo::WindowsVersion & QSysInfo::WV_NT_based))
+ && QWindowsVistaStylePrivate::useXP();
+}
+
+/* \internal
+ Checks and returns the style object
+*/
+inline QObject *styleObject(const QStyleOption *option) {
+ return option ? option->styleObject : 0;
+}
+
+/* \internal
+ Checks if we can animate on a style option
+*/
+bool canAnimate(const QStyleOption *option) {
+ return option
+ && option->styleObject
+ && !option->styleObject->property("_q_no_animation").toBool();
+}
+
+static inline QImage createAnimationBuffer(const QStyleOption *option, const QWidget *widget)
+{
+ const int devicePixelRatio = widget ? widget->devicePixelRatio() : 1;
+ QImage result(option->rect.size() * devicePixelRatio, QImage::Format_ARGB32_Premultiplied);
+ result.setDevicePixelRatio(devicePixelRatio);
+ result.fill(0);
+ return result;
+}
+
+/* \internal
+ Used by animations to clone a styleoption and shift its offset
+*/
+QStyleOption *clonedAnimationStyleOption(const QStyleOption*option) {
+ QStyleOption *styleOption = 0;
+ if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider*>(option))
+ styleOption = new QStyleOptionSlider(*slider);
+ else if (const QStyleOptionSpinBox *spinbox = qstyleoption_cast<const QStyleOptionSpinBox*>(option))
+ styleOption = new QStyleOptionSpinBox(*spinbox);
+ else if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox*>(option))
+ styleOption = new QStyleOptionGroupBox(*groupBox);
+ else if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox*>(option))
+ styleOption = new QStyleOptionComboBox(*combo);
+ else if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton*>(option))
+ styleOption = new QStyleOptionButton(*button);
+ else
+ styleOption = new QStyleOption(*option);
+ styleOption->rect = QRect(QPoint(0,0), option->rect.size());
+ return styleOption;
+}
+
+/* \internal
+ Used by animations to delete cloned styleoption
+*/
+void deleteClonedAnimationStyleOption(const QStyleOption *option)
+{
+ if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider*>(option))
+ delete slider;
+ else if (const QStyleOptionSpinBox *spinbox = qstyleoption_cast<const QStyleOptionSpinBox*>(option))
+ delete spinbox;
+ else if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox*>(option))
+ delete groupBox;
+ else if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox*>(option))
+ delete combo;
+ else if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton*>(option))
+ delete button;
+ else
+ delete option;
+}
+
+/*!
+ \class QWindowsVistaStyle
+ \brief The QWindowsVistaStyle class provides a look and feel suitable for applications on Microsoft Windows Vista.
+ \since 4.3
+ \ingroup appearance
+ \inmodule QtWidgets
+ \internal
+
+ \warning This style is only available on the Windows Vista platform
+ because it makes use of Windows Vista's style engine.
+
+ \sa QMacStyle, QWindowsXPStyle, QFusionStyle
+*/
+
+/*!
+ Constructs a QWindowsVistaStyle object.
+*/
+QWindowsVistaStyle::QWindowsVistaStyle()
+ : QWindowsXPStyle(*new QWindowsVistaStylePrivate)
+{
+}
+
+/*!
+ Destructor.
+*/
+QWindowsVistaStyle::~QWindowsVistaStyle()
+{
+}
+
+//convert Qt state flags to uxtheme button states
+static int buttonStateId(int flags, int partId)
+{
+ int stateId = 0;
+ if (partId == BP_RADIOBUTTON || partId == BP_CHECKBOX) {
+ if (!(flags & QStyle::State_Enabled))
+ stateId = RBS_UNCHECKEDDISABLED;
+ else if (flags & QStyle::State_Sunken)
+ stateId = RBS_UNCHECKEDPRESSED;
+ else if (flags & QStyle::State_MouseOver)
+ stateId = RBS_UNCHECKEDHOT;
+ else
+ stateId = RBS_UNCHECKEDNORMAL;
+
+ if (flags & QStyle::State_On)
+ stateId += RBS_CHECKEDNORMAL-1;
+
+ } else if (partId == BP_PUSHBUTTON) {
+ if (!(flags & QStyle::State_Enabled))
+ stateId = PBS_DISABLED;
+ else if (flags & (QStyle::State_Sunken | QStyle::State_On))
+ stateId = PBS_PRESSED;
+ else if (flags & QStyle::State_MouseOver)
+ stateId = PBS_HOT;
+ else
+ stateId = PBS_NORMAL;
+ } else {
+ Q_ASSERT(1);
+ }
+ return stateId;
+}
+
+bool QWindowsVistaAnimation::isUpdateNeeded() const
+{
+ return QWindowsVistaStylePrivate::useVista();
+}
+
+void QWindowsVistaAnimation::paint(QPainter *painter, const QStyleOption *option)
+{
+ painter->drawImage(option->rect, currentImage());
+}
+
+static inline bool supportsStateTransition(QStyle::PrimitiveElement element,
+ const QStyleOption *option,
+ const QWidget *widget)
+{
+ bool result = false;
+ switch (element) {
+ case QStyle::PE_IndicatorRadioButton:
+ case QStyle::PE_IndicatorCheckBox:
+ result = true;
+ break;
+ // QTBUG-40634, do not animate when color is set in palette for PE_PanelLineEdit.
+ case QStyle::PE_FrameLineEdit:
+ result = !QWindowsXPStylePrivate::isLineEditBaseColorSet(option, widget);
+ break;
+ default:
+ break;
+ }
+ return result;
+}
+
+/*!
+ \internal
+
+ Animations are used for some state transitions on specific widgets.
+
+ Only one running animation can exist for a widget at any specific
+ time. Animations can be added through
+ QWindowsVistaStylePrivate::startAnimation(Animation *) and any
+ existing animation on a widget can be retrieved with
+ QWindowsVistaStylePrivate::widgetAnimation(Widget *).
+
+ Once an animation has been started,
+ QWindowsVistaStylePrivate::timerEvent(QTimerEvent *) will
+ continuously call update() on the widget until it is stopped,
+ meaning that drawPrimitive will be called many times until the
+ transition has completed. During this time, the result will be
+ retrieved by the Animation::paint(...) function and not by the style
+ itself.
+
+ To determine if a transition should occur, the style needs to know
+ the previous state of the widget as well as the current one. This is
+ solved by updating dynamic properties on the widget every time the
+ function is called.
+
+ Transitions interrupting existing transitions should always be
+ smooth, so whenever a hover-transition is started on a pulsating
+ button, it uses the current frame of the pulse-animation as the
+ starting image for the hover transition.
+
+ */
+
+void QWindowsVistaStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option,
+ QPainter *painter, const QWidget *widget) const
+{
+ QWindowsVistaStylePrivate *d = const_cast<QWindowsVistaStylePrivate*>(d_func());
+
+ int state = option->state;
+ if (!QWindowsVistaStylePrivate::useVista()) {
+ QWindowsStyle::drawPrimitive(element, option, painter, widget);
+ return;
+ }
+
+ if ((option->state & State_Enabled) && d->transitionsEnabled() && canAnimate(option)) {
+ {
+ QRect oldRect;
+ QRect newRect;
+
+ if (supportsStateTransition(element, option, widget)) {
+ // Retrieve and update the dynamic properties tracking
+ // the previous state of the widget:
+ QObject *styleObject = option->styleObject;
+ styleObject->setProperty("_q_no_animation", true);
+
+ int oldState = styleObject->property("_q_stylestate").toInt();
+ oldRect = styleObject->property("_q_stylerect").toRect();
+ newRect = option->rect;
+ styleObject->setProperty("_q_stylestate", (int)option->state);
+ styleObject->setProperty("_q_stylerect", option->rect);
+
+ bool doTransition = oldState &&
+ ((state & State_Sunken) != (oldState & State_Sunken) ||
+ (state & State_On) != (oldState & State_On) ||
+ (state & State_MouseOver) != (oldState & State_MouseOver));
+
+ if (oldRect != newRect ||
+ (state & State_Enabled) != (oldState & State_Enabled) ||
+ (state & State_Active) != (oldState & State_Active))
+ d->stopAnimation(styleObject);
+
+ if (option->state & State_ReadOnly && element == PE_FrameLineEdit) // Do not animate read only line edits
+ doTransition = false;
+
+ if (doTransition) {
+ QStyleOption *styleOption = clonedAnimationStyleOption(option);
+ styleOption->state = (QStyle::State)oldState;
+
+ QWindowsVistaAnimation *anim = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject));
+ QWindowsVistaTransition *t = new QWindowsVistaTransition(styleObject);
+
+ // We create separate images for the initial and final transition states and store them in the
+ // Transition object.
+ QImage startImage = createAnimationBuffer(option, widget);
+ QPainter startPainter(&startImage);
+
+ QImage endImage = createAnimationBuffer(option, widget);
+ QPainter endPainter(&endImage);
+
+ // If we have a running animation on the widget already, we will use that to paint the initial
+ // state of the new transition, this ensures a smooth transition from a current animation such as a
+ // pulsating default button into the intended target state.
+ if (!anim)
+ proxy()->drawPrimitive(element, styleOption, &startPainter, widget);
+ else
+ anim->paint(&startPainter, styleOption);
+
+ t->setStartImage(startImage);
+
+ // The end state of the transition is simply the result we would have painted
+ // if the style was not animated.
+ styleOption->styleObject = 0;
+ styleOption->state = option->state;
+ proxy()->drawPrimitive(element, styleOption, &endPainter, widget);
+
+
+ t->setEndImage(endImage);
+
+ HTHEME theme;
+ int partId;
+ DWORD duration;
+ int fromState = 0;
+ int toState = 0;
+
+ //translate state flags to UXTHEME states :
+ if (element == PE_FrameLineEdit) {
+ theme = OpenThemeData(0, L"Edit");
+ partId = EP_EDITBORDER_NOSCROLL;
+
+ if (oldState & State_MouseOver)
+ fromState = ETS_HOT;
+ else if (oldState & State_HasFocus)
+ fromState = ETS_FOCUSED;
+ else
+ fromState = ETS_NORMAL;
+
+ if (state & State_MouseOver)
+ toState = ETS_HOT;
+ else if (state & State_HasFocus)
+ toState = ETS_FOCUSED;
+ else
+ toState = ETS_NORMAL;
+
+ } else {
+ theme = OpenThemeData(0, L"Button");
+ if (element == PE_IndicatorRadioButton)
+ partId = BP_RADIOBUTTON;
+ else if (element == PE_IndicatorCheckBox)
+ partId = BP_CHECKBOX;
+ else
+ partId = BP_PUSHBUTTON;
+
+ fromState = buttonStateId(oldState, partId);
+ toState = buttonStateId(option->state, partId);
+ }
+
+ // Retrieve the transition time between the states from the system.
+ if (theme
+ && SUCCEEDED(GetThemeTransitionDuration(theme, partId, fromState, toState,
+ TMT_TRANSITIONDURATIONS, &duration))) {
+ t->setDuration(duration);
+ }
+ t->setStartTime(QTime::currentTime());
+
+ deleteClonedAnimationStyleOption(styleOption);
+ d->startAnimation(t);
+ }
+ styleObject->setProperty("_q_no_animation", false);
+ }
+
+ } // End of animation part
+ }
+
+ QRect rect = option->rect;
+
+ switch (element) {
+ case PE_IndicatorHeaderArrow:
+ if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) {
+ int stateId = HSAS_SORTEDDOWN;
+ if (header->sortIndicator & QStyleOptionHeader::SortDown)
+ stateId = HSAS_SORTEDUP; //note that the uxtheme sort down indicator is the inverse of ours
+ XPThemeData theme(widget, painter,
+ QWindowsXPStylePrivate::HeaderTheme,
+ HP_HEADERSORTARROW, stateId, option->rect);
+ d->drawBackground(theme);
+ }
+ break;
+
+ case PE_IndicatorBranch:
+ {
+ XPThemeData theme(widget, painter, QWindowsXPStylePrivate::VistaTreeViewTheme);
+ static int decoration_size = 0;
+ if (!decoration_size && theme.isValid()) {
+ XPThemeData themeSize = theme;
+ themeSize.partId = TVP_HOTGLYPH;
+ themeSize.stateId = GLPS_OPENED;
+ const QSizeF size = themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget);
+ decoration_size = qRound(qMax(size.width(), size.height()));
+ }
+ int mid_h = option->rect.x() + option->rect.width() / 2;
+ int mid_v = option->rect.y() + option->rect.height() / 2;
+ int bef_h = mid_h;
+ int bef_v = mid_v;
+ int aft_h = mid_h;
+ int aft_v = mid_v;
+ if (option->state & State_Children) {
+ int delta = decoration_size / 2;
+ theme.rect = QRect(bef_h - delta, bef_v - delta, decoration_size, decoration_size);
+ theme.partId = option->state & State_MouseOver ? TVP_HOTGLYPH : TVP_GLYPH;
+ theme.stateId = option->state & QStyle::State_Open ? GLPS_OPENED : GLPS_CLOSED;
+ if (option->direction == Qt::RightToLeft)
+ theme.mirrorHorizontally = true;
+ d->drawBackground(theme);
+ bef_h -= delta + 2;
+ bef_v -= delta + 2;
+ aft_h += delta - 2;
+ aft_v += delta - 2;
+ }
+#if 0
+ QBrush brush(option->palette.dark().color(), Qt::Dense4Pattern);
+ if (option->state & State_Item) {
+ if (option->direction == Qt::RightToLeft)
+ painter->fillRect(option->rect.left(), mid_v, bef_h - option->rect.left(), 1, brush);
+ else
+ painter->fillRect(aft_h, mid_v, option->rect.right() - aft_h + 1, 1, brush);
+ }
+ if (option->state & State_Sibling && option->rect.bottom() > aft_v)
+ painter->fillRect(mid_h, aft_v, 1, option->rect.bottom() - aft_v + 1, brush);
+ if (option->state & (State_Open | State_Children | State_Item | State_Sibling) && (bef_v > option->rect.y()))
+ painter->fillRect(mid_h, option->rect.y(), 1, bef_v - option->rect.y(), brush);
+#endif
+ }
+ break;
+
+ case PE_PanelButtonBevel:
+ case PE_IndicatorCheckBox:
+ case PE_IndicatorRadioButton:
+ {
+ if (QWindowsVistaAnimation *a =
+ qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject(option)))){
+ a->paint(painter, option);
+ } else {
+ QWindowsXPStyle::drawPrimitive(element, option, painter, widget);
+ }
+ }
+ break;
+
+ case PE_FrameMenu:
+ {
+ int stateId = option->state & State_Active ? MB_ACTIVE : MB_INACTIVE;
+ XPThemeData theme(widget, painter,
+ QWindowsXPStylePrivate::MenuTheme,
+ MENU_POPUPBORDERS, stateId, option->rect);
+ d->drawBackground(theme);
+ }
+ break;
+ case PE_Frame: {
+#ifndef QT_NO_ACCESSIBILITY
+ if (QStyleHelper::isInstanceOf(option->styleObject, QAccessible::EditableText)
+ || QStyleHelper::isInstanceOf(option->styleObject, QAccessible::StaticText) ||
+#else
+ if (
+#endif
+ (widget && widget->inherits("QTextEdit"))) {
+ painter->save();
+ int stateId = ETS_NORMAL;
+ if (!(state & State_Enabled))
+ stateId = ETS_DISABLED;
+ else if (state & State_ReadOnly)
+ stateId = ETS_READONLY;
+ else if (state & State_HasFocus)
+ stateId = ETS_SELECTED;
+ XPThemeData theme(widget, painter,
+ QWindowsXPStylePrivate::EditTheme,
+ EP_EDITBORDER_HVSCROLL, stateId, option->rect);
+ // Since EP_EDITBORDER_HVSCROLL does not us borderfill, theme.noContent cannot be used for clipping
+ int borderSize = 1;
+ GetThemeInt(theme.handle(), theme.partId, theme.stateId, TMT_BORDERSIZE, &borderSize);
+ QRegion clipRegion = option->rect;
+ QRegion content = option->rect.adjusted(borderSize, borderSize, -borderSize, -borderSize);
+ clipRegion ^= content;
+ painter->setClipRegion(clipRegion);
+ d->drawBackground(theme);
+ painter->restore();
+ } else {
+ QWindowsXPStyle::drawPrimitive(element, option, painter, widget);
+ }
+ }
+ break;
+
+ case PE_PanelLineEdit:
+ if (const QStyleOptionFrame *panel = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
+ bool isEnabled = option->state & State_Enabled;
+ if (QWindowsXPStylePrivate::isLineEditBaseColorSet(option, widget)) {
+ painter->fillRect(panel->rect, panel->palette.brush(QPalette::Base));
+ } else {
+ int partId = EP_BACKGROUND;
+ int stateId = EBS_NORMAL;
+ if (!isEnabled)
+ stateId = EBS_DISABLED;
+ else if (state & State_ReadOnly)
+ stateId = EBS_READONLY;
+ else if (state & State_MouseOver)
+ stateId = EBS_HOT;
+
+ XPThemeData theme(0, painter, QWindowsXPStylePrivate::EditTheme,
+ partId, stateId, rect);
+ if (!theme.isValid()) {
+ QWindowsStyle::drawPrimitive(element, option, painter, widget);
+ return;
+ }
+ int bgType;
+ GetThemeEnumValue(theme.handle(), partId, stateId, TMT_BGTYPE, &bgType);
+ if( bgType == BT_IMAGEFILE ) {
+ d->drawBackground(theme);
+ } else {
+ QBrush fillColor = option->palette.brush(QPalette::Base);
+ if (!isEnabled) {
+ PROPERTYORIGIN origin = PO_NOTFOUND;
+ GetThemePropertyOrigin(theme.handle(), theme.partId, theme.stateId, TMT_FILLCOLOR, &origin);
+ // Use only if the fill property comes from our part
+ if ((origin == PO_PART || origin == PO_STATE)) {
+ COLORREF bgRef;
+ GetThemeColor(theme.handle(), partId, stateId, TMT_FILLCOLOR, &bgRef);
+ fillColor = QBrush(qRgb(GetRValue(bgRef), GetGValue(bgRef), GetBValue(bgRef)));
+ }
+ }
+ painter->fillRect(option->rect, fillColor);
+ }
+ }
+ if (panel->lineWidth > 0)
+ proxy()->drawPrimitive(PE_FrameLineEdit, panel, painter, widget);
+ return;
+ }
+ break;
+
+ case PE_FrameLineEdit:
+ if (QWindowsVistaAnimation *anim = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject(option)))) {
+ anim->paint(painter, option);
+ } else {
+ QPainter *p = painter;
+ if (QWindowsXPStylePrivate::isItemViewDelegateLineEdit(widget)) {
+ // we try to check if this lineedit is a delegate on a QAbstractItemView-derived class.
+ QPen oldPen = p->pen();
+ // Inner white border
+ p->setPen(QPen(option->palette.base().color(), 1));
+ p->drawRect(option->rect.adjusted(1, 1, -2, -2));
+ // Outer dark border
+ p->setPen(QPen(option->palette.shadow().color(), 1));
+ p->drawRect(option->rect.adjusted(0, 0, -1, -1));
+ p->setPen(oldPen);
+ return;
+ } else {
+ int stateId = ETS_NORMAL;
+ if (!(state & State_Enabled))
+ stateId = ETS_DISABLED;
+ else if (state & State_ReadOnly)
+ stateId = ETS_READONLY;
+ else if (state & State_MouseOver)
+ stateId = ETS_HOT;
+ else if (state & State_HasFocus)
+ stateId = ETS_SELECTED;
+ XPThemeData theme(widget, painter,
+ QWindowsXPStylePrivate::EditTheme,
+ EP_EDITBORDER_NOSCROLL, stateId, option->rect);
+ theme.noContent = true;
+ painter->save();
+ QRegion clipRegion = option->rect;
+ clipRegion -= option->rect.adjusted(2, 2, -2, -2);
+ painter->setClipRegion(clipRegion);
+ d->drawBackground(theme);
+ painter->restore();
+ }
+ }
+ break;
+
+ case PE_IndicatorToolBarHandle:
+ {
+ XPThemeData theme;
+ QRect rect;
+ if (option->state & State_Horizontal) {
+ theme = XPThemeData(widget, painter,
+ QWindowsXPStylePrivate::RebarTheme,
+ RP_GRIPPER, ETS_NORMAL, option->rect.adjusted(0, 1, -2, -2));
+ rect = option->rect.adjusted(0, 1, 0, -2);
+ rect.setWidth(4);
+ } else {
+ theme = XPThemeData(widget, painter, QWindowsXPStylePrivate::RebarTheme,
+ RP_GRIPPERVERT, ETS_NORMAL, option->rect.adjusted(0, 1, -2, -2));
+ rect = option->rect.adjusted(1, 0, -1, 0);
+ rect.setHeight(4);
+ }
+ theme.rect = rect;
+ d->drawBackground(theme);
+ }
+ break;
+
+ case PE_IndicatorToolBarSeparator:
+ {
+ QPen pen = painter->pen();
+ int margin = 3;
+ painter->setPen(option->palette.background().color().darker(114));
+ if (option->state & State_Horizontal) {
+ int x1 = option->rect.center().x();
+ painter->drawLine(QPoint(x1, option->rect.top() + margin), QPoint(x1, option->rect.bottom() - margin));
+ } else {
+ int y1 = option->rect.center().y();
+ painter->drawLine(QPoint(option->rect.left() + margin, y1), QPoint(option->rect.right() - margin, y1));
+ }
+ painter->setPen(pen);
+ }
+ break;
+
+ case PE_PanelTipLabel: {
+ XPThemeData theme(widget, painter,
+ QWindowsXPStylePrivate::ToolTipTheme,
+ TTP_STANDARD, TTSS_NORMAL, option->rect);
+ d->drawBackground(theme);
+ break;
+ }
+
+ case PE_PanelItemViewItem:
+ {
+ const QStyleOptionViewItem *vopt;
+ bool newStyle = true;
+ QAbstractItemView::SelectionBehavior selectionBehavior = QAbstractItemView::SelectRows;
+ QAbstractItemView::SelectionMode selectionMode = QAbstractItemView::NoSelection;
+ if (const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(widget)) {
+ newStyle = !qobject_cast<const QTableView*>(view);
+ selectionBehavior = view->selectionBehavior();
+ selectionMode = view->selectionMode();
+#ifndef QT_NO_ACCESSIBILITY
+ } else if (!widget) {
+ newStyle = !QStyleHelper::hasAncestor(option->styleObject, QAccessible::MenuItem) ;
+#endif
+ }
+
+ if (newStyle && (vopt = qstyleoption_cast<const QStyleOptionViewItem *>(option))) {
+ bool selected = vopt->state & QStyle::State_Selected;
+ const bool hover = selectionMode != QAbstractItemView::NoSelection && (vopt->state & QStyle::State_MouseOver);
+ bool active = vopt->state & QStyle::State_Active;
+
+ if (vopt->features & QStyleOptionViewItem::Alternate)
+ painter->fillRect(vopt->rect, vopt->palette.alternateBase());
+
+ QPalette::ColorGroup cg = vopt->state & QStyle::State_Enabled
+ ? QPalette::Normal : QPalette::Disabled;
+ if (cg == QPalette::Normal && !(vopt->state & QStyle::State_Active))
+ cg = QPalette::Inactive;
+
+ QRect itemRect = subElementRect(QStyle::SE_ItemViewItemFocusRect, option, widget).adjusted(-1, 0, 1, 0);
+ itemRect.setTop(vopt->rect.top());
+ itemRect.setBottom(vopt->rect.bottom());
+
+ QSize sectionSize = itemRect.size();
+ if (vopt->showDecorationSelected)
+ sectionSize = vopt->rect.size();
+
+ if (selectionBehavior == QAbstractItemView::SelectRows)
+ sectionSize.setWidth(vopt->rect.width());
+ QPixmap pixmap;
+
+ if (vopt->backgroundBrush.style() != Qt::NoBrush) {
+ const QPointF oldBrushOrigin = painter->brushOrigin();
+ painter->setBrushOrigin(vopt->rect.topLeft());
+ painter->fillRect(vopt->rect, vopt->backgroundBrush);
+ painter->setBrushOrigin(oldBrushOrigin);
+ }
+
+ if (hover || selected) {
+ if (sectionSize.width() > 0 && sectionSize.height() > 0) {
+ QString key = QString::fromLatin1("qvdelegate-%1-%2-%3-%4-%5").arg(sectionSize.width())
+ .arg(sectionSize.height()).arg(selected).arg(active).arg(hover);
+ if (!QPixmapCache::find(key, pixmap)) {
+ pixmap = QPixmap(sectionSize);
+ pixmap.fill(Qt::transparent);
+
+ int state;
+ if (selected && hover)
+ state = LISS_HOTSELECTED;
+ else if (selected && !active)
+ state = LISS_SELECTEDNOTFOCUS;
+ else if (selected)
+ state = LISS_SELECTED;
+ else
+ state = LISS_HOT;
+
+ QPainter pixmapPainter(&pixmap);
+ XPThemeData theme(widget, &pixmapPainter,
+ QWindowsXPStylePrivate::VistaTreeViewTheme,
+ LVP_LISTITEM, state, QRect(0, 0, sectionSize.width(), sectionSize.height()));
+ if (theme.isValid()) {
+ d->drawBackground(theme);
+ } else {
+ QWindowsXPStyle::drawPrimitive(PE_PanelItemViewItem, option, painter, widget);
+ break;;
+ }
+ QPixmapCache::insert(key, pixmap);
+ }
+ }
+
+ if (vopt->showDecorationSelected) {
+ const int frame = 2; //Assumes a 2 pixel pixmap border
+ QRect srcRect = QRect(0, 0, sectionSize.width(), sectionSize.height());
+ QRect pixmapRect = vopt->rect;
+ bool reverse = vopt->direction == Qt::RightToLeft;
+ bool leftSection = vopt->viewItemPosition == QStyleOptionViewItem::Beginning;
+ bool rightSection = vopt->viewItemPosition == QStyleOptionViewItem::End;
+ if (vopt->viewItemPosition == QStyleOptionViewItem::OnlyOne
+ || vopt->viewItemPosition == QStyleOptionViewItem::Invalid)
+ painter->drawPixmap(pixmapRect.topLeft(), pixmap);
+ else if (reverse ? rightSection : leftSection){
+ painter->drawPixmap(QRect(pixmapRect.topLeft(),
+ QSize(frame, pixmapRect.height())), pixmap,
+ QRect(QPoint(0, 0), QSize(frame, pixmapRect.height())));
+ painter->drawPixmap(pixmapRect.adjusted(frame, 0, 0, 0),
+ pixmap, srcRect.adjusted(frame, 0, -frame, 0));
+ } else if (reverse ? leftSection : rightSection) {
+ painter->drawPixmap(QRect(pixmapRect.topRight() - QPoint(frame - 1, 0),
+ QSize(frame, pixmapRect.height())), pixmap,
+ QRect(QPoint(pixmapRect.width() - frame, 0),
+ QSize(frame, pixmapRect.height())));
+ painter->drawPixmap(pixmapRect.adjusted(0, 0, -frame, 0),
+ pixmap, srcRect.adjusted(frame, 0, -frame, 0));
+ } else if (vopt->viewItemPosition == QStyleOptionViewItem::Middle)
+ painter->drawPixmap(pixmapRect, pixmap,
+ srcRect.adjusted(frame, 0, -frame, 0));
+ } else {
+ if (vopt->text.isEmpty() && vopt->icon.isNull())
+ break;
+ painter->drawPixmap(itemRect.topLeft(), pixmap);
+ }
+ }
+ } else {
+ QWindowsXPStyle::drawPrimitive(element, option, painter, widget);
+ }
+ break;
+ }
+ case PE_Widget:
+ {
+#if QT_CONFIG(dialogbuttonbox)
+ const QDialogButtonBox *buttonBox = 0;
+
+ if (qobject_cast<const QMessageBox *> (widget))
+ buttonBox = widget->findChild<const QDialogButtonBox *>(QLatin1String("qt_msgbox_buttonbox"));
+#if QT_CONFIG(inputdialog)
+ else if (qobject_cast<const QInputDialog *> (widget))
+ buttonBox = widget->findChild<const QDialogButtonBox *>(QLatin1String("qt_inputdlg_buttonbox"));
+#endif // QT_CONFIG(inputdialog)
+
+ if (buttonBox) {
+ //draw white panel part
+ XPThemeData theme(widget, painter,
+ QWindowsXPStylePrivate::TaskDialogTheme,
+ TDLG_PRIMARYPANEL, 0, option->rect);
+ QRect toprect = option->rect;
+ toprect.setBottom(buttonBox->geometry().top());
+ theme.rect = toprect;
+ d->drawBackground(theme);
+
+ //draw bottom panel part
+ QRect buttonRect = option->rect;
+ buttonRect.setTop(buttonBox->geometry().top());
+ theme.rect = buttonRect;
+ theme.partId = TDLG_SECONDARYPANEL;
+ d->drawBackground(theme);
+ }
+#endif
+ }
+ break;
+ default:
+ QWindowsXPStyle::drawPrimitive(element, option, painter, widget);
+ break;
+ }
+}
+
+
+/*!
+ \internal
+
+ see drawPrimitive for comments on the animation support
+ */
+void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption *option,
+ QPainter *painter, const QWidget *widget) const
+{
+ QWindowsVistaStylePrivate *d = const_cast<QWindowsVistaStylePrivate*>(d_func());
+
+ if (!QWindowsVistaStylePrivate::useVista()) {
+ QWindowsStyle::drawControl(element, option, painter, widget);
+ return;
+ }
+
+ bool selected = option->state & State_Selected;
+ bool pressed = option->state & State_Sunken;
+ bool disabled = !(option->state & State_Enabled);
+
+ int state = option->state;
+ int themeNumber = -1;
+
+ QRect rect(option->rect);
+ State flags = option->state;
+ int partId = 0;
+ int stateId = 0;
+
+ if (d->transitionsEnabled() && canAnimate(option))
+ {
+ if (element == CE_PushButtonBevel) {
+ QRect oldRect;
+ QRect newRect;
+
+ QObject *styleObject = option->styleObject;
+
+ int oldState = styleObject->property("_q_stylestate").toInt();
+ oldRect = styleObject->property("_q_stylerect").toRect();
+ newRect = option->rect;
+ styleObject->setProperty("_q_stylestate", (int)option->state);
+ styleObject->setProperty("_q_stylerect", option->rect);
+
+ bool wasDefault = false;
+ bool isDefault = false;
+ if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton *>(option)) {
+ wasDefault = styleObject->property("_q_isdefault").toBool();
+ isDefault = button->features & QStyleOptionButton::DefaultButton;
+ styleObject->setProperty("_q_isdefault", isDefault);
+ }
+
+ bool doTransition = ((state & State_Sunken) != (oldState & State_Sunken) ||
+ (state & State_On) != (oldState & State_On) ||
+ (state & State_MouseOver) != (oldState & State_MouseOver));
+
+ if (oldRect != newRect || (wasDefault && !isDefault)) {
+ doTransition = false;
+ d->stopAnimation(styleObject);
+ }
+
+ if (doTransition) {
+ styleObject->setProperty("_q_no_animation", true);
+
+ QWindowsVistaTransition *t = new QWindowsVistaTransition(styleObject);
+ QWindowsVistaAnimation *anim = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject));
+ QStyleOption *styleOption = clonedAnimationStyleOption(option);
+ styleOption->state = (QStyle::State)oldState;
+
+ QImage startImage = createAnimationBuffer(option, widget);
+ QPainter startPainter(&startImage);
+
+ // Use current state of existing animation if already one is running
+ if (!anim) {
+ proxy()->drawControl(element, styleOption, &startPainter, widget);
+ } else {
+ anim->paint(&startPainter, styleOption);
+ d->stopAnimation(styleObject);
+ }
+
+ t->setStartImage(startImage);
+ QImage endImage = createAnimationBuffer(option, widget);
+ QPainter endPainter(&endImage);
+ styleOption->state = option->state;
+ proxy()->drawControl(element, styleOption, &endPainter, widget);
+ t->setEndImage(endImage);
+
+
+ DWORD duration = 0;
+ const HTHEME theme = OpenThemeData(0, L"Button");
+
+ int fromState = buttonStateId(oldState, BP_PUSHBUTTON);
+ int toState = buttonStateId(option->state, BP_PUSHBUTTON);
+ if (GetThemeTransitionDuration(theme, BP_PUSHBUTTON, fromState, toState, TMT_TRANSITIONDURATIONS, &duration) == S_OK)
+ t->setDuration(duration);
+ else
+ t->setDuration(0);
+ t->setStartTime(QTime::currentTime());
+ styleObject->setProperty("_q_no_animation", false);
+
+ deleteClonedAnimationStyleOption(styleOption);
+ d->startAnimation(t);
+ }
+
+ QWindowsVistaAnimation *anim = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject));
+ if (anim) {
+ anim->paint(painter, option);
+ return;
+ }
+
+ }
+ }
+ switch (element) {
+ case CE_PushButtonBevel:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option))
+ {
+ themeNumber = QWindowsXPStylePrivate::ButtonTheme;
+ partId = BP_PUSHBUTTON;
+ if (btn->features & QStyleOptionButton::CommandLinkButton)
+ partId = BP_COMMANDLINK;
+ bool justFlat = (btn->features & QStyleOptionButton::Flat) && !(flags & (State_On|State_Sunken));
+ if (!(flags & State_Enabled) && !(btn->features & QStyleOptionButton::Flat))
+ stateId = PBS_DISABLED;
+ else if (justFlat)
+ ;
+ else if (flags & (State_Sunken | State_On))
+ stateId = PBS_PRESSED;
+ else if (flags & State_MouseOver)
+ stateId = PBS_HOT;
+ else if (btn->features & QStyleOptionButton::DefaultButton && (state & State_Active))
+ stateId = PBS_DEFAULTED;
+ else
+ stateId = PBS_NORMAL;
+
+ if (!justFlat) {
+
+ if (d->transitionsEnabled() && (btn->features & QStyleOptionButton::DefaultButton) &&
+ !(state & (State_Sunken | State_On)) && !(state & State_MouseOver) &&
+ (state & State_Enabled) && (state & State_Active))
+ {
+ QWindowsVistaAnimation *anim = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject(option)));
+
+ if (!anim) {
+ QImage startImage = createAnimationBuffer(option, widget);
+ QImage alternateImage = createAnimationBuffer(option, widget);
+
+ QWindowsVistaPulse *pulse = new QWindowsVistaPulse(styleObject(option));
+
+ QPainter startPainter(&startImage);
+ stateId = PBS_DEFAULTED;
+ XPThemeData theme(widget, &startPainter, themeNumber, partId, stateId, rect);
+ d->drawBackground(theme);
+
+ QPainter alternatePainter(&alternateImage);
+ theme.stateId = PBS_DEFAULTED_ANIMATING;
+ theme.painter = &alternatePainter;
+ d->drawBackground(theme);
+ pulse->setStartImage(startImage);
+ pulse->setEndImage(alternateImage);
+ pulse->setStartTime(QTime::currentTime());
+ pulse->setDuration(2000);
+ d->startAnimation(pulse);
+ anim = pulse;
+ }
+
+ if (anim)
+ anim->paint(painter, option);
+ else {
+ XPThemeData theme(widget, painter, themeNumber, partId, stateId, rect);
+ d->drawBackground(theme);
+ }
+ }
+ else {
+ XPThemeData theme(widget, painter, themeNumber, partId, stateId, rect);
+ d->drawBackground(theme);
+ }
+ }
+
+ if (btn->features & QStyleOptionButton::HasMenu) {
+ int mbiw = 0, mbih = 0;
+ XPThemeData theme(widget, 0, QWindowsXPStylePrivate::ToolBarTheme,
+ TP_DROPDOWNBUTTON);
+ if (theme.isValid()) {
+ const QSizeF size = theme.size() * QStyleHelper::dpiScaled(1);
+ if (!size.isEmpty()) {
+ mbiw = qRound(size.width());
+ mbih = qRound(size.height());
+ }
+ }
+ QRect ir = subElementRect(SE_PushButtonContents, option, 0);
+ QStyleOptionButton newBtn = *btn;
+ newBtn.rect = QStyle::visualRect(option->direction, option->rect,
+ QRect(ir.right() - mbiw - 2,
+ option->rect.top() + (option->rect.height()/2) - (mbih/2),
+ mbiw + 1, mbih + 1));
+ proxy()->drawPrimitive(PE_IndicatorArrowDown, &newBtn, painter, widget);
+ }
+ return;
+ }
+ break;
+
+ case CE_ProgressBarContents:
+ if (const QStyleOptionProgressBar *bar
+ = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) {
+ bool isIndeterminate = (bar->minimum == 0 && bar->maximum == 0);
+ const bool vertical = bar->orientation == Qt::Vertical;
+ const bool inverted = bar->invertedAppearance;
+
+ if (isIndeterminate || (bar->progress > 0 && (bar->progress < bar->maximum) && d->transitionsEnabled())) {
+ if (!d->animation(styleObject(option)))
+ d->startAnimation(new QProgressStyleAnimation(d->animationFps, styleObject(option)));
+ } else {
+ d->stopAnimation(styleObject(option));
+ }
+
+ XPThemeData theme(widget, painter,
+ QWindowsXPStylePrivate::ProgressTheme,
+ vertical ? PP_FILLVERT : PP_FILL);
+ theme.rect = option->rect;
+ bool reverse = (bar->direction == Qt::LeftToRight && inverted) || (bar->direction == Qt::RightToLeft && !inverted);
+ QTime current = QTime::currentTime();
+
+ if (isIndeterminate) {
+ if (QProgressStyleAnimation *a = qobject_cast<QProgressStyleAnimation *>(d->animation(styleObject(option)))) {
+ int glowSize = 120;
+ int animationWidth = glowSize * 2 + (vertical ? theme.rect.height() : theme.rect.width());
+ int animOffset = a->startTime().msecsTo(current) / 4;
+ if (animOffset > animationWidth)
+ a->setStartTime(QTime::currentTime());
+ painter->save();
+ painter->setClipRect(theme.rect);
+ QRect animRect;
+ QSize pixmapSize(14, 14);
+ if (vertical) {
+ animRect = QRect(theme.rect.left(),
+ inverted ? rect.top() - glowSize + animOffset :
+ rect.bottom() + glowSize - animOffset,
+ rect.width(), glowSize);
+ pixmapSize.setHeight(animRect.height());
+ } else {
+ animRect = QRect(rect.left() - glowSize + animOffset,
+ rect.top(), glowSize, rect.height());
+ animRect = QStyle::visualRect(reverse ? Qt::RightToLeft : Qt::LeftToRight,
+ option->rect, animRect);
+ pixmapSize.setWidth(animRect.width());
+ }
+ QString name = QString::fromLatin1("qiprogress-%1-%2").arg(pixmapSize.width()).arg(pixmapSize.height());
+ QPixmap pixmap;
+ if (!QPixmapCache::find(name, pixmap)) {
+ QImage image(pixmapSize, QImage::Format_ARGB32);
+ image.fill(Qt::transparent);
+ QPainter imagePainter(&image);
+ theme.painter = &imagePainter;
+ theme.partId = vertical ? PP_FILLVERT : PP_FILL;
+ theme.rect = QRect(QPoint(0,0), animRect.size());
+ QLinearGradient alphaGradient(0, 0, vertical ? 0 : image.width(),
+ vertical ? image.height() : 0);
+ alphaGradient.setColorAt(0, QColor(0, 0, 0, 0));
+ alphaGradient.setColorAt(0.5, QColor(0, 0, 0, 220));
+ alphaGradient.setColorAt(1, QColor(0, 0, 0, 0));
+ imagePainter.fillRect(image.rect(), alphaGradient);
+ imagePainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
+ d->drawBackground(theme);
+ imagePainter.end();
+ pixmap = QPixmap::fromImage(image);
+ QPixmapCache::insert(name, pixmap);
+ }
+ painter->drawPixmap(animRect, pixmap);
+ painter->restore();
+ }
+ }
+ else {
+ qint64 progress = qMax<qint64>(bar->progress, bar->minimum); // workaround for bug in QProgressBar
+
+ if (vertical) {
+ int maxHeight = option->rect.height();
+ int minHeight = 0;
+ double vc6_workaround = ((progress - qint64(bar->minimum)) / qMax(double(1.0), double(qint64(bar->maximum) - qint64(bar->minimum))) * maxHeight);
+ int height = isIndeterminate ? maxHeight: qMax(int(vc6_workaround), minHeight);
+ theme.rect.setHeight(height);
+ if (!inverted)
+ theme.rect.moveTop(rect.height() - theme.rect.height());
+ } else {
+ int maxWidth = option->rect.width();
+ int minWidth = 0;
+ double vc6_workaround = ((progress - qint64(bar->minimum)) / qMax(double(1.0), double(qint64(bar->maximum) - qint64(bar->minimum))) * maxWidth);
+ int width = isIndeterminate ? maxWidth : qMax(int(vc6_workaround), minWidth);
+ theme.rect.setWidth(width);
+ theme.rect = QStyle::visualRect(reverse ? Qt::RightToLeft : Qt::LeftToRight,
+ option->rect, theme.rect);
+ }
+ d->drawBackground(theme);
+
+ if (QProgressStyleAnimation *a = qobject_cast<QProgressStyleAnimation *>(d->animation(styleObject(option)))) {
+ int glowSize = 140;
+ int animationWidth = glowSize * 2 + (vertical ? theme.rect.height() : theme.rect.width());
+ int animOffset = a->startTime().msecsTo(current) / 4;
+ theme.partId = vertical ? PP_MOVEOVERLAYVERT : PP_MOVEOVERLAY;
+ if (animOffset > animationWidth) {
+ if (bar->progress < bar->maximum)
+ a->setStartTime(QTime::currentTime());
+ else
+ d->stopAnimation(styleObject(option)); //we stop the glow motion only after it has
+ //moved out of view
+ }
+ painter->save();
+ painter->setClipRect(theme.rect);
+ if (vertical) {
+ theme.rect = QRect(theme.rect.left(),
+ inverted ? rect.top() - glowSize + animOffset :
+ rect.bottom() + glowSize - animOffset,
+ rect.width(), glowSize);
+ } else {
+ theme.rect = QRect(rect.left() - glowSize + animOffset,rect.top(), glowSize, rect.height());
+ theme.rect = QStyle::visualRect(reverse ? Qt::RightToLeft : Qt::LeftToRight, option->rect, theme.rect);
+ }
+ d->drawBackground(theme);
+ painter->restore();
+ }
+ }
+ }
+ break;
+
+ case CE_MenuBarItem:
+ {
+
+ if (const QStyleOptionMenuItem *mbi = qstyleoption_cast<const QStyleOptionMenuItem *>(option))
+ {
+ if (mbi->menuItemType == QStyleOptionMenuItem::DefaultItem)
+ break;
+
+ QPalette::ColorRole textRole = disabled ? QPalette::Text : QPalette::ButtonText;
+ QPixmap pix = mbi->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize, option, widget), QIcon::Normal);
+
+ uint alignment = Qt::AlignCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine;
+ if (!proxy()->styleHint(SH_UnderlineShortcut, mbi, widget))
+ alignment |= Qt::TextHideMnemonic;
+
+ if (widget && mbi->palette.color(QPalette::Window) != Qt::transparent) { // Not needed for QtQuick Controls
+ //The rect adjustment is a workaround for the menu not really filling its background.
+ XPThemeData theme(widget, painter,
+ QWindowsXPStylePrivate::MenuTheme,
+ MENU_BARBACKGROUND, 0, option->rect.adjusted(-1, 0, 2, 1));
+ d->drawBackground(theme);
+ }
+
+ int stateId = MBI_NORMAL;
+ if (disabled)
+ stateId = MBI_DISABLED;
+ else if (pressed)
+ stateId = MBI_PUSHED;
+ else if (selected)
+ stateId = MBI_HOT;
+
+ XPThemeData theme2(widget, painter,
+ QWindowsXPStylePrivate::MenuTheme,
+ MENU_BARITEM, stateId, option->rect);
+ d->drawBackground(theme2);
+
+ if (!pix.isNull())
+ drawItemPixmap(painter, mbi->rect, alignment, pix);
+ else
+ drawItemText(painter, mbi->rect, alignment, mbi->palette, mbi->state & State_Enabled, mbi->text, textRole);
+ }
+ }
+ break;
+#if QT_CONFIG(menu)
+ case CE_MenuItem:
+ if (const QStyleOptionMenuItem *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
+ // windows always has a check column, regardless whether we have an icon or not
+ const qreal factor = QWindowsXPStylePrivate::nativeMetricScaleFactor(widget);
+ int checkcol = qRound(qreal(25) * factor);
+ const int gutterWidth = qRound(qreal(3) * factor);
+ {
+ XPThemeData theme(widget, 0, QWindowsXPStylePrivate::MenuTheme,
+ MENU_POPUPCHECKBACKGROUND, MBI_HOT);
+ XPThemeData themeSize = theme;
+ themeSize.partId = MENU_POPUPCHECK;
+ themeSize.stateId = 0;
+ const QSizeF size = themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget);
+ const QMarginsF margins = themeSize.margins() * QWindowsStylePrivate::nativeMetricScaleFactor(widget);
+ checkcol = qMax(menuitem->maxIconWidth, qRound(gutterWidth + size.width() + margins.left() + margins.right()));
+ }
+ QRect rect = option->rect;
+
+ //draw vertical menu line
+ if (option->direction == Qt::LeftToRight)
+ checkcol += rect.x();
+ QPoint p1 = QStyle::visualPos(option->direction, menuitem->rect, QPoint(checkcol, rect.top()));
+ QPoint p2 = QStyle::visualPos(option->direction, menuitem->rect, QPoint(checkcol, rect.bottom()));
+ QRect gutterRect(p1.x(), p1.y(), gutterWidth, p2.y() - p1.y() + 1);
+ XPThemeData theme2(widget, painter, QWindowsXPStylePrivate::MenuTheme,
+ MENU_POPUPGUTTER, stateId, gutterRect);
+ d->drawBackground(theme2);
+
+ int x, y, w, h;
+ menuitem->rect.getRect(&x, &y, &w, &h);
+ int tab = menuitem->tabWidth;
+ bool dis = !(menuitem->state & State_Enabled);
+ bool checked = menuitem->checkType != QStyleOptionMenuItem::NotCheckable
+ ? menuitem->checked : false;
+ bool act = menuitem->state & State_Selected;
+
+ if (menuitem->menuItemType == QStyleOptionMenuItem::Separator) {
+ int yoff = y-2 + h / 2;
+ const int separatorSize = qRound(qreal(6) * QWindowsStylePrivate::nativeMetricScaleFactor(widget));
+ QPoint p1 = QPoint(x + checkcol, yoff);
+ QPoint p2 = QPoint(x + w + separatorSize, yoff);
+ stateId = MBI_HOT;
+ QRect subRect(p1.x() + (gutterWidth - menuitem->rect.x()), p1.y(),
+ p2.x() - p1.x(), separatorSize);
+ subRect = QStyle::visualRect(option->direction, option->rect, subRect );
+ XPThemeData theme2(widget, painter,
+ QWindowsXPStylePrivate::MenuTheme,
+ MENU_POPUPSEPARATOR, stateId, subRect);
+ d->drawBackground(theme2);
+ return;
+ }
+
+ QRect vCheckRect = visualRect(option->direction, menuitem->rect, QRect(menuitem->rect.x(),
+ menuitem->rect.y(), checkcol - (gutterWidth + menuitem->rect.x()), menuitem->rect.height()));
+
+ if (act) {
+ stateId = dis ? MBI_DISABLED : MBI_HOT;
+ XPThemeData theme2(widget, painter,
+ QWindowsXPStylePrivate::MenuTheme,
+ MENU_POPUPITEM, stateId, option->rect);
+ d->drawBackground(theme2);
+ }
+
+ if (checked) {
+ XPThemeData theme(widget, painter,
+ QWindowsXPStylePrivate::MenuTheme,
+ MENU_POPUPCHECKBACKGROUND,
+ menuitem->icon.isNull() ? MBI_HOT : MBI_PUSHED, vCheckRect);
+ XPThemeData themeSize = theme;
+ themeSize.partId = MENU_POPUPCHECK;
+ themeSize.stateId = 0;
+ const QSizeF size = themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget);
+ const QMarginsF margins = themeSize.margins() * QWindowsStylePrivate::nativeMetricScaleFactor(widget);
+ QRect checkRect(0, 0, qRound(size.width() + margins.left() + margins.right()),
+ qRound(size.height() + margins.bottom() + margins.top()));
+ checkRect.moveCenter(vCheckRect.center());
+ theme.rect = checkRect;
+
+ d->drawBackground(theme);
+
+ if (menuitem->icon.isNull()) {
+ checkRect = QRect(QPoint(0, 0), size.toSize());
+ checkRect.moveCenter(theme.rect.center());
+ theme.rect = checkRect;
+
+ theme.partId = MENU_POPUPCHECK;
+ bool bullet = menuitem->checkType & QStyleOptionMenuItem::Exclusive;
+ if (dis)
+ theme.stateId = bullet ? MC_BULLETDISABLED: MC_CHECKMARKDISABLED;
+ else
+ theme.stateId = bullet ? MC_BULLETNORMAL: MC_CHECKMARKNORMAL;
+ d->drawBackground(theme);
+ }
+ }
+
+ if (!menuitem->icon.isNull()) {
+ QIcon::Mode mode = dis ? QIcon::Disabled : QIcon::Normal;
+ if (act && !dis)
+ mode = QIcon::Active;
+ QPixmap pixmap;
+ if (checked)
+ pixmap = menuitem->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize, option, widget), mode, QIcon::On);
+ else
+ pixmap = menuitem->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize, option, widget), mode);
+ const int pixw = pixmap.width() / pixmap.devicePixelRatio();
+ const int pixh = pixmap.height() / pixmap.devicePixelRatio();
+ QRect pmr(0, 0, pixw, pixh);
+ pmr.moveCenter(vCheckRect.center());
+ painter->setPen(menuitem->palette.text().color());
+ painter->drawPixmap(pmr.topLeft(), pixmap);
+ }
+
+ painter->setPen(menuitem->palette.buttonText().color());
+
+ const QColor textColor = menuitem->palette.text().color();
+ if (dis)
+ painter->setPen(textColor);
+
+ int xm = windowsItemFrame + checkcol + windowsItemHMargin + (gutterWidth - menuitem->rect.x()) - 1;
+ int xpos = menuitem->rect.x() + xm;
+ QRect textRect(xpos, y + windowsItemVMargin, w - xm - windowsRightBorder - tab + 1, h - 2 * windowsItemVMargin);
+ QRect vTextRect = visualRect(option->direction, menuitem->rect, textRect);
+ QString s = menuitem->text;
+ if (!s.isEmpty()) { // draw text
+ painter->save();
+ int t = s.indexOf(QLatin1Char('\t'));
+ int text_flags = Qt::AlignVCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine;
+ if (!proxy()->styleHint(SH_UnderlineShortcut, menuitem, widget))
+ text_flags |= Qt::TextHideMnemonic;
+ text_flags |= Qt::AlignLeft;
+ if (t >= 0) {
+ QRect vShortcutRect = visualRect(option->direction, menuitem->rect,
+ QRect(textRect.topRight(), QPoint(menuitem->rect.right(), textRect.bottom())));
+ painter->drawText(vShortcutRect, text_flags, s.mid(t + 1));
+ s = s.left(t);
+ }
+ QFont font = menuitem->font;
+ if (menuitem->menuItemType == QStyleOptionMenuItem::DefaultItem)
+ font.setBold(true);
+ painter->setFont(font);
+ painter->setPen(textColor);
+ painter->drawText(vTextRect, text_flags, s.left(t));
+ painter->restore();
+ }
+ if (menuitem->menuItemType == QStyleOptionMenuItem::SubMenu) {// draw sub menu arrow
+ int dim = (h - 2 * windowsItemFrame) / 2;
+ PrimitiveElement arrow;
+ arrow = (option->direction == Qt::RightToLeft) ? PE_IndicatorArrowLeft : PE_IndicatorArrowRight;
+ xpos = x + w - windowsArrowHMargin - windowsItemFrame - dim;
+ QRect vSubMenuRect = visualRect(option->direction, menuitem->rect, QRect(xpos, y + h / 2 - dim / 2, dim, dim));
+ QStyleOptionMenuItem newMI = *menuitem;
+ newMI.rect = vSubMenuRect;
+ newMI.state = dis ? State_None : State_Enabled;
+ proxy()->drawPrimitive(arrow, &newMI, painter, widget);
+ }
+ }
+ break;
+#endif // QT_CONFIG(menu)
+ case CE_HeaderSection:
+ if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) {
+ partId = HP_HEADERITEM;
+ if (flags & State_Sunken)
+ stateId = HIS_PRESSED;
+ else if (flags & State_MouseOver)
+ stateId = HIS_HOT;
+ else
+ stateId = HIS_NORMAL;
+
+ if (header->sortIndicator != QStyleOptionHeader::None)
+ stateId += 3;
+
+ XPThemeData theme(widget, painter,
+ QWindowsXPStylePrivate::HeaderTheme,
+ partId, stateId, option->rect);
+ d->drawBackground(theme);
+ }
+ break;
+ case CE_MenuBarEmptyArea:
+ {
+ stateId = MBI_NORMAL;
+ if (!(state & State_Enabled))
+ stateId = MBI_DISABLED;
+ XPThemeData theme(widget, painter,
+ QWindowsXPStylePrivate::MenuTheme,
+ MENU_BARBACKGROUND, stateId, option->rect);
+ d->drawBackground(theme);
+ }
+ break;
+ case CE_ToolBar:
+ if (const QStyleOptionToolBar *toolbar = qstyleoption_cast<const QStyleOptionToolBar *>(option)) {
+ QPalette pal = option->palette;
+ pal.setColor(QPalette::Dark, option->palette.background().color().darker(130));
+ QStyleOptionToolBar copyOpt = *toolbar;
+ copyOpt.palette = pal;
+ QWindowsStyle::drawControl(element, &copyOpt, painter, widget);
+ }
+ break;
+ case CE_DockWidgetTitle:
+ if (const QStyleOptionDockWidget *dwOpt = qstyleoption_cast<const QStyleOptionDockWidget *>(option)) {
+ const QDockWidget *dockWidget = qobject_cast<const QDockWidget *>(widget);
+ QRect rect = option->rect;
+ if (dockWidget && dockWidget->isFloating()) {
+ QWindowsXPStyle::drawControl(element, option, painter, widget);
+ break; //otherwise fall through
+ }
+
+ const bool verticalTitleBar = dwOpt->verticalTitleBar;
+
+ if (verticalTitleBar) {
+ rect = rect.transposed();
+
+ painter->translate(rect.left() - 1, rect.top() + rect.width());
+ painter->rotate(-90);
+ painter->translate(-rect.left() + 1, -rect.top());
+ }
+
+ painter->setBrush(option->palette.background().color().darker(110));
+ painter->setPen(option->palette.background().color().darker(130));
+ painter->drawRect(rect.adjusted(0, 1, -1, -3));
+
+ int buttonMargin = 4;
+ int mw = proxy()->pixelMetric(QStyle::PM_DockWidgetTitleMargin, dwOpt, widget);
+ int fw = proxy()->pixelMetric(PM_DockWidgetFrameWidth, dwOpt, widget);
+ const QDockWidget *dw = qobject_cast<const QDockWidget *>(widget);
+ bool isFloating = dw != 0 && dw->isFloating();
+
+ QRect r = option->rect.adjusted(0, 2, -1, -3);
+ QRect titleRect = r;
+
+ if (dwOpt->closable) {
+ QSize sz = proxy()->standardIcon(QStyle::SP_TitleBarCloseButton, dwOpt, widget).actualSize(QSize(10, 10));
+ titleRect.adjust(0, 0, -sz.width() - mw - buttonMargin, 0);
+ }
+
+ if (dwOpt->floatable) {
+ QSize sz = proxy()->standardIcon(QStyle::SP_TitleBarMaxButton, dwOpt, widget).actualSize(QSize(10, 10));
+ titleRect.adjust(0, 0, -sz.width() - mw - buttonMargin, 0);
+ }
+
+ if (isFloating) {
+ titleRect.adjust(0, -fw, 0, 0);
+ if (widget != 0 && widget->windowIcon().cacheKey() != QApplication::windowIcon().cacheKey())
+ titleRect.adjust(titleRect.height() + mw, 0, 0, 0);
+ } else {
+ titleRect.adjust(mw, 0, 0, 0);
+ if (!dwOpt->floatable && !dwOpt->closable)
+ titleRect.adjust(0, 0, -mw, 0);
+ }
+ if (!verticalTitleBar)
+ titleRect = visualRect(dwOpt->direction, r, titleRect);
+
+ if (!dwOpt->title.isEmpty()) {
+ QString titleText = painter->fontMetrics().elidedText(dwOpt->title, Qt::ElideRight,
+ verticalTitleBar ? titleRect.height() : titleRect.width());
+ const int indent = 4;
+ drawItemText(painter, rect.adjusted(indent + 1, 1, -indent - 1, -1),
+ Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic,
+ dwOpt->palette,
+ dwOpt->state & State_Enabled, titleText,
+ QPalette::WindowText);
+ }
+ }
+ break;
+#if QT_CONFIG(itemviews)
+ case CE_ItemViewItem:
+ {
+ const QStyleOptionViewItem *vopt;
+
+ const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(widget);
+ bool newStyle = true;
+
+ if (qobject_cast<const QTableView*>(widget))
+ newStyle = false;
+
+ if (newStyle && view && (vopt = qstyleoption_cast<const QStyleOptionViewItem *>(option))) {
+ /*
+ // We cannot currently get the correct selection color for "explorer style" views
+ COLORREF cref = 0;
+ XPThemeData theme(d->treeViewHelper(), 0, QLatin1String("LISTVIEW"), 0, 0);
+ unsigned int res = GetThemeColor(theme.handle(), LVP_LISTITEM, LISS_SELECTED, TMT_TEXTCOLOR, &cref);
+ QColor textColor(GetRValue(cref), GetGValue(cref), GetBValue(cref));
+ */
+ QPalette palette = vopt->palette;
+ palette.setColor(QPalette::All, QPalette::HighlightedText, palette.color(QPalette::Active, QPalette::Text));
+ // Note that setting a saturated color here results in ugly XOR colors in the focus rect
+ palette.setColor(QPalette::All, QPalette::Highlight, palette.base().color().darker(108));
+ QStyleOptionViewItem adjustedOption = *vopt;
+ adjustedOption.palette = palette;
+ // We hide the focusrect in singleselection as it is not required
+ if ((view->selectionMode() == QAbstractItemView::SingleSelection)
+ && !(vopt->state & State_KeyboardFocusChange))
+ adjustedOption.state &= ~State_HasFocus;
+ QWindowsXPStyle::drawControl(element, &adjustedOption, painter, widget);
+ } else {
+ QWindowsXPStyle::drawControl(element, option, painter, widget);
+ }
+ break;
+ }
+#endif // QT_CONFIG(itemviews)
+#if QT_CONFIG(combobox)
+ case CE_ComboBoxLabel:
+ QCommonStyle::drawControl(element, option, painter, widget);
+ break;
+#endif // QT_CONFIG(combobox)
+ default:
+ QWindowsXPStyle::drawControl(element, option, painter, widget);
+ break;
+ }
+}
+
+/*!
+ \internal
+
+ see drawPrimitive for comments on the animation support
+
+ */
+void QWindowsVistaStyle::drawComplexControl(ComplexControl control, const QStyleOptionComplex *option,
+ QPainter *painter, const QWidget *widget) const
+{
+ QWindowsVistaStylePrivate *d = const_cast<QWindowsVistaStylePrivate*>(d_func());
+ if (!QWindowsVistaStylePrivate::useVista()) {
+ QWindowsStyle::drawComplexControl(control, option, painter, widget);
+ return;
+ }
+
+ State state = option->state;
+ SubControls sub = option->subControls;
+ QRect r = option->rect;
+
+ int partId = 0;
+ int stateId = 0;
+
+ State flags = option->state;
+ if (widget && widget->testAttribute(Qt::WA_UnderMouse) && widget->isActiveWindow())
+ flags |= State_MouseOver;
+
+ if (d->transitionsEnabled() && canAnimate(option))
+ {
+
+ if (control == CC_ScrollBar || control == CC_SpinBox || control == CC_ComboBox) {
+
+ QObject *styleObject = option->styleObject; // Can be widget or qquickitem
+
+ int oldState = styleObject->property("_q_stylestate").toInt();
+ int oldActiveControls = styleObject->property("_q_stylecontrols").toInt();
+
+ QRect oldRect = styleObject->property("_q_stylerect").toRect();
+ styleObject->setProperty("_q_stylestate", (int)option->state);
+ styleObject->setProperty("_q_stylecontrols", (int)option->activeSubControls);
+ styleObject->setProperty("_q_stylerect", option->rect);
+
+ bool doTransition = ((state & State_Sunken) != (oldState & State_Sunken) ||
+ (state & State_On) != (oldState & State_On) ||
+ (state & State_MouseOver) != (oldState & State_MouseOver) ||
+ oldActiveControls != int(option->activeSubControls));
+
+ if (qstyleoption_cast<const QStyleOptionSlider *>(option)) {
+ QRect oldSliderPos = styleObject->property("_q_stylesliderpos").toRect();
+ QRect currentPos = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget);
+ styleObject->setProperty("_q_stylesliderpos", currentPos);
+ if (oldSliderPos != currentPos) {
+ doTransition = false;
+ d->stopAnimation(styleObject);
+ }
+ } else if (control == CC_SpinBox) {
+ //spinboxes have a transition when focus changes
+ if (!doTransition)
+ doTransition = (state & State_HasFocus) != (oldState & State_HasFocus);
+ }
+
+ if (oldRect != option->rect) {
+ doTransition = false;
+ d->stopAnimation(styleObject);
+ }
+
+ if (doTransition) {
+ QImage startImage = createAnimationBuffer(option, widget);
+ QPainter startPainter(&startImage);
+
+ QImage endImage = createAnimationBuffer(option, widget);
+ QPainter endPainter(&endImage);
+
+ QWindowsVistaAnimation *anim = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject));
+ QWindowsVistaTransition *t = new QWindowsVistaTransition(styleObject);
+
+ // Draw the image that ends the animation by using the current styleoption
+ QStyleOptionComplex *styleOption = qstyleoption_cast<QStyleOptionComplex*>(clonedAnimationStyleOption(option));
+
+ styleObject->setProperty("_q_no_animation", true);
+
+ // Draw transition source
+ if (!anim) {
+ styleOption->state = (QStyle::State)oldState;
+ styleOption->activeSubControls = (QStyle::SubControl)oldActiveControls;
+ proxy()->drawComplexControl(control, styleOption, &startPainter, widget);
+ } else {
+ anim->paint(&startPainter, option);
+ }
+ t->setStartImage(startImage);
+
+ // Draw transition target
+ styleOption->state = option->state;
+ styleOption->activeSubControls = option->activeSubControls;
+ proxy()->drawComplexControl(control, styleOption, &endPainter, widget);
+
+ styleObject->setProperty("_q_no_animation", false);
+
+ t->setEndImage(endImage);
+ t->setStartTime(QTime::currentTime());
+
+ if (option->state & State_MouseOver || option->state & State_Sunken)
+ t->setDuration(150);
+ else
+ t->setDuration(500);
+
+ deleteClonedAnimationStyleOption(styleOption);
+ d->startAnimation(t);
+ }
+ if (QWindowsVistaAnimation *anim = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject))) {
+ anim->paint(painter, option);
+ return;
+ }
+ }
+ }
+
+ switch (control) {
+ case CC_ComboBox:
+ if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(option))
+ {
+ if (cmb->editable) {
+ if (sub & SC_ComboBoxEditField) {
+ partId = EP_EDITBORDER_NOSCROLL;
+ if (!(flags & State_Enabled))
+ stateId = ETS_DISABLED;
+ else if (flags & State_MouseOver)
+ stateId = ETS_HOT;
+ else if (flags & State_HasFocus)
+ stateId = ETS_FOCUSED;
+ else
+ stateId = ETS_NORMAL;
+
+ XPThemeData theme(widget, painter,
+ QWindowsXPStylePrivate::EditTheme,
+ partId, stateId, r);
+
+ d->drawBackground(theme);
+ }
+ if (sub & SC_ComboBoxArrow) {
+ QRect subRect = proxy()->subControlRect(CC_ComboBox, option, SC_ComboBoxArrow, widget);
+ XPThemeData theme(widget, painter, QWindowsXPStylePrivate::ComboboxTheme);
+ theme.rect = subRect;
+ partId = option->direction == Qt::RightToLeft ? CP_DROPDOWNBUTTONLEFT : CP_DROPDOWNBUTTONRIGHT;
+
+ if (!(cmb->state & State_Enabled))
+ stateId = CBXS_DISABLED;
+ else if (cmb->state & State_Sunken || cmb->state & State_On)
+ stateId = CBXS_PRESSED;
+ else if (cmb->state & State_MouseOver && option->activeSubControls & SC_ComboBoxArrow)
+ stateId = CBXS_HOT;
+ else
+ stateId = CBXS_NORMAL;
+
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+
+ } else {
+ if (sub & SC_ComboBoxFrame) {
+ XPThemeData theme(widget, painter, QWindowsXPStylePrivate::ComboboxTheme);
+ theme.rect = option->rect;
+ theme.partId = CP_READONLY;
+ if (!(cmb->state & State_Enabled))
+ theme.stateId = CBXS_DISABLED;
+ else if (cmb->state & State_Sunken || cmb->state & State_On)
+ theme.stateId = CBXS_PRESSED;
+ else if (cmb->state & State_MouseOver)
+ theme.stateId = CBXS_HOT;
+ else
+ theme.stateId = CBXS_NORMAL;
+ d->drawBackground(theme);
+ }
+ if (sub & SC_ComboBoxArrow) {
+ XPThemeData theme(widget, painter, QWindowsXPStylePrivate::ComboboxTheme);
+ theme.rect = proxy()->subControlRect(CC_ComboBox, option, SC_ComboBoxArrow, widget);
+ theme.partId = option->direction == Qt::RightToLeft ? CP_DROPDOWNBUTTONLEFT : CP_DROPDOWNBUTTONRIGHT;
+ if (!(cmb->state & State_Enabled))
+ theme.stateId = CBXS_DISABLED;
+ else
+ theme.stateId = CBXS_NORMAL;
+ d->drawBackground(theme);
+ }
+ }
+ }
+ break;
+ case CC_ScrollBar:
+ if (const QStyleOptionSlider *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(option))
+ {
+ XPThemeData theme(widget, painter, QWindowsXPStylePrivate::ScrollBarTheme);
+ bool maxedOut = (scrollbar->maximum == scrollbar->minimum);
+ if (maxedOut)
+ flags &= ~State_Enabled;
+
+ bool isHorz = flags & State_Horizontal;
+ bool isRTL = option->direction == Qt::RightToLeft;
+ if (sub & SC_ScrollBarAddLine) {
+ theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddLine, widget);
+ partId = SBP_ARROWBTN;
+ if (!(flags & State_Enabled))
+ stateId = (isHorz ? (isRTL ? ABS_LEFTDISABLED : ABS_RIGHTDISABLED) : ABS_DOWNDISABLED);
+ else if (scrollbar->activeSubControls & SC_ScrollBarAddLine && (scrollbar->state & State_Sunken))
+ stateId = (isHorz ? (isRTL ? ABS_LEFTPRESSED : ABS_RIGHTPRESSED) : ABS_DOWNPRESSED);
+ else if (scrollbar->activeSubControls & SC_ScrollBarAddLine && (scrollbar->state & State_MouseOver))
+ stateId = (isHorz ? (isRTL ? ABS_LEFTHOT : ABS_RIGHTHOT) : ABS_DOWNHOT);
+ else if (scrollbar->state & State_MouseOver)
+ stateId = (isHorz ? (isRTL ? ABS_LEFTHOVER : ABS_RIGHTHOVER) : ABS_DOWNHOVER);
+ else
+ stateId = (isHorz ? (isRTL ? ABS_LEFTNORMAL : ABS_RIGHTNORMAL) : ABS_DOWNNORMAL);
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ if (sub & SC_ScrollBarSubLine) {
+ theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubLine, widget);
+ partId = SBP_ARROWBTN;
+ if (!(flags & State_Enabled))
+ stateId = (isHorz ? (isRTL ? ABS_RIGHTDISABLED : ABS_LEFTDISABLED) : ABS_UPDISABLED);
+ else if (scrollbar->activeSubControls & SC_ScrollBarSubLine && (scrollbar->state & State_Sunken))
+ stateId = (isHorz ? (isRTL ? ABS_RIGHTPRESSED : ABS_LEFTPRESSED) : ABS_UPPRESSED);
+ else if (scrollbar->activeSubControls & SC_ScrollBarSubLine && (scrollbar->state & State_MouseOver))
+ stateId = (isHorz ? (isRTL ? ABS_RIGHTHOT : ABS_LEFTHOT) : ABS_UPHOT);
+ else if (scrollbar->state & State_MouseOver)
+ stateId = (isHorz ? (isRTL ? ABS_RIGHTHOVER : ABS_LEFTHOVER) : ABS_UPHOVER);
+ else
+ stateId = (isHorz ? (isRTL ? ABS_RIGHTNORMAL : ABS_LEFTNORMAL) : ABS_UPNORMAL);
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ if (maxedOut) {
+ theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget);
+ theme.rect = theme.rect.united(proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubPage, widget));
+ theme.rect = theme.rect.united(proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddPage, widget));
+ partId = flags & State_Horizontal ? SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT;
+ stateId = SCRBS_DISABLED;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ } else {
+ if (sub & SC_ScrollBarSubPage) {
+ theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubPage, widget);
+ partId = flags & State_Horizontal ? SBP_UPPERTRACKHORZ : SBP_UPPERTRACKVERT;
+ if (!(flags & State_Enabled))
+ stateId = SCRBS_DISABLED;
+ else if (scrollbar->activeSubControls & SC_ScrollBarSubPage && (scrollbar->state & State_Sunken))
+ stateId = SCRBS_PRESSED;
+ else if (scrollbar->activeSubControls & SC_ScrollBarSubPage && (scrollbar->state & State_MouseOver))
+ stateId = SCRBS_HOT;
+ else
+ stateId = SCRBS_NORMAL;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ if (sub & SC_ScrollBarAddPage) {
+ theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddPage, widget);
+ partId = flags & State_Horizontal ? SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT;
+ if (!(flags & State_Enabled))
+ stateId = SCRBS_DISABLED;
+ else if (scrollbar->activeSubControls & SC_ScrollBarAddPage && (scrollbar->state & State_Sunken))
+ stateId = SCRBS_PRESSED;
+ else if (scrollbar->activeSubControls & SC_ScrollBarAddPage && (scrollbar->state & State_MouseOver))
+ stateId = SCRBS_HOT;
+ else
+ stateId = SCRBS_NORMAL;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ if (sub & SC_ScrollBarSlider) {
+ theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget);
+ if (!(flags & State_Enabled))
+ stateId = SCRBS_DISABLED;
+ else if (scrollbar->activeSubControls & SC_ScrollBarSlider && (scrollbar->state & State_Sunken))
+ stateId = SCRBS_PRESSED;
+ else if (scrollbar->activeSubControls & SC_ScrollBarSlider && (scrollbar->state & State_MouseOver))
+ stateId = SCRBS_HOT;
+ else if (option->state & State_MouseOver)
+ stateId = SCRBS_HOVER;
+ else
+ stateId = SCRBS_NORMAL;
+
+ // Draw handle
+ theme.partId = flags & State_Horizontal ? SBP_THUMBBTNHORZ : SBP_THUMBBTNVERT;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+
+ if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8) {
+ const QRect gripperBounds = QWindowsXPStylePrivate::scrollBarGripperBounds(flags, widget, &theme);
+ // Draw gripper if there is enough space
+ if (!gripperBounds.isEmpty() && flags & State_Enabled) {
+ painter->save();
+ XPThemeData grippBackground = theme;
+ grippBackground.partId = flags & State_Horizontal ? SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT;
+ theme.rect = gripperBounds;
+ painter->setClipRegion(d->region(theme));// Only change inside the region of the gripper
+ d->drawBackground(grippBackground);// The gutter is the grippers background
+ d->drawBackground(theme); // Transparent gripper ontop of background
+ painter->restore();
+ }
+ }
+ }
+ }
+ }
+ break;
+#if QT_CONFIG(spinbox)
+ case CC_SpinBox:
+ if (const QStyleOptionSpinBox *sb = qstyleoption_cast<const QStyleOptionSpinBox *>(option))
+ {
+ XPThemeData theme(widget, painter, QWindowsXPStylePrivate::SpinTheme);
+ if (sb->frame && (sub & SC_SpinBoxFrame)) {
+ partId = EP_EDITBORDER_NOSCROLL;
+ if (!(flags & State_Enabled))
+ stateId = ETS_DISABLED;
+ else if (flags & State_MouseOver)
+ stateId = ETS_HOT;
+ else if (flags & State_HasFocus)
+ stateId = ETS_SELECTED;
+ else
+ stateId = ETS_NORMAL;
+
+ XPThemeData ftheme(widget, painter,
+ QWindowsXPStylePrivate::EditTheme,
+ partId, stateId, r);
+ // The spinbox in Windows QStyle is drawn with frameless QLineEdit inside it
+ // That however breaks with QtQuickControls where this results in transparent
+ // spinbox background, so if there's no "widget" passed (QtQuickControls case),
+ // let ftheme.noContent be false, which fixes the spinbox rendering in QQC
+ ftheme.noContent = (widget != NULL);
+ d->drawBackground(ftheme);
+ }
+ if (sub & SC_SpinBoxUp) {
+ theme.rect = proxy()->subControlRect(CC_SpinBox, option, SC_SpinBoxUp, widget).adjusted(0, 0, 0, 1);
+ partId = SPNP_UP;
+ if (!(sb->stepEnabled & QAbstractSpinBox::StepUpEnabled) || !(flags & State_Enabled))
+ stateId = UPS_DISABLED;
+ else if (sb->activeSubControls == SC_SpinBoxUp && (sb->state & State_Sunken))
+ stateId = UPS_PRESSED;
+ else if (sb->activeSubControls == SC_SpinBoxUp && (sb->state & State_MouseOver))
+ stateId = UPS_HOT;
+ else
+ stateId = UPS_NORMAL;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ if (sub & SC_SpinBoxDown) {
+ theme.rect = proxy()->subControlRect(CC_SpinBox, option, SC_SpinBoxDown, widget);
+ partId = SPNP_DOWN;
+ if (!(sb->stepEnabled & QAbstractSpinBox::StepDownEnabled) || !(flags & State_Enabled))
+ stateId = DNS_DISABLED;
+ else if (sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_Sunken))
+ stateId = DNS_PRESSED;
+ else if (sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_MouseOver))
+ stateId = DNS_HOT;
+ else
+ stateId = DNS_NORMAL;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ }
+ break;
+#endif // QT_CONFIG(spinbox)
+ default:
+ QWindowsXPStyle::drawComplexControl(control, option, painter, widget);
+ break;
+ }
+}
+
+/*!
+ \internal
+ */
+QSize QWindowsVistaStyle::sizeFromContents(ContentsType type, const QStyleOption *option,
+ const QSize &size, const QWidget *widget) const
+{
+ if (!QWindowsVistaStylePrivate::useVista())
+ return QWindowsStyle::sizeFromContents(type, option, size, widget);
+
+ QSize sz(size);
+ switch (type) {
+ case CT_MenuItem:
+ sz = QWindowsXPStyle::sizeFromContents(type, option, size, widget);
+ int minimumHeight;
+ {
+ XPThemeData theme(widget, 0,
+ QWindowsXPStylePrivate::MenuTheme,
+ MENU_POPUPCHECKBACKGROUND, MBI_HOT);
+ XPThemeData themeSize = theme;
+ themeSize.partId = MENU_POPUPCHECK;
+ themeSize.stateId = 0;
+ const QSizeF size = themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget);
+ const QMarginsF margins = themeSize.margins() * QWindowsStylePrivate::nativeMetricScaleFactor(widget);
+ minimumHeight = qMax(qRound(size.height() + margins.bottom() + margins.top()), sz.height());
+ sz.rwidth() += qRound(size.width() + margins.left() + margins.right());
+ }
+
+ if (const QStyleOptionMenuItem *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
+ if (menuitem->menuItemType != QStyleOptionMenuItem::Separator)
+ sz.setHeight(minimumHeight);
+ }
+ return sz;
+#if QT_CONFIG(menubar)
+ case CT_MenuBarItem:
+ if (!sz.isEmpty())
+ sz += QSize(windowsItemHMargin * 5 + 1, 5);
+ return sz;
+#endif
+ case CT_ItemViewItem:
+ sz = QWindowsXPStyle::sizeFromContents(type, option, size, widget);
+ sz.rheight() += 2;
+ return sz;
+ case CT_SpinBox:
+ {
+ //Spinbox adds frame twice
+ sz = QWindowsStyle::sizeFromContents(type, option, size, widget);
+ int border = proxy()->pixelMetric(PM_SpinBoxFrameWidth, option, widget);
+ sz -= QSize(2*border, 2*border);
+ }
+ return sz;
+ case CT_HeaderSection:
+ {
+ // When there is a sort indicator it adds to the width but it is shown
+ // above the text natively and not on the side
+ if (QStyleOptionHeader *hdr = qstyleoption_cast<QStyleOptionHeader *>(const_cast<QStyleOption *>(option))) {
+ QStyleOptionHeader::SortIndicator sortInd = hdr->sortIndicator;
+ hdr->sortIndicator = QStyleOptionHeader::None;
+ sz = QWindowsXPStyle::sizeFromContents(type, hdr, size, widget);
+ hdr->sortIndicator = sortInd;
+ return sz;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return QWindowsXPStyle::sizeFromContents(type, option, size, widget);
+}
+
+/*!
+ \internal
+ */
+QRect QWindowsVistaStyle::subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const
+{
+ if (!QWindowsVistaStylePrivate::useVista())
+ return QWindowsStyle::subElementRect(element, option, widget);
+
+ QRect rect = QWindowsXPStyle::subElementRect(element, option, widget);
+ switch (element) {
+
+ case SE_PushButtonContents:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) {
+ MARGINS borderSize;
+ const HTHEME theme = OpenThemeData(widget ? QWindowsVistaStylePrivate::winId(widget) : 0, L"Button");
+ if (theme) {
+ int stateId = PBS_NORMAL;
+ if (!(option->state & State_Enabled))
+ stateId = PBS_DISABLED;
+ else if (option->state & State_Sunken)
+ stateId = PBS_PRESSED;
+ else if (option->state & State_MouseOver)
+ stateId = PBS_HOT;
+ else if (btn->features & QStyleOptionButton::DefaultButton)
+ stateId = PBS_DEFAULTED;
+
+ int border = proxy()->pixelMetric(PM_DefaultFrameWidth, btn, widget);
+ rect = option->rect.adjusted(border, border, -border, -border);
+
+ if (SUCCEEDED(GetThemeMargins(theme, NULL, BP_PUSHBUTTON, stateId, TMT_CONTENTMARGINS, NULL, &borderSize))) {
+ rect.adjust(borderSize.cxLeftWidth, borderSize.cyTopHeight,
+ -borderSize.cxRightWidth, -borderSize.cyBottomHeight);
+ rect = visualRect(option->direction, option->rect, rect);
+ }
+ }
+ }
+ break;
+
+ case SE_HeaderArrow:
+ {
+ QRect r = rect;
+ int h = option->rect.height();
+ int w = option->rect.width();
+ int x = option->rect.x();
+ int y = option->rect.y();
+ int margin = proxy()->pixelMetric(QStyle::PM_HeaderMargin, option, widget);
+
+ XPThemeData theme(widget, 0,
+ QWindowsXPStylePrivate::HeaderTheme,
+ HP_HEADERSORTARROW, HSAS_SORTEDDOWN, option->rect);
+
+ int arrowWidth = 13;
+ int arrowHeight = 5;
+ if (theme.isValid()) {
+ const QSizeF size = theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget);
+ if (!size.isEmpty()) {
+ arrowWidth = qRound(size.width());
+ arrowHeight = qRound(size.height());
+ }
+ }
+ if (option->state & State_Horizontal) {
+ r.setRect(x + w/2 - arrowWidth/2, y , arrowWidth, arrowHeight);
+ } else {
+ int vert_size = w / 2;
+ r.setRect(x + 5, y + h - margin * 2 - vert_size,
+ w - margin * 2 - 5, vert_size);
+ }
+ rect = visualRect(option->direction, option->rect, r);
+ }
+ break;
+
+ case SE_HeaderLabel:
+ {
+ int margin = proxy()->pixelMetric(QStyle::PM_HeaderMargin, option, widget);
+ QRect r = option->rect;
+ r.setRect(option->rect.x() + margin, option->rect.y() + margin,
+ option->rect.width() - margin * 2, option->rect.height() - margin * 2);
+ if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) {
+ // Subtract width needed for arrow, if there is one
+ if (header->sortIndicator != QStyleOptionHeader::None) {
+ if (!(option->state & State_Horizontal)) //horizontal arrows are positioned on top
+ r.setHeight(r.height() - (option->rect.width() / 2) - (margin * 2));
+ }
+ }
+ rect = visualRect(option->direction, option->rect, r);
+ }
+ break;
+ case SE_ProgressBarContents:
+ rect = QCommonStyle::subElementRect(SE_ProgressBarGroove, option, widget);
+ break;
+ case SE_ItemViewItemDecoration:
+ if (qstyleoption_cast<const QStyleOptionViewItem *>(option))
+ rect.adjust(-2, 0, 2, 0);
+ break;
+ case SE_ItemViewItemFocusRect:
+ if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(option)) {
+ QRect textRect = subElementRect(QStyle::SE_ItemViewItemText, option, widget);
+ QRect displayRect = subElementRect(QStyle::SE_ItemViewItemDecoration, option, widget);
+ if (!vopt->icon.isNull())
+ rect = textRect.united(displayRect);
+ else
+ rect = textRect;
+ rect = rect.adjusted(1, 0, -1, 0);
+ }
+ break;
+ default:
+ break;
+ }
+ return rect;
+}
+
+
+/*
+ This function is used by subControlRect to check if a button
+ should be drawn for the given subControl given a set of window flags.
+*/
+static bool buttonVisible(const QStyle::SubControl sc, const QStyleOptionTitleBar *tb){
+
+ bool isMinimized = tb->titleBarState & Qt::WindowMinimized;
+ bool isMaximized = tb->titleBarState & Qt::WindowMaximized;
+ const uint flags = tb->titleBarFlags;
+ bool retVal = false;
+ switch (sc) {
+ case QStyle::SC_TitleBarContextHelpButton:
+ if (flags & Qt::WindowContextHelpButtonHint)
+ retVal = true;
+ break;
+ case QStyle::SC_TitleBarMinButton:
+ if (!isMinimized && (flags & Qt::WindowMinimizeButtonHint))
+ retVal = true;
+ break;
+ case QStyle::SC_TitleBarNormalButton:
+ if (isMinimized && (flags & Qt::WindowMinimizeButtonHint))
+ retVal = true;
+ else if (isMaximized && (flags & Qt::WindowMaximizeButtonHint))
+ retVal = true;
+ break;
+ case QStyle::SC_TitleBarMaxButton:
+ if (!isMaximized && (flags & Qt::WindowMaximizeButtonHint))
+ retVal = true;
+ break;
+ case QStyle::SC_TitleBarShadeButton:
+ if (!isMinimized && flags & Qt::WindowShadeButtonHint)
+ retVal = true;
+ break;
+ case QStyle::SC_TitleBarUnshadeButton:
+ if (isMinimized && flags & Qt::WindowShadeButtonHint)
+ retVal = true;
+ break;
+ case QStyle::SC_TitleBarCloseButton:
+ if (flags & Qt::WindowSystemMenuHint)
+ retVal = true;
+ break;
+ case QStyle::SC_TitleBarSysMenu:
+ if (flags & Qt::WindowSystemMenuHint)
+ retVal = true;
+ break;
+ default :
+ retVal = true;
+ }
+ return retVal;
+}
+
+
+/*! \internal */
+int QWindowsVistaStyle::styleHint(StyleHint hint, const QStyleOption *option, const QWidget *widget,
+ QStyleHintReturn *returnData) const
+{
+ QWindowsVistaStylePrivate *d = const_cast<QWindowsVistaStylePrivate*>(d_func());
+ int ret = 0;
+ switch (hint) {
+ case SH_MessageBox_CenterButtons:
+ ret = false;
+ break;
+ case SH_ToolTip_Mask:
+ if (option) {
+ if (QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask*>(returnData)) {
+ ret = true;
+ XPThemeData themeData(widget, 0,
+ QWindowsXPStylePrivate::ToolTipTheme,
+ TTP_STANDARD, TTSS_NORMAL, option->rect);
+ mask->region = d->region(themeData);
+ }
+ }
+ break;
+ case SH_Table_GridLineColor:
+ if (option)
+ ret = option->palette.color(QPalette::Base).darker(118).rgb();
+ else
+ ret = -1;
+ break;
+ case SH_Header_ArrowAlignment:
+ ret = Qt::AlignTop | Qt::AlignHCenter;
+ break;
+ default:
+ ret = QWindowsXPStyle::styleHint(hint, option, widget, returnData);
+ break;
+ }
+ return ret;
+}
+
+
+/*!
+ \internal
+ */
+QRect QWindowsVistaStyle::subControlRect(ComplexControl control, const QStyleOptionComplex *option,
+ SubControl subControl, const QWidget *widget) const
+{
+ if (!QWindowsVistaStylePrivate::useVista())
+ return QWindowsStyle::subControlRect(control, option, subControl, widget);
+
+ QRect rect = QWindowsXPStyle::subControlRect(control, option, subControl, widget);
+ switch (control) {
+#if QT_CONFIG(combobox)
+ case CC_ComboBox:
+ if (const QStyleOptionComboBox *cb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
+ const int x = cb->rect.x(), y = cb->rect.y(), wi = cb->rect.width(), he = cb->rect.height();
+ const int margin = cb->frame ? 3 : 0;
+ const int bmarg = cb->frame ? 2 : 0;
+ const int arrowWidth = qRound(QStyleHelper::dpiScaled(16));
+ const int arrowButtonWidth = bmarg + arrowWidth;
+ const int xpos = x + wi - arrowButtonWidth;
+
+ switch (subControl) {
+ case SC_ComboBoxFrame:
+ rect = cb->rect;
+ break;
+ case SC_ComboBoxArrow:
+ rect.setRect(xpos, y , arrowButtonWidth, he);
+ break;
+ case SC_ComboBoxEditField:
+ rect.setRect(x + margin, y + margin, wi - 2 * margin - arrowWidth, he - 2 * margin);
+ break;
+ case SC_ComboBoxListBoxPopup:
+ rect = cb->rect;
+ break;
+ default:
+ break;
+ }
+ rect = visualRect(cb->direction, cb->rect, rect);
+ return rect;
+ }
+ break;
+#endif // QT_CONFIG(combobox)
+ case CC_TitleBar:
+ if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(option)) {
+ if (!buttonVisible(subControl, tb))
+ return rect;
+ const bool isToolTitle = false;
+ const int height = tb->rect.height();
+ const int width = tb->rect.width();
+ const int buttonWidth =
+ qRound(qreal(GetSystemMetrics(SM_CXSIZE)) * QWindowsStylePrivate::nativeMetricScaleFactor(widget)
+ - QStyleHelper::dpiScaled(4));
+
+ const int frameWidth = proxy()->pixelMetric(PM_MdiSubWindowFrameWidth, option, widget);
+ const bool sysmenuHint = (tb->titleBarFlags & Qt::WindowSystemMenuHint) != 0;
+ const bool minimizeHint = (tb->titleBarFlags & Qt::WindowMinimizeButtonHint) != 0;
+ const bool maximizeHint = (tb->titleBarFlags & Qt::WindowMaximizeButtonHint) != 0;
+ const bool contextHint = (tb->titleBarFlags & Qt::WindowContextHelpButtonHint) != 0;
+ const bool shadeHint = (tb->titleBarFlags & Qt::WindowShadeButtonHint) != 0;
+
+ switch (subControl) {
+ case SC_TitleBarLabel:
+ rect = QRect(frameWidth, 0, width - (buttonWidth + frameWidth + 10), height);
+ if (isToolTitle) {
+ if (sysmenuHint) {
+ rect.adjust(0, 0, -buttonWidth - 3, 0);
+ }
+ if (minimizeHint || maximizeHint)
+ rect.adjust(0, 0, -buttonWidth - 2, 0);
+ } else {
+ if (sysmenuHint) {
+ const int leftOffset = height - 8;
+ rect.adjust(leftOffset, 0, 0, 4);
+ }
+ if (minimizeHint)
+ rect.adjust(0, 0, -buttonWidth - 2, 0);
+ if (maximizeHint)
+ rect.adjust(0, 0, -buttonWidth - 2, 0);
+ if (contextHint)
+ rect.adjust(0, 0, -buttonWidth - 2, 0);
+ if (shadeHint)
+ rect.adjust(0, 0, -buttonWidth - 2, 0);
+ }
+ rect.translate(0, 2);
+ rect = visualRect(option->direction, option->rect, rect);
+ break;
+ case SC_TitleBarSysMenu:
+ {
+ const int controlTop = 6;
+ const int controlHeight = height - controlTop - 3;
+ int iconExtent = proxy()->pixelMetric(PM_SmallIconSize);
+ QSize iconSize = tb->icon.actualSize(QSize(iconExtent, iconExtent));
+ if (tb->icon.isNull())
+ iconSize = QSize(controlHeight, controlHeight);
+ int hPad = (controlHeight - iconSize.height())/2;
+ int vPad = (controlHeight - iconSize.width())/2;
+ rect = QRect(frameWidth + hPad, controlTop + vPad, iconSize.width(), iconSize.height());
+ rect.translate(0, 3);
+ rect = visualRect(option->direction, option->rect, rect);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ return rect;
+}
+
+/*!
+ \internal
+ */
+QStyle::SubControl QWindowsVistaStyle::hitTestComplexControl(ComplexControl control, const QStyleOptionComplex *option,
+ const QPoint &pos, const QWidget *widget) const
+{
+ if (!QWindowsVistaStylePrivate::useVista()) {
+ return QWindowsStyle::hitTestComplexControl(control, option, pos, widget);
+ }
+ return QWindowsXPStyle::hitTestComplexControl(control, option, pos, widget);
+}
+
+int QWindowsVistaStylePrivate::fixedPixelMetric(QStyle::PixelMetric pm)
+{
+ switch (pm) {
+ case QStyle::PM_DockWidgetTitleBarButtonMargin:
+ return 5;
+ case QStyle::PM_ScrollBarSliderMin:
+ return 18;
+ case QStyle::PM_MenuHMargin:
+ case QStyle::PM_MenuVMargin:
+ return 0;
+ case QStyle::PM_MenuPanelWidth:
+ return 3;
+ default:
+ break;
+ }
+ return QWindowsVistaStylePrivate::InvalidMetric;
+}
+
+/*!
+ \internal
+ */
+int QWindowsVistaStyle::pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const
+{
+ if (!QWindowsVistaStylePrivate::useVista())
+ return QWindowsStyle::pixelMetric(metric, option, widget);
+
+ int ret = QWindowsVistaStylePrivate::fixedPixelMetric(metric);
+ if (ret != QWindowsStylePrivate::InvalidMetric)
+ return int(QStyleHelper::dpiScaled(ret));
+
+ return QWindowsXPStyle::pixelMetric(metric, option, widget);
+}
+
+/*!
+ \internal
+ */
+QPalette QWindowsVistaStyle::standardPalette() const
+{
+ return QWindowsXPStyle::standardPalette();
+}
+
+/*!
+ \internal
+ */
+void QWindowsVistaStyle::polish(QApplication *app)
+{
+ QWindowsXPStyle::polish(app);
+}
+
+/*!
+ \internal
+ */
+void QWindowsVistaStyle::polish(QWidget *widget)
+{
+ QWindowsXPStyle::polish(widget);
+#if QT_CONFIG(lineedit)
+ if (qobject_cast<QLineEdit*>(widget))
+ widget->setAttribute(Qt::WA_Hover);
+ else
+#endif // QT_CONFIG(lineedit)
+ if (qobject_cast<QGroupBox*>(widget))
+ widget->setAttribute(Qt::WA_Hover);
+ else if (qobject_cast<QCommandLinkButton*>(widget)) {
+ QFont buttonFont = widget->font();
+ buttonFont.setFamily(QLatin1String("Segoe UI"));
+ widget->setFont(buttonFont);
+ }
+ else if (widget->inherits("QTipLabel")){
+ //note that since tooltips are not reused
+ //we do not have to care about unpolishing
+ widget->setContentsMargins(3, 0, 4, 0);
+ COLORREF bgRef;
+ HTHEME theme = OpenThemeData(widget ? QWindowsVistaStylePrivate::winId(widget) : 0, L"TOOLTIP");
+ if (theme && SUCCEEDED(GetThemeColor(theme, TTP_STANDARD, TTSS_NORMAL, TMT_TEXTCOLOR, &bgRef))) {
+ QColor textColor = QColor::fromRgb(bgRef);
+ QPalette pal;
+ pal.setColor(QPalette::All, QPalette::ToolTipText, textColor);
+ widget->setPalette(pal);
+ }
+ } else if (qobject_cast<QMessageBox *> (widget)) {
+ widget->setAttribute(Qt::WA_StyledBackground);
+#if QT_CONFIG(dialogbuttonbox)
+ QDialogButtonBox *buttonBox = widget->findChild<QDialogButtonBox *>(QLatin1String("qt_msgbox_buttonbox"));
+ if (buttonBox)
+ buttonBox->setContentsMargins(0, 9, 0, 0);
+#endif
+ }
+#if QT_CONFIG(inputdialog)
+ else if (qobject_cast<QInputDialog *> (widget)) {
+ widget->setAttribute(Qt::WA_StyledBackground);
+#if QT_CONFIG(dialogbuttonbox)
+ QDialogButtonBox *buttonBox = widget->findChild<QDialogButtonBox *>(QLatin1String("qt_inputdlg_buttonbox"));
+ if (buttonBox)
+ buttonBox->setContentsMargins(0, 9, 0, 0);
+#endif
+ }
+#endif // QT_CONFIG(inputdialog)
+ else if (QTreeView *tree = qobject_cast<QTreeView *> (widget)) {
+ tree->viewport()->setAttribute(Qt::WA_Hover);
+ }
+ else if (QListView *list = qobject_cast<QListView *> (widget)) {
+ list->viewport()->setAttribute(Qt::WA_Hover);
+ }
+}
+
+/*!
+ \internal
+ */
+void QWindowsVistaStyle::unpolish(QWidget *widget)
+{
+ QWindowsXPStyle::unpolish(widget);
+
+ QWindowsVistaStylePrivate *d = d_func();
+
+ d->stopAnimation(widget);
+
+#if QT_CONFIG(lineedit)
+ if (qobject_cast<QLineEdit*>(widget))
+ widget->setAttribute(Qt::WA_Hover, false);
+ else
+#endif // QT_CONFIG(lineedit)
+ if (qobject_cast<QGroupBox*>(widget))
+ widget->setAttribute(Qt::WA_Hover, false);
+ else if (qobject_cast<QMessageBox *> (widget)) {
+ widget->setAttribute(Qt::WA_StyledBackground, false);
+#if QT_CONFIG(dialogbuttonbox)
+ QDialogButtonBox *buttonBox = widget->findChild<QDialogButtonBox *>(QLatin1String("qt_msgbox_buttonbox"));
+ if (buttonBox)
+ buttonBox->setContentsMargins(0, 0, 0, 0);
+#endif
+ }
+#if QT_CONFIG(inputdialog)
+ else if (qobject_cast<QInputDialog *> (widget)) {
+ widget->setAttribute(Qt::WA_StyledBackground, false);
+#if QT_CONFIG(dialogbuttonbox)
+ QDialogButtonBox *buttonBox = widget->findChild<QDialogButtonBox *>(QLatin1String("qt_inputdlg_buttonbox"));
+ if (buttonBox)
+ buttonBox->setContentsMargins(0, 0, 0, 0);
+#endif
+ }
+#endif // QT_CONFIG(inputdialog)
+ else if (QTreeView *tree = qobject_cast<QTreeView *> (widget)) {
+ tree->viewport()->setAttribute(Qt::WA_Hover, false);
+ } else if (qobject_cast<QCommandLinkButton*>(widget)) {
+ QFont font = QApplication::font("QCommandLinkButton");
+ QFont widgetFont = widget->font();
+ widgetFont.setFamily(font.family()); //Only family set by polish
+ widget->setFont(widgetFont);
+ }
+}
+
+
+/*!
+ \internal
+ */
+void QWindowsVistaStyle::unpolish(QApplication *app)
+{
+ QWindowsXPStyle::unpolish(app);
+}
+
+/*!
+ \internal
+ */
+void QWindowsVistaStyle::polish(QPalette &pal)
+{
+ QWindowsStyle::polish(pal);
+ pal.setBrush(QPalette::AlternateBase, pal.base().color().darker(104));
+}
+
+/*!
+ \internal
+ */
+QPixmap QWindowsVistaStyle::standardPixmap(StandardPixmap standardPixmap, const QStyleOption *option,
+ const QWidget *widget) const
+{
+ if (!QWindowsVistaStylePrivate::useVista()) {
+ return QWindowsStyle::standardPixmap(standardPixmap, option, widget);
+ }
+ return QWindowsXPStyle::standardPixmap(standardPixmap, option, widget);
+}
+
+QWindowsVistaStylePrivate::QWindowsVistaStylePrivate() :
+ QWindowsXPStylePrivate()
+{
+}
+
+bool QWindowsVistaStylePrivate::transitionsEnabled() const
+{
+ BOOL animEnabled = false;
+ if (SystemParametersInfo(SPI_GETCLIENTAREAANIMATION, 0, &animEnabled, 0))
+ {
+ if (animEnabled)
+ return true;
+ }
+ return false;
+}
+
+/*!
+\reimp
+*/
+QIcon QWindowsVistaStyle::standardIcon(StandardPixmap standardIcon,
+ const QStyleOption *option,
+ const QWidget *widget) const
+{
+ if (!QWindowsVistaStylePrivate::useVista()) {
+ return QWindowsStyle::standardIcon(standardIcon, option, widget);
+ }
+
+ QWindowsVistaStylePrivate *d = const_cast<QWindowsVistaStylePrivate *>(d_func());
+ switch(standardIcon) {
+ case SP_CommandLink:
+ {
+ XPThemeData theme(0, 0,
+ QWindowsXPStylePrivate::ButtonTheme,
+ BP_COMMANDLINKGLYPH, CMDLGS_NORMAL);
+ if (theme.isValid()) {
+ const QSize size = (theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize();
+ QIcon linkGlyph;
+ QPixmap pm(size);
+ pm.fill(Qt::transparent);
+ QPainter p(&pm);
+ theme.painter = &p;
+ theme.rect = QRect(QPoint(0, 0), size);
+ d->drawBackground(theme);
+ linkGlyph.addPixmap(pm, QIcon::Normal, QIcon::Off); // Normal
+ pm.fill(Qt::transparent);
+
+ theme.stateId = CMDLGS_PRESSED;
+ d->drawBackground(theme);
+ linkGlyph.addPixmap(pm, QIcon::Normal, QIcon::On); // Pressed
+ pm.fill(Qt::transparent);
+
+ theme.stateId = CMDLGS_HOT;
+ d->drawBackground(theme);
+ linkGlyph.addPixmap(pm, QIcon::Active, QIcon::Off); // Hover
+ pm.fill(Qt::transparent);
+
+ theme.stateId = CMDLGS_DISABLED;
+ d->drawBackground(theme);
+ linkGlyph.addPixmap(pm, QIcon::Disabled, QIcon::Off); // Disabled
+ return linkGlyph;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ return QWindowsXPStyle::standardIcon(standardIcon, option, widget);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/styles/windowsvista/qwindowsvistastyle_p.h b/src/plugins/styles/windowsvista/qwindowsvistastyle_p.h
new file mode 100644
index 0000000000..5ffcbc6aa9
--- /dev/null
+++ b/src/plugins/styles/windowsvista/qwindowsvistastyle_p.h
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWidgets module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSVISTASTYLE_P_H
+#define QWINDOWSVISTASTYLE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtWidgets/private/qtwidgetsglobal_p.h>
+#include "qwindowsxpstyle_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QWindowsVistaStylePrivate;
+class QWindowsVistaStyle : public QWindowsXPStyle
+{
+ Q_OBJECT
+public:
+ QWindowsVistaStyle();
+ ~QWindowsVistaStyle();
+
+ void drawPrimitive(PrimitiveElement element, const QStyleOption *option,
+ QPainter *painter, const QWidget *widget = 0) const;
+ void drawControl(ControlElement element, const QStyleOption *option,
+ QPainter *painter, const QWidget *widget) const;
+ void drawComplexControl(ComplexControl control, const QStyleOptionComplex *option,
+ QPainter *painter, const QWidget *widget) const;
+ QSize sizeFromContents(ContentsType type, const QStyleOption *option,
+ const QSize &size, const QWidget *widget) const;
+
+ QRect subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const;
+ QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *opt,
+ SubControl sc, const QWidget *widget) const;
+
+ SubControl hitTestComplexControl(ComplexControl control, const QStyleOptionComplex *option,
+ const QPoint &pos, const QWidget *widget = 0) const;
+
+ QIcon standardIcon(StandardPixmap standardIcon, const QStyleOption *option = 0,
+ const QWidget *widget = 0) const;
+ QPixmap standardPixmap(StandardPixmap standardPixmap, const QStyleOption *opt,
+ const QWidget *widget = 0) const;
+ int pixelMetric(PixelMetric metric, const QStyleOption *option = 0, const QWidget *widget = 0) const;
+ int styleHint(StyleHint hint, const QStyleOption *opt = 0, const QWidget *widget = 0,
+ QStyleHintReturn *returnData = 0) const;
+
+ void polish(QWidget *widget);
+ void unpolish(QWidget *widget);
+ void polish(QPalette &pal);
+ void polish(QApplication *app);
+ void unpolish(QApplication *app);
+ QPalette standardPalette() const;
+
+private:
+ Q_DISABLE_COPY(QWindowsVistaStyle)
+ Q_DECLARE_PRIVATE(QWindowsVistaStyle)
+ friend class QStyleFactory;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWSVISTASTYLE_P_H
diff --git a/src/plugins/styles/windowsvista/qwindowsvistastyle_p_p.h b/src/plugins/styles/windowsvista/qwindowsvistastyle_p_p.h
new file mode 100644
index 0000000000..b649426811
--- /dev/null
+++ b/src/plugins/styles/windowsvista/qwindowsvistastyle_p_p.h
@@ -0,0 +1,200 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWidgets module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSVISTASTYLE_P_P_H
+#define QWINDOWSVISTASTYLE_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header
+// file may change from version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtWidgets/private/qtwidgetsglobal_p.h>
+#include "qwindowsvistastyle_p.h"
+#include "qwindowsxpstyle_p_p.h"
+#include <private/qstyleanimation_p.h>
+#include <private/qpaintengine_raster_p.h>
+#include <qpaintengine.h>
+#include <qwidget.h>
+#include <qapplication.h>
+#include <qpixmapcache.h>
+#include <qstyleoption.h>
+#if QT_CONFIG(pushbutton)
+#include <qpushbutton.h>
+#endif
+#include <qradiobutton.h>
+#if QT_CONFIG(lineedit)
+#include <qlineedit.h>
+#endif
+#include <qgroupbox.h>
+#if QT_CONFIG(toolbutton)
+#include <qtoolbutton.h>
+#endif
+#if QT_CONFIG(spinbox)
+#include <qspinbox.h>
+#endif
+#include <qtoolbar.h>
+#if QT_CONFIG(combobox)
+#include <qcombobox.h>
+#endif
+#if QT_CONFIG(scrollbar)
+#include <qscrollbar.h>
+#endif
+#if QT_CONFIG(progressbar)
+#include <qprogressbar.h>
+#endif
+#if QT_CONFIG(dockwidget)
+#include <qdockwidget.h>
+#endif
+#if QT_CONFIG(listview)
+#include <qlistview.h>
+#endif
+#if QT_CONFIG(treeview)
+#include <qtreeview.h>
+#endif
+#include <qtextedit.h>
+#include <qmessagebox.h>
+#if QT_CONFIG(dialogbuttonbox)
+#include <qdialogbuttonbox.h>
+#endif
+#include <qinputdialog.h>
+#if QT_CONFIG(tableview)
+#include <qtableview.h>
+#endif
+#include <qdatetime.h>
+#include <qcommandlinkbutton.h>
+
+QT_BEGIN_NAMESPACE
+
+#if !defined(SCHEMA_VERIFY_VSSYM32)
+#define TMT_ANIMATIONDURATION 5006
+#define TMT_TRANSITIONDURATIONS 6000
+#define EP_EDITBORDER_NOSCROLL 6
+#define EP_EDITBORDER_HVSCROLL 9
+#define EP_BACKGROUND 3
+#define EBS_NORMAL 1
+#define EBS_HOT 2
+#define EBS_DISABLED 3
+#define EBS_READONLY 5
+#define PBS_DEFAULTED_ANIMATING 6
+#define MBI_NORMAL 1
+#define MBI_HOT 2
+#define MBI_PUSHED 3
+#define MBI_DISABLED 4
+#define MB_ACTIVE 1
+#define MB_INACTIVE 2
+#define PP_FILL 5
+#define PP_FILLVERT 6
+#define PP_MOVEOVERLAY 8
+#define PP_MOVEOVERLAYVERT 10
+#define MENU_BARBACKGROUND 7
+#define MENU_BARITEM 8
+#define MENU_POPUPCHECK 11
+#define MENU_POPUPCHECKBACKGROUND 12
+#define MENU_POPUPGUTTER 13
+#define MENU_POPUPITEM 14
+#define MENU_POPUPBORDERS 10
+#define MENU_POPUPSEPARATOR 15
+#define MC_CHECKMARKNORMAL 1
+#define MC_CHECKMARKDISABLED 2
+#define MC_BULLETNORMAL 3
+#define MC_BULLETDISABLED 4
+#define ABS_UPHOVER 17
+#define ABS_DOWNHOVER 18
+#define ABS_LEFTHOVER 19
+#define ABS_RIGHTHOVER 20
+#define CP_DROPDOWNBUTTONRIGHT 6
+#define CP_DROPDOWNBUTTONLEFT 7
+#define SCRBS_HOVER 5
+#define TVP_HOTGLYPH 4
+#define SPI_GETCLIENTAREAANIMATION 0x1042
+#define TDLG_PRIMARYPANEL 1
+#define TDLG_SECONDARYPANEL 8
+#endif
+
+class QWindowsVistaAnimation : public QBlendStyleAnimation
+{
+ Q_OBJECT
+public:
+ QWindowsVistaAnimation(Type type, QObject *target) : QBlendStyleAnimation(type, target) { }
+
+ virtual bool isUpdateNeeded() const;
+ void paint(QPainter *painter, const QStyleOption *option);
+};
+
+
+// Handles state transition animations
+class QWindowsVistaTransition : public QWindowsVistaAnimation
+{
+ Q_OBJECT
+public:
+ QWindowsVistaTransition(QObject *target) : QWindowsVistaAnimation(Transition, target) {}
+};
+
+
+// Handles pulse animations (default buttons)
+class QWindowsVistaPulse: public QWindowsVistaAnimation
+{
+ Q_OBJECT
+public:
+ QWindowsVistaPulse(QObject *target) : QWindowsVistaAnimation(Pulse, target) {}
+};
+
+
+class QWindowsVistaStylePrivate : public QWindowsXPStylePrivate
+{
+ Q_DECLARE_PUBLIC(QWindowsVistaStyle)
+
+public:
+ QWindowsVistaStylePrivate();
+
+ static int fixedPixelMetric(QStyle::PixelMetric pm);
+ static inline bool useVista();
+ bool transitionsEnabled() const;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWSVISTASTYLE_P_P_H
diff --git a/src/plugins/styles/windowsvista/qwindowsxpstyle.cpp b/src/plugins/styles/windowsvista/qwindowsxpstyle.cpp
new file mode 100644
index 0000000000..ff27cab98a
--- /dev/null
+++ b/src/plugins/styles/windowsvista/qwindowsxpstyle.cpp
@@ -0,0 +1,4240 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWidgets module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "qwindowsxpstyle_p.h"
+#include "qwindowsxpstyle_p_p.h"
+
+#include <private/qobject_p.h>
+#include <private/qpaintengine_raster_p.h>
+#include <private/qapplication_p.h>
+#include <qpa/qplatformnativeinterface.h>
+#include <private/qstylehelper_p.h>
+#include <private/qwidget_p.h>
+#include <qpainter.h>
+#include <qpaintengine.h>
+#include <qwidget.h>
+#include <qbackingstore.h>
+#include <qapplication.h>
+#include <qpixmapcache.h>
+#include <private/qapplication_p.h>
+#include <qpa/qplatformnativeinterface.h>
+
+#if QT_CONFIG(toolbutton)
+#include <qtoolbutton.h>
+#endif
+#if QT_CONFIG(tabbar)
+#include <qtabbar.h>
+#endif
+#if QT_CONFIG(combobox)
+#include <qcombobox.h>
+#endif
+#if QT_CONFIG(scrollbar)
+#include <qscrollbar.h>
+#endif
+#include <qheaderview.h>
+#if QT_CONFIG(spinbox)
+#include <qspinbox.h>
+#endif
+#if QT_CONFIG(listview)
+#include <qlistview.h>
+#endif
+#if QT_CONFIG(stackedwidget)
+#include <qstackedwidget.h>
+#endif
+#if QT_CONFIG(pushbutton)
+#include <qpushbutton.h>
+#endif
+#include <qtoolbar.h>
+#include <qlabel.h>
+#include <qvarlengtharray.h>
+#include <qdebug.h>
+
+#include <algorithm>
+
+QT_BEGIN_NAMESPACE
+
+// General const values
+static const int windowsItemFrame = 2; // menu item frame width
+static const int windowsItemHMargin = 3; // menu item hor text margin
+static const int windowsItemVMargin = 0; // menu item ver text margin
+static const int windowsArrowHMargin = 6; // arrow horizontal margin
+static const int windowsRightBorder = 12; // right border on windows
+
+// External function calls
+extern Q_WIDGETS_EXPORT HDC qt_win_display_dc();
+extern QRegion qt_region_from_HRGN(HRGN rgn);
+
+// Theme names matching the QWindowsXPStylePrivate::Theme enumeration.
+static const wchar_t *themeNames[QWindowsXPStylePrivate::NThemes] =
+{
+ L"BUTTON", L"COMBOBOX", L"EDIT", L"HEADER", L"LISTVIEW",
+ L"MENU", L"PROGRESS", L"REBAR", L"SCROLLBAR", L"SPIN",
+ L"TAB", L"TASKDIALOG", L"TOOLBAR", L"TOOLTIP", L"TRACKBAR",
+ L"TREEVIEW", L"WINDOW", L"STATUS", L"TREEVIEW"
+};
+
+static inline QBackingStore *backingStoreForWidget(const QWidget *widget)
+{
+ if (QBackingStore *backingStore = widget->backingStore())
+ return backingStore;
+ if (const QWidget *topLevel = widget->nativeParentWidget())
+ if (QBackingStore *topLevelBackingStore = topLevel->backingStore())
+ return topLevelBackingStore;
+ return 0;
+}
+
+static inline HDC hdcForWidgetBackingStore(const QWidget *widget)
+{
+ if (QBackingStore *backingStore = backingStoreForWidget(widget)) {
+ QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface();
+ return static_cast<HDC>(nativeInterface->nativeResourceForBackingStore(QByteArrayLiteral("getDC"), backingStore));
+ }
+ return 0;
+}
+
+// Theme data helper ------------------------------------------------------------------------------
+/* \internal
+ Returns \c true if the themedata is valid for use.
+*/
+bool XPThemeData::isValid()
+{
+ return QWindowsXPStylePrivate::useXP() && theme >= 0 && handle();
+}
+
+
+/* \internal
+ Returns the theme engine handle to the specific class.
+ If the handle hasn't been opened before, it opens the data, and
+ adds it to a static map, for caching.
+*/
+HTHEME XPThemeData::handle()
+{
+ if (!QWindowsXPStylePrivate::useXP())
+ return 0;
+
+ if (!htheme)
+ htheme = QWindowsXPStylePrivate::createTheme(theme, QWindowsXPStylePrivate::winId(widget));
+ return htheme;
+}
+
+/* \internal
+ Converts a QRect to the native RECT structure.
+*/
+RECT XPThemeData::toRECT(const QRect &qr)
+{
+ RECT r;
+ r.left = qr.x();
+ r.right = qr.x() + qr.width();
+ r.top = qr.y();
+ r.bottom = qr.y() + qr.height();
+ return r;
+}
+
+/* \internal
+ Returns the native region of a part, if the part is considered
+ transparent. The region is scaled to the parts size (rect).
+*/
+HRGN XPThemeData::mask(QWidget *widget)
+{
+ if (!IsThemeBackgroundPartiallyTransparent(handle(), partId, stateId))
+ return 0;
+
+ HRGN hrgn;
+ HDC dc = 0;
+ if (widget)
+ dc = hdcForWidgetBackingStore(widget);
+ RECT nativeRect = toRECT(rect);
+ GetThemeBackgroundRegion(handle(), dc, partId, stateId, &nativeRect, &hrgn);
+ return hrgn;
+}
+
+// QWindowsXPStylePrivate -------------------------------------------------------------------------
+// Static initializations
+HWND QWindowsXPStylePrivate::m_vistaTreeViewHelper = 0;
+HTHEME QWindowsXPStylePrivate::m_themes[NThemes];
+bool QWindowsXPStylePrivate::use_xp = false;
+QBasicAtomicInt QWindowsXPStylePrivate::ref = Q_BASIC_ATOMIC_INITIALIZER(-1); // -1 based refcounting
+
+static void qt_add_rect(HRGN &winRegion, QRect r)
+{
+ HRGN rgn = CreateRectRgn(r.left(), r.top(), r.x() + r.width(), r.y() + r.height());
+ if (rgn) {
+ HRGN dest = CreateRectRgn(0,0,0,0);
+ int result = CombineRgn(dest, winRegion, rgn, RGN_OR);
+ if (result) {
+ DeleteObject(winRegion);
+ winRegion = dest;
+ }
+ DeleteObject(rgn);
+ }
+}
+
+static HRGN qt_hrgn_from_qregion(const QRegion &region)
+{
+ HRGN hRegion = CreateRectRgn(0,0,0,0);
+ if (region.rectCount() == 1) {
+ qt_add_rect(hRegion, region.boundingRect());
+ return hRegion;
+ }
+ for (const QRect &rect : region)
+ qt_add_rect(hRegion, rect);
+ return hRegion;
+}
+
+/* \internal
+ Checks if the theme engine can/should be used, or if we should
+ fall back to Windows style.
+*/
+bool QWindowsXPStylePrivate::useXP(bool update)
+{
+ if (!update)
+ return use_xp;
+ return use_xp = IsThemeActive() && (IsAppThemed() || !QApplication::instance());
+}
+
+/* \internal
+ Handles refcounting, and queries the theme engine for usage.
+*/
+void QWindowsXPStylePrivate::init(bool force)
+{
+ if (ref.ref() && !force)
+ return;
+ if (!force) // -1 based atomic refcounting
+ ref.ref();
+
+ useXP(true);
+ std::fill(m_themes, m_themes + NThemes, HTHEME(0));
+}
+
+/* \internal
+ Cleans up all static data.
+*/
+void QWindowsXPStylePrivate::cleanup(bool force)
+{
+ if(bufferBitmap) {
+ if (bufferDC && nullBitmap)
+ SelectObject(bufferDC, nullBitmap);
+ DeleteObject(bufferBitmap);
+ bufferBitmap = 0;
+ }
+
+ if(bufferDC)
+ DeleteDC(bufferDC);
+ bufferDC = 0;
+
+ if (ref.deref() && !force)
+ return;
+ if (!force) // -1 based atomic refcounting
+ ref.deref();
+
+ use_xp = false;
+ cleanupHandleMap();
+}
+
+/* In order to obtain the correct VistaTreeViewTheme (arrows for PE_IndicatorBranch),
+ * we need to set the windows "explorer" theme explicitly on a native
+ * window and open the "TREEVIEW" theme handle passing its window handle
+ * in order to get Vista-style item view themes (particulary drawBackground()
+ * for selected items needs this).
+ * We invoke a service of the native Windows interface to create
+ * a non-visible window handle, open the theme on it and insert it into
+ * the cache so that it is found by XPThemeData::handle() first.
+ */
+
+static inline HWND createTreeViewHelperWindow()
+{
+ if (QPlatformNativeInterface *ni = QGuiApplication::platformNativeInterface()) {
+ void *hwnd = 0;
+ void *wndProc = reinterpret_cast<void *>(DefWindowProc);
+ if (QMetaObject::invokeMethod(ni, "createMessageWindow", Qt::DirectConnection,
+ Q_RETURN_ARG(void*, hwnd),
+ Q_ARG(QString, QStringLiteral("QTreeViewThemeHelperWindowClass")),
+ Q_ARG(QString, QStringLiteral("QTreeViewThemeHelperWindow")),
+ Q_ARG(void*, wndProc)) && hwnd) {
+ return reinterpret_cast<HWND>(hwnd);
+ }
+ }
+ return 0;
+}
+
+bool QWindowsXPStylePrivate::initVistaTreeViewTheming()
+{
+ if (m_vistaTreeViewHelper)
+ return true;
+
+ m_vistaTreeViewHelper = createTreeViewHelperWindow();
+ if (!m_vistaTreeViewHelper) {
+ qWarning("Unable to create the treeview helper window.");
+ return false;
+ }
+ if (FAILED(SetWindowTheme(m_vistaTreeViewHelper, L"explorer", NULL))) {
+ qErrnoWarning("SetWindowTheme() failed.");
+ cleanupVistaTreeViewTheming();
+ return false;
+ }
+ return true;
+}
+
+void QWindowsXPStylePrivate::cleanupVistaTreeViewTheming()
+{
+ if (m_vistaTreeViewHelper) {
+ DestroyWindow(m_vistaTreeViewHelper);
+ m_vistaTreeViewHelper = 0;
+ }
+}
+
+/* \internal
+ Closes all open theme data handles to ensure that we don't leak
+ resources, and that we don't refere to old handles when for
+ example the user changes the theme style.
+*/
+void QWindowsXPStylePrivate::cleanupHandleMap()
+{
+ for (int i = 0; i < NThemes; ++i)
+ if (m_themes[i]) {
+ CloseThemeData(m_themes[i]);
+ m_themes[i] = 0;
+ }
+ QWindowsXPStylePrivate::cleanupVistaTreeViewTheming();
+}
+
+HTHEME QWindowsXPStylePrivate::createTheme(int theme, HWND hwnd)
+{
+ if (Q_UNLIKELY(theme < 0 || theme >= NThemes || !hwnd)) {
+ qWarning("Invalid parameters #%d, %p", theme, hwnd);
+ return 0;
+ }
+ if (!m_themes[theme]) {
+ const wchar_t *name = themeNames[theme];
+ if (theme == VistaTreeViewTheme && QWindowsXPStylePrivate::initVistaTreeViewTheming())
+ hwnd = QWindowsXPStylePrivate::m_vistaTreeViewHelper;
+ m_themes[theme] = OpenThemeData(hwnd, name);
+ if (Q_UNLIKELY(!m_themes[theme]))
+ qErrnoWarning("OpenThemeData() failed for theme %d (%s).",
+ theme, qPrintable(themeName(theme)));
+ }
+ return m_themes[theme];
+}
+
+QString QWindowsXPStylePrivate::themeName(int theme)
+{
+ return theme >= 0 && theme < NThemes ?
+ QString::fromWCharArray(themeNames[theme]) :
+ QString();
+}
+
+bool QWindowsXPStylePrivate::isItemViewDelegateLineEdit(const QWidget *widget)
+{
+ if (!widget)
+ return false;
+ const QWidget *parent1 = widget->parentWidget();
+ // Exlude dialogs or other toplevels parented on item views.
+ if (!parent1 || parent1->isWindow())
+ return false;
+ const QWidget *parent2 = parent1->parentWidget();
+ return parent2 && widget->inherits("QLineEdit")
+ && parent2->inherits("QAbstractItemView");
+}
+
+// Returns whether base color is set for this widget
+bool QWindowsXPStylePrivate::isLineEditBaseColorSet(const QStyleOption *option, const QWidget *widget)
+{
+ uint resolveMask = option->palette.resolve();
+ if (widget) {
+ // Since spin box includes a line edit we need to resolve the palette mask also from
+ // the parent, as while the color is always correct on the palette supplied by panel,
+ // the mask can still be empty. If either mask specifies custom base color, use that.
+#if QT_CONFIG(spinbox)
+ if (const QAbstractSpinBox *spinbox = qobject_cast<QAbstractSpinBox*>(widget->parentWidget()))
+ resolveMask |= spinbox->palette().resolve();
+#endif // QT_CONFIG(spinbox)
+ }
+ return (resolveMask & (1 << QPalette::Base)) != 0;
+}
+
+/*! \internal
+ This function will always return a valid window handle, and might
+ create a limbo widget to do so.
+ We often need a window handle to for example open theme data, so
+ this function ensures that we get one.
+*/
+HWND QWindowsXPStylePrivate::winId(const QWidget *widget)
+{
+ if (widget)
+ if (const HWND hwnd = QApplicationPrivate::getHWNDForWidget(const_cast<QWidget *>(widget)))
+ return hwnd;
+
+ // Find top level with native window (there might be dialogs that do not have one).
+ const auto allWindows = QGuiApplication::allWindows();
+ for (const QWindow *window : allWindows) {
+ if (window->isTopLevel() && window->type() != Qt::Desktop && window->handle() != nullptr)
+ return reinterpret_cast<HWND>(window->winId());
+ }
+
+ return GetDesktopWindow();
+}
+
+/*! \internal
+ Returns a native buffer (DIB section) of at least the size of
+ ( \a x , \a y ). The buffer has a 32 bit depth, to not lose
+ the alpha values on proper alpha-pixmaps.
+*/
+HBITMAP QWindowsXPStylePrivate::buffer(int w, int h)
+{
+ // If we already have a HBITMAP which is of adequate size, just return that
+ if (bufferBitmap) {
+ if (bufferW >= w && bufferH >= h)
+ return bufferBitmap;
+ // Not big enough, discard the old one
+ if (bufferDC && nullBitmap)
+ SelectObject(bufferDC, nullBitmap);
+ DeleteObject(bufferBitmap);
+ bufferBitmap = 0;
+ }
+
+ w = qMax(bufferW, w);
+ h = qMax(bufferH, h);
+
+ if (!bufferDC) {
+ HDC displayDC = GetDC(0);
+ bufferDC = CreateCompatibleDC(displayDC);
+ ReleaseDC(0, displayDC);
+ }
+
+ // Define the header
+ BITMAPINFO bmi;
+ memset(&bmi, 0, sizeof(bmi));
+ bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bmi.bmiHeader.biWidth = w;
+ bmi.bmiHeader.biHeight = -h;
+ bmi.bmiHeader.biPlanes = 1;
+ bmi.bmiHeader.biBitCount = 32;
+ bmi.bmiHeader.biCompression = BI_RGB;
+
+ // Create the pixmap
+ bufferPixels = 0;
+ bufferBitmap = CreateDIBSection(bufferDC, &bmi, DIB_RGB_COLORS, (void **) &bufferPixels, 0, 0);
+ GdiFlush();
+ nullBitmap = (HBITMAP)SelectObject(bufferDC, bufferBitmap);
+
+ if (Q_UNLIKELY(!bufferBitmap)) {
+ qErrnoWarning("QWindowsXPStylePrivate::buffer(%dx%d), CreateDIBSection() failed.", w, h);
+ bufferW = 0;
+ bufferH = 0;
+ return 0;
+ }
+ if (Q_UNLIKELY(!bufferPixels)) {
+ qErrnoWarning("QWindowsXPStylePrivate::buffer(%dx%d), CreateDIBSection() did not allocate pixel data.", w, h);
+ bufferW = 0;
+ bufferH = 0;
+ return 0;
+ }
+ bufferW = w;
+ bufferH = h;
+#ifdef DEBUG_XP_STYLE
+ qDebug("Creating new dib section (%d, %d)", w, h);
+#endif
+ return bufferBitmap;
+}
+
+/*! \internal
+ Returns \c true if the part contains any transparency at all. This does
+ not indicate what kind of transparency we're dealing with. It can be
+ - Alpha transparency
+ - Masked transparency
+*/
+bool QWindowsXPStylePrivate::isTransparent(XPThemeData &themeData)
+{
+ return IsThemeBackgroundPartiallyTransparent(themeData.handle(), themeData.partId,
+ themeData.stateId);
+}
+
+
+/*! \internal
+ Returns a QRegion of the region of the part
+*/
+QRegion QWindowsXPStylePrivate::region(XPThemeData &themeData)
+{
+ HRGN hRgn = 0;
+ RECT rect = themeData.toRECT(themeData.rect);
+ if (!SUCCEEDED(GetThemeBackgroundRegion(themeData.handle(), bufferHDC(), themeData.partId,
+ themeData.stateId, &rect, &hRgn))) {
+ return QRegion();
+ }
+
+ HRGN dest = CreateRectRgn(0, 0, 0, 0);
+ const bool success = CombineRgn(dest, hRgn, 0, RGN_COPY) != ERROR;
+
+ QRegion region;
+
+ if (success) {
+ int numBytes = GetRegionData(dest, 0, 0);
+ if (numBytes == 0)
+ return QRegion();
+
+ char *buf = new char[numBytes];
+ if (buf == 0)
+ return QRegion();
+
+ RGNDATA *rd = reinterpret_cast<RGNDATA*>(buf);
+ if (GetRegionData(dest, numBytes, rd) == 0) {
+ delete [] buf;
+ return QRegion();
+ }
+
+ RECT *r = reinterpret_cast<RECT*>(rd->Buffer);
+ for (uint i = 0; i < rd->rdh.nCount; ++i) {
+ QRect rect;
+ rect.setCoords(r->left, r->top, r->right - 1, r->bottom - 1);
+ ++r;
+ region |= rect;
+ }
+
+ delete [] buf;
+ }
+
+ DeleteObject(hRgn);
+ DeleteObject(dest);
+
+ return region;
+}
+
+/*! \internal
+ Returns \c true if the native doublebuffer contains pixels with
+ varying alpha value.
+*/
+bool QWindowsXPStylePrivate::hasAlphaChannel(const QRect &rect)
+{
+ const int startX = rect.left();
+ const int startY = rect.top();
+ const int w = rect.width();
+ const int h = rect.height();
+
+ int firstAlpha = -1;
+ for (int y = startY; y < h/2; ++y) {
+ DWORD *buffer = (DWORD*)bufferPixels + (y * bufferW);
+ for (int x = startX; x < w; ++x, ++buffer) {
+ int alpha = (*buffer) >> 24;
+ if (firstAlpha == -1)
+ firstAlpha = alpha;
+ else if (alpha != firstAlpha)
+ return true;
+ }
+ }
+ return false;
+}
+
+/*! \internal
+ When the theme engine paints both a true alpha pixmap and a glyph
+ into our buffer, the glyph might not contain a proper alpha value.
+ The rule of thumb for premultiplied pixmaps is that the color
+ values of a pixel can never be higher than the alpha values, so
+ we use this to our advantage here, and fix all instances where
+ this occures.
+*/
+bool QWindowsXPStylePrivate::fixAlphaChannel(const QRect &rect)
+{
+ const int startX = rect.left();
+ const int startY = rect.top();
+ const int w = rect.width();
+ const int h = rect.height();
+ bool hasFixedAlphaValue = false;
+
+ for (int y = startY; y < h; ++y) {
+ DWORD *buffer = (DWORD*)bufferPixels + (y * bufferW);
+ for (int x = startX; x < w; ++x, ++buffer) {
+ uint pixel = *buffer;
+ int alpha = qAlpha(pixel);
+ if (qRed(pixel) > alpha || qGreen(pixel) > alpha || qBlue(pixel) > alpha) {
+ *buffer |= 0xff000000;
+ hasFixedAlphaValue = true;
+ }
+ }
+ }
+ return hasFixedAlphaValue;
+}
+
+/*! \internal
+ Swaps the alpha values on certain pixels:
+ 0xFF?????? -> 0x00??????
+ 0x00?????? -> 0xFF??????
+ Used to determin the mask of a non-alpha transparent pixmap in
+ the native doublebuffer, and swap the alphas so we may paint
+ the image as a Premultiplied QImage with drawImage(), and obtain
+ the mask transparency.
+*/
+bool QWindowsXPStylePrivate::swapAlphaChannel(const QRect &rect, bool allPixels)
+{
+ const int startX = rect.left();
+ const int startY = rect.top();
+ const int w = rect.width();
+ const int h = rect.height();
+ bool valueChange = false;
+
+ // Flip the alphas, so that 255-alpha pixels are 0, and 0-alpha are 255.
+ for (int y = startY; y < h; ++y) {
+ DWORD *buffer = (DWORD*)bufferPixels + (y * bufferW);
+ for (int x = startX; x < w; ++x, ++buffer) {
+ if (allPixels) {
+ *buffer |= 0xFF000000;
+ continue;
+ }
+ unsigned int alphaValue = (*buffer) & 0xFF000000;
+ if (alphaValue == 0xFF000000) {
+ *buffer = 0;
+ valueChange = true;
+ } else if (alphaValue == 0) {
+ *buffer |= 0xFF000000;
+ valueChange = true;
+ }
+ }
+ }
+ return valueChange;
+}
+
+enum TransformType { SimpleTransform, HighDpiScalingTransform, ComplexTransform };
+
+static inline TransformType transformType(const QTransform &transform, qreal devicePixelRatio)
+{
+ if (transform.type() <= QTransform::TxTranslate)
+ return SimpleTransform;
+ if (transform.type() > QTransform::TxScale)
+ return ComplexTransform;
+ return qFuzzyCompare(transform.m11(), devicePixelRatio)
+ && qFuzzyCompare(transform.m22(), devicePixelRatio)
+ ? HighDpiScalingTransform : ComplexTransform;
+}
+
+// QTBUG-60571: Exclude known fully opaque theme parts which produce values
+// invalid in ARGB32_Premultiplied (for example, 0x00ffffff).
+static inline bool isFullyOpaque(const XPThemeData &themeData)
+{
+ return themeData.theme == QWindowsXPStylePrivate::TaskDialogTheme && themeData.partId == TDLG_PRIMARYPANEL;
+}
+
+/*! \internal
+ Main theme drawing function.
+ Determines the correct lowlevel drawing method depending on several
+ factors.
+ Use drawBackgroundThruNativeBuffer() if:
+ - Painter does not have an HDC
+ - Theme part is flipped (mirrored horizontally)
+ else use drawBackgroundDirectly().
+ \note drawBackgroundThruNativeBuffer() can return false for large
+ sizes due to buffer()/CreateDIBSection() failing.
+*/
+bool QWindowsXPStylePrivate::drawBackground(XPThemeData &themeData)
+{
+ if (themeData.rect.isEmpty())
+ return true;
+
+ QPainter *painter = themeData.painter;
+ Q_ASSERT_X(painter != 0, "QWindowsXPStylePrivate::drawBackground()", "Trying to draw a theme part without a painter");
+ if (!painter || !painter->isActive())
+ return false;
+
+ painter->save();
+
+ // Access paintDevice via engine since the painter may
+ // return the clip device which can still be a widget device in case of grabWidget().
+
+ bool translucentToplevel = false;
+ const QPaintDevice *paintDevice = painter->device();
+ const qreal aditionalDevicePixelRatio = themeData.widget ? themeData.widget->devicePixelRatioF() : qreal(1);
+ if (paintDevice->devType() == QInternal::Widget) {
+ const QWidget *window = static_cast<const QWidget *>(paintDevice)->window();
+ translucentToplevel = window->testAttribute(Qt::WA_TranslucentBackground);
+ }
+
+ const TransformType tt = transformType(painter->deviceTransform(), aditionalDevicePixelRatio);
+
+ bool canDrawDirectly = false;
+ if (themeData.widget && painter->opacity() == 1.0 && !themeData.rotate
+ && !isFullyOpaque(themeData)
+ && tt != ComplexTransform && !themeData.mirrorVertically
+ && !translucentToplevel) {
+ // Draw on backing store DC only for real widgets or backing store images.
+ const QPaintDevice *enginePaintDevice = painter->paintEngine()->paintDevice();
+ switch (enginePaintDevice->devType()) {
+ case QInternal::Widget:
+ canDrawDirectly = true;
+ break;
+ case QInternal::Image:
+ // Ensure the backing store has received as resize and is initialized.
+ if (QBackingStore *bs = backingStoreForWidget(themeData.widget))
+ if (bs->size().isValid() && bs->paintDevice() == enginePaintDevice)
+ canDrawDirectly = true;
+ }
+ }
+
+ const HDC dc = canDrawDirectly ? hdcForWidgetBackingStore(themeData.widget) : HDC(0);
+ const bool result = dc
+ ? drawBackgroundDirectly(dc, themeData, aditionalDevicePixelRatio)
+ : drawBackgroundThruNativeBuffer(themeData, aditionalDevicePixelRatio);
+ painter->restore();
+ return result;
+}
+
+static inline QRectF scaleRect(const QRectF &r, qreal factor)
+{
+ return r.isValid() && factor > 1
+ ? QRectF(r.topLeft() * factor, r.size() * factor)
+ : r;
+}
+
+static QRegion scaleRegion(const QRegion &region, qreal factor)
+{
+ if (region.isEmpty() || qFuzzyCompare(factor, qreal(1)))
+ return region;
+ QRegion result;
+ for (const QRect &rect : region)
+ result += QRectF(QPointF(rect.topLeft()) * factor, QSizeF(rect.size() * factor)).toRect();
+ return result;
+}
+
+/*! \internal
+ This function draws the theme parts directly to the paintengines HDC.
+ Do not use this if you need to perform other transformations on the
+ resulting data.
+*/
+bool QWindowsXPStylePrivate::drawBackgroundDirectly(HDC dc, XPThemeData &themeData, qreal additionalDevicePixelRatio)
+{
+ QPainter *painter = themeData.painter;
+
+ const QPointF redirectionDelta(painter->deviceMatrix().dx(), painter->deviceMatrix().dy());
+ const QRect area = scaleRect(QRectF(themeData.rect), additionalDevicePixelRatio).translated(redirectionDelta).toRect();
+
+ QRegion sysRgn = painter->paintEngine()->systemClip();
+ if (sysRgn.isEmpty())
+ sysRgn = area;
+ else
+ sysRgn &= area;
+ if (painter->hasClipping())
+ sysRgn &= scaleRegion(painter->clipRegion(), additionalDevicePixelRatio).translated(redirectionDelta.toPoint());
+ HRGN hrgn = qt_hrgn_from_qregion(sysRgn);
+ SelectClipRgn(dc, hrgn);
+
+#ifdef DEBUG_XP_STYLE
+ printf("---[ DIRECT PAINTING ]------------------> Name(%-10s) Part(%d) State(%d)\n",
+ qPrintable(themeData.name), themeData.partId, themeData.stateId);
+ showProperties(themeData);
+#endif
+
+ RECT drawRECT = themeData.toRECT(area);
+ DTBGOPTS drawOptions;
+ memset(&drawOptions, 0, sizeof(drawOptions));
+ drawOptions.dwSize = sizeof(drawOptions);
+ drawOptions.rcClip = themeData.toRECT(sysRgn.boundingRect());
+ drawOptions.dwFlags = DTBG_CLIPRECT
+ | (themeData.noBorder ? DTBG_OMITBORDER : 0)
+ | (themeData.noContent ? DTBG_OMITCONTENT : 0)
+ | (themeData.mirrorHorizontally ? DTBG_MIRRORDC : 0);
+
+ const HRESULT result = DrawThemeBackgroundEx(themeData.handle(), dc, themeData.partId, themeData.stateId, &(drawRECT), &drawOptions);
+ SelectClipRgn(dc, 0);
+ DeleteObject(hrgn);
+ return SUCCEEDED(result);
+}
+
+/*! \internal
+ This function uses a secondary Native doublebuffer for painting parts.
+ It should only be used when the painteengine doesn't provide a proper
+ HDC for direct painting (e.g. when doing a grabWidget(), painting to
+ other pixmaps etc), or when special transformations are needed (e.g.
+ flips (horizonal mirroring only, vertical are handled by the theme
+ engine).
+*/
+bool QWindowsXPStylePrivate::drawBackgroundThruNativeBuffer(XPThemeData &themeData,
+ qreal additionalDevicePixelRatio)
+{
+ QPainter *painter = themeData.painter;
+ QRectF rectF = scaleRect(QRectF(themeData.rect), additionalDevicePixelRatio);
+
+ if ((themeData.rotate + 90) % 180 == 0) { // Catch 90,270,etc.. degree flips.
+ rectF = QRectF(0, 0, rectF.height(), rectF.width());
+ }
+ rectF.moveTo(0, 0);
+ QRect rect = rectF.toRect();
+ int partId = themeData.partId;
+ int stateId = themeData.stateId;
+ int w = rect.width();
+ int h = rect.height();
+
+ // Values initialized later, either from cached values, or from function calls
+ AlphaChannelType alphaType = UnknownAlpha;
+ bool stateHasData = true; // We assume so;
+ bool hasAlpha = false;
+ bool partIsTransparent;
+ bool potentialInvalidAlpha;
+
+ QString pixmapCacheKey = QStringLiteral("$qt_xp_");
+ pixmapCacheKey.append(themeName(themeData.theme));
+ pixmapCacheKey.append(QLatin1Char('p'));
+ pixmapCacheKey.append(QString::number(partId));
+ pixmapCacheKey.append(QLatin1Char('s'));
+ pixmapCacheKey.append(QString::number(stateId));
+ pixmapCacheKey.append(QLatin1Char('s'));
+ pixmapCacheKey.append(themeData.noBorder ? QLatin1Char('0') : QLatin1Char('1'));
+ pixmapCacheKey.append(QLatin1Char('b'));
+ pixmapCacheKey.append(themeData.noContent ? QLatin1Char('0') : QLatin1Char('1'));
+ pixmapCacheKey.append(QString::number(w));
+ pixmapCacheKey.append(QLatin1Char('w'));
+ pixmapCacheKey.append(QString::number(h));
+ pixmapCacheKey.append(QLatin1Char('h'));
+ pixmapCacheKey.append(QString::number(additionalDevicePixelRatio));
+ pixmapCacheKey.append(QLatin1Char('d'));
+
+ QPixmap cachedPixmap;
+ ThemeMapKey key(themeData);
+ ThemeMapData data = alphaCache.value(key);
+
+ bool haveCachedPixmap = false;
+ bool isCached = data.dataValid;
+ if (isCached) {
+ partIsTransparent = data.partIsTransparent;
+ hasAlpha = data.hasAlphaChannel;
+ alphaType = data.alphaType;
+ potentialInvalidAlpha = data.hadInvalidAlpha;
+
+ haveCachedPixmap = QPixmapCache::find(pixmapCacheKey, cachedPixmap);
+
+#ifdef DEBUG_XP_STYLE
+ char buf[25];
+ ::sprintf(buf, "+ Pixmap(%3d, %3d) ]", w, h);
+ printf("---[ CACHED %s--------> Name(%-10s) Part(%d) State(%d)\n",
+ haveCachedPixmap ? buf : "]-------------------",
+ qPrintable(themeData.name), themeData.partId, themeData.stateId);
+#endif
+ } else {
+ // Not cached, so get values from Theme Engine
+ BOOL tmt_borderonly = false;
+ COLORREF tmt_transparentcolor = 0x0;
+ PROPERTYORIGIN proporigin = PO_NOTFOUND;
+ GetThemeBool(themeData.handle(), themeData.partId, themeData.stateId, TMT_BORDERONLY, &tmt_borderonly);
+ GetThemeColor(themeData.handle(), themeData.partId, themeData.stateId, TMT_TRANSPARENTCOLOR, &tmt_transparentcolor);
+ GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, TMT_CAPTIONMARGINS, &proporigin);
+
+ partIsTransparent = isTransparent(themeData);
+
+ potentialInvalidAlpha = false;
+ GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, TMT_GLYPHTYPE, &proporigin);
+ if (proporigin == PO_PART || proporigin == PO_STATE) {
+ int tmt_glyphtype = GT_NONE;
+ GetThemeEnumValue(themeData.handle(), themeData.partId, themeData.stateId, TMT_GLYPHTYPE, &tmt_glyphtype);
+ potentialInvalidAlpha = partIsTransparent && tmt_glyphtype == GT_IMAGEGLYPH;
+ }
+
+#ifdef DEBUG_XP_STYLE
+ printf("---[ NOT CACHED ]-----------------------> Name(%-10s) Part(%d) State(%d)\n",
+ qPrintable(themeData.name), themeData.partId, themeData.stateId);
+ printf("-->partIsTransparen = %d\n", partIsTransparent);
+ printf("-->potentialInvalidAlpha = %d\n", potentialInvalidAlpha);
+ showProperties(themeData);
+#endif
+ }
+ bool wasAlphaSwapped = false;
+ bool wasAlphaFixed = false;
+
+ // OLD PSDK Workaround ------------------------------------------------------------------------
+ // See if we need extra clipping for the older PSDK, which does
+ // not have a DrawThemeBackgroundEx function for DTGB_OMITBORDER
+ // and DTGB_OMITCONTENT
+ bool addBorderContentClipping = false;
+ QRegion extraClip;
+ QRect area = rect;
+ if (themeData.noBorder || themeData.noContent) {
+ extraClip = area;
+ // We are running on a system where the uxtheme.dll does not have
+ // the DrawThemeBackgroundEx function, so we need to clip away
+ // borders or contents manually.
+
+ int borderSize = 0;
+ PROPERTYORIGIN origin = PO_NOTFOUND;
+ GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, TMT_BORDERSIZE, &origin);
+ GetThemeInt(themeData.handle(), themeData.partId, themeData.stateId, TMT_BORDERSIZE, &borderSize);
+ borderSize *= additionalDevicePixelRatio;
+
+ // Clip away border region
+ if ((origin == PO_CLASS || origin == PO_PART || origin == PO_STATE) && borderSize > 0) {
+ if (themeData.noBorder) {
+ extraClip &= area;
+ area = area.adjusted(-borderSize, -borderSize, borderSize, borderSize);
+ }
+
+ // Clip away content region
+ if (themeData.noContent) {
+ QRegion content = area.adjusted(borderSize, borderSize, -borderSize, -borderSize);
+ extraClip ^= content;
+ }
+ }
+ addBorderContentClipping = (themeData.noBorder | themeData.noContent);
+ }
+
+ QImage img;
+ if (!haveCachedPixmap) { // If the pixmap is not cached, generate it! -------------------------
+ if (!buffer(w, h)) // Ensure a buffer of at least (w, h) in size
+ return false;
+ HDC dc = bufferHDC();
+
+ // Clear the buffer
+ if (alphaType != NoAlpha) {
+ // Consider have separate "memset" function for small chunks for more speedup
+ memset(bufferPixels, 0x00, bufferW * h * 4);
+ }
+
+ // Difference between area and rect
+ int dx = area.x() - rect.x();
+ int dy = area.y() - rect.y();
+
+ // Adjust so painting rect starts from Origo
+ rect.moveTo(0,0);
+ area.moveTo(dx,dy);
+ DTBGOPTS drawOptions;
+ drawOptions.dwSize = sizeof(drawOptions);
+ drawOptions.rcClip = themeData.toRECT(rect);
+ drawOptions.dwFlags = DTBG_CLIPRECT
+ | (themeData.noBorder ? DTBG_OMITBORDER : 0)
+ | (themeData.noContent ? DTBG_OMITCONTENT : 0);
+
+ // Drawing the part into the backing store
+ RECT wRect(themeData.toRECT(area));
+ DrawThemeBackgroundEx(themeData.handle(), dc, themeData.partId, themeData.stateId, &wRect, &drawOptions);
+
+ // If not cached, analyze the buffer data to figure
+ // out alpha type, and if it contains data
+ if (!isCached) {
+ // SHORTCUT: If the part's state has no data, cache it for NOOP later
+ if (!stateHasData) {
+ memset(&data, 0, sizeof(data));
+ data.dataValid = true;
+ alphaCache.insert(key, data);
+ return true;
+ }
+ hasAlpha = hasAlphaChannel(rect);
+ if (!hasAlpha && partIsTransparent)
+ potentialInvalidAlpha = true;
+#if defined(DEBUG_XP_STYLE) && 1
+ dumpNativeDIB(w, h);
+#endif
+ }
+
+ // Fix alpha values, if needed
+ if (potentialInvalidAlpha)
+ wasAlphaFixed = fixAlphaChannel(rect);
+
+ QImage::Format format;
+ if ((partIsTransparent && !wasAlphaSwapped) || (!partIsTransparent && hasAlpha)) {
+ format = QImage::Format_ARGB32_Premultiplied;
+ alphaType = RealAlpha;
+ } else if (wasAlphaSwapped) {
+ format = QImage::Format_ARGB32_Premultiplied;
+ alphaType = MaskAlpha;
+ } else {
+ format = QImage::Format_RGB32;
+ // The image data we got from the theme engine does not have any transparency,
+ // thus the alpha channel is set to 0.
+ // However, Format_RGB32 requires the alpha part to be set to 0xff, thus
+ // we must flip it from 0x00 to 0xff
+ swapAlphaChannel(rect, true);
+ alphaType = NoAlpha;
+ }
+#if defined(DEBUG_XP_STYLE) && 1
+ printf("Image format is: %s\n", alphaType == RealAlpha ? "Real Alpha" : alphaType == MaskAlpha ? "Masked Alpha" : "No Alpha");
+#endif
+ img = QImage(bufferPixels, bufferW, bufferH, format);
+ img.setDevicePixelRatio(additionalDevicePixelRatio);
+ }
+
+ // Blitting backing store
+ bool useRegion = partIsTransparent && !hasAlpha && !wasAlphaSwapped;
+
+ QRegion newRegion;
+ QRegion oldRegion;
+ if (useRegion) {
+ newRegion = region(themeData);
+ oldRegion = painter->clipRegion();
+ painter->setClipRegion(newRegion);
+#if defined(DEBUG_XP_STYLE) && 0
+ printf("Using region:\n");
+ for (const QRect &r : newRegion)
+ printf(" (%d, %d, %d, %d)\n", r.x(), r.y(), r.right(), r.bottom());
+#endif
+ }
+
+ if (addBorderContentClipping)
+ painter->setClipRegion(scaleRegion(extraClip, 1.0 / additionalDevicePixelRatio), Qt::IntersectClip);
+
+ if (!themeData.mirrorHorizontally && !themeData.mirrorVertically && !themeData.rotate) {
+ if (!haveCachedPixmap)
+ painter->drawImage(themeData.rect, img, rect);
+ else
+ painter->drawPixmap(themeData.rect, cachedPixmap);
+ } else {
+ // This is _slow_!
+ // Make a copy containing only the necessary data, and mirror
+ // on all wanted axes. Then draw the copy.
+ // If cached, the normal pixmap is cached, instead of caching
+ // all possible orientations for each part and state.
+ QImage imgCopy;
+ if (!haveCachedPixmap)
+ imgCopy = img.copy(rect);
+ else
+ imgCopy = cachedPixmap.toImage();
+
+ if (themeData.rotate) {
+ QMatrix rotMatrix;
+ rotMatrix.rotate(themeData.rotate);
+ imgCopy = imgCopy.transformed(rotMatrix);
+ }
+ if (themeData.mirrorHorizontally || themeData.mirrorVertically) {
+ imgCopy = imgCopy.mirrored(themeData.mirrorHorizontally, themeData.mirrorVertically);
+ }
+ painter->drawImage(themeData.rect,
+ imgCopy);
+ }
+
+ if (useRegion || addBorderContentClipping) {
+ if (oldRegion.isEmpty())
+ painter->setClipping(false);
+ else
+ painter->setClipRegion(oldRegion);
+ }
+
+ // Cache the pixmap to avoid expensive swapAlphaChannel() calls
+ if (!haveCachedPixmap && w && h) {
+ QPixmap pix = QPixmap::fromImage(img).copy(rect);
+ QPixmapCache::insert(pixmapCacheKey, pix);
+#ifdef DEBUG_XP_STYLE
+ printf("+++Adding pixmap to cache, size(%d, %d), wasAlphaSwapped(%d), wasAlphaFixed(%d), name(%s)\n",
+ w, h, wasAlphaSwapped, wasAlphaFixed, qPrintable(pixmapCacheKey));
+#endif
+ }
+
+ // Add to theme part cache
+ if (!isCached) {
+ memset(&data, 0, sizeof(data));
+ data.dataValid = true;
+ data.partIsTransparent = partIsTransparent;
+ data.alphaType = alphaType;
+ data.hasAlphaChannel = hasAlpha;
+ data.wasAlphaSwapped = wasAlphaSwapped;
+ data.hadInvalidAlpha = wasAlphaFixed;
+ alphaCache.insert(key, data);
+ }
+ return true;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+
+/*!
+ \class QWindowsXPStyle
+ \brief The QWindowsXPStyle class provides a Microsoft Windows XP-like look and feel.
+
+ \ingroup appearance
+ \inmodule QtWidgets
+ \internal
+
+ \warning This style is only available on the Windows XP platform
+ because it makes use of Windows XP's style engine.
+
+ Most of the functions are documented in the base classes
+ QWindowsStyle, QCommonStyle, and QStyle, but the
+ QWindowsXPStyle overloads of drawComplexControl(), drawControl(),
+ drawControlMask(), drawPrimitive(), proxy()->subControlRect(), and
+ sizeFromContents(), are documented here.
+
+ \image qwindowsxpstyle.png
+ \sa QMacStyle, QWindowsStyle, QFusionStyle
+*/
+
+/*!
+ Constructs a QWindowsStyle
+*/
+QWindowsXPStyle::QWindowsXPStyle()
+ : QWindowsStyle(*new QWindowsXPStylePrivate)
+{
+}
+
+/*!
+ Destroys the style.
+*/
+QWindowsXPStyle::~QWindowsXPStyle()
+{
+}
+
+/*! \reimp */
+void QWindowsXPStyle::unpolish(QApplication *app)
+{
+ QWindowsStyle::unpolish(app);
+}
+
+/*! \reimp */
+void QWindowsXPStyle::polish(QApplication *app)
+{
+ QWindowsStyle::polish(app);
+ if (!QWindowsXPStylePrivate::useXP())
+ return;
+}
+
+/*! \reimp */
+void QWindowsXPStyle::polish(QWidget *widget)
+{
+ QWindowsStyle::polish(widget);
+ if (!QWindowsXPStylePrivate::useXP())
+ return;
+
+ if (false
+#if QT_CONFIG(abstractbutton)
+ || qobject_cast<QAbstractButton*>(widget)
+#endif
+ || qobject_cast<QToolButton*>(widget)
+ || qobject_cast<QTabBar*>(widget)
+#if QT_CONFIG(combobox)
+ || qobject_cast<QComboBox*>(widget)
+#endif // QT_CONFIG(combobox)
+ || qobject_cast<QScrollBar*>(widget)
+ || qobject_cast<QSlider*>(widget)
+ || qobject_cast<QHeaderView*>(widget)
+#if QT_CONFIG(spinbox)
+ || qobject_cast<QAbstractSpinBox*>(widget)
+ || qobject_cast<QSpinBox*>(widget)
+#endif // QT_CONFIG(spinbox)
+ ) {
+ widget->setAttribute(Qt::WA_Hover);
+ }
+
+#if QT_CONFIG(rubberband)
+ if (qobject_cast<QRubberBand*>(widget)) {
+ widget->setWindowOpacity(0.6);
+ }
+#endif
+ if (qobject_cast<QStackedWidget*>(widget) &&
+ qobject_cast<QTabWidget*>(widget->parent()))
+ widget->parentWidget()->setAttribute(Qt::WA_ContentsPropagated);
+
+ Q_D(QWindowsXPStyle);
+ if (!d->hasInitColors) {
+ // Get text color for group box labels
+ COLORREF cref;
+ XPThemeData theme(widget, 0, QWindowsXPStylePrivate::ButtonTheme, 0, 0);
+ GetThemeColor(theme.handle(), BP_GROUPBOX, GBS_NORMAL, TMT_TEXTCOLOR, &cref);
+ d->groupBoxTextColor = qRgb(GetRValue(cref), GetGValue(cref), GetBValue(cref));
+ GetThemeColor(theme.handle(), BP_GROUPBOX, GBS_DISABLED, TMT_TEXTCOLOR, &cref);
+ d->groupBoxTextColorDisabled = qRgb(GetRValue(cref), GetGValue(cref), GetBValue(cref));
+ // Where does this color come from?
+ //GetThemeColor(theme.handle(), TKP_TICS, TSS_NORMAL, TMT_COLOR, &cref);
+ d->sliderTickColor = qRgb(165, 162, 148);
+ d->hasInitColors = true;
+ }
+}
+
+/*! \reimp */
+void QWindowsXPStyle::polish(QPalette &pal)
+{
+ QWindowsStyle::polish(pal);
+ pal.setBrush(QPalette::AlternateBase, pal.base().color().darker(110));
+}
+
+/*! \reimp */
+void QWindowsXPStyle::unpolish(QWidget *widget)
+{
+#if QT_CONFIG(rubberband)
+ if (qobject_cast<QRubberBand*>(widget)) {
+ widget->setWindowOpacity(1.0);
+ }
+#endif
+ Q_D(QWindowsXPStyle);
+ // Unpolish of widgets is the first thing that
+ // happens when a theme changes, or the theme
+ // engine is turned off. So we detect it here.
+ bool oldState = QWindowsXPStylePrivate::useXP();
+ bool newState = QWindowsXPStylePrivate::useXP(true);
+ if ((oldState != newState) && newState) {
+ d->cleanup(true);
+ d->init(true);
+ } else {
+ // Cleanup handle map, if just changing style,
+ // or turning it on. In both cases the values
+ // already in the map might be old (other style).
+ d->cleanupHandleMap();
+ }
+ if (false
+#if QT_CONFIG(abstractbutton)
+ || qobject_cast<QAbstractButton*>(widget)
+#endif
+ || qobject_cast<QToolButton*>(widget)
+ || qobject_cast<QTabBar*>(widget)
+#if QT_CONFIG(combobox)
+ || qobject_cast<QComboBox*>(widget)
+#endif // QT_CONFIG(combobox)
+ || qobject_cast<QScrollBar*>(widget)
+ || qobject_cast<QSlider*>(widget)
+ || qobject_cast<QHeaderView*>(widget)
+#if QT_CONFIG(spinbox)
+ || qobject_cast<QAbstractSpinBox*>(widget)
+ || qobject_cast<QSpinBox*>(widget)
+#endif // QT_CONFIG(spinbox)
+ ) {
+ widget->setAttribute(Qt::WA_Hover, false);
+ }
+ QWindowsStyle::unpolish(widget);
+}
+
+/*! \reimp */
+QRect QWindowsXPStyle::subElementRect(SubElement sr, const QStyleOption *option, const QWidget *widget) const
+{
+ if (!QWindowsXPStylePrivate::useXP()) {
+ return QWindowsStyle::subElementRect(sr, option, widget);
+ }
+
+ QRect rect(option->rect);
+ switch(sr) {
+ case SE_DockWidgetCloseButton:
+ case SE_DockWidgetFloatButton:
+ rect = QWindowsStyle::subElementRect(sr, option, widget);
+ return rect.translated(0, 1);
+ break;
+ case SE_TabWidgetTabContents:
+ if (qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option))
+ {
+ rect = QWindowsStyle::subElementRect(sr, option, widget);
+ if (sr == SE_TabWidgetTabContents) {
+ if (const QTabWidget *tabWidget = qobject_cast<const QTabWidget *>(widget)) {
+ if (tabWidget->documentMode())
+ break;
+ }
+
+ rect.adjust(0, 0, -2, -2);
+ }
+ }
+ break;
+ case SE_TabWidgetTabBar: {
+ rect = QWindowsStyle::subElementRect(sr, option, widget);
+ const QStyleOptionTabWidgetFrame *twfOption =
+ qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option);
+ if (twfOption && twfOption->direction == Qt::RightToLeft
+ && (twfOption->shape == QTabBar::RoundedNorth
+ || twfOption->shape == QTabBar::RoundedSouth))
+ {
+ QStyleOptionTab otherOption;
+ otherOption.shape = (twfOption->shape == QTabBar::RoundedNorth
+ ? QTabBar::RoundedEast : QTabBar::RoundedSouth);
+ int overlap = proxy()->pixelMetric(PM_TabBarBaseOverlap, &otherOption, widget);
+ int borderThickness = proxy()->pixelMetric(PM_DefaultFrameWidth, option, widget);
+ rect.adjust(-overlap + borderThickness, 0, -overlap + borderThickness, 0);
+ }
+ break;}
+
+ case SE_PushButtonContents:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) {
+ MARGINS borderSize;
+ if (widget) {
+ XPThemeData buttontheme(widget, 0, QWindowsXPStylePrivate::ButtonTheme);
+ HTHEME theme = buttontheme.handle();
+ if (theme) {
+ int stateId;
+ if (!(option->state & State_Enabled))
+ stateId = PBS_DISABLED;
+ else if (option->state & State_Sunken)
+ stateId = PBS_PRESSED;
+ else if (option->state & State_MouseOver)
+ stateId = PBS_HOT;
+ else if (btn->features & QStyleOptionButton::DefaultButton)
+ stateId = PBS_DEFAULTED;
+ else
+ stateId = PBS_NORMAL;
+
+ int border = proxy()->pixelMetric(PM_DefaultFrameWidth, btn, widget);
+ rect = option->rect.adjusted(border, border, -border, -border);
+
+ if (SUCCEEDED(GetThemeMargins(theme, NULL, BP_PUSHBUTTON, stateId, TMT_CONTENTMARGINS, NULL, &borderSize))) {
+ rect.adjust(borderSize.cxLeftWidth, borderSize.cyTopHeight,
+ -borderSize.cxRightWidth, -borderSize.cyBottomHeight);
+ rect = visualRect(option->direction, option->rect, rect);
+ }
+ }
+ }
+ }
+ break;
+ case SE_ProgressBarContents:
+ rect = QCommonStyle::subElementRect(SE_ProgressBarGroove, option, widget);
+ if (option->state & QStyle::State_Horizontal)
+ rect.adjust(4, 3, -4, -3);
+ else
+ rect.adjust(3, 2, -3, -2);
+ break;
+ default:
+ rect = QWindowsStyle::subElementRect(sr, option, widget);
+ }
+ return rect;
+}
+
+/*!
+ \reimp
+*/
+void QWindowsXPStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *option, QPainter *p,
+ const QWidget *widget) const
+{
+ QWindowsXPStylePrivate *d = const_cast<QWindowsXPStylePrivate*>(d_func());
+
+ if (!QWindowsXPStylePrivate::useXP()) {
+ QWindowsStyle::drawPrimitive(pe, option, p, widget);
+ return;
+ }
+
+ int themeNumber = -1;
+ int partId = 0;
+ int stateId = 0;
+ QRect rect = option->rect;
+ State flags = option->state;
+ bool hMirrored = false;
+ bool vMirrored = false;
+ bool noBorder = false;
+ bool noContent = false;
+ int rotate = 0;
+
+ switch (pe) {
+ case PE_FrameTabBarBase:
+ if (const QStyleOptionTabBarBase *tbb
+ = qstyleoption_cast<const QStyleOptionTabBarBase *>(option)) {
+ p->save();
+ switch (tbb->shape) {
+ case QTabBar::RoundedNorth:
+ p->setPen(QPen(tbb->palette.dark(), 0));
+ p->drawLine(tbb->rect.topLeft(), tbb->rect.topRight());
+ break;
+ case QTabBar::RoundedWest:
+ p->setPen(QPen(tbb->palette.dark(), 0));
+ p->drawLine(tbb->rect.left(), tbb->rect.top(), tbb->rect.left(), tbb->rect.bottom());
+ break;
+ case QTabBar::RoundedSouth:
+ p->setPen(QPen(tbb->palette.dark(), 0));
+ p->drawLine(tbb->rect.left(), tbb->rect.top(),
+ tbb->rect.right(), tbb->rect.top());
+ break;
+ case QTabBar::RoundedEast:
+ p->setPen(QPen(tbb->palette.dark(), 0));
+ p->drawLine(tbb->rect.topLeft(), tbb->rect.bottomLeft());
+ break;
+ case QTabBar::TriangularNorth:
+ case QTabBar::TriangularEast:
+ case QTabBar::TriangularWest:
+ case QTabBar::TriangularSouth:
+ p->restore();
+ QWindowsStyle::drawPrimitive(pe, option, p, widget);
+ return;
+ }
+ p->restore();
+ }
+ return;
+ case PE_PanelButtonBevel:
+ themeNumber = QWindowsXPStylePrivate::ButtonTheme;
+ partId = BP_PUSHBUTTON;
+ if (!(flags & State_Enabled))
+ stateId = PBS_DISABLED;
+ else if ((flags & State_Sunken) || (flags & State_On))
+ stateId = PBS_PRESSED;
+ else if (flags & State_MouseOver)
+ stateId = PBS_HOT;
+ //else if (flags & State_ButtonDefault)
+ // stateId = PBS_DEFAULTED;
+ else
+ stateId = PBS_NORMAL;
+ break;
+
+ case PE_PanelButtonTool:
+ if (widget && widget->inherits("QDockWidgetTitleButton")) {
+ if (const QWidget *dw = widget->parentWidget())
+ if (dw->isWindow())
+ return;
+ }
+ themeNumber = QWindowsXPStylePrivate::ToolBarTheme;
+ partId = TP_BUTTON;
+ if (!(flags & State_Enabled))
+ stateId = TS_DISABLED;
+ else if (flags & State_Sunken)
+ stateId = TS_PRESSED;
+ else if (flags & State_MouseOver)
+ stateId = flags & State_On ? TS_HOTCHECKED : TS_HOT;
+ else if (flags & State_On)
+ stateId = TS_CHECKED;
+ else if (!(flags & State_AutoRaise))
+ stateId = TS_HOT;
+ else
+ stateId = TS_NORMAL;
+ break;
+
+ case PE_IndicatorButtonDropDown:
+ themeNumber = QWindowsXPStylePrivate::ToolBarTheme;
+ partId = TP_SPLITBUTTONDROPDOWN;
+ if (!(flags & State_Enabled))
+ stateId = TS_DISABLED;
+ else if (flags & State_Sunken)
+ stateId = TS_PRESSED;
+ else if (flags & State_MouseOver)
+ stateId = flags & State_On ? TS_HOTCHECKED : TS_HOT;
+ else if (flags & State_On)
+ stateId = TS_CHECKED;
+ else if (!(flags & State_AutoRaise))
+ stateId = TS_HOT;
+ else
+ stateId = TS_NORMAL;
+ if (option->direction == Qt::RightToLeft)
+ hMirrored = true;
+ break;
+
+ case PE_IndicatorCheckBox:
+ themeNumber = QWindowsXPStylePrivate::ButtonTheme;
+ partId = BP_CHECKBOX;
+ if (!(flags & State_Enabled))
+ stateId = CBS_UNCHECKEDDISABLED;
+ else if (flags & State_Sunken)
+ stateId = CBS_UNCHECKEDPRESSED;
+ else if (flags & State_MouseOver)
+ stateId = CBS_UNCHECKEDHOT;
+ else
+ stateId = CBS_UNCHECKEDNORMAL;
+
+ if (flags & State_On)
+ stateId += CBS_CHECKEDNORMAL-1;
+ else if (flags & State_NoChange)
+ stateId += CBS_MIXEDNORMAL-1;
+
+ break;
+
+ case PE_IndicatorRadioButton:
+ themeNumber = QWindowsXPStylePrivate::ButtonTheme;
+ partId = BP_RADIOBUTTON;
+ if (!(flags & State_Enabled))
+ stateId = RBS_UNCHECKEDDISABLED;
+ else if (flags & State_Sunken)
+ stateId = RBS_UNCHECKEDPRESSED;
+ else if (flags & State_MouseOver)
+ stateId = RBS_UNCHECKEDHOT;
+ else
+ stateId = RBS_UNCHECKEDNORMAL;
+
+ if (flags & State_On)
+ stateId += RBS_CHECKEDNORMAL-1;
+ break;
+
+ case PE_IndicatorDockWidgetResizeHandle:
+ return;
+
+case PE_Frame:
+ {
+ if (flags & State_Raised)
+ return;
+ themeNumber = QWindowsXPStylePrivate::ListViewTheme;
+ partId = LVP_LISTGROUP;
+ XPThemeData theme(widget, 0, themeNumber, partId, 0);
+
+ if (!(flags & State_Enabled))
+ stateId = ETS_DISABLED;
+ else
+ stateId = ETS_NORMAL;
+ int fillType;
+ if (GetThemeEnumValue(theme.handle(), partId, stateId, TMT_BGTYPE, &fillType) == S_OK) {
+ if (fillType == BT_BORDERFILL) {
+ COLORREF bcRef;
+ GetThemeColor(theme.handle(), partId, stateId, TMT_BORDERCOLOR, &bcRef);
+ QColor bordercolor(qRgb(GetRValue(bcRef), GetGValue(bcRef), GetBValue(bcRef)));
+ QPen oldPen = p->pen();
+ // int borderSize = 1;
+ // GetThemeInt(theme.handle(), partId, stateId, TMT_BORDERCOLOR, &borderSize);
+
+ // Inner white border
+ p->setPen(QPen(option->palette.base().color(), 0));
+ p->drawRect(QRectF(option->rect).adjusted(QStyleHelper::dpiScaled(0.5), QStyleHelper::dpiScaled(0.5),
+ QStyleHelper::dpiScaled(-1), QStyleHelper::dpiScaled(-1)));
+ // Outer dark border
+ p->setPen(QPen(bordercolor, 0));
+ p->drawRect(QRectF(option->rect).adjusted(0, 0, QStyleHelper::dpiScaled(-0.5), QStyleHelper::dpiScaled(-0.5)));
+ p->setPen(oldPen);
+ return;
+ } else if (fillType == BT_NONE) {
+ return;
+ }
+ }
+ break;
+ }
+ case PE_FrameLineEdit: {
+ // we try to check if this lineedit is a delegate on a QAbstractItemView-derived class.
+ if (QWindowsXPStylePrivate::isItemViewDelegateLineEdit(widget)) {
+ QPen oldPen = p->pen();
+ // Inner white border
+ p->setPen(QPen(option->palette.base().color(), 1));
+ p->drawRect(option->rect.adjusted(1, 1, -2, -2));
+ // Outer dark border
+ p->setPen(QPen(option->palette.shadow().color(), 1));
+ p->drawRect(option->rect.adjusted(0, 0, -1, -1));
+ p->setPen(oldPen);
+ return;
+ } else if (qstyleoption_cast<const QStyleOptionFrame *>(option)) {
+ themeNumber = QWindowsXPStylePrivate::EditTheme;
+ partId = EP_EDITTEXT;
+ noContent = true;
+ if (!(flags & State_Enabled))
+ stateId = ETS_DISABLED;
+ else
+ stateId = ETS_NORMAL;
+ }
+ break;
+ }
+
+ case PE_PanelLineEdit:
+ if (const QStyleOptionFrame *panel = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
+ themeNumber = QWindowsXPStylePrivate::EditTheme;
+ partId = EP_EDITTEXT;
+ noBorder = true;
+ bool isEnabled = flags & State_Enabled;
+
+ stateId = isEnabled ? ETS_NORMAL : ETS_DISABLED;
+
+ if (QWindowsXPStylePrivate::isLineEditBaseColorSet(option, widget)) {
+ p->fillRect(panel->rect, panel->palette.brush(QPalette::Base));
+ } else {
+ XPThemeData theme(0, p, themeNumber, partId, stateId, rect);
+ if (!theme.isValid()) {
+ QWindowsStyle::drawPrimitive(pe, option, p, widget);
+ return;
+ }
+ int bgType;
+ GetThemeEnumValue(theme.handle(), partId, stateId, TMT_BGTYPE, &bgType);
+ if( bgType == BT_IMAGEFILE ) {
+ theme.mirrorHorizontally = hMirrored;
+ theme.mirrorVertically = vMirrored;
+ theme.noBorder = noBorder;
+ theme.noContent = noContent;
+ theme.rotate = rotate;
+ d->drawBackground(theme);
+ } else {
+ QBrush fillColor = option->palette.brush(QPalette::Base);
+
+ if (!isEnabled) {
+ PROPERTYORIGIN origin = PO_NOTFOUND;
+ GetThemePropertyOrigin(theme.handle(), theme.partId, theme.stateId, TMT_FILLCOLOR, &origin);
+ // Use only if the fill property comes from our part
+ if ((origin == PO_PART || origin == PO_STATE)) {
+ COLORREF bgRef;
+ GetThemeColor(theme.handle(), partId, stateId, TMT_FILLCOLOR, &bgRef);
+ fillColor = QBrush(qRgb(GetRValue(bgRef), GetGValue(bgRef), GetBValue(bgRef)));
+ }
+ }
+ p->fillRect(option->rect, fillColor);
+ }
+ }
+
+ if (panel->lineWidth > 0)
+ proxy()->drawPrimitive(PE_FrameLineEdit, panel, p, widget);
+ return;
+ }
+ break;
+
+ case PE_FrameTabWidget:
+ if (const QStyleOptionTabWidgetFrame *tab = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option))
+ {
+ themeNumber = QWindowsXPStylePrivate::TabTheme;
+ partId = TABP_PANE;
+
+ if (widget) {
+ bool useGradient = true;
+ const int maxlength = 256;
+ wchar_t themeFileName[maxlength];
+ wchar_t themeColor[maxlength];
+ // Due to a a scaling issue with the XP Silver theme, tab gradients are not used with it
+ if (GetCurrentThemeName(themeFileName, maxlength, themeColor, maxlength, NULL, 0) == S_OK) {
+ wchar_t *offset = 0;
+ if ((offset = wcsrchr(themeFileName, QChar(QLatin1Char('\\')).unicode())) != NULL) {
+ offset++;
+ if (!lstrcmp(offset, L"Luna.msstyles") && !lstrcmp(offset, L"Metallic")) {
+ useGradient = false;
+ }
+ }
+ }
+ // This should work, but currently there's an error in the ::drawBackgroundDirectly()
+ // code, when using the HDC directly..
+ if (useGradient) {
+ QStyleOptionTabWidgetFrame frameOpt = *tab;
+ frameOpt.rect = widget->rect();
+ QRect contentsRect = subElementRect(SE_TabWidgetTabContents, &frameOpt, widget);
+ QRegion reg = option->rect;
+ reg -= contentsRect;
+ p->setClipRegion(reg);
+ XPThemeData theme(widget, p, themeNumber, partId, stateId, rect);
+ theme.mirrorHorizontally = hMirrored;
+ theme.mirrorVertically = vMirrored;
+ d->drawBackground(theme);
+ p->setClipRect(contentsRect);
+ partId = TABP_BODY;
+ }
+ }
+ switch (tab->shape) {
+ case QTabBar::RoundedNorth:
+ case QTabBar::TriangularNorth:
+ break;
+ case QTabBar::RoundedSouth:
+ case QTabBar::TriangularSouth:
+ vMirrored = true;
+ break;
+ case QTabBar::RoundedEast:
+ case QTabBar::TriangularEast:
+ rotate = 90;
+ break;
+ case QTabBar::RoundedWest:
+ case QTabBar::TriangularWest:
+ rotate = 90;
+ hMirrored = true;
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+
+ case PE_FrameMenu:
+ p->save();
+ p->setPen(option->palette.dark().color());
+ p->drawRect(rect.adjusted(0, 0, -1, -1));
+ p->restore();
+ return;
+
+ case PE_PanelMenuBar:
+ break;
+
+ case PE_FrameDockWidget:
+ if (const QStyleOptionFrame *frm = qstyleoption_cast<const QStyleOptionFrame *>(option))
+ {
+ themeNumber = QWindowsXPStylePrivate::WindowTheme;
+ if (flags & State_Active)
+ stateId = FS_ACTIVE;
+ else
+ stateId = FS_INACTIVE;
+
+ int fwidth = proxy()->pixelMetric(PM_DockWidgetFrameWidth, frm, widget);
+
+ XPThemeData theme(widget, p, themeNumber, 0, stateId);
+ if (!theme.isValid())
+ break;
+ theme.rect = QRect(frm->rect.x(), frm->rect.y(), frm->rect.x()+fwidth, frm->rect.height()-fwidth); theme.partId = WP_SMALLFRAMELEFT;
+ d->drawBackground(theme);
+ theme.rect = QRect(frm->rect.width()-fwidth, frm->rect.y(), fwidth, frm->rect.height()-fwidth);
+ theme.partId = WP_SMALLFRAMERIGHT;
+ d->drawBackground(theme);
+ theme.rect = QRect(frm->rect.x(), frm->rect.bottom()-fwidth+1, frm->rect.width(), fwidth);
+ theme.partId = WP_SMALLFRAMEBOTTOM;
+ d->drawBackground(theme);
+ return;
+ }
+ break;
+
+ case PE_IndicatorHeaderArrow:
+ {
+#if 0 // XP theme engine doesn't know about this :(
+ name = QWindowsXPStylePrivate::HeaderTheme;
+ partId = HP_HEADERSORTARROW;
+ if (flags & State_Down)
+ stateId = HSAS_SORTEDDOWN;
+ else
+ stateId = HSAS_SORTEDUP;
+#else
+ if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) {
+ p->save();
+ p->setPen(option->palette.dark().color());
+ p->translate(0, option->rect.height()/2 - 4);
+ if (header->sortIndicator & QStyleOptionHeader::SortUp) { // invert logic to follow Windows style guide
+ p->drawLine(option->rect.x(), option->rect.y(), option->rect.x()+8, option->rect.y());
+ p->drawLine(option->rect.x()+1, option->rect.y()+1, option->rect.x()+7, option->rect.y()+1);
+ p->drawLine(option->rect.x()+2, option->rect.y()+2, option->rect.x()+6, option->rect.y()+2);
+ p->drawLine(option->rect.x()+3, option->rect.y()+3, option->rect.x()+5, option->rect.y()+3);
+ p->drawPoint(option->rect.x()+4, option->rect.y()+4);
+ } else if(header->sortIndicator & QStyleOptionHeader::SortDown) {
+ p->drawLine(option->rect.x(), option->rect.y()+4, option->rect.x()+8, option->rect.y()+4);
+ p->drawLine(option->rect.x()+1, option->rect.y()+3, option->rect.x()+7, option->rect.y()+3);
+ p->drawLine(option->rect.x()+2, option->rect.y()+2, option->rect.x()+6, option->rect.y()+2);
+ p->drawLine(option->rect.x()+3, option->rect.y()+1, option->rect.x()+5, option->rect.y()+1);
+ p->drawPoint(option->rect.x()+4, option->rect.y());
+ }
+ p->restore();
+ return;
+ }
+#endif
+ }
+ break;
+
+ case PE_FrameStatusBarItem:
+ themeNumber = QWindowsXPStylePrivate::StatusTheme;
+ partId = SP_PANE;
+ break;
+
+ case PE_FrameGroupBox:
+ themeNumber = QWindowsXPStylePrivate::ButtonTheme;
+ partId = BP_GROUPBOX;
+ if (!(flags & State_Enabled))
+ stateId = GBS_DISABLED;
+ else
+ stateId = GBS_NORMAL;
+ if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
+ if (frame->features & QStyleOptionFrame::Flat) {
+ // Windows XP does not have a theme part for a flat GroupBox, paint it with the windows style
+ QRect fr = frame->rect;
+ QPoint p1(fr.x(), fr.y() + 1);
+ QPoint p2(fr.x() + fr.width(), p1.y() + 1);
+ rect = QRect(p1, p2);
+ themeNumber = -1;
+ }
+ }
+ break;
+
+ case PE_IndicatorProgressChunk:
+ {
+ Qt::Orientation orient = Qt::Horizontal;
+ bool inverted = false;
+ if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) {
+ orient = pb->orientation;
+ inverted = pb->invertedAppearance;
+ }
+ if (orient == Qt::Horizontal) {
+ partId = PP_CHUNK;
+ rect = QRect(option->rect.x(), option->rect.y(), option->rect.width(), option->rect.height() );
+ if (inverted && option->direction == Qt::LeftToRight)
+ hMirrored = true;
+ } else {
+ partId = PP_CHUNKVERT;
+ rect = QRect(option->rect.x(), option->rect.y(), option->rect.width(), option->rect.height());
+ }
+ themeNumber = QWindowsXPStylePrivate::ProgressTheme;
+ stateId = 1;
+ }
+ break;
+
+ case PE_FrameWindow:
+ if (const QStyleOptionFrame *frm = qstyleoption_cast<const QStyleOptionFrame *>(option))
+ {
+ themeNumber = QWindowsXPStylePrivate::WindowTheme;
+ if (flags & State_Active)
+ stateId = FS_ACTIVE;
+ else
+ stateId = FS_INACTIVE;
+
+ int fwidth = frm->lineWidth + frm->midLineWidth;
+
+ XPThemeData theme(0, p, themeNumber, 0, stateId);
+ if (!theme.isValid())
+ break;
+
+ // May fail due to too-large buffers for large widgets, fall back to Windows style.
+ theme.rect = QRect(option->rect.x(), option->rect.y()+fwidth, option->rect.x()+fwidth, option->rect.height()-fwidth);
+ theme.partId = WP_FRAMELEFT;
+ if (!d->drawBackground(theme)) {
+ QWindowsStyle::drawPrimitive(pe, option, p, widget);
+ return;
+ }
+ theme.rect = QRect(option->rect.width()-fwidth, option->rect.y()+fwidth, fwidth, option->rect.height()-fwidth);
+ theme.partId = WP_FRAMERIGHT;
+ if (!d->drawBackground(theme)) {
+ QWindowsStyle::drawPrimitive(pe, option, p, widget);
+ return;
+ }
+ theme.rect = QRect(option->rect.x(), option->rect.height()-fwidth, option->rect.width(), fwidth);
+ theme.partId = WP_FRAMEBOTTOM;
+ if (!d->drawBackground(theme)) {
+ QWindowsStyle::drawPrimitive(pe, option, p, widget);
+ return;
+ }
+ theme.rect = QRect(option->rect.x(), option->rect.y(), option->rect.width(), option->rect.y()+fwidth);
+ theme.partId = WP_CAPTION;
+ if (!d->drawBackground(theme))
+ QWindowsStyle::drawPrimitive(pe, option, p, widget);
+ return;
+ }
+ break;
+
+ case PE_IndicatorBranch:
+ {
+ static const int decoration_size = 9;
+ int mid_h = option->rect.x() + option->rect.width() / 2;
+ int mid_v = option->rect.y() + option->rect.height() / 2;
+ int bef_h = mid_h;
+ int bef_v = mid_v;
+ int aft_h = mid_h;
+ int aft_v = mid_v;
+ QBrush brush(option->palette.dark().color(), Qt::Dense4Pattern);
+ if (option->state & State_Item) {
+ if (option->direction == Qt::RightToLeft)
+ p->fillRect(option->rect.left(), mid_v, bef_h - option->rect.left(), 1, brush);
+ else
+ p->fillRect(aft_h, mid_v, option->rect.right() - aft_h + 1, 1, brush);
+ }
+ if (option->state & State_Sibling)
+ p->fillRect(mid_h, aft_v, 1, option->rect.bottom() - aft_v + 1, brush);
+ if (option->state & (State_Open | State_Children | State_Item | State_Sibling))
+ p->fillRect(mid_h, option->rect.y(), 1, bef_v - option->rect.y(), brush);
+ if (option->state & State_Children) {
+ int delta = decoration_size / 2;
+ bef_h -= delta;
+ bef_v -= delta;
+ aft_h += delta;
+ aft_v += delta;
+ XPThemeData theme(0, p, QWindowsXPStylePrivate::XpTreeViewTheme);
+ theme.rect = QRect(bef_h, bef_v, decoration_size, decoration_size);
+ theme.partId = TVP_GLYPH;
+ theme.stateId = flags & QStyle::State_Open ? GLPS_OPENED : GLPS_CLOSED;
+ d->drawBackground(theme);
+ }
+ }
+ return;
+
+ case PE_IndicatorToolBarSeparator:
+ if (option->rect.height() < 3) {
+ // XP style requires a few pixels for the separator
+ // to be visible.
+ QWindowsStyle::drawPrimitive(pe, option, p, widget);
+ return;
+ }
+ themeNumber = QWindowsXPStylePrivate::ToolBarTheme;
+ partId = TP_SEPARATOR;
+
+ if (option->state & State_Horizontal)
+ partId = TP_SEPARATOR;
+ else
+ partId = TP_SEPARATORVERT;
+
+ break;
+
+ case PE_IndicatorToolBarHandle:
+
+ themeNumber = QWindowsXPStylePrivate::RebarTheme;
+ partId = RP_GRIPPER;
+ if (option->state & State_Horizontal) {
+ partId = RP_GRIPPER;
+ rect.adjust(0, 0, -2, 0);
+ }
+ else {
+ partId = RP_GRIPPERVERT;
+ rect.adjust(0, 0, 0, -2);
+ }
+ break;
+
+ case PE_IndicatorItemViewItemCheck: {
+ QStyleOptionButton button;
+ button.QStyleOption::operator=(*option);
+ button.state &= ~State_MouseOver;
+ proxy()->drawPrimitive(PE_IndicatorCheckBox, &button, p, widget);
+ return;
+ }
+
+ default:
+ break;
+ }
+
+ XPThemeData theme(widget, p, themeNumber, partId, stateId, rect);
+ if (!theme.isValid()) {
+ QWindowsStyle::drawPrimitive(pe, option, p, widget);
+ return;
+ }
+ theme.mirrorHorizontally = hMirrored;
+ theme.mirrorVertically = vMirrored;
+ theme.noBorder = noBorder;
+ theme.noContent = noContent;
+ theme.rotate = rotate;
+ d->drawBackground(theme);
+}
+
+/*!
+ \reimp
+*/
+void QWindowsXPStyle::drawControl(ControlElement element, const QStyleOption *option, QPainter *p,
+ const QWidget *widget) const
+{
+ QWindowsXPStylePrivate *d = const_cast<QWindowsXPStylePrivate*>(d_func());
+ if (!QWindowsXPStylePrivate::useXP()) {
+ QWindowsStyle::drawControl(element, option, p, widget);
+ return;
+ }
+
+ QRect rect(option->rect);
+ State flags = option->state;
+
+ int rotate = 0;
+ bool hMirrored = false;
+ bool vMirrored = false;
+
+ int themeNumber = -1;
+ int partId = 0;
+ int stateId = 0;
+ switch (element) {
+ case CE_SizeGrip:
+ {
+ themeNumber = QWindowsXPStylePrivate::StatusTheme;
+ partId = SP_GRIPPER;
+ XPThemeData theme(0, p, themeNumber, partId, 0);
+ QSize size = (theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize();
+ size.rheight()--;
+ if (const QStyleOptionSizeGrip *sg = qstyleoption_cast<const QStyleOptionSizeGrip *>(option)) {
+ switch (sg->corner) {
+ case Qt::BottomRightCorner:
+ rect = QRect(QPoint(rect.right() - size.width(), rect.bottom() - size.height()), size);
+ break;
+ case Qt::BottomLeftCorner:
+ rect = QRect(QPoint(rect.left() + 1, rect.bottom() - size.height()), size);
+ hMirrored = true;
+ break;
+ case Qt::TopRightCorner:
+ rect = QRect(QPoint(rect.right() - size.width(), rect.top() + 1), size);
+ vMirrored = true;
+ break;
+ case Qt::TopLeftCorner:
+ rect = QRect(rect.topLeft() + QPoint(1, 1), size);
+ hMirrored = vMirrored = true;
+ }
+ }
+ }
+ break;
+
+ case CE_HeaderSection:
+ themeNumber = QWindowsXPStylePrivate::HeaderTheme;
+ partId = HP_HEADERITEM;
+ if (flags & State_Sunken)
+ stateId = HIS_PRESSED;
+ else if (flags & State_MouseOver)
+ stateId = HIS_HOT;
+ else
+ stateId = HIS_NORMAL;
+ break;
+
+ case CE_Splitter:
+ p->eraseRect(option->rect);
+ return;
+
+ case CE_PushButtonBevel:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option))
+ {
+ themeNumber = QWindowsXPStylePrivate::ButtonTheme;
+ partId = BP_PUSHBUTTON;
+ bool justFlat = ((btn->features & QStyleOptionButton::Flat) && !(flags & (State_On|State_Sunken)))
+ || ((btn->features & QStyleOptionButton::CommandLinkButton)
+ && !(flags & State_MouseOver)
+ && !(btn->features & QStyleOptionButton::DefaultButton));
+ if (!(flags & State_Enabled) && !(btn->features & QStyleOptionButton::Flat))
+ stateId = PBS_DISABLED;
+ else if (justFlat)
+ ;
+ else if (flags & (State_Sunken | State_On))
+ stateId = PBS_PRESSED;
+ else if (flags & State_MouseOver)
+ stateId = PBS_HOT;
+ else if (btn->features & QStyleOptionButton::DefaultButton)
+ stateId = PBS_DEFAULTED;
+ else
+ stateId = PBS_NORMAL;
+
+ if (!justFlat) {
+ XPThemeData theme(widget, p, themeNumber, partId, stateId, rect);
+ d->drawBackground(theme);
+ }
+
+ if (btn->features & QStyleOptionButton::HasMenu) {
+ int mbiw = 0, mbih = 0;
+ XPThemeData theme(widget, 0,
+ QWindowsXPStylePrivate::ToolBarTheme,
+ TP_SPLITBUTTONDROPDOWN);
+ if (theme.isValid()) {
+ const QSize size = (theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize();
+ mbiw = size.width();
+ mbih = size.height();
+ }
+
+ QRect ir = btn->rect;
+ QStyleOptionButton newBtn = *btn;
+ newBtn.rect = QRect(ir.right() - mbiw - 1, 1 + (ir.height()/2) - (mbih/2), mbiw, mbih);
+ proxy()->drawPrimitive(PE_IndicatorArrowDown, &newBtn, p, widget);
+ }
+ return;
+ }
+ break;
+ case CE_TabBarTab:
+ if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option))
+ {
+ stateId = tab->state & State_Enabled ? TIS_NORMAL : TIS_DISABLED;
+ }
+ break;
+
+ case CE_TabBarTabShape:
+ if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option))
+ {
+ themeNumber = QWindowsXPStylePrivate::TabTheme;
+ bool isDisabled = !(tab->state & State_Enabled);
+ bool hasFocus = tab->state & State_HasFocus;
+ bool isHot = tab->state & State_MouseOver;
+ bool selected = tab->state & State_Selected;
+ bool lastTab = tab->position == QStyleOptionTab::End;
+ bool firstTab = tab->position == QStyleOptionTab::Beginning;
+ bool onlyOne = tab->position == QStyleOptionTab::OnlyOneTab;
+ bool leftAligned = proxy()->styleHint(SH_TabBar_Alignment, tab, widget) == Qt::AlignLeft;
+ bool centerAligned = proxy()->styleHint(SH_TabBar_Alignment, tab, widget) == Qt::AlignCenter;
+ int borderThickness = proxy()->pixelMetric(PM_DefaultFrameWidth, option, widget);
+ int tabOverlap = proxy()->pixelMetric(PM_TabBarTabOverlap, option, widget);
+
+ if (isDisabled)
+ stateId = TIS_DISABLED;
+ else if (selected)
+ stateId = TIS_SELECTED;
+ else if (hasFocus)
+ stateId = TIS_FOCUSED;
+ else if (isHot)
+ stateId = TIS_HOT;
+ else
+ stateId = TIS_NORMAL;
+
+ // Selecting proper part depending on position
+ if (firstTab || onlyOne) {
+ if (leftAligned) {
+ partId = TABP_TABITEMLEFTEDGE;
+ } else if (centerAligned) {
+ partId = TABP_TABITEM;
+ } else { // rightAligned
+ partId = TABP_TABITEMRIGHTEDGE;
+ }
+ } else {
+ partId = TABP_TABITEM;
+ }
+
+ if (tab->direction == Qt::RightToLeft
+ && (tab->shape == QTabBar::RoundedNorth
+ || tab->shape == QTabBar::RoundedSouth)) {
+ bool temp = firstTab;
+ firstTab = lastTab;
+ lastTab = temp;
+ }
+ bool begin = firstTab || onlyOne;
+ bool end = lastTab || onlyOne;
+ switch (tab->shape) {
+ case QTabBar::RoundedNorth:
+ if (selected)
+ rect.adjust(begin ? 0 : -tabOverlap, 0, end ? 0 : tabOverlap, borderThickness);
+ else
+ rect.adjust(begin? tabOverlap : 0, tabOverlap, end ? -tabOverlap : 0, 0);
+ break;
+ case QTabBar::RoundedSouth:
+ //vMirrored = true;
+ rotate = 180; // Not 100% correct, but works
+ if (selected)
+ rect.adjust(begin ? 0 : -tabOverlap , -borderThickness, end ? 0 : tabOverlap, 0);
+ else
+ rect.adjust(begin ? tabOverlap : 0, 0, end ? -tabOverlap : 0 , -tabOverlap);
+ break;
+ case QTabBar::RoundedEast:
+ rotate = 90;
+ if (selected) {
+ rect.adjust(-borderThickness, begin ? 0 : -tabOverlap, 0, end ? 0 : tabOverlap);
+ }else{
+ rect.adjust(0, begin ? tabOverlap : 0, -tabOverlap, end ? -tabOverlap : 0);
+ }
+ break;
+ case QTabBar::RoundedWest:
+ hMirrored = true;
+ rotate = 90;
+ if (selected) {
+ rect.adjust(0, begin ? 0 : -tabOverlap, borderThickness, end ? 0 : tabOverlap);
+ }else{
+ rect.adjust(tabOverlap, begin ? tabOverlap : 0, 0, end ? -tabOverlap : 0);
+ }
+ break;
+ default:
+ themeNumber = -1; // Do our own painting for triangular
+ break;
+ }
+
+ if (!selected) {
+ switch (tab->shape) {
+ case QTabBar::RoundedNorth:
+ rect.adjust(0,0, 0,-1);
+ break;
+ case QTabBar::RoundedSouth:
+ rect.adjust(0,1, 0,0);
+ break;
+ case QTabBar::RoundedEast:
+ rect.adjust( 1,0, 0,0);
+ break;
+ case QTabBar::RoundedWest:
+ rect.adjust(0,0, -1,0);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ break;
+
+ case CE_ProgressBarGroove:
+ {
+ Qt::Orientation orient = Qt::Horizontal;
+ if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(option))
+ orient = pb->orientation;
+ partId = (orient == Qt::Horizontal) ? PP_BAR : PP_BARVERT;
+ themeNumber = QWindowsXPStylePrivate::ProgressTheme;
+ stateId = 1;
+ }
+ break;
+
+ case CE_MenuEmptyArea:
+ case CE_MenuItem:
+ if (const QStyleOptionMenuItem *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(option))
+ {
+ int tab = menuitem->tabWidth;
+ bool dis = !(menuitem->state & State_Enabled);
+ bool act = menuitem->state & State_Selected;
+ bool checkable = menuitem->menuHasCheckableItems;
+ bool checked = checkable ? menuitem->checked : false;
+
+ // windows always has a check column, regardless whether we have an icon or not
+ int checkcol = qMax(menuitem->maxIconWidth, 12);
+
+ int x, y, w, h;
+ rect.getRect(&x, &y, &w, &h);
+
+ QBrush fill = menuitem->palette.brush(act ? QPalette::Highlight : QPalette::Button);
+ p->fillRect(rect, fill);
+
+ if (element == CE_MenuEmptyArea)
+ break;
+
+ // draw separator -------------------------------------------------
+ if (menuitem->menuItemType == QStyleOptionMenuItem::Separator) {
+ int yoff = y-1 + h / 2;
+ p->setPen(menuitem->palette.dark().color());
+ p->drawLine(x, yoff, x+w, yoff);
+ ++yoff;
+ p->setPen(menuitem->palette.light().color());
+ p->drawLine(x, yoff, x+w, yoff);
+ return;
+ }
+
+ int xpos = x;
+
+ // draw icon ------------------------------------------------------
+ if (!menuitem->icon.isNull()) {
+ QIcon::Mode mode = dis ? QIcon::Disabled : QIcon::Normal;
+ if (act && !dis)
+ mode = QIcon::Active;
+ QPixmap pixmap = checked ?
+ menuitem->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize, option, widget), mode, QIcon::On) :
+ menuitem->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize, option, widget), mode);
+ const int pixw = pixmap.width() / pixmap.devicePixelRatio();
+ const int pixh = pixmap.height() / pixmap.devicePixelRatio();
+ QRect iconRect(0, 0, pixw, pixh);
+ iconRect.moveCenter(QRect(xpos, y, checkcol, h).center());
+ QRect vIconRect = visualRect(option->direction, option->rect, iconRect);
+ p->setPen(menuitem->palette.text().color());
+ p->setBrush(Qt::NoBrush);
+ if (checked)
+ p->drawRect(vIconRect.adjusted(-1, -1, 0, 0));
+ p->drawPixmap(vIconRect.topLeft(), pixmap);
+
+ // draw checkmark -------------------------------------------------
+ } else if (checked) {
+ QStyleOptionMenuItem newMi = *menuitem;
+ newMi.state = State_None;
+ if (!dis)
+ newMi.state |= State_Enabled;
+ if (act)
+ newMi.state |= State_On;
+
+ QRect checkMarkRect = QRect(menuitem->rect.x() + windowsItemFrame,
+ menuitem->rect.y() + windowsItemFrame,
+ checkcol - 2 * windowsItemFrame,
+ menuitem->rect.height() - 2*windowsItemFrame);
+ newMi.rect = visualRect(option->direction, option->rect, checkMarkRect);
+ proxy()->drawPrimitive(PE_IndicatorMenuCheckMark, &newMi, p, widget);
+ }
+
+ QColor textColor = dis ? menuitem->palette.text().color() :
+ act ? menuitem->palette.highlightedText().color() : menuitem->palette.buttonText().color();
+ p->setPen(textColor);
+
+ // draw text ------------------------------------------------------
+ int xm = windowsItemFrame + checkcol + windowsItemHMargin;
+ xpos = menuitem->rect.x() + xm;
+ QRect textRect(xpos, y + windowsItemVMargin, w - xm - windowsRightBorder - tab + 1, h - 2 * windowsItemVMargin);
+ QRect vTextRect = visualRect(option->direction, option->rect, textRect);
+ QString s = menuitem->text;
+ if (!s.isEmpty()) {
+ p->save();
+ int t = s.indexOf(QLatin1Char('\t'));
+ int text_flags = Qt::AlignVCenter|Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine | Qt::AlignLeft;
+ if (!proxy()->styleHint(SH_UnderlineShortcut, menuitem, widget))
+ text_flags |= Qt::TextHideMnemonic;
+ // draw tab text ----------------
+ if (t >= 0) {
+ QRect vShortcutRect = visualRect(option->direction, option->rect, QRect(textRect.topRight(), menuitem->rect.bottomRight()));
+ if (dis && !act && proxy()->styleHint(SH_EtchDisabledText, option, widget)) {
+ p->setPen(menuitem->palette.light().color());
+ p->drawText(vShortcutRect.adjusted(1,1,1,1), text_flags, s.mid(t + 1));
+ p->setPen(textColor);
+ }
+ p->drawText(vShortcutRect, text_flags, s.mid(t + 1));
+ s = s.left(t);
+ }
+ QFont font = menuitem->font;
+ if (menuitem->menuItemType == QStyleOptionMenuItem::DefaultItem)
+ font.setBold(true);
+ p->setFont(font);
+ if (dis && !act && proxy()->styleHint(SH_EtchDisabledText, option, widget)) {
+ p->setPen(menuitem->palette.light().color());
+ p->drawText(vTextRect.adjusted(1,1,1,1), text_flags, s.left(t));
+ p->setPen(textColor);
+ }
+ p->drawText(vTextRect, text_flags, s);
+ p->restore();
+ }
+
+ // draw sub menu arrow --------------------------------------------
+ if (menuitem->menuItemType == QStyleOptionMenuItem::SubMenu) {
+ int dim = (h - 2) / 2;
+ PrimitiveElement arrow;
+ arrow = (option->direction == Qt::RightToLeft) ? PE_IndicatorArrowLeft : PE_IndicatorArrowRight;
+ xpos = x + w - windowsArrowHMargin - windowsItemFrame - dim;
+ QRect vSubMenuRect = visualRect(option->direction, option->rect, QRect(xpos, y + h / 2 - dim / 2, dim, dim));
+ QStyleOptionMenuItem newMI = *menuitem;
+ newMI.rect = vSubMenuRect;
+ newMI.state = dis ? State_None : State_Enabled;
+ if (act)
+ newMI.palette.setColor(QPalette::ButtonText, newMI.palette.highlightedText().color());
+ proxy()->drawPrimitive(arrow, &newMI, p, widget);
+ }
+ }
+ return;
+
+ case CE_MenuBarItem:
+ if (const QStyleOptionMenuItem *mbi = qstyleoption_cast<const QStyleOptionMenuItem *>(option))
+ {
+ if (mbi->menuItemType == QStyleOptionMenuItem::DefaultItem)
+ break;
+
+ bool act = mbi->state & State_Selected;
+ bool dis = !(mbi->state & State_Enabled);
+
+ QBrush fill = mbi->palette.brush(act ? QPalette::Highlight : QPalette::Button);
+ QPalette::ColorRole textRole = dis ? QPalette::Text:
+ act ? QPalette::HighlightedText : QPalette::ButtonText;
+ QPixmap pix = mbi->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize, option, widget), QIcon::Normal);
+
+ uint alignment = Qt::AlignCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine;
+ if (!proxy()->styleHint(SH_UnderlineShortcut, mbi, widget))
+ alignment |= Qt::TextHideMnemonic;
+
+ p->fillRect(rect, fill);
+ if (!pix.isNull())
+ drawItemPixmap(p, mbi->rect, alignment, pix);
+ else
+ drawItemText(p, mbi->rect, alignment, mbi->palette, mbi->state & State_Enabled, mbi->text, textRole);
+ }
+ return;
+#if QT_CONFIG(dockwidget)
+ case CE_DockWidgetTitle:
+ if (const QStyleOptionDockWidget *dwOpt = qstyleoption_cast<const QStyleOptionDockWidget *>(option))
+ {
+ int buttonMargin = 4;
+ int mw = proxy()->pixelMetric(QStyle::PM_DockWidgetTitleMargin, dwOpt, widget);
+ int fw = proxy()->pixelMetric(PM_DockWidgetFrameWidth, dwOpt, widget);
+ bool isFloating = widget && widget->isWindow();
+ bool isActive = dwOpt->state & State_Active;
+
+ const bool verticalTitleBar = dwOpt->verticalTitleBar;
+
+ if (verticalTitleBar) {
+ rect = rect.transposed();
+
+ p->translate(rect.left() - 1, rect.top() + rect.width());
+ p->rotate(-90);
+ p->translate(-rect.left() + 1, -rect.top());
+ }
+ QRect r = rect.adjusted(0, 2, -1, -3);
+ QRect titleRect = r;
+
+ if (dwOpt->closable) {
+ QSize sz = proxy()->standardIcon(QStyle::SP_TitleBarCloseButton, dwOpt, widget).actualSize(QSize(10, 10));
+ titleRect.adjust(0, 0, -sz.width() - mw - buttonMargin, 0);
+ }
+
+ if (dwOpt->floatable) {
+ QSize sz = proxy()->standardIcon(QStyle::SP_TitleBarMaxButton, dwOpt, widget).actualSize(QSize(10, 10));
+ titleRect.adjust(0, 0, -sz.width() - mw - buttonMargin, 0);
+ }
+
+ if (isFloating) {
+ titleRect.adjust(0, -fw, 0, 0);
+ if (widget != 0 && widget->windowIcon().cacheKey() != QApplication::windowIcon().cacheKey())
+ titleRect.adjust(titleRect.height() + mw, 0, 0, 0);
+ } else {
+ titleRect.adjust(mw, 0, 0, 0);
+ if (!dwOpt->floatable && !dwOpt->closable)
+ titleRect.adjust(0, 0, -mw, 0);
+ }
+
+ if (!verticalTitleBar)
+ titleRect = visualRect(dwOpt->direction, r, titleRect);
+
+ if (!isFloating) {
+ QPen oldPen = p->pen();
+ QString titleText = p->fontMetrics().elidedText(dwOpt->title, Qt::ElideRight, titleRect.width());
+ p->setPen(dwOpt->palette.color(QPalette::Dark));
+ p->drawRect(r);
+
+ if (!titleText.isEmpty()) {
+ drawItemText(p, titleRect,
+ Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic, dwOpt->palette,
+ dwOpt->state & State_Enabled, titleText,
+ QPalette::WindowText);
+ }
+
+ p->setPen(oldPen);
+ } else {
+ themeNumber = QWindowsXPStylePrivate::WindowTheme;
+ if (isActive)
+ stateId = CS_ACTIVE;
+ else
+ stateId = CS_INACTIVE;
+
+ int titleHeight = rect.height() - 2;
+ rect = rect.adjusted(-fw, -fw, fw, 0);
+
+ XPThemeData theme(widget, p, themeNumber, 0, stateId);
+ if (!theme.isValid())
+ break;
+
+ // Draw small type title bar
+ theme.rect = rect;
+ theme.partId = WP_SMALLCAPTION;
+ d->drawBackground(theme);
+
+ // Figure out maximal button space on title bar
+
+ QIcon ico = widget->windowIcon();
+ bool hasIcon = (ico.cacheKey() != QApplication::windowIcon().cacheKey());
+ if (hasIcon) {
+ QPixmap pxIco = ico.pixmap(titleHeight);
+ if (!verticalTitleBar && dwOpt->direction == Qt::RightToLeft)
+ p->drawPixmap(rect.width() - titleHeight - pxIco.width(), rect.bottom() - titleHeight - 2, pxIco);
+ else
+ p->drawPixmap(fw, rect.bottom() - titleHeight - 2, pxIco);
+ }
+ if (!dwOpt->title.isEmpty()) {
+ QPen oldPen = p->pen();
+ QFont oldFont = p->font();
+ QFont titleFont = oldFont;
+ titleFont.setBold(true);
+ p->setFont(titleFont);
+ QString titleText
+ = p->fontMetrics().elidedText(dwOpt->title, Qt::ElideRight, titleRect.width());
+
+ int result = TST_NONE;
+ GetThemeEnumValue(theme.handle(), WP_SMALLCAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWTYPE, &result);
+ if (result != TST_NONE) {
+ COLORREF textShadowRef;
+ GetThemeColor(theme.handle(), WP_SMALLCAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWCOLOR, &textShadowRef);
+ QColor textShadow = qRgb(GetRValue(textShadowRef), GetGValue(textShadowRef), GetBValue(textShadowRef));
+ p->setPen(textShadow);
+ drawItemText(p, titleRect.adjusted(1, 1, 1, 1),
+ Qt::AlignLeft | Qt::AlignBottom, dwOpt->palette,
+ dwOpt->state & State_Enabled, titleText);
+ }
+
+ COLORREF captionText = GetSysColor(isActive ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT);
+ QColor textColor = qRgb(GetRValue(captionText), GetGValue(captionText), GetBValue(captionText));
+ p->setPen(textColor);
+ drawItemText(p, titleRect,
+ Qt::AlignLeft | Qt::AlignBottom, dwOpt->palette,
+ dwOpt->state & State_Enabled, titleText);
+ p->setFont(oldFont);
+ p->setPen(oldPen);
+ }
+
+ }
+
+ return;
+ }
+ break;
+#endif // QT_CONFIG(dockwidget)
+#if QT_CONFIG(rubberband)
+ case CE_RubberBand:
+ if (qstyleoption_cast<const QStyleOptionRubberBand *>(option)) {
+ QColor highlight = option->palette.color(QPalette::Active, QPalette::Highlight);
+ p->save();
+ p->setPen(highlight.darker(120));
+ QColor dimHighlight(qMin(highlight.red()/2 + 110, 255),
+ qMin(highlight.green()/2 + 110, 255),
+ qMin(highlight.blue()/2 + 110, 255),
+ (widget && widget->isTopLevel())? 255 : 127);
+ p->setBrush(dimHighlight);
+ p->drawRect(option->rect.adjusted(0, 0, -1, -1));
+ p->restore();
+ return;
+ }
+ break;
+#endif // QT_CONFIG(rubberband)
+ case CE_HeaderEmptyArea:
+ if (option->state & State_Horizontal)
+ {
+ themeNumber = QWindowsXPStylePrivate::HeaderTheme;
+ stateId = HIS_NORMAL;
+ }
+ else {
+ QWindowsStyle::drawControl(CE_HeaderEmptyArea, option, p, widget);
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+
+ XPThemeData theme(widget, p, themeNumber, partId, stateId, rect);
+ if (!theme.isValid()) {
+ QWindowsStyle::drawControl(element, option, p, widget);
+ return;
+ }
+
+ theme.rotate = rotate;
+ theme.mirrorHorizontally = hMirrored;
+ theme.mirrorVertically = vMirrored;
+ d->drawBackground(theme);
+}
+
+QRect QWindowsXPStylePrivate::scrollBarGripperBounds(QStyle::State flags, const QWidget *widget, XPThemeData *theme)
+{
+ const bool horizontal = flags & QStyle::State_Horizontal;
+ const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(widget);
+ const QMargins contentsMargin =
+ (theme->margins(theme->rect, TMT_SIZINGMARGINS) * factor).toMargins();
+ theme->partId = horizontal ? SBP_GRIPPERHORZ : SBP_GRIPPERVERT;
+ const QSize size = (theme->size() * factor).toSize();
+
+ const int hSpace = theme->rect.width() - size.width();
+ const int vSpace = theme->rect.height() - size.height();
+ const bool sufficientSpace = (horizontal && hSpace > (contentsMargin.left() + contentsMargin.right()))
+ || vSpace > contentsMargin.top() + contentsMargin.bottom();
+ return sufficientSpace ? QRect(theme->rect.topLeft() + QPoint(hSpace, vSpace) / 2, size) : QRect();
+}
+
+/*!
+ \reimp
+*/
+void QWindowsXPStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *option,
+ QPainter *p, const QWidget *widget) const
+{
+ QWindowsXPStylePrivate *d = const_cast<QWindowsXPStylePrivate*>(d_func());
+
+ if (!QWindowsXPStylePrivate::useXP()) {
+ QWindowsStyle::drawComplexControl(cc, option, p, widget);
+ return;
+ }
+
+ State flags = option->state;
+ SubControls sub = option->subControls;
+ QRect r = option->rect;
+
+ int partId = 0;
+ int stateId = 0;
+ if (widget && widget->testAttribute(Qt::WA_UnderMouse) && widget->isActiveWindow())
+ flags |= State_MouseOver;
+
+ switch (cc) {
+#if QT_CONFIG(spinbox)
+ case CC_SpinBox:
+ if (const QStyleOptionSpinBox *sb = qstyleoption_cast<const QStyleOptionSpinBox *>(option))
+ {
+ XPThemeData theme(widget, p, QWindowsXPStylePrivate::SpinTheme);
+
+ if (sb->frame && (sub & SC_SpinBoxFrame)) {
+ partId = EP_EDITTEXT;
+ if (!(flags & State_Enabled))
+ stateId = ETS_DISABLED;
+ else if (flags & State_HasFocus)
+ stateId = ETS_FOCUSED;
+ else
+ stateId = ETS_NORMAL;
+
+ XPThemeData ftheme(widget, p, QWindowsXPStylePrivate::EditTheme,
+ partId, stateId, r);
+ ftheme.noContent = true;
+ d->drawBackground(ftheme);
+ }
+ if (sub & SC_SpinBoxUp) {
+ theme.rect = proxy()->subControlRect(CC_SpinBox, option, SC_SpinBoxUp, widget);
+ partId = SPNP_UP;
+ if (!(sb->stepEnabled & QAbstractSpinBox::StepUpEnabled) || !(flags & State_Enabled))
+ stateId = UPS_DISABLED;
+ else if (sb->activeSubControls == SC_SpinBoxUp && (sb->state & State_Sunken))
+ stateId = UPS_PRESSED;
+ else if (sb->activeSubControls == SC_SpinBoxUp && (sb->state & State_MouseOver))
+ stateId = UPS_HOT;
+ else
+ stateId = UPS_NORMAL;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ if (sub & SC_SpinBoxDown) {
+ theme.rect = proxy()->subControlRect(CC_SpinBox, option, SC_SpinBoxDown, widget);
+ partId = SPNP_DOWN;
+ if (!(sb->stepEnabled & QAbstractSpinBox::StepDownEnabled) || !(flags & State_Enabled))
+ stateId = DNS_DISABLED;
+ else if (sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_Sunken))
+ stateId = DNS_PRESSED;
+ else if (sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_MouseOver))
+ stateId = DNS_HOT;
+ else
+ stateId = DNS_NORMAL;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ }
+ break;
+#endif // QT_CONFIG(spinbox)
+#if QT_CONFIG(combobox)
+ case CC_ComboBox:
+ if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(option))
+ {
+ if (sub & SC_ComboBoxEditField) {
+ if (cmb->frame) {
+ partId = EP_EDITTEXT;
+ if (!(flags & State_Enabled))
+ stateId = ETS_DISABLED;
+ else if (flags & State_HasFocus)
+ stateId = ETS_FOCUSED;
+ else
+ stateId = ETS_NORMAL;
+ XPThemeData theme(widget, p, QWindowsXPStylePrivate::EditTheme, partId, stateId, r);
+ d->drawBackground(theme);
+ } else {
+ QBrush editBrush = cmb->palette.brush(QPalette::Base);
+ p->fillRect(option->rect, editBrush);
+ }
+ if (!cmb->editable) {
+ QRect re = proxy()->subControlRect(CC_ComboBox, option, SC_ComboBoxEditField, widget);
+ if (option->state & State_HasFocus) {
+ p->fillRect(re, option->palette.highlight());
+ p->setPen(option->palette.highlightedText().color());
+ p->setBackground(option->palette.highlight());
+ } else {
+ p->fillRect(re, option->palette.base());
+ p->setPen(option->palette.text().color());
+ p->setBackground(option->palette.base());
+ }
+ }
+ }
+
+ if (sub & SC_ComboBoxArrow) {
+ XPThemeData theme(widget, p, QWindowsXPStylePrivate::ComboboxTheme);
+ theme.rect = proxy()->subControlRect(CC_ComboBox, option, SC_ComboBoxArrow, widget);
+ partId = CP_DROPDOWNBUTTON;
+ if (!(flags & State_Enabled))
+ stateId = CBXS_DISABLED;
+ else if (cmb->activeSubControls == SC_ComboBoxArrow && (cmb->state & State_Sunken))
+ stateId = CBXS_PRESSED;
+ else if (cmb->activeSubControls == SC_ComboBoxArrow && (cmb->state & State_MouseOver))
+ stateId = CBXS_HOT;
+ else
+ stateId = CBXS_NORMAL;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ }
+ break;
+#endif // QT_CONFIG(combobox)
+ case CC_ScrollBar:
+ if (const QStyleOptionSlider *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(option))
+ {
+ XPThemeData theme(widget, p, QWindowsXPStylePrivate::ScrollBarTheme);
+ bool maxedOut = (scrollbar->maximum == scrollbar->minimum);
+ if (maxedOut)
+ flags &= ~State_Enabled;
+
+ bool isHorz = flags & State_Horizontal;
+ bool isRTL = option->direction == Qt::RightToLeft;
+ if (sub & SC_ScrollBarAddLine) {
+ theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddLine, widget);
+ partId = SBP_ARROWBTN;
+ if (!(flags & State_Enabled))
+ stateId = (isHorz ? (isRTL ? ABS_LEFTDISABLED : ABS_RIGHTDISABLED) : ABS_DOWNDISABLED);
+ else if (scrollbar->activeSubControls & SC_ScrollBarAddLine && (scrollbar->state & State_Sunken))
+ stateId = (isHorz ? (isRTL ? ABS_LEFTPRESSED : ABS_RIGHTPRESSED) : ABS_DOWNPRESSED);
+ else if (scrollbar->activeSubControls & SC_ScrollBarAddLine && (scrollbar->state & State_MouseOver))
+ stateId = (isHorz ? (isRTL ? ABS_LEFTHOT : ABS_RIGHTHOT) : ABS_DOWNHOT);
+ else
+ stateId = (isHorz ? (isRTL ? ABS_LEFTNORMAL : ABS_RIGHTNORMAL) : ABS_DOWNNORMAL);
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ if (sub & SC_ScrollBarSubLine) {
+ theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubLine, widget);
+ partId = SBP_ARROWBTN;
+ if (!(flags & State_Enabled))
+ stateId = (isHorz ? (isRTL ? ABS_RIGHTDISABLED : ABS_LEFTDISABLED) : ABS_UPDISABLED);
+ else if (scrollbar->activeSubControls & SC_ScrollBarSubLine && (scrollbar->state & State_Sunken))
+ stateId = (isHorz ? (isRTL ? ABS_RIGHTPRESSED : ABS_LEFTPRESSED) : ABS_UPPRESSED);
+ else if (scrollbar->activeSubControls & SC_ScrollBarSubLine && (scrollbar->state & State_MouseOver))
+ stateId = (isHorz ? (isRTL ? ABS_RIGHTHOT : ABS_LEFTHOT) : ABS_UPHOT);
+ else
+ stateId = (isHorz ? (isRTL ? ABS_RIGHTNORMAL : ABS_LEFTNORMAL) : ABS_UPNORMAL);
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ if (maxedOut) {
+ theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget);
+ theme.rect = theme.rect.united(proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubPage, widget));
+ theme.rect = theme.rect.united(proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddPage, widget));
+ partId = scrollbar->orientation == Qt::Horizontal ? SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT;
+ stateId = SCRBS_DISABLED;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ } else {
+ if (sub & SC_ScrollBarSubPage) {
+ theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubPage, widget);
+ partId = flags & State_Horizontal ? SBP_UPPERTRACKHORZ : SBP_UPPERTRACKVERT;
+ if (!(flags & State_Enabled))
+ stateId = SCRBS_DISABLED;
+ else if (scrollbar->activeSubControls & SC_ScrollBarSubPage && (scrollbar->state & State_Sunken))
+ stateId = SCRBS_PRESSED;
+ else if (scrollbar->activeSubControls & SC_ScrollBarSubPage && (scrollbar->state & State_MouseOver))
+ stateId = SCRBS_HOT;
+ else
+ stateId = SCRBS_NORMAL;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ if (sub & SC_ScrollBarAddPage) {
+ theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddPage, widget);
+ partId = flags & State_Horizontal ? SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT;
+ if (!(flags & State_Enabled))
+ stateId = SCRBS_DISABLED;
+ else if (scrollbar->activeSubControls & SC_ScrollBarAddPage && (scrollbar->state & State_Sunken))
+ stateId = SCRBS_PRESSED;
+ else if (scrollbar->activeSubControls & SC_ScrollBarAddPage && (scrollbar->state & State_MouseOver))
+ stateId = SCRBS_HOT;
+ else
+ stateId = SCRBS_NORMAL;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ if (sub & SC_ScrollBarSlider) {
+ theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget);
+ if (!(flags & State_Enabled))
+ stateId = SCRBS_DISABLED;
+ else if (scrollbar->activeSubControls & SC_ScrollBarSlider && (scrollbar->state & State_Sunken))
+ stateId = SCRBS_PRESSED;
+ else if (scrollbar->activeSubControls & SC_ScrollBarSlider && (scrollbar->state & State_MouseOver))
+ stateId = SCRBS_HOT;
+ else
+ stateId = SCRBS_NORMAL;
+
+ // Draw handle
+ theme.partId = flags & State_Horizontal ? SBP_THUMBBTNHORZ : SBP_THUMBBTNVERT;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+
+ const QRect gripperBounds = QWindowsXPStylePrivate::scrollBarGripperBounds(flags, widget, &theme);
+ // Draw gripper if there is enough space
+ if (!gripperBounds.isEmpty()) {
+ p->save();
+ theme.rect = gripperBounds;
+ p->setClipRegion(d->region(theme));// Only change inside the region of the gripper
+ d->drawBackground(theme); // Transparent gripper ontop of background
+ p->restore();
+ }
+ }
+ }
+ }
+ break;
+
+#if QT_CONFIG(slider)
+ case CC_Slider:
+ if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option))
+ {
+ XPThemeData theme(widget, p, QWindowsXPStylePrivate::TrackBarTheme);
+ QRect slrect = slider->rect;
+ QRegion tickreg = slrect;
+ if (sub & SC_SliderGroove) {
+ theme.rect = proxy()->subControlRect(CC_Slider, option, SC_SliderGroove, widget);
+ if (slider->orientation == Qt::Horizontal) {
+ partId = TKP_TRACK;
+ stateId = TRS_NORMAL;
+ theme.rect = QRect(slrect.left(), theme.rect.center().y() - 2, slrect.width(), 4);
+ } else {
+ partId = TKP_TRACKVERT;
+ stateId = TRVS_NORMAL;
+ theme.rect = QRect(theme.rect.center().x() - 2, slrect.top(), 4, slrect.height());
+ }
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ tickreg -= theme.rect;
+ }
+ if (sub & SC_SliderTickmarks) {
+ int tickOffset = proxy()->pixelMetric(PM_SliderTickmarkOffset, slider, widget);
+ int ticks = slider->tickPosition;
+ int thickness = proxy()->pixelMetric(PM_SliderControlThickness, slider, widget);
+ int len = proxy()->pixelMetric(PM_SliderLength, slider, widget);
+ int available = proxy()->pixelMetric(PM_SliderSpaceAvailable, slider, widget);
+ int interval = slider->tickInterval;
+ if (interval <= 0) {
+ interval = slider->singleStep;
+ if (QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, interval,
+ available)
+ - QStyle::sliderPositionFromValue(slider->minimum, slider->maximum,
+ 0, available) < 3)
+ interval = slider->pageStep;
+ }
+ if (!interval)
+ interval = 1;
+ int fudge = len / 2;
+ int pos;
+ int bothOffset = (ticks & QSlider::TicksAbove && ticks & QSlider::TicksBelow) ? 1 : 0;
+ p->setPen(d->sliderTickColor);
+ QVarLengthArray<QLine, 32> lines;
+ int v = slider->minimum;
+ while (v <= slider->maximum + 1) {
+ if (v == slider->maximum + 1 && interval == 1)
+ break;
+ const int v_ = qMin(v, slider->maximum);
+ int tickLength = (v_ == slider->minimum || v_ >= slider->maximum) ? 4 : 3;
+ pos = QStyle::sliderPositionFromValue(slider->minimum, slider->maximum,
+ v_, available) + fudge;
+ if (slider->orientation == Qt::Horizontal) {
+ if (ticks & QSlider::TicksAbove)
+ lines.append(QLine(pos, tickOffset - 1 - bothOffset,
+ pos, tickOffset - 1 - bothOffset - tickLength));
+
+ if (ticks & QSlider::TicksBelow)
+ lines.append(QLine(pos, tickOffset + thickness + bothOffset,
+ pos, tickOffset + thickness + bothOffset + tickLength));
+ } else {
+ if (ticks & QSlider::TicksAbove)
+ lines.append(QLine(tickOffset - 1 - bothOffset, pos,
+ tickOffset - 1 - bothOffset - tickLength, pos));
+
+ if (ticks & QSlider::TicksBelow)
+ lines.append(QLine(tickOffset + thickness + bothOffset, pos,
+ tickOffset + thickness + bothOffset + tickLength, pos));
+ }
+ // in the case where maximum is max int
+ int nextInterval = v + interval;
+ if (nextInterval < v)
+ break;
+ v = nextInterval;
+ }
+ if (lines.size() > 0) {
+ p->save();
+ p->translate(slrect.topLeft());
+ p->drawLines(lines.constData(), lines.size());
+ p->restore();
+ }
+ }
+ if (sub & SC_SliderHandle) {
+ theme.rect = proxy()->subControlRect(CC_Slider, option, SC_SliderHandle, widget);
+ if (slider->orientation == Qt::Horizontal) {
+ if (slider->tickPosition == QSlider::TicksAbove)
+ partId = TKP_THUMBTOP;
+ else if (slider->tickPosition == QSlider::TicksBelow)
+ partId = TKP_THUMBBOTTOM;
+ else
+ partId = TKP_THUMB;
+
+ if (!(slider->state & State_Enabled))
+ stateId = TUS_DISABLED;
+ else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_Sunken))
+ stateId = TUS_PRESSED;
+ else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_MouseOver))
+ stateId = TUS_HOT;
+ else if (flags & State_HasFocus)
+ stateId = TUS_FOCUSED;
+ else
+ stateId = TUS_NORMAL;
+ } else {
+ if (slider->tickPosition == QSlider::TicksLeft)
+ partId = TKP_THUMBLEFT;
+ else if (slider->tickPosition == QSlider::TicksRight)
+ partId = TKP_THUMBRIGHT;
+ else
+ partId = TKP_THUMBVERT;
+
+ if (!(slider->state & State_Enabled))
+ stateId = TUVS_DISABLED;
+ else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_Sunken))
+ stateId = TUVS_PRESSED;
+ else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_MouseOver))
+ stateId = TUVS_HOT;
+ else if (flags & State_HasFocus)
+ stateId = TUVS_FOCUSED;
+ else
+ stateId = TUVS_NORMAL;
+ }
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ if (slider->state & State_HasFocus) {
+ QStyleOptionFocusRect fropt;
+ fropt.QStyleOption::operator=(*slider);
+ fropt.rect = subElementRect(SE_SliderFocusRect, slider, widget);
+ proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, p, widget);
+ }
+ }
+ break;
+#endif
+#if QT_CONFIG(toolbutton)
+ case CC_ToolButton:
+ if (const QStyleOptionToolButton *toolbutton
+ = qstyleoption_cast<const QStyleOptionToolButton *>(option)) {
+ QRect button, menuarea;
+ button = proxy()->subControlRect(cc, toolbutton, SC_ToolButton, widget);
+ menuarea = proxy()->subControlRect(cc, toolbutton, SC_ToolButtonMenu, widget);
+
+ State bflags = toolbutton->state & ~State_Sunken;
+ State mflags = bflags;
+ bool autoRaise = flags & State_AutoRaise;
+ if (autoRaise) {
+ if (!(bflags & State_MouseOver) || !(bflags & State_Enabled)) {
+ bflags &= ~State_Raised;
+ }
+ }
+
+ if (toolbutton->state & State_Sunken) {
+ if (toolbutton->activeSubControls & SC_ToolButton) {
+ bflags |= State_Sunken;
+ mflags |= State_MouseOver | State_Sunken;
+ } else if (toolbutton->activeSubControls & SC_ToolButtonMenu) {
+ mflags |= State_Sunken;
+ bflags |= State_MouseOver;
+ }
+ }
+
+ QStyleOption tool = *toolbutton;
+ if (toolbutton->subControls & SC_ToolButton) {
+ if (flags & (State_Sunken | State_On | State_Raised) || !autoRaise) {
+ if (toolbutton->features & QStyleOptionToolButton::MenuButtonPopup && autoRaise) {
+ XPThemeData theme(widget, p, QWindowsXPStylePrivate::ToolBarTheme);
+ theme.partId = TP_SPLITBUTTON;
+ theme.rect = button;
+ if (!(bflags & State_Enabled))
+ stateId = TS_DISABLED;
+ else if (bflags & State_Sunken)
+ stateId = TS_PRESSED;
+ else if (bflags & State_MouseOver || !(flags & State_AutoRaise))
+ stateId = flags & State_On ? TS_HOTCHECKED : TS_HOT;
+ else if (bflags & State_On)
+ stateId = TS_CHECKED;
+ else
+ stateId = TS_NORMAL;
+ if (option->direction == Qt::RightToLeft)
+ theme.mirrorHorizontally = true;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ } else {
+ tool.rect = option->rect;
+ tool.state = bflags;
+ if (autoRaise) // for tool bars
+ proxy()->drawPrimitive(PE_PanelButtonTool, &tool, p, widget);
+ else
+ proxy()->drawPrimitive(PE_PanelButtonBevel, &tool, p, widget);
+ }
+ }
+ }
+
+ if (toolbutton->state & State_HasFocus) {
+ QStyleOptionFocusRect fr;
+ fr.QStyleOption::operator=(*toolbutton);
+ fr.rect.adjust(3, 3, -3, -3);
+ if (toolbutton->features & QStyleOptionToolButton::MenuButtonPopup)
+ fr.rect.adjust(0, 0, -proxy()->pixelMetric(QStyle::PM_MenuButtonIndicator,
+ toolbutton, widget), 0);
+ proxy()->drawPrimitive(PE_FrameFocusRect, &fr, p, widget);
+ }
+ QStyleOptionToolButton label = *toolbutton;
+ label.state = bflags;
+ int fw = 2;
+ if (!autoRaise)
+ label.state &= ~State_Sunken;
+ label.rect = button.adjusted(fw, fw, -fw, -fw);
+ proxy()->drawControl(CE_ToolButtonLabel, &label, p, widget);
+
+ if (toolbutton->subControls & SC_ToolButtonMenu) {
+ tool.rect = menuarea;
+ tool.state = mflags;
+ if (autoRaise) {
+ proxy()->drawPrimitive(PE_IndicatorButtonDropDown, &tool, p, widget);
+ } else {
+ tool.state = mflags;
+ menuarea.adjust(-2, 0, 0, 0);
+ // Draw menu button
+ if ((bflags & State_Sunken) != (mflags & State_Sunken)){
+ p->save();
+ p->setClipRect(menuarea);
+ tool.rect = option->rect;
+ proxy()->drawPrimitive(PE_PanelButtonBevel, &tool, p, 0);
+ p->restore();
+ }
+ // Draw arrow
+ p->save();
+ p->setPen(option->palette.dark().color());
+ p->drawLine(menuarea.left(), menuarea.top() + 3,
+ menuarea.left(), menuarea.bottom() - 3);
+ p->setPen(option->palette.light().color());
+ p->drawLine(menuarea.left() - 1, menuarea.top() + 3,
+ menuarea.left() - 1, menuarea.bottom() - 3);
+
+ tool.rect = menuarea.adjusted(2, 3, -2, -1);
+ proxy()->drawPrimitive(PE_IndicatorArrowDown, &tool, p, widget);
+ p->restore();
+ }
+ } else if (toolbutton->features & QStyleOptionToolButton::HasMenu) {
+ int mbi = proxy()->pixelMetric(PM_MenuButtonIndicator, toolbutton, widget);
+ QRect ir = toolbutton->rect;
+ QStyleOptionToolButton newBtn = *toolbutton;
+ newBtn.rect = QRect(ir.right() + 4 - mbi, ir.height() - mbi + 4, mbi - 5, mbi - 5);
+ proxy()->drawPrimitive(PE_IndicatorArrowDown, &newBtn, p, widget);
+ }
+ }
+ break;
+#endif // QT_CONFIG(toolbutton)
+
+ case CC_TitleBar:
+ {
+ if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(option))
+ {
+ bool isActive = tb->titleBarState & QStyle::State_Active;
+ XPThemeData theme(widget, p, QWindowsXPStylePrivate::WindowTheme);
+ if (sub & SC_TitleBarLabel) {
+
+ partId = (tb->titleBarState & Qt::WindowMinimized) ? WP_MINCAPTION : WP_CAPTION;
+ theme.rect = option->rect;
+ if (widget && !widget->isEnabled())
+ stateId = CS_DISABLED;
+ else if (isActive)
+ stateId = CS_ACTIVE;
+ else
+ stateId = CS_INACTIVE;
+
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+
+ QRect ir = proxy()->subControlRect(CC_TitleBar, tb, SC_TitleBarLabel, widget);
+
+ int result = TST_NONE;
+ GetThemeEnumValue(theme.handle(), WP_CAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWTYPE, &result);
+ if (result != TST_NONE) {
+ COLORREF textShadowRef;
+ GetThemeColor(theme.handle(), WP_CAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWCOLOR, &textShadowRef);
+ QColor textShadow = qRgb(GetRValue(textShadowRef), GetGValue(textShadowRef), GetBValue(textShadowRef));
+ p->setPen(textShadow);
+ p->drawText(ir.x() + 3, ir.y() + 2, ir.width() - 1, ir.height(),
+ Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, tb->text);
+ }
+ COLORREF captionText = GetSysColor(isActive ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT);
+ QColor textColor = qRgb(GetRValue(captionText), GetGValue(captionText), GetBValue(captionText));
+ p->setPen(textColor);
+ p->drawText(ir.x() + 2, ir.y() + 1, ir.width() - 2, ir.height(),
+ Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, tb->text);
+ }
+ if (sub & SC_TitleBarSysMenu && tb->titleBarFlags & Qt::WindowSystemMenuHint) {
+ theme.rect = proxy()->subControlRect(CC_TitleBar, option, SC_TitleBarSysMenu, widget);
+ partId = WP_SYSBUTTON;
+ if ((widget && !widget->isEnabled()) || !isActive)
+ stateId = SBS_DISABLED;
+ else if (option->activeSubControls == SC_TitleBarSysMenu && (option->state & State_Sunken))
+ stateId = SBS_PUSHED;
+ else if (option->activeSubControls == SC_TitleBarSysMenu && (option->state & State_MouseOver))
+ stateId = SBS_HOT;
+ else
+ stateId = SBS_NORMAL;
+ if (!tb->icon.isNull()) {
+ tb->icon.paint(p, theme.rect);
+ } else {
+ theme.partId = partId;
+ theme.stateId = stateId;
+ if (theme.size().isEmpty()) {
+ int iconSize = proxy()->pixelMetric(PM_SmallIconSize, tb, widget);
+ QPixmap pm = proxy()->standardIcon(SP_TitleBarMenuButton, tb, widget).pixmap(iconSize, iconSize);
+ p->save();
+ drawItemPixmap(p, theme.rect, Qt::AlignCenter, pm);
+ p->restore();
+ } else {
+ d->drawBackground(theme);
+ }
+ }
+ }
+
+ if (sub & SC_TitleBarMinButton && tb->titleBarFlags & Qt::WindowMinimizeButtonHint
+ && !(tb->titleBarState & Qt::WindowMinimized)) {
+ theme.rect = proxy()->subControlRect(CC_TitleBar, option, SC_TitleBarMinButton, widget);
+ partId = WP_MINBUTTON;
+ if (widget && !widget->isEnabled())
+ stateId = MINBS_DISABLED;
+ else if (option->activeSubControls == SC_TitleBarMinButton && (option->state & State_Sunken))
+ stateId = MINBS_PUSHED;
+ else if (option->activeSubControls == SC_TitleBarMinButton && (option->state & State_MouseOver))
+ stateId = MINBS_HOT;
+ else if (!isActive)
+ stateId = MINBS_INACTIVE;
+ else
+ stateId = MINBS_NORMAL;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ if (sub & SC_TitleBarMaxButton && tb->titleBarFlags & Qt::WindowMaximizeButtonHint
+ && !(tb->titleBarState & Qt::WindowMaximized)) {
+ theme.rect = proxy()->subControlRect(CC_TitleBar, option, SC_TitleBarMaxButton, widget);
+ partId = WP_MAXBUTTON;
+ if (widget && !widget->isEnabled())
+ stateId = MAXBS_DISABLED;
+ else if (option->activeSubControls == SC_TitleBarMaxButton && (option->state & State_Sunken))
+ stateId = MAXBS_PUSHED;
+ else if (option->activeSubControls == SC_TitleBarMaxButton && (option->state & State_MouseOver))
+ stateId = MAXBS_HOT;
+ else if (!isActive)
+ stateId = MAXBS_INACTIVE;
+ else
+ stateId = MAXBS_NORMAL;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ if (sub & SC_TitleBarContextHelpButton
+ && tb->titleBarFlags & Qt::WindowContextHelpButtonHint) {
+ theme.rect = proxy()->subControlRect(CC_TitleBar, option, SC_TitleBarContextHelpButton, widget);
+ partId = WP_HELPBUTTON;
+ if (widget && !widget->isEnabled())
+ stateId = MINBS_DISABLED;
+ else if (option->activeSubControls == SC_TitleBarContextHelpButton && (option->state & State_Sunken))
+ stateId = MINBS_PUSHED;
+ else if (option->activeSubControls == SC_TitleBarContextHelpButton && (option->state & State_MouseOver))
+ stateId = MINBS_HOT;
+ else if (!isActive)
+ stateId = MINBS_INACTIVE;
+ else
+ stateId = MINBS_NORMAL;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ bool drawNormalButton = (sub & SC_TitleBarNormalButton)
+ && (((tb->titleBarFlags & Qt::WindowMinimizeButtonHint)
+ && (tb->titleBarState & Qt::WindowMinimized))
+ || ((tb->titleBarFlags & Qt::WindowMaximizeButtonHint)
+ && (tb->titleBarState & Qt::WindowMaximized)));
+ if (drawNormalButton) {
+ theme.rect = proxy()->subControlRect(CC_TitleBar, option, SC_TitleBarNormalButton, widget);
+ partId = WP_RESTOREBUTTON;
+ if (widget && !widget->isEnabled())
+ stateId = RBS_DISABLED;
+ else if (option->activeSubControls == SC_TitleBarNormalButton && (option->state & State_Sunken))
+ stateId = RBS_PUSHED;
+ else if (option->activeSubControls == SC_TitleBarNormalButton && (option->state & State_MouseOver))
+ stateId = RBS_HOT;
+ else if (!isActive)
+ stateId = RBS_INACTIVE;
+ else
+ stateId = RBS_NORMAL;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ if (sub & SC_TitleBarShadeButton && tb->titleBarFlags & Qt::WindowShadeButtonHint
+ && !(tb->titleBarState & Qt::WindowMinimized)) {
+ theme.rect = proxy()->subControlRect(CC_TitleBar, option, SC_TitleBarShadeButton, widget);
+ partId = WP_MINBUTTON;
+ if (widget && !widget->isEnabled())
+ stateId = MINBS_DISABLED;
+ else if (option->activeSubControls == SC_TitleBarShadeButton && (option->state & State_Sunken))
+ stateId = MINBS_PUSHED;
+ else if (option->activeSubControls == SC_TitleBarShadeButton && (option->state & State_MouseOver))
+ stateId = MINBS_HOT;
+ else if (!isActive)
+ stateId = MINBS_INACTIVE;
+ else
+ stateId = MINBS_NORMAL;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ if (sub & SC_TitleBarUnshadeButton && tb->titleBarFlags & Qt::WindowShadeButtonHint
+ && tb->titleBarState & Qt::WindowMinimized) {
+ theme.rect = proxy()->subControlRect(CC_TitleBar, option, SC_TitleBarUnshadeButton, widget);
+ partId = WP_RESTOREBUTTON;
+ if (widget && !widget->isEnabled())
+ stateId = RBS_DISABLED;
+ else if (option->activeSubControls == SC_TitleBarUnshadeButton && (option->state & State_Sunken))
+ stateId = RBS_PUSHED;
+ else if (option->activeSubControls == SC_TitleBarUnshadeButton && (option->state & State_MouseOver))
+ stateId = RBS_HOT;
+ else if (!isActive)
+ stateId = RBS_INACTIVE;
+ else
+ stateId = RBS_NORMAL;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ if (sub & SC_TitleBarCloseButton && tb->titleBarFlags & Qt::WindowSystemMenuHint) {
+ theme.rect = proxy()->subControlRect(CC_TitleBar, option, SC_TitleBarCloseButton, widget);
+ //partId = titlebar->testWFlags(Qt::WA_WState_Tool) ? WP_SMALLCLOSEBUTTON : WP_CLOSEBUTTON;
+ partId = WP_CLOSEBUTTON;
+ if (widget && !widget->isEnabled())
+ stateId = CBS_DISABLED;
+ else if (option->activeSubControls == SC_TitleBarCloseButton && (option->state & State_Sunken))
+ stateId = CBS_PUSHED;
+ else if (option->activeSubControls == SC_TitleBarCloseButton && (option->state & State_MouseOver))
+ stateId = CBS_HOT;
+ else if (!isActive)
+ stateId = CBS_INACTIVE;
+ else
+ stateId = CBS_NORMAL;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ }
+ }
+ break;
+
+#if QT_CONFIG(mdiarea)
+ case CC_MdiControls:
+ {
+ QRect buttonRect;
+ XPThemeData theme(widget, p, QWindowsXPStylePrivate::WindowTheme, WP_MDICLOSEBUTTON, CBS_NORMAL);
+
+ if (option->subControls & SC_MdiCloseButton) {
+ buttonRect = proxy()->subControlRect(CC_MdiControls, option, SC_MdiCloseButton, widget);
+ if (theme.isValid()) {
+ theme.partId = WP_MDICLOSEBUTTON;
+ theme.rect = buttonRect;
+ if (!(flags & State_Enabled))
+ theme.stateId = CBS_INACTIVE;
+ else if (flags & State_Sunken && (option->activeSubControls & SC_MdiCloseButton))
+ theme.stateId = CBS_PUSHED;
+ else if (flags & State_MouseOver && (option->activeSubControls & SC_MdiCloseButton))
+ theme.stateId = CBS_HOT;
+ else
+ theme.stateId = CBS_NORMAL;
+ d->drawBackground(theme);
+ }
+ }
+ if (option->subControls & SC_MdiNormalButton) {
+ buttonRect = proxy()->subControlRect(CC_MdiControls, option, SC_MdiNormalButton, widget);
+ if (theme.isValid()) {
+ theme.partId = WP_MDIRESTOREBUTTON;
+ theme.rect = buttonRect;
+ if (!(flags & State_Enabled))
+ theme.stateId = CBS_INACTIVE;
+ else if (flags & State_Sunken && (option->activeSubControls & SC_MdiNormalButton))
+ theme.stateId = CBS_PUSHED;
+ else if (flags & State_MouseOver && (option->activeSubControls & SC_MdiNormalButton))
+ theme.stateId = CBS_HOT;
+ else
+ theme.stateId = CBS_NORMAL;
+ d->drawBackground(theme);
+ }
+ }
+ if (option->subControls & QStyle::SC_MdiMinButton) {
+ buttonRect = proxy()->subControlRect(CC_MdiControls, option, SC_MdiMinButton, widget);
+ if (theme.isValid()) {
+ theme.partId = WP_MDIMINBUTTON;
+ theme.rect = buttonRect;
+ if (!(flags & State_Enabled))
+ theme.stateId = CBS_INACTIVE;
+ else if (flags & State_Sunken && (option->activeSubControls & SC_MdiMinButton))
+ theme.stateId = CBS_PUSHED;
+ else if (flags & State_MouseOver && (option->activeSubControls & SC_MdiMinButton))
+ theme.stateId = CBS_HOT;
+ else
+ theme.stateId = CBS_NORMAL;
+ d->drawBackground(theme);
+ }
+ }
+ }
+ break;
+#endif // QT_CONFIG(mdiarea)
+#if QT_CONFIG(dial)
+ case CC_Dial:
+ if (const QStyleOptionSlider *dial = qstyleoption_cast<const QStyleOptionSlider *>(option))
+ QStyleHelper::drawDial(dial, p);
+ break;
+#endif // QT_CONFIG(dial)
+ default:
+ QWindowsStyle::drawComplexControl(cc, option, p, widget);
+ break;
+ }
+}
+
+static inline Qt::Orientation progressBarOrientation(const QStyleOption *option = 0)
+{
+ if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(option))
+ return pb->orientation;
+ return Qt::Horizontal;
+}
+
+int QWindowsXPStylePrivate::pixelMetricFromSystemDp(QStyle::PixelMetric pm, const QStyleOption *option, const QWidget *widget)
+{
+ switch (pm) {
+ case QStyle::PM_IndicatorWidth:
+ return XPThemeData::themeSize(widget, 0, QWindowsXPStylePrivate::ButtonTheme, BP_CHECKBOX, CBS_UNCHECKEDNORMAL).width();
+ case QStyle::PM_IndicatorHeight:
+ return XPThemeData::themeSize(widget, 0, QWindowsXPStylePrivate::ButtonTheme, BP_CHECKBOX, CBS_UNCHECKEDNORMAL).height();
+ case QStyle::PM_ExclusiveIndicatorWidth:
+ return XPThemeData::themeSize(widget, 0, QWindowsXPStylePrivate::ButtonTheme, BP_RADIOBUTTON, RBS_UNCHECKEDNORMAL).width();
+ case QStyle::PM_ExclusiveIndicatorHeight:
+ return XPThemeData::themeSize(widget, 0, QWindowsXPStylePrivate::ButtonTheme, BP_RADIOBUTTON, RBS_UNCHECKEDNORMAL).height();
+ case QStyle::PM_ProgressBarChunkWidth:
+ return progressBarOrientation(option) == Qt::Horizontal
+ ? XPThemeData::themeSize(widget, 0, QWindowsXPStylePrivate::ProgressTheme, PP_CHUNK).width()
+ : XPThemeData::themeSize(widget, 0, QWindowsXPStylePrivate::ProgressTheme, PP_CHUNKVERT).height();
+ case QStyle::PM_SliderThickness:
+ return XPThemeData::themeSize(widget, 0, QWindowsXPStylePrivate::TrackBarTheme, TKP_THUMB).height();
+ case QStyle::PM_TitleBarHeight:
+ return widget && (widget->windowType() == Qt::Tool)
+ ? GetSystemMetrics(SM_CYSMCAPTION) + GetSystemMetrics(SM_CXSIZEFRAME)
+ : GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CXSIZEFRAME);
+ case QStyle::PM_MdiSubWindowFrameWidth:
+ return XPThemeData::themeSize(widget, 0, QWindowsXPStylePrivate::WindowTheme, WP_FRAMELEFT, FS_ACTIVE).width();
+ case QStyle::PM_DockWidgetFrameWidth:
+ return XPThemeData::themeSize(widget, 0, QWindowsXPStylePrivate::WindowTheme, WP_SMALLFRAMERIGHT, FS_ACTIVE).width();
+ default:
+ break;
+ }
+ return QWindowsXPStylePrivate::InvalidMetric;
+}
+
+/*! \reimp */
+int QWindowsXPStyle::pixelMetric(PixelMetric pm, const QStyleOption *option, const QWidget *widget) const
+{
+ if (!QWindowsXPStylePrivate::useXP())
+ return QWindowsStyle::pixelMetric(pm, option, widget);
+
+ int res = QWindowsXPStylePrivate::pixelMetricFromSystemDp(pm, option, widget);
+ if (res != QWindowsStylePrivate::InvalidMetric)
+ return qRound(qreal(res) * QWindowsStylePrivate::nativeMetricScaleFactor(widget));
+
+ res = 0;
+ switch (pm) {
+ case PM_MenuBarPanelWidth:
+ case PM_ButtonDefaultIndicator:
+ res = 0;
+ break;
+
+ case PM_DefaultFrameWidth:
+ res = qobject_cast<const QListView*>(widget) ? 2 : 1;
+ break;
+ case PM_MenuPanelWidth:
+ case PM_SpinBoxFrameWidth:
+ res = 1;
+ break;
+
+ case PM_TabBarTabOverlap:
+ case PM_MenuHMargin:
+ case PM_MenuVMargin:
+ res = 2;
+ break;
+
+ case PM_TabBarBaseOverlap:
+ if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) {
+ switch (tab->shape) {
+ case QTabBar::RoundedNorth:
+ case QTabBar::TriangularNorth:
+ case QTabBar::RoundedWest:
+ case QTabBar::TriangularWest:
+ res = 1;
+ break;
+ case QTabBar::RoundedSouth:
+ case QTabBar::TriangularSouth:
+ res = 2;
+ break;
+ case QTabBar::RoundedEast:
+ case QTabBar::TriangularEast:
+ res = 3;
+ break;
+ }
+ }
+ break;
+
+ case PM_SplitterWidth:
+ res = qMax(int(QStyleHelper::dpiScaled(5.)), QApplication::globalStrut().width());
+ break;
+
+ case PM_MdiSubWindowMinimizedWidth:
+ res = 160;
+ break;
+
+#ifndef QT_NO_TOOLBAR
+ case PM_ToolBarHandleExtent:
+ res = int(QStyleHelper::dpiScaled(8.));
+ break;
+
+#endif // QT_NO_TOOLBAR
+ case PM_DockWidgetSeparatorExtent:
+ case PM_DockWidgetTitleMargin:
+ res = int(QStyleHelper::dpiScaled(4.));
+ break;
+
+ case PM_ButtonShiftHorizontal:
+ case PM_ButtonShiftVertical:
+ res = qstyleoption_cast<const QStyleOptionToolButton *>(option) ? 1 : 0;
+ break;
+
+ default:
+ res = QWindowsStyle::pixelMetric(pm, option, widget);
+ }
+
+ return res;
+}
+
+/*
+ This function is used by subControlRect to check if a button
+ should be drawn for the given subControl given a set of window flags.
+*/
+static bool buttonVisible(const QStyle::SubControl sc, const QStyleOptionTitleBar *tb){
+
+ bool isMinimized = tb->titleBarState & Qt::WindowMinimized;
+ bool isMaximized = tb->titleBarState & Qt::WindowMaximized;
+ const uint flags = tb->titleBarFlags;
+ bool retVal = false;
+ switch (sc) {
+ case QStyle::SC_TitleBarContextHelpButton:
+ if (flags & Qt::WindowContextHelpButtonHint)
+ retVal = true;
+ break;
+ case QStyle::SC_TitleBarMinButton:
+ if (!isMinimized && (flags & Qt::WindowMinimizeButtonHint))
+ retVal = true;
+ break;
+ case QStyle::SC_TitleBarNormalButton:
+ if (isMinimized && (flags & Qt::WindowMinimizeButtonHint))
+ retVal = true;
+ else if (isMaximized && (flags & Qt::WindowMaximizeButtonHint))
+ retVal = true;
+ break;
+ case QStyle::SC_TitleBarMaxButton:
+ if (!isMaximized && (flags & Qt::WindowMaximizeButtonHint))
+ retVal = true;
+ break;
+ case QStyle::SC_TitleBarShadeButton:
+ if (!isMinimized && flags & Qt::WindowShadeButtonHint)
+ retVal = true;
+ break;
+ case QStyle::SC_TitleBarUnshadeButton:
+ if (isMinimized && flags & Qt::WindowShadeButtonHint)
+ retVal = true;
+ break;
+ case QStyle::SC_TitleBarCloseButton:
+ if (flags & Qt::WindowSystemMenuHint)
+ retVal = true;
+ break;
+ case QStyle::SC_TitleBarSysMenu:
+ if (flags & Qt::WindowSystemMenuHint)
+ retVal = true;
+ break;
+ default :
+ retVal = true;
+ }
+ return retVal;
+}
+
+/*!
+ \reimp
+*/
+QRect QWindowsXPStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex *option,
+ SubControl subControl, const QWidget *widget) const
+{
+ if (!QWindowsXPStylePrivate::useXP())
+ return QWindowsStyle::subControlRect(cc, option, subControl, widget);
+
+ QRect rect;
+
+ switch (cc) {
+ case CC_TitleBar:
+ if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(option)) {
+ if (!buttonVisible(subControl, tb))
+ return rect;
+ const bool isToolTitle = false;
+ const int height = tb->rect.height();
+ const int width = tb->rect.width();
+ const int buttonMargin = int(QStyleHelper::dpiScaled(4));
+ const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(widget);
+ int buttonHeight = qRound(qreal(GetSystemMetrics(SM_CYSIZE)) * factor)
+ - buttonMargin;
+ int buttonWidth = qRound(qreal(GetSystemMetrics(SM_CXSIZE)) * factor)
+ - buttonMargin;
+ const int delta = buttonWidth + 2;
+ int controlTop = option->rect.bottom() - buttonHeight - 2;
+ const int frameWidth = proxy()->pixelMetric(PM_MdiSubWindowFrameWidth, option, widget);
+ const bool sysmenuHint = (tb->titleBarFlags & Qt::WindowSystemMenuHint) != 0;
+ const bool minimizeHint = (tb->titleBarFlags & Qt::WindowMinimizeButtonHint) != 0;
+ const bool maximizeHint = (tb->titleBarFlags & Qt::WindowMaximizeButtonHint) != 0;
+ const bool contextHint = (tb->titleBarFlags & Qt::WindowContextHelpButtonHint) != 0;
+ const bool shadeHint = (tb->titleBarFlags & Qt::WindowShadeButtonHint) != 0;
+ bool isMinimized = tb->titleBarState & Qt::WindowMinimized;
+ bool isMaximized = tb->titleBarState & Qt::WindowMaximized;
+ int offset = 0;
+
+ switch (subControl) {
+ case SC_TitleBarLabel:
+ rect = QRect(frameWidth, 0, width - (buttonWidth + frameWidth + 10), height);
+ if (isToolTitle) {
+ if (sysmenuHint) {
+ rect.adjust(0, 0, -buttonWidth - 3, 0);
+ }
+ if (minimizeHint || maximizeHint)
+ rect.adjust(0, 0, -buttonWidth - 2, 0);
+ } else {
+ if (sysmenuHint) {
+ const int leftOffset = height - 8;
+ rect.adjust(leftOffset, 0, 0, 0);
+ }
+ if (minimizeHint)
+ rect.adjust(0, 0, -buttonWidth - 2, 0);
+ if (maximizeHint)
+ rect.adjust(0, 0, -buttonWidth - 2, 0);
+ if (contextHint)
+ rect.adjust(0, 0, -buttonWidth - 2, 0);
+ if (shadeHint)
+ rect.adjust(0, 0, -buttonWidth - 2, 0);
+ }
+ break;
+
+ case SC_TitleBarContextHelpButton:
+ if (tb->titleBarFlags & Qt::WindowContextHelpButtonHint)
+ offset += delta;
+ Q_FALLTHROUGH();
+ case SC_TitleBarMinButton:
+ if (!isMinimized && (tb->titleBarFlags & Qt::WindowMinimizeButtonHint))
+ offset += delta;
+ else if (subControl == SC_TitleBarMinButton)
+ break;
+ Q_FALLTHROUGH();
+ case SC_TitleBarNormalButton:
+ if (isMinimized && (tb->titleBarFlags & Qt::WindowMinimizeButtonHint))
+ offset += delta;
+ else if (isMaximized && (tb->titleBarFlags & Qt::WindowMaximizeButtonHint))
+ offset += delta;
+ else if (subControl == SC_TitleBarNormalButton)
+ break;
+ Q_FALLTHROUGH();
+ case SC_TitleBarMaxButton:
+ if (!isMaximized && (tb->titleBarFlags & Qt::WindowMaximizeButtonHint))
+ offset += delta;
+ else if (subControl == SC_TitleBarMaxButton)
+ break;
+ Q_FALLTHROUGH();
+ case SC_TitleBarShadeButton:
+ if (!isMinimized && (tb->titleBarFlags & Qt::WindowShadeButtonHint))
+ offset += delta;
+ else if (subControl == SC_TitleBarShadeButton)
+ break;
+ Q_FALLTHROUGH();
+ case SC_TitleBarUnshadeButton:
+ if (isMinimized && (tb->titleBarFlags & Qt::WindowShadeButtonHint))
+ offset += delta;
+ else if (subControl == SC_TitleBarUnshadeButton)
+ break;
+ Q_FALLTHROUGH();
+ case SC_TitleBarCloseButton:
+ if (tb->titleBarFlags & Qt::WindowSystemMenuHint)
+ offset += delta;
+ else if (subControl == SC_TitleBarCloseButton)
+ break;
+
+ rect.setRect(width - offset - controlTop + 1, controlTop,
+ buttonWidth, buttonHeight);
+ break;
+
+ case SC_TitleBarSysMenu:
+ {
+ const int controlTop = 6;
+ const int controlHeight = height - controlTop - 3;
+ const int iconExtent = proxy()->pixelMetric(PM_SmallIconSize);
+ QSize iconSize = tb->icon.actualSize(QSize(iconExtent, iconExtent));
+ if (tb->icon.isNull())
+ iconSize = QSize(controlHeight, controlHeight);
+ int hPad = (controlHeight - iconSize.height())/2;
+ int vPad = (controlHeight - iconSize.width())/2;
+ rect = QRect(frameWidth + hPad, controlTop + vPad, iconSize.width(), iconSize.height());
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+
+ case CC_ComboBox:
+ if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
+ const int x = cmb->rect.x(), y = cmb->rect.y(), wi = cmb->rect.width(), he = cmb->rect.height();
+ const int xpos = x + wi - qRound(QStyleHelper::dpiScaled(1 + 16));
+
+ switch (subControl) {
+ case SC_ComboBoxFrame:
+ rect = cmb->rect;
+ break;
+
+ case SC_ComboBoxArrow:
+ rect = QRect(xpos, y + qRound(QStyleHelper::dpiScaled(1)),
+ qRound(QStyleHelper::dpiScaled(16)), he - qRound(QStyleHelper::dpiScaled(2)));
+ break;
+
+ case SC_ComboBoxEditField:
+ rect = QRect(x + qRound(QStyleHelper::dpiScaled(2)), y + qRound(QStyleHelper::dpiScaled(2)),
+ wi - qRound(QStyleHelper::dpiScaled(3 + 16)), he - qRound(QStyleHelper::dpiScaled(4)));
+ break;
+
+ case SC_ComboBoxListBoxPopup:
+ rect = cmb->rect;
+ break;
+
+ default:
+ break;
+ }
+ }
+ break;
+#if QT_CONFIG(mdiarea)
+ case CC_MdiControls:
+ {
+ int numSubControls = 0;
+ if (option->subControls & SC_MdiCloseButton)
+ ++numSubControls;
+ if (option->subControls & SC_MdiMinButton)
+ ++numSubControls;
+ if (option->subControls & SC_MdiNormalButton)
+ ++numSubControls;
+ if (numSubControls == 0)
+ break;
+
+ int buttonWidth = option->rect.width() / numSubControls;
+ int offset = 0;
+ switch (subControl) {
+ case SC_MdiCloseButton:
+ // Only one sub control, no offset needed.
+ if (numSubControls == 1)
+ break;
+ offset += buttonWidth;
+ Q_FALLTHROUGH();
+ case SC_MdiNormalButton:
+ // No offset needed if
+ // 1) There's only one sub control
+ // 2) We have a close button and a normal button (offset already added in SC_MdiClose)
+ if (numSubControls == 1 || (numSubControls == 2 && !(option->subControls & SC_MdiMinButton)))
+ break;
+ if (option->subControls & SC_MdiNormalButton)
+ offset += buttonWidth;
+ break;
+ default:
+ break;
+ }
+ rect = QRect(offset, 0, buttonWidth, option->rect.height());
+ break;
+ }
+#endif // QT_CONFIG(mdiarea)
+
+ default:
+ rect = visualRect(option->direction, option->rect,
+ QWindowsStyle::subControlRect(cc, option, subControl, widget));
+ break;
+ }
+ return visualRect(option->direction, option->rect, rect);
+}
+
+/*!
+ \reimp
+*/
+QSize QWindowsXPStyle::sizeFromContents(ContentsType ct, const QStyleOption *option,
+ const QSize &contentsSize, const QWidget *widget) const
+{
+ if (!QWindowsXPStylePrivate::useXP())
+ return QWindowsStyle::sizeFromContents(ct, option, contentsSize, widget);
+
+ QSize sz(contentsSize);
+ switch (ct) {
+ case CT_LineEdit:
+ case CT_ComboBox:
+ {
+ XPThemeData buttontheme(widget, 0, QWindowsXPStylePrivate::ButtonTheme, BP_PUSHBUTTON, PBS_NORMAL);
+ if (buttontheme.isValid()) {
+ const qreal factor = QWindowsXPStylePrivate::nativeMetricScaleFactor(widget);
+ const QMarginsF borderSize = buttontheme.margins() * factor;
+ if (!borderSize.isNull()) {
+ const qreal margin = qreal(2) * factor;
+ sz.rwidth() += qRound(borderSize.left() + borderSize.right() - margin);
+ sz.rheight() += int(borderSize.bottom() + borderSize.top() - margin
+ + qreal(1) / factor - 1);
+ }
+ const int textMargins = 2*(proxy()->pixelMetric(PM_FocusFrameHMargin) + 1);
+ sz += QSize(qMax(pixelMetric(QStyle::PM_ScrollBarExtent, option, widget)
+ + textMargins, 23), 0); //arrow button
+ }
+ }
+ break;
+ case CT_SpinBox:
+ {
+ //Spinbox adds frame twice
+ sz = QWindowsStyle::sizeFromContents(ct, option, contentsSize, widget);
+ int border = proxy()->pixelMetric(PM_SpinBoxFrameWidth, option, widget);
+ sz -= QSize(2*border, 2*border);
+ }
+ break;
+ case CT_TabWidget:
+ sz += QSize(6, 6);
+ break;
+ case CT_Menu:
+ sz += QSize(1, 0);
+ break;
+#if QT_CONFIG(menubar)
+ case CT_MenuBarItem:
+ if (!sz.isEmpty())
+ sz += QSize(windowsItemHMargin * 5 + 1, 6);
+ break;
+#endif
+ case CT_MenuItem:
+ if (const QStyleOptionMenuItem *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(option))
+ {
+ if (menuitem->menuItemType != QStyleOptionMenuItem::Separator) {
+ sz = QWindowsStyle::sizeFromContents(ct, option, sz, widget);
+ sz.setHeight(sz.height() - 2);
+ return sz;
+ }
+ }
+ sz = QWindowsStyle::sizeFromContents(ct, option, sz, widget);
+ break;
+
+ case CT_MdiControls:
+ if (const QStyleOptionComplex *styleOpt = qstyleoption_cast<const QStyleOptionComplex *>(option)) {
+ int width = 0;
+ if (styleOpt->subControls & SC_MdiMinButton)
+ width += 17 + 1;
+ if (styleOpt->subControls & SC_MdiNormalButton)
+ width += 17 + 1;
+ if (styleOpt->subControls & SC_MdiCloseButton)
+ width += 17 + 1;
+ sz = QSize(width, 19);
+ } else {
+ sz = QSize(54, 19);
+ }
+ break;
+
+ default:
+ sz = QWindowsStyle::sizeFromContents(ct, option, sz, widget);
+ break;
+ }
+
+ return sz;
+}
+
+
+/*! \reimp */
+int QWindowsXPStyle::styleHint(StyleHint hint, const QStyleOption *option, const QWidget *widget,
+ QStyleHintReturn *returnData) const
+{
+ QWindowsXPStylePrivate *d = const_cast<QWindowsXPStylePrivate*>(d_func());
+ if (!QWindowsXPStylePrivate::useXP())
+ return QWindowsStyle::styleHint(hint, option, widget, returnData);
+
+ int res = 0;
+ switch (hint) {
+
+ case SH_EtchDisabledText:
+ res = (qobject_cast<const QLabel*>(widget) != 0);
+ break;
+
+ case SH_SpinControls_DisableOnBounds:
+ res = 0;
+ break;
+
+ case SH_TitleBar_AutoRaise:
+ case SH_TitleBar_NoBorder:
+ res = 1;
+ break;
+
+ case SH_GroupBox_TextLabelColor:
+ if (!widget || (widget && widget->isEnabled()))
+ res = d->groupBoxTextColor;
+ else
+ res = d->groupBoxTextColorDisabled;
+ break;
+
+ case SH_Table_GridLineColor:
+ res = 0xC0C0C0;
+ break;
+
+ case SH_WindowFrame_Mask:
+ {
+ res = 1;
+ QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask *>(returnData);
+ const QStyleOptionTitleBar *titlebar = qstyleoption_cast<const QStyleOptionTitleBar *>(option);
+ if (mask && titlebar) {
+ // Note certain themes will not return the whole window frame but only the titlebar part when
+ // queried This function needs to return the entire window mask, hence we will only fetch the mask for the
+ // titlebar itself and add the remaining part of the window rect at the bottom.
+ int tbHeight = proxy()->pixelMetric(PM_TitleBarHeight, option, widget);
+ QRect titleBarRect = option->rect;
+ titleBarRect.setHeight(tbHeight);
+ XPThemeData themeData;
+ if (titlebar->titleBarState & Qt::WindowMinimized) {
+ themeData = XPThemeData(widget, 0,
+ QWindowsXPStylePrivate::WindowTheme,
+ WP_MINCAPTION, CS_ACTIVE, titleBarRect);
+ } else
+ themeData = XPThemeData(widget, 0,
+ QWindowsXPStylePrivate::WindowTheme,
+ WP_CAPTION, CS_ACTIVE, titleBarRect);
+ mask->region = d->region(themeData) +
+ QRect(0, tbHeight, option->rect.width(), option->rect.height() - tbHeight);
+ }
+ }
+ break;
+#if QT_CONFIG(rubberband)
+ case SH_RubberBand_Mask:
+ if (qstyleoption_cast<const QStyleOptionRubberBand *>(option))
+ res = 0;
+ break;
+#endif // QT_CONFIG(rubberband)
+
+ case SH_ItemView_DrawDelegateFrame:
+ res = 1;
+ break;
+
+ default:
+ res =QWindowsStyle::styleHint(hint, option, widget, returnData);
+ }
+
+ return res;
+}
+
+/*! \reimp */
+QPalette QWindowsXPStyle::standardPalette() const
+{
+ if (QWindowsXPStylePrivate::useXP() && QApplicationPrivate::sys_pal)
+ return *QApplicationPrivate::sys_pal;
+ else
+ return QWindowsStyle::standardPalette();
+}
+
+/*!
+ \reimp
+*/
+QPixmap QWindowsXPStyle::standardPixmap(StandardPixmap standardPixmap, const QStyleOption *option,
+ const QWidget *widget) const
+{
+ if (!QWindowsXPStylePrivate::useXP())
+ return QWindowsStyle::standardPixmap(standardPixmap, option, widget);
+
+ switch(standardPixmap) {
+ case SP_TitleBarMaxButton:
+ case SP_TitleBarCloseButton:
+ if (qstyleoption_cast<const QStyleOptionDockWidget *>(option))
+ {
+ if (widget && widget->isWindow()) {
+ XPThemeData theme(widget, 0, QWindowsXPStylePrivate::WindowTheme, WP_SMALLCLOSEBUTTON, CBS_NORMAL);
+ if (theme.isValid()) {
+ const QSize size = (theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize();
+ return QIcon(QWindowsStyle::standardPixmap(standardPixmap, option, widget)).pixmap(size);
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ return QWindowsStyle::standardPixmap(standardPixmap, option, widget);
+}
+
+/*!
+ \reimp
+*/
+QIcon QWindowsXPStyle::standardIcon(StandardPixmap standardIcon,
+ const QStyleOption *option,
+ const QWidget *widget) const
+{
+ if (!QWindowsXPStylePrivate::useXP()) {
+ return QWindowsStyle::standardIcon(standardIcon, option, widget);
+ }
+
+ QWindowsXPStylePrivate *d = const_cast<QWindowsXPStylePrivate*>(d_func());
+ switch(standardIcon) {
+ case SP_TitleBarMaxButton:
+ if (qstyleoption_cast<const QStyleOptionDockWidget *>(option))
+ {
+ if (d->dockFloat.isNull()) {
+ XPThemeData themeSize(0, 0, QWindowsXPStylePrivate::WindowTheme,
+ WP_SMALLCLOSEBUTTON, CBS_NORMAL);
+ XPThemeData theme(0, 0, QWindowsXPStylePrivate::WindowTheme,
+ WP_MAXBUTTON, MAXBS_NORMAL);
+ if (theme.isValid()) {
+ const QSize size = (themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize();
+ QPixmap pm(size);
+ pm.fill(Qt::transparent);
+ QPainter p(&pm);
+ theme.painter = &p;
+ theme.rect = QRect(QPoint(0, 0), size);
+ d->drawBackground(theme);
+ d->dockFloat.addPixmap(pm, QIcon::Normal, QIcon::Off); // Normal
+ pm.fill(Qt::transparent);
+ theme.stateId = MAXBS_PUSHED;
+ d->drawBackground(theme);
+ d->dockFloat.addPixmap(pm, QIcon::Normal, QIcon::On); // Pressed
+ pm.fill(Qt::transparent);
+ theme.stateId = MAXBS_HOT;
+ d->drawBackground(theme);
+ d->dockFloat.addPixmap(pm, QIcon::Active, QIcon::Off); // Hover
+ pm.fill(Qt::transparent);
+ theme.stateId = MAXBS_INACTIVE;
+ d->drawBackground(theme);
+ d->dockFloat.addPixmap(pm, QIcon::Disabled, QIcon::Off); // Disabled
+ }
+ }
+ if (widget && widget->isWindow())
+ return d->dockFloat;
+
+ }
+ break;
+ case SP_TitleBarCloseButton:
+ if (qstyleoption_cast<const QStyleOptionDockWidget *>(option))
+ {
+ if (d->dockClose.isNull()) {
+ XPThemeData theme(0, 0, QWindowsXPStylePrivate::WindowTheme,
+ WP_SMALLCLOSEBUTTON, CBS_NORMAL);
+ if (theme.isValid()) {
+ const QSize size = (theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize();
+ QPixmap pm(size);
+ pm.fill(Qt::transparent);
+ QPainter p(&pm);
+ theme.painter = &p;
+ theme.partId = WP_CLOSEBUTTON; // ####
+ theme.rect = QRect(QPoint(0, 0), size);
+ d->drawBackground(theme);
+ d->dockClose.addPixmap(pm, QIcon::Normal, QIcon::Off); // Normal
+ pm.fill(Qt::transparent);
+ theme.stateId = CBS_PUSHED;
+ d->drawBackground(theme);
+ d->dockClose.addPixmap(pm, QIcon::Normal, QIcon::On); // Pressed
+ pm.fill(Qt::transparent);
+ theme.stateId = CBS_HOT;
+ d->drawBackground(theme);
+ d->dockClose.addPixmap(pm, QIcon::Active, QIcon::Off); // Hover
+ pm.fill(Qt::transparent);
+ theme.stateId = CBS_INACTIVE;
+ d->drawBackground(theme);
+ d->dockClose.addPixmap(pm, QIcon::Disabled, QIcon::Off); // Disabled
+ }
+ }
+ if (widget && widget->isWindow())
+ return d->dockClose;
+ }
+ break;
+ case SP_TitleBarNormalButton:
+ if (qstyleoption_cast<const QStyleOptionDockWidget *>(option))
+ {
+ if (d->dockFloat.isNull()) {
+ XPThemeData themeSize(0, 0, QWindowsXPStylePrivate::WindowTheme,
+ WP_SMALLCLOSEBUTTON, CBS_NORMAL);
+ XPThemeData theme(0, 0, QWindowsXPStylePrivate::WindowTheme,
+ WP_RESTOREBUTTON, RBS_NORMAL);
+ if (theme.isValid()) {
+ const QSize size = (themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize();
+ QPixmap pm(size);
+ pm.fill(Qt::transparent);
+ QPainter p(&pm);
+ theme.painter = &p;
+ theme.rect = QRect(QPoint(0, 0), size);
+ d->drawBackground(theme);
+ d->dockFloat.addPixmap(pm, QIcon::Normal, QIcon::Off); // Normal
+ pm.fill(Qt::transparent);
+ theme.stateId = RBS_PUSHED;
+ d->drawBackground(theme);
+ d->dockFloat.addPixmap(pm, QIcon::Normal, QIcon::On); // Pressed
+ pm.fill(Qt::transparent);
+ theme.stateId = RBS_HOT;
+ d->drawBackground(theme);
+ d->dockFloat.addPixmap(pm, QIcon::Active, QIcon::Off); // Hover
+ pm.fill(Qt::transparent);
+ theme.stateId = RBS_INACTIVE;
+ d->drawBackground(theme);
+ d->dockFloat.addPixmap(pm, QIcon::Disabled, QIcon::Off); // Disabled
+ }
+ }
+ if (widget && widget->isWindow())
+ return d->dockFloat;
+
+ }
+ break;
+ default:
+ break;
+ }
+
+ return QWindowsStyle::standardIcon(standardIcon, option, widget);
+}
+
+/*!
+ \internal
+
+ Constructs a QWindowsXPStyle object.
+*/
+QWindowsXPStyle::QWindowsXPStyle(QWindowsXPStylePrivate &dd) : QWindowsStyle(dd)
+{
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug d, const XPThemeData &t)
+{
+ QDebugStateSaver saver(d);
+ d.nospace();
+ d << "XPThemeData(" << t.widget << ", theme=#" << t.theme << ", " << t.htheme
+ << ", partId=" << t.partId << ", stateId=" << t.stateId << ", rect=" << t.rect
+ << ", mirrorHorizontally=" << t.mirrorHorizontally << ", mirrorVertically="
+ << t.mirrorVertically << ", noBorder=" << t.noBorder << ", noContent=" << t.noContent
+ << ", rotate=" << t.rotate << ')';
+ return d;
+}
+
+QDebug operator<<(QDebug d, const ThemeMapKey &k)
+{
+ QDebugStateSaver saver(d);
+ d.nospace();
+ d << "ThemeMapKey(theme=#" << k.theme
+ << ", partId=" << k.partId << ", stateId=" << k.stateId
+ << ", noBorder=" << k.noBorder << ", noContent=" << k.noContent << ')';
+ return d;
+}
+
+QDebug operator<<(QDebug d, const ThemeMapData &td)
+{
+ QDebugStateSaver saver(d);
+ d.nospace();
+ d << "ThemeMapData(alphaType=" << td.alphaType
+ << ", dataValid=" << td.dataValid << ", partIsTransparent=" << td.partIsTransparent
+ << ", hasAlphaChannel=" << td.hasAlphaChannel << ", wasAlphaSwapped=" << td.wasAlphaSwapped
+ << ", hadInvalidAlpha=" << td.hadInvalidAlpha << ')';
+ return d;
+}
+#endif // QT_NO_DEBUG_STREAM
+
+// Debugging code ---------------------------------------------------------------------[ START ]---
+// The code for this point on is not compiled by default, but only used as assisting
+// debugging code when you uncomment the DEBUG_XP_STYLE define at the top of the file.
+
+#ifdef DEBUG_XP_STYLE
+// The schema file expects these to be defined by the user.
+#define TMT_ENUMDEF 8
+#define TMT_ENUMVAL TEXT('A')
+#define TMT_ENUM TEXT('B')
+#define SCHEMA_STRINGS // For 2nd pass on schema file
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <tmschema.h>
+QT_END_INCLUDE_NAMESPACE
+
+// A property's value, type and name combo
+struct PropPair {
+ int propValue;
+ int propType;
+ LPCWSTR propName;
+};
+
+// Operator for sorting of PropPairs
+bool operator<(PropPair a, PropPair b) {
+ return wcscmp(a.propName, b.propName) < 0;
+}
+
+// Our list of all possible properties
+static QList<PropPair> all_props;
+
+
+/*! \internal
+ Dumps a portion of the full native DIB section double buffer.
+ The DIB section double buffer is only used when doing special
+ transformations to the theme part, or when the real double
+ buffer in the paintengine does not have an HDC we may use
+ directly.
+ Since we cannot rely on the pixel data we get from Microsoft
+ when drawing into the DIB section, we use this function to
+ see the actual data we got, and can determin the appropriate
+ action.
+*/
+void QWindowsXPStylePrivate::dumpNativeDIB(int w, int h)
+{
+ if (w && h) {
+ static int pCount = 0;
+ DWORD *bufPix = (DWORD*)bufferPixels;
+
+ char *bufferDump = new char[bufferH * bufferW * 16];
+ char *bufferPos = bufferDump;
+
+ memset(bufferDump, 0, sizeof(bufferDump));
+ bufferPos += sprintf(bufferPos, "const int pixelBufferW%d = %d;\n", pCount, w);
+ bufferPos += sprintf(bufferPos, "const int pixelBufferH%d = %d;\n", pCount, h);
+ bufferPos += sprintf(bufferPos, "const unsigned DWORD pixelBuffer%d[] = {", pCount);
+ for (int iy = 0; iy < h; ++iy) {
+ bufferPos += sprintf(bufferPos, "\n ");
+ bufPix = (DWORD*)(bufferPixels + (iy * bufferW * 4));
+ for (int ix = 0; ix < w; ++ix) {
+ bufferPos += sprintf(bufferPos, "0x%08x, ", *bufPix);
+ ++bufPix;
+ }
+ }
+ bufferPos += sprintf(bufferPos, "\n};\n\n");
+ printf(bufferDump);
+
+ delete[] bufferDump;
+ ++pCount;
+ }
+}
+
+/*! \internal
+ Shows the value of a given property for a part.
+*/
+static void showProperty(XPThemeData &themeData, const PropPair &prop)
+{
+ PROPERTYORIGIN origin = PO_NOTFOUND;
+ GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &origin);
+ const char *originStr;
+ switch(origin) {
+ case PO_STATE:
+ originStr = "State ";
+ break;
+ case PO_PART:
+ originStr = "Part ";
+ break;
+ case PO_CLASS:
+ originStr = "Class ";
+ break;
+ case PO_GLOBAL:
+ originStr = "Globl ";
+ break;
+ case PO_NOTFOUND:
+ default:
+ originStr = "Unkwn ";
+ break;
+ }
+
+ switch(prop.propType) {
+ case TMT_STRING:
+ {
+ wchar_t buffer[512];
+ GetThemeString(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, buffer, 512);
+ printf(" (%sString) %-20S: %S\n", originStr, prop.propName, buffer);
+ }
+ break;
+ case TMT_ENUM:
+ {
+ int result = -1;
+ GetThemeEnumValue(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result);
+ printf(" (%sEnum) %-20S: %d\n", originStr, prop.propName, result);
+ }
+ break;
+ case TMT_INT:
+ {
+ int result = -1;
+ GetThemeInt(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result);
+ printf(" (%sint) %-20S: %d\n", originStr, prop.propName, result);
+ }
+ break;
+ case TMT_BOOL:
+ {
+ BOOL result = false;
+ GetThemeBool(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result);
+ printf(" (%sbool) %-20S: %d\n", originStr, prop.propName, result);
+ }
+ break;
+ case TMT_COLOR:
+ {
+ COLORREF result = 0;
+ GetThemeColor(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result);
+ printf(" (%scolor) %-20S: 0x%08X\n", originStr, prop.propName, result);
+ }
+ break;
+ case TMT_MARGINS:
+ {
+ MARGINS result;
+ memset(&result, 0, sizeof(result));
+ GetThemeMargins(themeData.handle(), 0, themeData.partId, themeData.stateId, prop.propValue, 0, &result);
+ printf(" (%smargins) %-20S: (%d, %d, %d, %d)\n", originStr,
+ prop.propName, result.cxLeftWidth, result.cyTopHeight, result.cxRightWidth, result.cyBottomHeight);
+ }
+ break;
+ case TMT_FILENAME:
+ {
+ wchar_t buffer[512];
+ GetThemeFilename(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, buffer, 512);
+ printf(" (%sfilename)%-20S: %S\n", originStr, prop.propName, buffer);
+ }
+ break;
+ case TMT_SIZE:
+ {
+ SIZE result1;
+ SIZE result2;
+ SIZE result3;
+ memset(&result1, 0, sizeof(result1));
+ memset(&result2, 0, sizeof(result2));
+ memset(&result3, 0, sizeof(result3));
+ GetThemePartSize(themeData.handle(), 0, themeData.partId, themeData.stateId, 0, TS_MIN, &result1);
+ GetThemePartSize(themeData.handle(), 0, themeData.partId, themeData.stateId, 0, TS_TRUE, &result2);
+ GetThemePartSize(themeData.handle(), 0, themeData.partId, themeData.stateId, 0, TS_DRAW, &result3);
+ printf(" (%ssize) %-20S: Min (%d, %d), True(%d, %d), Draw(%d, %d)\n", originStr, prop.propName,
+ result1.cx, result1.cy, result2.cx, result2.cy, result3.cx, result3.cy);
+ }
+ break;
+ case TMT_POSITION:
+ {
+ POINT result;
+ memset(&result, 0, sizeof(result));
+ GetThemePosition(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result);
+ printf(" (%sPosition)%-20S: (%d, %d)\n", originStr, prop.propName, result.x, result.y);
+ }
+ break;
+ case TMT_RECT:
+ {
+ RECT result;
+ memset(&result, 0, sizeof(result));
+ GetThemeRect(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result);
+ printf(" (%sRect) %-20S: (%d, %d, %d, %d)\n", originStr, prop.propName, result.left, result.top, result.right, result.bottom);
+ }
+ break;
+ case TMT_FONT:
+ {
+ LOGFONT result;
+ memset(&result, 0, sizeof(result));
+ GetThemeFont(themeData.handle(), 0, themeData.partId, themeData.stateId, prop.propValue, &result);
+ printf(" (%sFont) %-20S: %S height(%d) width(%d) weight(%d)\n", originStr, prop.propName,
+ result.lfFaceName, result.lfHeight, result.lfWidth, result.lfWeight);
+ }
+ break;
+ case TMT_INTLIST:
+ {
+ INTLIST result;
+ memset(&result, 0, sizeof(result));
+ GetThemeIntList(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result);
+ printf(" (%sInt list)%-20S: { ", originStr, prop.propName);
+ for (int i = 0; i < result.iValueCount; ++i)
+ printf("%d ", result.iValues[i]);
+ printf("}\n");
+ }
+ break;
+ default:
+ printf(" %s%S : Unknown property type (%d)!\n", originStr, prop.propName, prop.propType);
+ }
+}
+
+/*! \internal
+ Dump all valid properties for a part.
+ If it's the first time this function is called, then the name,
+ enum value and documentation of all properties are shown, as
+ well as all global properties.
+*/
+void QWindowsXPStylePrivate::showProperties(XPThemeData &themeData)
+{
+ if (!all_props.count()) {
+ const TMSCHEMAINFO *infoTable = GetSchemaInfo();
+ for (int i = 0; i < infoTable->iPropCount; ++i) {
+ int propType = infoTable->pPropTable[i].bPrimVal;
+ int propValue = infoTable->pPropTable[i].sEnumVal;
+ LPCWSTR propName = infoTable->pPropTable[i].pszName;
+
+ switch(propType) {
+ case TMT_ENUMDEF:
+ case TMT_ENUMVAL:
+ continue;
+ default:
+ if (propType != propValue) {
+ PropPair prop;
+ prop.propValue = propValue;
+ prop.propName = propName;
+ prop.propType = propType;
+ all_props.append(prop);
+ }
+ }
+ }
+ std::sort(all_props.begin(), all_props.end());
+
+ {// List all properties
+ printf("part properties count = %d:\n", all_props.count());
+ printf(" Enum Property Name Description\n");
+ printf("-----------------------------------------------------------\n");
+ wchar_t themeName[256];
+ pGetCurrentThemeName(themeName, 256, 0, 0, 0, 0);
+ for (int j = 0; j < all_props.count(); ++j) {
+ PropPair prop = all_props.at(j);
+ wchar_t buf[500];
+ GetThemeDocumentationProperty(themeName, prop.propName, buf, 500);
+ printf("%3d: (%4d) %-20S %S\n", j, prop.propValue, prop.propName, buf);
+ }
+ }
+
+ {// Show Global values
+ printf("Global Properties:\n");
+ for (int j = 0; j < all_props.count(); ++j) {
+ PropPair prop = all_props.at(j);
+ PROPERTYORIGIN origin = PO_NOTFOUND;
+ GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &origin);
+ if (origin == PO_GLOBAL) {
+ showProperty(themeData, prop);
+ }
+ }
+ }
+ }
+
+ for (int j = 0; j < all_props.count(); ++j) {
+ PropPair prop = all_props.at(j);
+ PROPERTYORIGIN origin = PO_NOTFOUND;
+ GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &origin);
+ if (origin != PO_NOTFOUND)
+ {
+ showProperty(themeData, prop);
+ }
+ }
+}
+#endif
+// Debugging code -----------------------------------------------------------------------[ END ]---
+
+
+QT_END_NAMESPACE
diff --git a/src/plugins/styles/windowsvista/qwindowsxpstyle_p.h b/src/plugins/styles/windowsvista/qwindowsxpstyle_p.h
new file mode 100644
index 0000000000..d00620eefa
--- /dev/null
+++ b/src/plugins/styles/windowsvista/qwindowsxpstyle_p.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWidgets module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSXPSTYLE_P_H
+#define QWINDOWSXPSTYLE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtWidgets/private/qtwidgetsglobal_p.h>
+#include <QtWidgets/private/qwindowsstyle_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWindowsXPStylePrivate;
+class QWindowsXPStyle : public QWindowsStyle
+{
+ Q_OBJECT
+public:
+ QWindowsXPStyle();
+ QWindowsXPStyle(QWindowsXPStylePrivate &dd);
+ ~QWindowsXPStyle();
+
+ void unpolish(QApplication*);
+ void polish(QApplication*);
+ void polish(QWidget*);
+ void polish(QPalette&);
+ void unpolish(QWidget*);
+
+ void drawPrimitive(PrimitiveElement pe, const QStyleOption *option, QPainter *p,
+ const QWidget *widget = 0) const;
+ void drawControl(ControlElement element, const QStyleOption *option, QPainter *p,
+ const QWidget *wwidget = 0) const;
+ QRect subElementRect(SubElement r, const QStyleOption *option, const QWidget *widget = 0) const;
+ QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *option, SubControl sc,
+ const QWidget *widget = 0) const;
+ void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *option, QPainter *p,
+ const QWidget *widget = 0) const;
+ QSize sizeFromContents(ContentsType ct, const QStyleOption *option, const QSize &contentsSize,
+ const QWidget *widget = 0) const;
+ int pixelMetric(PixelMetric pm, const QStyleOption *option = 0,
+ const QWidget *widget = 0) const;
+ int styleHint(StyleHint hint, const QStyleOption *option = 0, const QWidget *widget = 0,
+ QStyleHintReturn *returnData = 0) const;
+
+ QPalette standardPalette() const;
+ QPixmap standardPixmap(StandardPixmap standardIcon, const QStyleOption *option,
+ const QWidget *widget = 0) const;
+ QIcon standardIcon(StandardPixmap standardIcon, const QStyleOption *option = 0,
+ const QWidget *widget = 0) const;
+
+private:
+ Q_DISABLE_COPY(QWindowsXPStyle)
+ Q_DECLARE_PRIVATE(QWindowsXPStyle)
+ friend class QStyleFactory;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWSXPSTYLE_P_H
diff --git a/src/plugins/styles/windowsvista/qwindowsxpstyle_p_p.h b/src/plugins/styles/windowsvista/qwindowsxpstyle_p_p.h
new file mode 100644
index 0000000000..60f9d7e9b7
--- /dev/null
+++ b/src/plugins/styles/windowsvista/qwindowsxpstyle_p_p.h
@@ -0,0 +1,346 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWidgets module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSXPSTYLE_P_P_H
+#define QWINDOWSXPSTYLE_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header
+// file may change from version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtWidgets/private/qtwidgetsglobal_p.h>
+#include "qwindowsxpstyle_p.h"
+#include <QtWidgets/private/qwindowsstyle_p_p.h>
+#include <qmap.h>
+#include <qt_windows.h>
+
+#include <uxtheme.h>
+#include <vssym32.h>
+
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+class QDebug;
+
+// TMT_TEXTSHADOWCOLOR is wrongly defined in mingw
+#if TMT_TEXTSHADOWCOLOR != 3818
+#undef TMT_TEXTSHADOWCOLOR
+#define TMT_TEXTSHADOWCOLOR 3818
+#endif
+#ifndef TST_NONE
+# define TST_NONE 0
+#endif
+
+// These defines are missing from the tmschema, but still exist as
+// states for their parts
+#ifndef MINBS_INACTIVE
+#define MINBS_INACTIVE 5
+#endif
+#ifndef MAXBS_INACTIVE
+#define MAXBS_INACTIVE 5
+#endif
+#ifndef RBS_INACTIVE
+#define RBS_INACTIVE 5
+#endif
+#ifndef HBS_INACTIVE
+#define HBS_INACTIVE 5
+#endif
+#ifndef CBS_INACTIVE
+#define CBS_INACTIVE 5
+#endif
+
+// Uncomment define below to build debug assisting code, and output
+// #define DEBUG_XP_STYLE
+
+// Declarations -----------------------------------------------------------------------------------
+class XPThemeData
+{
+public:
+ explicit XPThemeData(const QWidget *w = 0, QPainter *p = 0, int themeIn = -1,
+ int part = 0, int state = 0, const QRect &r = QRect())
+ : widget(w), painter(p), theme(themeIn), htheme(0), partId(part), stateId(state),
+ mirrorHorizontally(false), mirrorVertically(false), noBorder(false),
+ noContent(false), rotate(0), rect(r)
+ {}
+
+ HRGN mask(QWidget *widget);
+ HTHEME handle();
+
+ static RECT toRECT(const QRect &qr);
+ bool isValid();
+
+ QSizeF size();
+ QMarginsF margins(const QRect &rect, int propId = TMT_CONTENTMARGINS);
+ QMarginsF margins(int propId = TMT_CONTENTMARGINS);
+
+ static QSizeF themeSize(const QWidget *w = 0, QPainter *p = 0, int themeIn = -1, int part = 0, int state = 0);
+ static QMarginsF themeMargins(const QRect &rect, const QWidget *w = 0, QPainter *p = 0, int themeIn = -1,
+ int part = 0, int state = 0, int propId = TMT_CONTENTMARGINS);
+ static QMarginsF themeMargins(const QWidget *w = 0, QPainter *p = 0, int themeIn = -1,
+ int part = 0, int state = 0, int propId = TMT_CONTENTMARGINS);
+
+ const QWidget *widget;
+ QPainter *painter;
+
+ int theme;
+ HTHEME htheme;
+ int partId;
+ int stateId;
+
+ uint mirrorHorizontally : 1;
+ uint mirrorVertically : 1;
+ uint noBorder : 1;
+ uint noContent : 1;
+ uint rotate;
+ QRect rect;
+};
+
+struct ThemeMapKey {
+ int theme;
+ int partId;
+ int stateId;
+ bool noBorder;
+ bool noContent;
+
+ ThemeMapKey() : partId(-1), stateId(-1) {}
+ ThemeMapKey(const XPThemeData &data)
+ : theme(data.theme), partId(data.partId), stateId(data.stateId),
+ noBorder(data.noBorder), noContent(data.noContent) {}
+
+};
+
+inline uint qHash(const ThemeMapKey &key)
+{ return key.theme ^ key.partId ^ key.stateId; }
+
+inline bool operator==(const ThemeMapKey &k1, const ThemeMapKey &k2)
+{
+ return k1.theme == k2.theme
+ && k1.partId == k2.partId
+ && k1.stateId == k2.stateId;
+}
+
+enum AlphaChannelType {
+ UnknownAlpha = -1, // Alpha of part & state not yet known
+ NoAlpha, // Totally opaque, no need to touch alpha (RGB)
+ MaskAlpha, // Alpha channel must be fixed (ARGB)
+ RealAlpha // Proper alpha values from Windows (ARGB_Premultiplied)
+};
+
+struct ThemeMapData {
+ AlphaChannelType alphaType; // Which type of alpha on part & state
+
+ bool dataValid : 1; // Only used to detect if hash value is ok
+ bool partIsTransparent : 1;
+ bool hasAlphaChannel : 1; // True = part & state has real Alpha
+ bool wasAlphaSwapped : 1; // True = alpha channel needs to be swapped
+ bool hadInvalidAlpha : 1; // True = alpha channel contained invalid alpha values
+
+ ThemeMapData() : dataValid(false), partIsTransparent(false),
+ hasAlphaChannel(false), wasAlphaSwapped(false), hadInvalidAlpha(false) {}
+};
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug d, const XPThemeData &t);
+QDebug operator<<(QDebug d, const ThemeMapKey &k);
+QDebug operator<<(QDebug d, const ThemeMapData &td);
+#endif
+
+class QWindowsXPStylePrivate : public QWindowsStylePrivate
+{
+ Q_DECLARE_PUBLIC(QWindowsXPStyle)
+public:
+ enum Theme {
+ ButtonTheme,
+ ComboboxTheme,
+ EditTheme,
+ HeaderTheme,
+ ListViewTheme,
+ MenuTheme,
+ ProgressTheme,
+ RebarTheme,
+ ScrollBarTheme,
+ SpinTheme,
+ TabTheme,
+ TaskDialogTheme,
+ ToolBarTheme,
+ ToolTipTheme,
+ TrackBarTheme,
+ XpTreeViewTheme, // '+'/'-' shape treeview indicators (XP)
+ WindowTheme,
+ StatusTheme,
+ VistaTreeViewTheme, // arrow shape treeview indicators (Vista) obtained from "explorer" theme.
+ NThemes
+ };
+
+ QWindowsXPStylePrivate()
+ : QWindowsStylePrivate(), hasInitColors(false), bufferDC(0), bufferBitmap(0), nullBitmap(0),
+ bufferPixels(0), bufferW(0), bufferH(0)
+ { init(); }
+
+ ~QWindowsXPStylePrivate()
+ { cleanup(); }
+
+ static int pixelMetricFromSystemDp(QStyle::PixelMetric pm, const QStyleOption *option = 0, const QWidget *widget = 0);
+ static int fixedPixelMetric(QStyle::PixelMetric pm, const QStyleOption *option = 0, const QWidget *widget = 0);
+
+ static HWND winId(const QWidget *widget);
+
+ void init(bool force = false);
+ void cleanup(bool force = false);
+ void cleanupHandleMap();
+
+ HBITMAP buffer(int w = 0, int h = 0);
+ HDC bufferHDC()
+ { return bufferDC;}
+
+ static bool useXP(bool update = false);
+ static QRect scrollBarGripperBounds(QStyle::State flags, const QWidget *widget, XPThemeData *theme);
+
+ bool isTransparent(XPThemeData &themeData);
+ QRegion region(XPThemeData &themeData);
+
+ bool drawBackground(XPThemeData &themeData);
+ bool drawBackgroundThruNativeBuffer(XPThemeData &themeData, qreal aditionalDevicePixelRatio);
+ bool drawBackgroundDirectly(HDC dc, XPThemeData &themeData, qreal aditionalDevicePixelRatio);
+
+ bool hasAlphaChannel(const QRect &rect);
+ bool fixAlphaChannel(const QRect &rect);
+ bool swapAlphaChannel(const QRect &rect, bool allPixels = false);
+
+ QRgb groupBoxTextColor;
+ QRgb groupBoxTextColorDisabled;
+ QRgb sliderTickColor;
+ bool hasInitColors;
+
+ static HTHEME createTheme(int theme, HWND hwnd);
+ static QString themeName(int theme);
+ static inline bool hasTheme(int theme) { return theme >= 0 && theme < NThemes && m_themes[theme]; }
+ static bool isItemViewDelegateLineEdit(const QWidget *widget);
+ static bool isLineEditBaseColorSet(const QStyleOption *option, const QWidget *widget);
+
+ QIcon dockFloat, dockClose;
+
+private:
+#ifdef DEBUG_XP_STYLE
+ void dumpNativeDIB(int w, int h);
+ void showProperties(XPThemeData &themeData);
+#endif
+
+ static bool initVistaTreeViewTheming();
+ static void cleanupVistaTreeViewTheming();
+
+ static QBasicAtomicInt ref;
+ static bool use_xp;
+
+ QHash<ThemeMapKey, ThemeMapData> alphaCache;
+ HDC bufferDC;
+ HBITMAP bufferBitmap;
+ HBITMAP nullBitmap;
+ uchar *bufferPixels;
+ int bufferW, bufferH;
+
+ static HWND m_vistaTreeViewHelper;
+ static HTHEME m_themes[NThemes];
+};
+
+inline QSizeF XPThemeData::size()
+{
+ QSizeF result(0, 0);
+ if (isValid()) {
+ SIZE size;
+ if (SUCCEEDED(GetThemePartSize(handle(), 0, partId, stateId, 0, TS_TRUE, &size)))
+ result = QSize(size.cx, size.cy);
+ }
+ return result;
+}
+
+inline QMarginsF XPThemeData::margins(const QRect &qRect, int propId)
+{
+ QMarginsF result(0, 0, 0 ,0);
+ if (isValid()) {
+ MARGINS margins;
+ RECT rect = XPThemeData::toRECT(qRect);
+ if (SUCCEEDED(GetThemeMargins(handle(), 0, partId, stateId, propId, &rect, &margins)))
+ result = QMargins(margins.cxLeftWidth, margins.cyTopHeight, margins.cxRightWidth, margins.cyBottomHeight);
+ }
+ return result;
+}
+
+inline QMarginsF XPThemeData::margins(int propId)
+{
+ QMarginsF result(0, 0, 0 ,0);
+ if (isValid()) {
+ MARGINS margins;
+ if (SUCCEEDED(GetThemeMargins(handle(), 0, partId, stateId, propId, NULL, &margins)))
+ result = QMargins(margins.cxLeftWidth, margins.cyTopHeight, margins.cxRightWidth, margins.cyBottomHeight);
+ }
+ return result;
+}
+
+inline QSizeF XPThemeData::themeSize(const QWidget *w, QPainter *p, int themeIn, int part, int state)
+{
+ XPThemeData theme(w, p, themeIn, part, state);
+ return theme.size();
+}
+
+inline QMarginsF XPThemeData::themeMargins(const QRect &rect, const QWidget *w, QPainter *p, int themeIn,
+ int part, int state, int propId)
+{
+ XPThemeData theme(w, p, themeIn, part, state);
+ return theme.margins(rect, propId);
+}
+
+inline QMarginsF XPThemeData::themeMargins(const QWidget *w, QPainter *p, int themeIn,
+ int part, int state, int propId)
+{
+ XPThemeData theme(w, p, themeIn, part, state);
+ return theme.margins(propId);
+}
+
+QT_END_NAMESPACE
+
+#endif //QWINDOWSXPSTYLE_P_P_H
diff --git a/src/plugins/styles/windowsvista/windowsvista.pro b/src/plugins/styles/windowsvista/windowsvista.pro
new file mode 100644
index 0000000000..f82bcfc91b
--- /dev/null
+++ b/src/plugins/styles/windowsvista/windowsvista.pro
@@ -0,0 +1,22 @@
+TARGET = qwindowsvistastyle
+
+QT += widgets-private
+
+SOURCES += main.cpp
+
+HEADERS += qwindowsvistastyle_p.h qwindowsvistastyle_p_p.h
+SOURCES += qwindowsvistastyle.cpp
+
+HEADERS += qwindowsxpstyle_p.h qwindowsxpstyle_p_p.h
+SOURCES += qwindowsxpstyle.cpp
+
+LIBS_PRIVATE += -lgdi32 -luser32
+
+# DEFINES/LIBS needed for qwizard_win.cpp and the styles
+include(../../../widgets/kernel/win.pri)
+
+DISTFILES += windowsvistastyle.json
+
+PLUGIN_TYPE = styles
+PLUGIN_CLASS_NAME = QWindowsVistaStylePlugin
+load(qt_plugin)
diff --git a/src/plugins/styles/windowsvista/windowsvistastyle.json b/src/plugins/styles/windowsvista/windowsvistastyle.json
new file mode 100644
index 0000000000..771aa0c600
--- /dev/null
+++ b/src/plugins/styles/windowsvista/windowsvistastyle.json
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "windowsvista" ]
+}