System.Drawing.Graphics.DpiX always return 96 - dpi

I have vb.net winform app that has
AutoScaleMode = dpi
AutoScale = false
AutoSize = true
I've signed off after changing DPI setting.
I also tried restarting the machine.
Using g As Graphics = form.CreateGraphics()
Dim dpiX As Single = g.DpiX
dpiX is always 96 regardless of DPI setting. Anyone know what I am doing wrong?

I found solution. My app's manifest has to say it is dpiAware.
Because I am trying to detect high DPI and show a warning message box and not really trying to make my app dpi aware, I couldn't do that.
You can get dpi aware information from registry:
HKEY_CURRENT_USER\Control Panel\Desktop
LogPixels.
If you are using default, you won't have the key. Changing DPI setting will create one.

I ran into this problem and found that if you override the OnPaint(PaintEventArgs e) method of the form, and then get the Graphics object from the argument 'e' i.e. e.Graphics then the DpiX and DpiY value of this Graphics object is correct.

Unfortunately, the way windows handles DPI scaling is all over the place:
Using g As Graphics = form.CreateGraphics()
Dim dpiX As Single = g.DpiX
This code will only work if user has "Use Windows XP Style DPI Scaling" selected when setting custom DPI. Don't know if that option is even available in the new versions of Windows (8.x and 10) or if they've taken it out.
Your best bet would be to just read the registry:
Dim regUseDpiScaling As Integer
Try 'Use Try / Catch since the reg value may not exist if user using 96 DPI.
regUseDpiScaling = CInt(My.Computer.Registry.GetValue("HKEY_CURRENT_USER\Software\Microsoft\Windows\DWM", "UseDpiScaling", Nothing)) ' if this returns 1, it means users is using modern DPI scaling.
Catch ex As Exception
regUseDpiScaling = 0 ' 0 means no DPI scaling or XP DPI scaling
End Try
If Not (regUseDpiScaling = 0) Then
boolUsesModernDPIScaling = True 'Means you haven't clicked "Use Windows XP Style DPI Scaling" while setting DPI in your system.
Else
boolUsesModernDPIScaling = False
MsgBox("2")
End If
Dim regAppliedDPI As Integer
Try
regAppliedDPI = CInt(My.Computer.Registry.GetValue("HKEY_CURRENT_USER\Control Panel\Desktop\WindowMetrics", "AppliedDPI", Nothing))
Catch ex As Exception
regAppliedDPI = 96
End Try
DPIratioX = regAppliedDPI / 96
DPIratioY = regAppliedDPI / 96
I found that having XP DPI scaling can result in different behavior, so it's good to have the program detect if it's being used.

Related

Unable to make app DPI aware - this is not a duplicate

I cannot make my application DPI-aware.
In app.manifest I uncommented:
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
</windowsSettings>
</application>
In App.config I added:
<appSettings>
<add key="EnableWindowsFormsHighDpiAutoResizing" value="true" />
</appSettings>
I am following the questions and responses in
Make vb.net application DPI aware
and
https://www.telerik.com/blogs/winforms-scaling-at-large-dpi-settings-is-it-even-possible-
My application has a single form with a single user control. On each I tried running the app with the AutoScaleMode to each of the various settings: None, Dpi, Font, Inherit (they default to Font). I am using a single monitor which is factory original on my laptop.
In every case, e.graphics.dpix and e.graphics.dpiy (where e is PaintEventArgs) is 96.0. It should be 128.0 = 1920 pixels / 15 inches and 128.0 = 1080 / 8.4375 inches.
What am I missing?
Here's a partial solution.
For painting to the screen, set Graphics.PageUnit = GraphicsUnit.Point (the default is GraphicsUnit.Display).
(I haven't figured out how to AutoSize the UserControl that I'm painting without kludging DPI.)
For printing, use Graphics.PageUnit = GraphicsUnit.Pixel.
' printing
dim gs as Drawing2D.GraphicsState = e.Graphics.Save
Try
e.Graphics.PageUnit = GraphicsUnit.Pixel
dim DpiX as Single = e.Graphics.DpiX
dim DpiY as Single = e.Graphics.DpiY
DoPrinting(e.Graphics, DpiX, DpiY) ' this is where you implement the code to draw your page
Catch ex as Exception
Finally
e.Graphics.Restore(gs)
End Try
' painting to screen
dim gs as Drawing2D.GraphicsState = e.Graphics.Point
Try
e.Graphics.PageUnit = GraphicsUnit.Pixel
dim DpiX as Single = 128.0! ' your value may vary; to find out, divide
dim DpiY as Single = 128.0! ' physical size of screen by screen resolution
DoPrinting(e.Graphics, DpiX, DpiY) ' this is where you implement the code to paint to screen
Catch ex as Exception
Finally
e.Graphics.Restore(gs)
End Try

Roll up / shade floating windows in awesome?

