Solved

Wish to switch to twips

Posted on 1998-02-16
24
303 Views
Last Modified: 2013-12-03
     I have a shiny new API call returning x, y mouse
coordinates in pixels, or at least in something not twips.
Twips is what I need.

(I'll try to do it right this time):

  I have no access to class functions. If a Windows function
is needed, it has to be API. The constant values for any required arguments are also needed. I have no way of knowing how difficult this might be. If it involves SetMapMode, I can't see how I can get it done. In any case, I'll monitor the points accordingly. It's not important enought to my app
for me to be spending my sleeping hours on it, though,
so if it's real tricky, I won't bother.

 Nietod may have some ideas on this.
0
Comment
Question by:philc
  • 14
  • 10
24 Comments
 
LVL 22

Expert Comment

by:nietod
ID: 1411315
Answer coming.
0
 
LVL 22

Accepted Solution

by:
nietod earned 170 total points
ID: 1411316
I'm not sure how I got the honor of being singled out but...

Converting to twips is easy.  

given a DC handle "DCHnd" and a mouse X coordinate "X"

int Res = GetDeviceCaps(DCHnd,LOGPIXELSX); // Resolution
int XTwips = X*1440/Res; // Position in TWIPS.

Note if the horizontal resolution is different than the vertical resolution you should use LOGPIXELSY to calculate the Y coordinate.  However, they are usually the same so you can probably just use the one value to convert both.


0
 
LVL 22

Expert Comment

by:nietod
ID: 1411317
Just thought of something, you don't have a device context in this case, but you as you may or may not know, you can create a screen DC just using

DCH DCHnd = CreateDC("DISPLAY",NULL,NULL,NULL);
0
 

Author Comment

by:philc
ID: 1411318
Would you happen to know the values corresponding to
 LOGPIXELSx and  LOGPIXELSy?
0
 

Author Comment

by:philc
ID: 1411319
     I ' m not going to pursue this problem any more. There are too many glitches showing up. The formula you provided just did not work. I don't know if that's because I was mistaken in assuming that pixels were being reported by GetCursorPosition. Suspiciously if I multiply X * 14.4 I get the right result. But it doesn't work for y. (As I'm sure you know, 14.4 is
1/100 of the number of twips that corresponds to an inch).

       Other complications are that GetCursorPosition reports with integers which isn't accurate enough for doing conversions. GetCursorPosition is also too slow in my current environment.

 I obtained information from Microsoft on other areas I needed.
e.g. the values to supply to GetDeviceCaps, 8 and 10. And the prototype for GetActiveWindow. As I mentioned in the question, this problem isn't crucial enough for the kinds of headaches it's beginning to return. And I don't know enough Windows programming to play with it with any hope of a reasonable and reasonably quick result. I'll leave this open for a while in
case you want to comment. Incidentally, GetKeyState is working fine.

PhilC
0
 
LVL 22

Expert Comment

by:nietod
ID: 1411320
I think you are giving up too easy!  I'm willing to try to make it work.

The formula should work.  The cursor coordinates are returned in pixels.  

If you don't want to work in integers, you can convert the integer coordinates to floating point before doing the conversion.  However, this is not necessary if you do the math in the appropriate order  (first multiply then divide, my formula did.)

Why do you want the constants cooresponding to LOGPIXELSX and y?  why can't you use the constants?  Not working in C?


0
 
LVL 22

Expert Comment

by:nietod
ID: 1411321
If you do want to make it work, what is going wrong?
0
 
LVL 22

Expert Comment

by:nietod
ID: 1411322
What do you mean that GetCursorPos() is too slow in your environment?  It should be extremely fast as it is simply returning two integers that windows stores in memory.  You should be able to perform it tens or hundreds of thousands of times a second.  How fast do you need?
0
 

Author Comment

by:philc
ID: 1411323
     No, I'm not working in C, I'm working in Access Basic, so
I don't have the files where the constants are defined.

 The numbers that are being returned by GetCursorPosition
do not correspond (after conversion) with the numbers returned by the built in OnMouseDown.

  I want to use a stand alone procedure to handle OnMouseMove,
i.e. one that's not bound to the form. That's why I'm not
using the built in Event Procedure. The Event Procedure
returns single precision floating point for x and y,
and does so very quickly. It needs to be quick for drawing
purposes. GetCursorPos APPEARS to slow down the drawing
to screen in comparison. This may be partially because of
the need to convert to twips first. But I stress that I'm
working with older equipment. Although this application will
be implemented on fast machines, I am holding myself to the
standard set by the system.
      I can't explain why the formula
isn't working. I believe I have checked it rigourously.

     My current screen resolution is 640 x 480. And I wanted to ask you about this. You said that the horizontal resolution is usually the  same as vertical. Are you referring to newer systems? My familiarity is with resolutions such as 800 X 600, the above, and the SVGA resolution which I can't put exact numbers to right now, but in all cases the resolution is different for horizontal and vertical. This may not be important,but on the other hand maybe I'm missing something fundamental.

int XTwips = X*1440/Res

Example : ( 3 mouse clicks)

x returned by OnMouseMove             =  4125
GetCursorPos.x (before conversion)    =  284
Converted from Pixels to Twips        =  639

x returned by OnMouseMove             =  4125
GetCursorPos.x (before conversion)    =  284
Converted from Pixels to Twips        =  639

x returned by OnMouseMove             =  4125
GetCursorPos.x (before conversion)    =  284
Converted from Pixels to Twips        =  639


Here's the code for above:

(ThisPosxy as pointapi)

Call GetCursorPos(ThisPosxy)

Debug.Print "x returned by OnMouseMove         =  " & x
Debug.Print "GetCursorPos.x (before conversion)=  " & ThisPosxy.x
Debug.Print "Converted from Pixels to Twips    =  " &_

CDbl(ThisPosxy.x) * 1440 / 640

(640 = horizontal resolution as returned by GetDeviceCaps.)


     I checked to make sure  that the frame of reference was valid, in other words Pixel one exists, (roughly) in the same place on screen as Twip one.I even tried using the vertical resolution in case I was suffering from some kind of unusual
brain disease, and I couldn't tell the two apart. I still may be
missing something fundamental, but as you can see, I don't know what. I've been at it pretty hard and sometimes what's really needed is to stop and think for awhile. (I'll try anything!)


  Besides the obvious problem above, if I convert an integer to
