Display console window at last location

Post your FreeBASIC source, examples, tips and tricks here. Please don’t post code without including an explanation.
Post Reply
deltarho[1859]
Posts: 4292
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Display console window at last location

Post by deltarho[1859] »

Added: Apologies - I meant to put this into the Windows forum - no idea how it got here.

My set up is a primary 1920x1080 in front of me and a 1280x1024 secondary to the left. My web browser opens in the secondary monitor amongst many other applications. I rarely open anything on my primary monitor with the exception of my IDE.

Of course, any normal console applications open on my primary monitor which is a pain, for me, if I want to keep them open whilst editing the source code. I used to drag the console window to the secondary monitor and that got hidden if I clicked on another window that was open.

Not any more.<smile>

Below is a little bi file which saves the console window's location in "Loaction.dat" on closing and loads the same on the next opening. There is much more about a console which could be saved but my interest here is only the location. Save "Location.bi" in a folder called, say, "TestLoc".

Location.bi

Code: Select all

#include once "windows.bi"
#include once "file.bi"
 
DeleteMenu(GetSystemMenu(GetConsoleWindow,False), SC_CLOSE, MF_BYCOMMAND)
 
Sub on_init( ) Constructor
  Dim As Long x, y
	If Fileexists("Location.dat") Then
    Open "Location.dat" For Binary As #1
      Get #1, , x
      Get #1, , y
    Close #1
    SetWindowPos( GetConsoleWindow, HWND_TOPMOST, x, y, 0, 0, SWP_NOSIZE )
  End If
End Sub
 
Sub on_exit( ) Destructor
  Dim xy As Rect
  GetWindowRect( GetConsoleWindow, @xy )
  If Fileexists("Location.dat") Then Kill "Location.dat"
  Open "Location.dat" For Binary As #1
    Put #1, , xy.left
    Put #1, , xy.top
  Close #1
End Sub
Now copy the following, say Test.bas, and save in TestLoc.

Test.bas

Code: Select all

#include once "Location.bi"
 
Print "Main running"
Sleep 1000
Print "Done - Press any key"
Sleep
Now compile and run Test.bas. A console window will open initially displaying "Main running". Drag the console window to another location and then press any key.

Now, run Test again and it will open at the position it was on the previous close.

The 'eagle eyed' amongst you will have noticed that the console window does not have a Close button. If we closed the application via the Close button then the location will not get saved, so I removed the Close button. I got caught out a few times and I write the code. <laugh>.

That's it!

If you have just the one monitor and it is a, so called, wide-screen and your IDE is maximised then you will probably have a fair amount of white space on the right hand side. Run your console application, which uses "Location.bi", and drag the console window to the white space area and close. Next time the application will open in the white space. If you click on your IDE window the console window will not become 'hidden' because it is TopMost.

If you have more than one monitor then on first running drag the console window to a secondary window and close. Thereafter, the console will open on the secondary monitor. This assumes, of course, that the secondary monitors are extensions of the primary monitor.

I have a folder called "Experiments" and it is full of snippets testing ideas and many of them now use "Location.bi" everyone of which opens on my secondary monitor by virtue of just the one "Location.dat".

My background is GUI programming with very little Console programming but I should imagine that some of you console guys may have some ideas on improving the above. If you have then post them here so that we can all benefit.
deltarho[1859]
Posts: 4292
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: Display console window at last location

Post by deltarho[1859] »

A small modification: A location is saved only if the closing location differs to the opening location. As was a location was saved even if it had not changed.

Location.bi

Code: Select all

#include once "windows.bi"
#include once "file.bi"

Dim Shared As Long xLocation, yLocation

DeleteMenu(GetSystemMenu(GetConsoleWindow,False), SC_CLOSE, MF_BYCOMMAND)

Sub on_init( ) Constructor
  Dim As Long x, y
  If FileExists("Location.dat") Then
    Open "Location.dat" For Binary As #1
      Get #1, , xLocation
      Get #1, , yLocation
    Close #1
    SetWindowPos( GetConsoleWindow, HWND_TOPMOST, xLocation, yLocation, 0, 0, SWP_NOSIZE )
  End If
End Sub