I am trying out awesome at the moment coming from KDE/MATE since many years and I really like it a lot. There's really only one thing that I'm missing from my previous workflows.
Occasionally I'm working with applications that have a lot of floating windows. What I found tremendously helpful in floating WMs was the ability to roll up or shade a window, basically only keeping the titlebar of the application but hiding its window contents.
Is this possible in awesome? Alternatively are there other options like tabbing windows (like in i3) or do you have other suggestions?
Thanks a lot in advance!
Is this possible in awesome?
Theoretically yes, but practically I do not know about anyone who implemented the necessary magic yet to make this work properly. A semi-good first approximation might be to resize the window to height 1.
Untested sketch:
function toggle_roll_up_or_shade(c)
if c.shade then
c:geometry{ height = c.shade }
c.shade = nil
c.size_hints_honor = c.size_hints_honor_before_shade
elseif c.floating then
c.shade = c.height
c.size_hints_honor_before_shade = c.size_hints_honor
c.size_hints_honor = false
c:geometry{ height = 1 }
end
end
The above function would then be bound to some key similar to how Mod+Ctrl+Space is bound to awful.client.floating.toggle in the default config.
Here is a variant which might work on AwesomeWM v3.5:
function toggle_roll_up_or_shade(c)
if awful.client.property.get(c, "shade") then
c:geometry{ height = c.shade }
awful.client.property.set(c, "shade", nil)
c.size_hints_honor = c.size_hints_honor_before_shade
elseif c.floating then
client.property.set(c, "shade", c.height)
client.property.set(c, "size_hints_honor_before_shade", c.size_hints_honor)
c.size_hints_honor = false
c:geometry{ height = 1 }
end
end
Also, if you want to get the height of the titlebar, you should be able to use local _, height = c:titlebar_top(). I'm not sure if this also works in AwesomeWM v3.5.

How can I use DBus to set different wallpapers on each monitor in KDE Plasma 5.13.2

I have multiple monitors on which I want to set a different wallpaper and the best way I can figure out is to use dbus.
I've found a few snippets on the net that almost do what I want, however this puts the same wallpaper on all of my monitors. When I modify it like below, the newkdecommand string updates {monitor_loop} appropriately and the index is in range (it errors if I set it outside of the number of monitors I have) but it doesn't update the desktop wallpapers (they stay the same as before).
According to the documentation, desktops() should return an array of all desktops that currently exist. I just cannot reference them in a way that I can set a different wallpaper for each one. Below is the code that I currently have:
kdemonitorloop = 0
for monitor in monitors:
newkdecommand = """
qdbus org.kde.plasmashell /PlasmaShell org.kde.PlasmaShell.evaluateScript '
var allDesktops = desktops();
d = allDesktops[{monitor_loop}];
d.wallpaperPlugin = "org.kde.image";
d.currentConfigGroup = Array("Wallpaper",
"org.kde.image",
"General");
d.writeConfig("Image", "file:///{save_location}")
'
"""
saveloc = "/home/me/Kbgswitcher/testimg_" + str(kdemonitorloop) + ".png"
os.system(newkdecommand.format(save_location=saveloc, monitor_loop=str(kdemonitorloop)))
kdemonitorloop += 1
Update: on further investigation it appears that the desktops use non-sequential numbers. On my system the containments are 1, 11 and 12 for the middle, left and right screens respectively. I wonder if this is the case for others or if it just chooses random numbers for them?

System buttons changing to Windows 2000 style

I have an old VC6 application to maintain.
When it starts, it shows the system buttons (minimize, maximize, close) in the usual Windows 7 or Windows 8 style.
As soon as I do an operation that triggers a redraw (SetMenu, move the window…), these buttons change to a Windows 2000 style (just gray boxes with black icons). We have other applications (VC6 as well) that do not have this behaviour.
Any idea where I should start to look ?
Edit:
After more investigation, I found that a call to DrawFrameControl is causing all this trouble:
CWindowDC dcWnd(this);
CRect rectWindow, rectClient;
GetWindowRect(&rectWindow);
m_rectDisconnectButton.left = rectWindow.Width() - DISC_BUTTON_WIDTH 6;
m_rectDisconnectButton.top = ::GetSystemMetrics(SM_CYCAPTION) + 4;
m_rectDisconnectButton.right = m_rectDisconnectButton.left + DISC_BUTTON_WIDTH;
m_rectDisconnectButton.bottom = m_rectDisconnectButton.top + BUTTON_HEIGHT;
if ( m_bDisconnectButtonPressed )
dcWnd.DrawFrameControl(&m_rectDisconnectButton, DFC_BUTTON, DFCS_BUTTONPUSH | DFCS_PUSHED);
If I comment the last call, obviously my button will not be drawn, but I no longer have the issue with the "system buttons".

Detect all available scanner resolutions using WIA