floating point, am I not converting numbers that have already been rounded off, i.e. isn't inaccuracy already built in with the numbers returned by GetCursorPos?

I'm not inclined to give up at all, let alone easily, but my feeling was that I may be trying to stretch the capabilities of what I'm working with too far, in which case I'd be wasting your time and my own, something I'd like to avoid.

Actually, I tend to like a challenge, but that's my problem. If
you're still on board though, I'm all ears.




0
 
LVL 22

Expert Comment

by:nietod
ID: 1411324
Gosh a book!  just some points as I read through it...

first of all, GetCursorPos points are relative to top-left corner of screen.  WM_MOUSEMOVE, WM_LBUTTONDOWN, etc are relative to the client area of the window receiving the message.  You can use ClientToScreen() or ScreenToClient() to convert.

The word "Resolution" is used in different ways, mostly in an attempt to make things more incomprehesible  than they already are.  The resolution in my example was pixel densify, or pixels per inch.  The resolution you discus is the total number of pixels in a direction or the physical dimension of the screen measured in pixels (rather than inches or twips, etc.)  Make sense.  I'll try to avoid the word resolution in the future.

The conversion I propose could be done many thousands of times a second on a 8086.  It is not what is slowing things down.

In your sample the X returned by MOUSEMOVE message is in the 4000's  That must be converted to TWIPs already.  Right?  Otherwise it is 4000 pixels from the left edge of the window's client area.  That's a big window...  If it is in TWIPs did you do the conversion or does Access Basic do it for you?  If access basic is doing it for you and does not provide a ClientToScreen() that works in TWIPs you are going to have to make one.