Sub on_exit( ) Destructor
  Dim xy As Rect
  GetWindowRect( GetConsoleWindow, @xy )
  If xy.left <> xLocation Or xy.top <> yLocation Then
    If Fileexists("Location.dat") Then Kill "Location.dat"
    Open "Location.dat" For Binary As #1
      Put #1, , xy.left
      Put #1, , xy.top
    Close #1
  EndIf
End Sub
Kuan Hsu
Posts: 586
Joined: Sep 16, 2007 15:12
Location: Taiwan

Re: Display console window at last location

Post by Kuan Hsu »

deltarho[1859] wrote:A small modification: A location is saved only if the closing location differs to the opening location. As was a location was saved even if it had not changed.
I think we can modified consoleLauncher.exe in poseidonFB IDE to set the console window's position:

Code: Select all

#if defined(__fb_win32__) 
	#include once "crt/stdlib.bi"
	#include once "windows.bi"
#endif

dim as string	exeName, args

cls
if( __fb_argc__ >= 2 ) then
	exeName = command(1)
	for i as integer = 3 to __fb_argc__
		args +=  ( " " + command(i-1) )
	next
	
	dim as integer X, Y
	SetWindowPos( GetConsoleWindow, HWND_TOP, X, Y, 0, 0, SWP_NOSIZE )
	
	dim as integer result = exec( exeName, trim( args ) )
	if result = -1 then print "error running="; exeName
	
else
	print "error Args aren't enough!"
end if

#if defined(__fb_win32__) 
	system_( "pause" )
#else
	print( "Press any Key to continue...")
	sleep
#endif

end
deltarho[1859]
Posts: 4292
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: Display console window at last location

Post by deltarho[1859] »

Kuan Hsu wrote:I think we can modified consoleLauncher.exe in poseidonFB IDE to set the console window's position:
Could do. We then have a feature whereby a developing console application will open where we want it to.

However, I have now gone beyond that and now require that some release versions keep that feature.

I have put a copy of "location.bi" in "C:\Program Files (x86)\FreeBASIC\inc\".

If I want a console app to have this feature then I simply employ " #include once "location.bi" ". I have nothing to do at the release stage because the feature is already built in.

You could include "location.bi" in your package with some notes on how to use it. A Windows poseidonFB user then has the choice of whether to use it or not and you have nothing to do with regard consoleLauncher.exe. If a release version does not require that feature then we simply remove " #include once "location.bi" ".

What we now need is a Linux version of "location.bi". Found this by D.J.Peters to get started on that. I cannot help here - what I know about Linux could be put on the back of a postage stamp.<smile>
deltarho[1859]
Posts: 4292
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: Display console window at last location

Post by deltarho[1859] »

It is worth remembering that with a Windows console app if we right click on the Title bar and choose Properties>Layout, untick 'Let system position window' and then click OK the current position will be remembered on the next opening. If we want to re-position then we have to repeat the process at the new position except all we do is click OK. Moving and closing is not enough - the last opening position will not change.

I could have done that before writing 'location.bi' but I forgot about the method. Had I remembered I would not have put a request in with regard poseidonFB. 76 views and nobody reminded me?

'location.bi' is, obviously, easier since all we do is re-position and close.
deltarho[1859]
Posts: 4292
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: Display console window at last location

Post by deltarho[1859] »

When at PowerBASIC I wrote very few console applications and never got involved in the console properties. At one point I think I did change the Window Position on one or two but it was such a long time ago and why I forgot it was available. I never gave any thought as to where any changes to the default settings were put.

I figured that it was about time that I did. Now I am not going to tell you console gurus how to suck eggs but a bit of research has persuaded me to stick with 'location.bi'.

If I write a console app with a path of F:\FreeBASIC\WindowLocation\Test.exe and did not change any of the default settings then there will nothing new to put any where. However, with only one change in a default setting, such as un-ticking 'Let system position window', will see an entry put into the registry at HKEY_CURRENT_USER\Console and a new key added called F:_FreeBASIC_WindowLocation_Test.exe; note the replacing of '\' with '_'. In that key we will see a value for WindowPosition like 0x01f40190. The first two bytes are 'Top' and the next two bytes are 'Left'. In the above code I am using RECT because that is what GetWindowRect requires. RECT uses Longs. The console on the other hand uses SMALL_RECT and Shorts which is why the window position in the registry only takes up 4 bytes.