How can I programmatically detect all available resolutions (in dpi) for a specified scanner using WIA 2.0? What about page sizes supported? Any ideas?
Pseudo code:
Assume you have device info, connect to it:
var device = deviceInfo.Connect();
if device is not null….. then you can get the Item
Note that items begin at index 1 for device items
Item item = device.Items[1];
An Item has various properties which you can enumerate
e.g.
foreach (Property prop in item.Properties)
{
var temp = prop.Name + " " + prop.PropertyID + " " + prop.get_Value();
}
In this case to find Maximum DPI supported by your scanner you could use.
"Horizontal Optical Resolution" (property id: 3090)
"Vertical Optical Resolution" property id: 3091
.. However if you examine the properties enumeration you will see there is nothing to tell you the minimum or list of all available DPI settings.
.. Now I could not find anything either….
However I did find a way of discovering the minimum DPI…
You may be aware of WIA intent, the enumeration allows you to set scanning type e.g. colour, grey scale etc. (property id: 6146)
var currentIntent = item.Properties.get_Item("6146");
// set to colour
currentIntent.set_Value(WiaImageIntent.ColorIntent);
.. However you can also set WIA image bias in this way (to either maximum quality or minimum size)
This is not via image intent enumeration but is done by OR ing the appropriate values
// set minimum size as WIA bias
var intent = (int)item.Properties.get_Item("6146").get_Value();
item.Properties.get_Item("6146").set_Value(intent | 0x00010000);
http://msdn.microsoft.com/en-us/library/ms630190%28v=vs.85%29.aspx
And if you now look at the DPI (assuming previously DOI was not actually at minimum)
var dpiHorizontal = item.Properties.get_Item("6147").get_Value();
var dpiVertical = item.Properties.get_Item("6148").get_Value();
.. you should see that DPI is now set to it’s lowest (to give minimum size scan)
A big warning.
As soon as you set bias (be it minimum size or maximum quality) any previous DPI values you set are changed (as are various other properties for image dimensions etc – it makes wide ranging changes).
.. So, if you want to stay in control I suggest just finding minimum dpi once and storing it in configuration – Or if you do it each time, discard that item (after getting minimum DPI) and use a new item with no bias set.
.. So you now have max and min DPI values – how to get a list?
.. Again I know of no way .. but
You could have a list of “sensible DPI values”
e.g. 75, 100, 150, 200, 300, 600, 1200, 2400
As (depending on scanner resolution) these are “popular” values to support
.. Depending on your max / min DPI values you know what “popular” values may be worth trying.
.. and when setting DPI do it via try / catch
e.g.
try
{
item.Properties.get_Item("6147").set_Value(myDPI); // horizontal DPI
item.Properties.get_Item("6148").set_Value(myDPI); // vertical DPI
}
catch
{
// not supported so have some logic where try a different “popular” value
}
I see this type of issue with one scanner I use – just because a DPI is within the max / min limits does not mean it is supported.
Although many scanners WIA drivers support 100, 100 DPI, this one does not but does provide 75,75 DPI or 150,150 DPI
This approach is nasty and kludgy but works (and again you could just do this once, run through your list of popular “DPI” values, try and set them, and be left with a list of which DPI settings your scanner supports.
For the sake of simplicity sake these pseudocode examples assume vertical & horizontal DPI may both be set to same value .. this may not be the case with all scanners!
You can use the SubTypeMin and SubTypeMax properties of WIA_IPS_XRES and WIA_IPS_YRES to get the allowed resolution ranges. You can probably use something similar to determine which page sizes are supported.
WIA.Property horizontalResolutionProperty = FindProperty(_scannerDevice.Items[1].Properties, HORIZONTAL_RESOLUTION);
if (horizontalResolutionProperty != null)
{
// SubTypeMin and SubTypeMax are subproperties that tell what the min and max values are for the given property
if (horizontalResolutionProperty.SubTypeMin > minResolution) minResolution = horizontalResolutionProperty.SubTypeMin;
if (horizontalResolutionProperty.SubTypeMax < maxResolution) maxResolution = horizontalResolutionProperty.SubTypeMax;
}
WIA.Property verticalResolutionProperty = FindProperty(_scannerDevice.Items[1].Properties, VERTICAL_RESOLUTION);
if (verticalResolutionProperty != null)
{
// SubTypeMin and SubTypeMax are subproperties that tell what the min and max values are for the given property
if (verticalResolutionProperty.SubTypeMin > minResolution) minResolution = verticalResolutionProperty.SubTypeMin;
if (verticalResolutionProperty.SubTypeMax < maxResolution) maxResolution = verticalResolutionProperty.SubTypeMax;
}
The horizontal and vertical resolution properties have a Subtype property which, depending on it's value, let you acces the allowed value or values for the resolution:
var xResolutionProperty = item.Properties["Horizontal Resolution"];
switch (xResolutionProperty.SubType)
{
case WIASubType.ListSubType:
// Here you can access property SubTypeValues, which contains a list of allowed values for the resolution.
break;
case WIASubType.RangeSubTypes:
// Here you can access SubTypeMin and SubTypeMax properties, with the minimum and maximum allowed values.
break;
case WIASubType.UnspecifiedSubType:
// Here you can access SubTypeDefault property, which contains the default resolution value.
break;
}

Resources