Wait a second, you don't want to divide by 640.  Thats the wrong resolution.  You used the wrong constant for GetDeviceCaps().

Darn.  I have to go now.  I will get back to you.  Probably tonight.  If not tommorrow morning.  Bye.
0
 
LVL 22

Expert Comment

by:nietod
ID: 1411325
False alarm.  I have a few more minutes.

Get cursor position returns the pixel that the mouse hot spot is on.  You can't have fractional pixels so you don't need floating point.  That is, the mouse can't be 10.5 pixels from the left edge of the screen.  You could never draw it there.  Since twips are much smaller than pixels (on present day monitors), you can express pixel coordinates in twips using integers with very good accuracy.  Is pixel density (the number of pixels per inch) increases this will be a problem.  However, basically it hasn't increased at all in the last 10 years.  But if you want to be safe use floating point.  It is probably unnecessary but the only harm is a slight loss in speed and a little extra memory usage.

Now I do have to go.
0
 

Author Comment

by:philc
ID: 1411326
I'll need a little time to digest this.
0
Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

 
LVL 22

Expert Comment

by:nietod
ID: 1411327
I'm back.  Just let me sum up here.

Get rid of the 640 figure.  You want to use LOGPIXELSX = 88 and LOGPIXELSY == 90.  Those are the values to pass to GetDeviceCaps(), not the numbers to use in the calculation.

If you need more help, I need to know how you want the final coordinates expressed, that is in TWIPs, but from what location?

If you need to use the coordinates returned from WM_MOUSE (or related messages)  how are they being returned for you?  (In standard windows it is in pixels from the top left corner of the client area, but I suspect Access Basic is converting this for you.)
0
 

Author Comment

by:philc
ID: 1411328
     OK, I've fed the right numbers into GetDeviceCaps,
and things look to be working according to the way you
indicated. I have to be careful of my terminology here, cause
I'm not 100 per cent sure of it. What I need is the twip x y
location for the particular window I'm working with. That is,
NOT from the edge of the screen, but from the edge of whatever
dialog box or window I happen to be working with regardless of
its position relative to edge of the screen. If the window
happened to be 2 inches wide, 2880 twips, then the area I'm
concerned with is THAT 2 inch area, and everything is to be
relative to that. Access Basic does do this automatically so
that in a 2 inch window, if the mouse is one inch along it
horizontally, Access will return 1440 (Access returns in
twips) for x, and that's what I need. I've already tested with the proper numbers, and GetCursorPos converted appears to be returning coordinates, (I believe just as you said), from the edge of the screen.

  I'll check in first about 7 P.M. my time, (2 P.M. east
coast U.S., 11 A.M. west coast) Feb 19

0
 
LVL 22

Expert Comment

by:nietod
ID: 1411329
I think I understand you.  But maybe not.  You mentioned the width of the window several times.  That doesn't realy matter, right?  If the mouse is 1 inch from the left edge of the window you want x to be 1440 twips regardless of whethor or not the window is 2 inches wide or 3 inches wide.  Right?

I'll asume so.  Also I'll assume we're talking about the client area of the window, not the window (frame) area.  (If you need the frame, its the same solution with an adjustment.)

All you need to do is adjust the coordinates returned from GetCursorPos() to be relative to the window's client area with ScreenToClient().  The adjusted position will still be in pixels, but relative to the top-left corner of the window's client area not the screen.  Then convert to twips as before.

Now I don't know basic, but it would be something like

(ThisPosxy as pointapi)

Call GetCursorPos(ThisPosxy)
Call ClientToScreen(WndHnd,ThisPosxy)

CDbl(ThisPosxy.x) * 1440 / GetDeviceCaps(LOGPIXELSX)
CDbl(ThisPosxy.y) * 1440 / GetDeviceCaps(LOGPIXELSY)

Hope this helps.


0
 

Author Comment

by:philc
ID: 1411330
I'll give it a go.
0
 

Author Comment

by:philc
ID: 1411331
The answer to that is yes and also regardless of where
the window is located on the screen, so that the point
of reference is entirely the active window, not
including the frame. I hate to shock you, but wait til
you see...