If I moved Test.exe to another folder its console properties would not go with it - I'd have to configure the properties again and another entry would be put into the registry. The previous entry in the registry is carved in stone, unless I delete it or rename the key before moving Test.exe. Of course, such activities are an absolute no-no for the vast majority of folk. I did notice some keys which have no relevance anymore since the exes no longer exist. I doubt that a registry cleaner would look into the Console key and, of course, a registry cleaner is a no-no for the vast majority of folk; the need for one has diminished over the years anyway. A too large registry would bring Win98 to a grinding halt. Those were the days weren't they when we had to reboot because we hit the 64K resource usage limit. <laugh>.

If I wanted all my console exes to open on my secondary monitor I'd have to twiddle with the properties for every one. I mentioned my Experiments folder earlier. With 'location.bi' I'd only have to re-position one of them and every other exe which used 'location.bi' would also get re-positioned. I would then a have a batch re-position mode. <Ha, ha>. If I decided to kill an exe in its own folder then I would kill the 'location.dat' as well - no entries left as with the registry approach.

OK, if I wanted to change fonts, colours and so on then I would have to use the console properties but for the window position I will be using 'location.bi'.

Funny thing is had I remembered the console properties I would not have written 'location.bi' - I am glad that I forgot now.

I should have mentioned that I am a HWND_TOPMOST freak and some of you may not be. Check out SetWindowPos for some alternatives.

Breaking News

I was just about to post the above and decided to look at resizing. I shied away from that because of differing font sizes and other aspects. Working on the basis that the keyboard will not electrocute me if I get it wrong I had a bash. It seems to be working.

When resizing I could not get a situation where a character was partly shown - it was either shown or not and if not it got wrapped. I tried resizing at the pixel level but they only took at the character level. I tried quite a few font sizes as well but I was unable to break it.

Looking at the properties the new position and new size are showing but this is a read situation along the lines of GetWindowRect. Provided we do not click OK a registry entry will not be made. Clearly, on opening a console application a check is made to see if a registry entry exits and if not the default settings are used but then we override with SetWindowPos.

So, we can now re-position, resize and close. On opening again the re-positioning and resizing are honoured.

Well, thats it - I am definitely sticking with 'location.bi' now. <smile>

Here is the new Location.bi, which only adds 15KB to an exe.

Code: Select all

#include once "windows.bi"
#include once "file.bi"

Dim Shared As Long xLocation, yLocation, cxLocation, cyLocation

DeleteMenu(GetSystemMenu(GetConsoleWindow,False), SC_CLOSE, MF_BYCOMMAND)

Sub on_init_location( ) Constructor
  Dim As Long x, y
	If FileExists("Location.dat") Then
    Open "Location.dat" For Binary As #1
      Get #1, , xLocation
      Get #1, , yLocation
      Get #1, , cxLocation
      Get #1, , cyLocation
    Close #1
    SetWindowPos( GetConsoleWindow, HWND_TOPMOST, xLocation, yLocation, cxLocation - xLocation + 1, cyLocation - yLocation + 1, 0 )
  End If
End Sub

Sub on_exit_location( ) Destructor
  Dim xy As Rect
  GetWindowRect( GetConsoleWindow, @xy )
  If xy.left <> xLocation Or xy.top <> yLocation or xy.right <> cxLocation or xy.bottom <> cyLocation Then
    If Fileexists("Location.dat") Then Kill "Location.dat"
    Open "Location.dat" For Binary As #1
      Put #1, , xy.left
      Put #1, , xy.top
      Put #1, , xy.right
      Put #1, , xy.bottom
    Close #1
  EndIf
End Sub
deltarho[1859]
Posts: 4292
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: Display console window at last location

Post by deltarho[1859] »

I prefer an intensified yellow foreground on a normal blue background so added the following just after SetWindowPos.

Code: Select all

SetConsoleTextAttribute( GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED Or _
    FOREGROUND_GREEN Or FOREGROUND_INTENSITY Or BACKGROUND_BLUE )