This:

__________________________________________________________________________

Execution begins:

ScreenToClient:

A:

1. Before ScreenToClient         TPy =  97              - Pixels
2. y returned by (Access)OnMouseDown =  45  (Desired)   - Twips
3. GetCursorPos.y (before conversion)=  59  (After STC) - Pixels
4. Converted from Pixels to Twips    =  885 (Result)    - Twips

_________________________________________________________________
ClientToScreen

B:

1. Before ClientToScreen           TPy  =  97    
2. y returned by (Access)OnMouseDown    =  45
3. GetCursorPos.y (before conversion)   =  135  After CTS
4. Converted from Pixels to Twips       =  2025

Execution ends:
_________________________________________________________________
_________________________________________________________________
_________________________________________________________________


Execution begins:   (Window moved down):


ScreenToClient:

C:

1. Before STC                     TPy =  207              -Pixels
2. y returned by (Access)OnMouseDown  =  45   (Desired)   -Twips
3. GetCursorPos.y (before conversion) =  169  (After STC) -Pixels
4. Converted from Pixels to Twips     =  2535 (Result)    -Twips

_________________________________________________________________

ClientToScreen

D:

1. Before CTS                       TPy =  207
2. y returned by (Access)OnMouseDown    =  45
3. GetCursorPos.y (before conversion    =  245   After CTS
4. Converted from Pixels to Twips       =  3675

Execution ends:

_________________________________________________________________

Let me explain:

Line one in each SECTION is the unadjusted, unconverted, raw
GetCursorPos.y number.


Line two is what Access Basic returns for y
(the DESIRED Number, in TWIPS)


Line three for SECTION A and C is the figure returned
for GetCursorPos.y by ScreenToClient.


Line three for SECTION B and D is the figure returned
for GetCursorPos.y by ClientToScreen.


Line four is the TWIPS returned by GetCursorPos AFTER
all calls and conversions.


  Sections A and B are results from  OnMouseDown with
the active window near the top of the screen. For C
and D I moved the window down.

A and B report on the same execution, as do C and D.

  To repeat, Sections A and C ABOVE represent numbers that result
from calls to ScreenToClient, while B and D reprsent numbers
that result from calls to ClientToScreen,

ScreenToClient, as is to be expected produces a
lower number for GetCursorPos.y after it's called, while
ClientToScreen produces a larger one.

(Line one TPy for each section is GetCursorPos.y BEFORE
ScreenToClient, or ClientToScreen is called:

AND.....

Line three is GetCursorPos.y AFTER the call to
either ScreenToClient or ClientToScreen. )


In all cases the final result for twips is still way too
large.

  I'm speculating that the issue revolves around identifying
the correct window client area. What appears to be
happening above is that the ScreenToClient call is returning
numbers for the Access application window. ClientToScreen is
converting to the overall screen.

 I have another window inside the Access application
window. That's the one I'm clicking in. And the result
is as you see above. But having said all this,
I don't see why a click inside the active window wouldn't
give you the handle of the active window. Then again,
I did say this was speculation.

  If you look at the numbers above carefully, you'll
see that the returned values do indicate two context
levels, while it seems that the one I want is a third.

 As I mentioned before, I'm not big on Windows
programming. GetActiveWindow may not be appropriate
in this situation, (although it sure sounds right).

 Also, there are prototypes that need to be declared in
Access Basic, and for API calls these aren't always
easy to find. So some assumptions were made for
ClientToScreen and ScreenToClient. These calls
appear to be working however.


Well, I did say that the road could and probably
would be bumpy. I'm not panicing, and I'm not in any big
rush, so....whatever.  


Re: Call ClientToScreen(WndHnd,ThisPosxy)

I used GetActiveWindow for WndHnd


PhilC



0
 

Author Comment

by:philc
ID: 1411332
A thought: Could IsZoomed be a factor?
0
 
LVL 22

Expert Comment

by:nietod
ID: 1411333
allright.  I'm not sure what to say.

First of all.  You only need ScreenToClient() not ClientToScreen().  This because you are given coordinates relative to the screen and want to get them relative to the client area of the window.

I think the real issue here is which window.  (GetActiveWindow is not what you want.)

In typical windows programming this sort of processing is handled in a window procedure so you know which window you want to deal with.  I'm not sure how you are handling it, but I suspect it is not a window procedure.  Is that right?  Can you explain to me (1) the code design, i.e. where the code "is" and what it is doing, who calls it ect.  (2) The window design.  That is what windows do you have to work with.


0
 
LVL 22

Expert Comment

by:nietod
ID: 1411334
Just had a thought.  If you can't find a window any other way, (if your code isn't associated with a window) you can use GetWindowFromPoint() to get the window under the mouse.