Cls
Again, nothing is added to the registry.
deltarho[1859]
Posts: 4292
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: Display console window at last location

Post by deltarho[1859] »

Yours truly wrote:I shied away from that because of differing font sizes and other aspects.
I was right. <smile>

Everything was working fine then I took an existing console exe which included another bas file which in turn included a couple of bi files and then included location.bi.

The final exe appeared in the taskbar with a generic icon, as opposed to contents (Windows 10), when I hovered over it and had to click on its name after a right click when all appeared well.

I did wonder if I was playing 'footloose and fancy free' with the screen buffer size so decided to save the width and height. Doing so corrected the above issue. I have added the new location.bi to a few console apps which have a variety of includes and without issue.

The registry is still not getting a' look in'.

Latest location.bi which now adds 17.5KB to an exe. <Gulp, smile>

Code: Select all

#include once "windows.bi"
#include once "file.bi"

Dim Shared As Long LeftLocation, TopLocation, RightLocation, BottomLocation

Sub on_init_location( ) Constructor
Dim As HANDLE hConOut
Dim As Long f
Dim As Coord newCoord
  
  DeleteMenu(GetSystemMenu(GetConsoleWindow,False), SC_CLOSE, MF_BYCOMMAND)
  
  If Fileexists("Location.dat") Then
    hConOut = GetStdHandle(STD_OUTPUT_HANDLE)
    f = Freefile
    Open "Location.dat" For Binary As #f
      Get #f, , newCoord.X
      Get #f, , newCoord.Y
      Get #f, , LeftLocation
      Get #f, , TopLocation
      Get #f, , RightLocation
      Get #f, , BottomLocation
    Close #f
    SetConsoleScreenBufferSize( hConOut, newCoord )
    SetWindowPos( GetConsoleWindow, HWND_TOPMOST, LeftLocation, TopLocation, _
      RightLocation - LeftLocation, BottomLocation - TopLocation, 0 )
    SetConsoleTextAttribute( hConOut, FOREGROUND_RED Or _
    FOREGROUND_GREEN Or FOREGROUND_INTENSITY Or BACKGROUND_BLUE )
    Cls
  End If
End Sub

Sub on_exit_location( ) Destructor
Dim xy As RECT
Dim As CONSOLE_SCREEN_BUFFER_INFO csbi
Dim As Long f
  
  GetWindowRect( GetConsoleWindow, @xy )
  If xy.left <> LeftLocation Or xy.top <> TopLocation Or xy.right <> RightLocation _
    Or xy.bottom <> BottomLocation Then
  If Fileexists("Location.dat") Then Kill "Location.dat"
  GetConsoleScreenBufferInfo( GetStdHandle(STD_OUTPUT_HANDLE), @csbi )
  f = Freefile
  Open "Location.dat" For Binary As #f
    Put #f, , csbi.dwSize.X ' Width
    Put #f, , csbi.dwsize.Y ' Height
    Put #f, , xy.left
    Put #f, , xy.top
    Put #f, , xy.right
    Put #f, , xy.bottom
  Close #f
  EndIf
End Sub
Last edited by deltarho[1859] on Dec 22, 2017 2:42, edited 1 time in total.
deltarho[1859]
Posts: 4292
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: Display console window at last location

Post by deltarho[1859] »

I got my screen ruler out and measured the client area.

I had this: RightLocation - LeftLocation + 1, BottomLocation - TopLocation + 1

It now reads: RightLocation - LeftLocation, BottomLocation - TopLocation

Given 85, _ , _ , _ , 89 I would call that 5 wide but Windows says no. It says how many pixels from the left one is the right one and we get 4. Well, you know that they say, "When in Rome ..." <smile>
deltarho[1859]
Posts: 4292
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: Display console window at last location

Post by deltarho[1859] »

With the Constructor the first two Gets were assigned to wWidth and wHeight and they were then put into newCoord.X and newCoord.Y respectively. Now the first two Gets assign newCoord.X and newCoord.Y without further ado.
deltarho[1859]
Posts: 4292
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: Display console window at last location

Post by deltarho[1859] »