it returns a window handle and takes a POINT as a parameter. (not a point pointer)  Pass it the point as returned from GetCursorPos().  (That is, wefore converting to client or TWIPS.)  That gives you the window to use when converting to client.  The convert to twips.  That should be what you need.
0
 

Author Comment

by:philc
ID: 1411335
 Fooling around with  GetWindowFromPoint() could make for a nice
hobby, but here's what I wrote before seeing your latest
comment:


       That's it done. Because the regular edition of
Access is not what you'd call rich in API tools, I
just had a brain cloud and forgot that forms in Access
actually have an hwnd property, meaning you can refer
to that property directly, thusly: forms!myform.hwnd, simple
as that. What GetActiveWindow actually does remains a
mystery, what it doesn't seem to do is return the handle
to the active window, but I admit to a certain naivete
in these matters and I just might not know what active
window means. There is a 15 Twip disparity being returned
but it is consistent and simply requires adding it on.
I don't know what this fifteen twips is, it doesn't seem
enough for a window frame. Anyway, thanks for the assistance.
You can keep that last set of comments from me, show it
to your grand kids some day as the reason why you chucked
programming and went back to your first love, snowboarding
or something. Thanks again,


PhilC
0
 
LVL 22

Expert Comment

by:nietod
ID: 1411336
You may want to test on different screen pixel densities (I was going to say resolutions) to see if the 15 pixel offset stays constant.  That is, change your monitor from 640X480 to 800X600 or 1024X768 and test again.  If it changes you're not quite there yet.

What I suspect it is 1 pixel difference that ends up becoming an X twips difference (depending on pixel density).  If that is the case you need to make the adjustment before converting to twips to have it work on all pixel densities.  That is, add or subtract one and then convert.

A quick mathematcal check just seemed to show this is the case.  At 96 pixels per inch (typical pixel density for a 12 inch display) you get exactly 15 twips per pixel.  I don't beleive in coincidence.  
0
 
LVL 22

Expert Comment

by:nietod
ID: 1411337
FYI the active window is the top-level window that has the focus or has a discendant window that has the focus.  GetActiveWindow() was probably the parent (or grand parent etc.) of the window you wanted.
0
 

Author Comment

by:philc
ID: 1411338
 96 IS the number, and this looks like the piece that comes
after the penultimate piece in the puzzle. Much obliged...

Philc
0

Featured Post

Maximize Your Threat Intelligence Reporting

Reporting is one of the most important and least talked about aspects of a world-class threat intelligence program. Here’s how to do it right.

Join & Write a Comment

This article shows a few slightly more advanced techniques for Windows 7 gadget programming, including how to save and restore user settings for your gadget and how to populate the "details" panel that is displayed in the Windows 7 gadget gallery.  …
This article shows how to make a Windows 7 gadget that extends its U/I with a flyout panel -- a window that pops out next to the gadget.  The example gadget shows several additional techniques:  How to automatically resize a gadget or flyout panel t…
This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…
Illustrator's Shape Builder tool will let you combine shapes visually and interactively. This video shows the Mac version, but the tool works the same way in Windows. To follow along with this video, you can draw your own shapes or download the file…

760 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

16 Experts available now in Live!

Get 1:1 Help Now