It is worth noting that you could have a generic "Location.dat". This would very handy for the bone idle amongst you who cannot be bothered to re-position and resize on the first ever pass of location.bi. Of course, only the bone idle would come up with an idea like that. <smile>

I am not anti-registry, by the way. We could have a 14 point NSimSun, Windows 10, set via the Console Properties. The values saved by location.bi will take precedence over their counterparts in the registry. It does not matter whether 'Let system position window', in Layout, is ticked or not.
deltarho[1859]
Posts: 4292
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: Display console window at last location

Post by deltarho[1859] »

In the above Test.bas' last statement is Sleep. We could use something like

Code: Select all

Print "press q to quit"
Do
    Sleep 1, 1
Loop Until Inkey = "q"
In the first case any key will terminate the program and in the second case "q" will terminate the program. In both cases the Destructor Sub will be invoked.

The Close button was removed because using that to terminate the program stops the Destructor Sub being invoked.

It would seem that if we re-position and/or resize then we are locked into the new situation when we terminate with any key or "q".

However, if we employ CTRL+C instead of any key or "q" then the program will terminate without invoking the Destructor Sub, just like clicking on a Close button. So, we can temporarily re-position and/or resize and find ourselves back to the previous position on a new opening.

So, more power to location.bi courtesy of Windows and not me. <smile>
deltarho[1859]
Posts: 4292
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: Display console window at last location

Post by deltarho[1859] »

Just having a bit of fun with this version.

Instead of an absolute path in the Console Title, which can go on a bit with development folders, we now simply get a file name.

Nothing untoward has happened since getting the screen buffer size so I reckon that I am now done here.

location.bi

Code: Select all

#include once "windows.bi"
#include once "file.bi"

Dim Shared As Long LeftLocation, TopLocation, RightLocation, BottomLocation

Sub on_init_location( ) Constructor
Dim As HANDLE hConOut
Dim As Long f
Dim As Coord newCoord
Dim as Zstring*MAX_PATH szOldTitle, szNewTitle
  
  DeleteMenu(GetSystemMenu(GetConsoleWindow,False), SC_CLOSE, MF_BYCOMMAND)
  
  If FileExists("Location.dat") Then
    hConOut = GetStdHandle(STD_OUTPUT_HANDLE)
    f = Freefile
    Open "Location.dat" For Binary As #f
      Get #f, , newCoord.X
      Get #f, , newCoord.Y
      Get #f, , LeftLocation
      Get #f, , TopLocation
      Get #f, , RightLocation
      Get #f, , BottomLocation
    Close #f
    SetConsoleScreenBufferSize( hConOut, newCoord )
    SetConsoleTextAttribute( hConOut, FOREGROUND_RED Or _
    FOREGROUND_GREEN Or FOREGROUND_INTENSITY Or BACKGROUND_BLUE )
    GetConsoleTitle( @szOldTitle, MAX_PATH )
    szNewTitle = Mid( szOldTitle, InStrRev( szOldTitle, "\" ) + 1 )
    SetConsoleTitle( @szNewTitle )
    SetWindowPos( GetConsoleWindow, HWND_TOPMOST, LeftLocation, TopLocation, _
      RightLocation - LeftLocation, BottomLocation - TopLocation, 0 )
    Cls
  End If
End Sub

Sub on_exit_location( ) Destructor
Dim xy As RECT
Dim As CONSOLE_SCREEN_BUFFER_INFO csbi
Dim As Long f
  
  GetWindowRect( GetConsoleWindow, @xy )
  If xy.left <> LeftLocation Or xy.top <> TopLocation Or xy.right <> RightLocation _
    Or xy.bottom <> BottomLocation Then
    If FileExists("Location.dat") Then Kill "Location.dat"
    GetConsoleScreenBufferInfo( GetStdHandle(STD_OUTPUT_HANDLE), @csbi )
    f = Freefile
    Open "Location.dat" For Binary As #f
      Put #f, , csbi.dwSize.X ' Width
      Put #f, , csbi.dwsize.Y ' Height
      Put #f, , xy.left
      Put #f, , xy.top
      Put #f, , xy.right
      Put #f, , xy.bottom
    Close #f
  EndIf
End Sub
Post Reply