Recent

Author Topic: TProcess Thread handing HOW TO?  (Read 32680 times)

userx-bw

  • Full Member
  • ***
  • Posts: 178
TProcess Thread handing HOW TO?
« on: January 30, 2014, 05:21:37 pm »
the External Program works with the code yet the button sticks in the down position and the form freezes.

Even though it is not in this code:

I've Placed the "Application.ProcessMessages" in various places trying to find where it should go, and nothing helps. I use this same code to set the Image and background colors without a loop so it just stays like that and I do not get this problem.

it is only when I put it in this loop that it causes the Button to stick in the down position while still running the external program in the loop like it is suppose to without errors, leaving the button stuck and
the form frozen on the screen.

I've tried to figure out all of the suggestions but I know  absolutely nothing about how to handle Threads.  Nor how to write what ever it is "they" show one in the examples, or what ever units or forms that needed to be used other then the main unit in order to know where to put whatever wherever it needs to be. They just confuse me as they are written for someone with more experience in programming then I.

This code here is where the code for the TProcess use to be.

I even used another TProcess component  and named it BProcess then I just kept all the error checking in there then took the code for the TProcess then put it into a procedure of its own, it runs properly the way it is suppose to run, still just leaving the form frozen with the Cycle_bn (TButton) stuck down.
 
Code: [Select]

procedure TForm1.Cycle_bnClick( Sender: TObject) ;
begin
   
     //if it has an Image check for Dimentions
   if (imgWidth_bx.Text = '') or (imgHeight_bx.Text = '') then
     if MessageDlg('Information', 'No Diamentions set', mtInformation, [mbCancel], 0) = mrCancel then
      abort;

   if (orgSRG.ItemIndex =-1) and (userSRG.ItemIndex =-1) then
    if MessageDlg('Information', 'No Background Set.', mtInformation, [mbCancel],0)= mrCancel then
    abort;

   if IsValidEntry(imgHeight_bx.Text) then  ImgHeight := imgHeight_bx.Text ;
   if IsValidEntry(imgWidth_bx.Text)  then  ImgWidth := imgWidth_bx.Text;
      // cantanates the two valaues and adds the x between them
      // image size and formats to numberXnumber
      ImgWidth += 'x' + ImgHeight;

    if ColorsGB.Enabled then
      SetColors;

     // set milliseconds to minutes
   cycleimage := (CycleTS.Value * 1000 * 60);

   FindImageFiles := FindAllFiles(chosenDirectory, '*.*', false);
   FindImageFiles.Sort;

  CycleBG( SetColorsRG, tint_cb, blur_cb, Sharp_cb, contrast_cb, Bright_cb,
           gamma_cb, alpha_cb, FlipBgRg, orgSRG, UserSRG);


end;


This is the code for the TProcess. I took out a lot of it to make it
easer to read. Leaving the major parts.

Code: [Select]

procedure CycleBG( SetColorsRG: TRadioGroup ; tint_cb: TCheckBox ; blur_cb: TCheckBox ;
  Sharp_cb: TCheckBox ; contrast_cb: TCheckBox ; Bright_cb: TCheckBox ;
  gamma_cb: TCheckBox ; alpha_cb: TCheckBox ; FlipBgRg: TRadioGroup ;
  orgSRG: TRadioGroup ; UserSRG: TRadioGroup);

begin

   i := 0;
   try
   while i < FindImageFiles.Count do
      begin
        if (CompareFileExt(FindImageFiles.Strings[i], 'jpg') = 0 ) or
           (CompareFileExt(FindImageFiles.Strings[i], 'png') = 0 )
            then
             begin
                BProcess := TProcess.Create(nil);
                BProcess.Executable:= 'mhsetroot';

               if SetColorsRG.ItemIndex > -1 then
               begin
                case SetColorsRG.ItemIndex of
               ........
                 end;
           end; //end case
        end;

    if tint_cb.Checked then
    begin
    .........
    end;

    if orgSRG.ItemIndex > -1 then
    case orgSRG.ItemIndex of
        ..........
    end;

    if UserSRG.ItemIndex > -1 then
    case userSRG.ItemIndex of
       ...........
    end;

        BProcess.Options := BProcess.Options + [poWaitOnExit, poUsePipes];
       
        BProcess.Execute;

        BProcess.Free;

        sleep(cycleimage);
       
   end; //end compare file types
       
        i := i + 1;
       
        // keep cycling through images
        if i = FindImageFiles.Count then i := 0;
       
     end; // end while loop
   finally
      FindImageFiles.Free;
   end;
end;


all vaiabales have been declaried in the var aera.
« Last Edit: January 30, 2014, 05:26:05 pm by userx-bw »
My Finished Projects
https://sourceforge.net/projects/mhsetroot/
https://sourceforge.net/projects/gmhsetrootfreepascalfrontend/

HP Elitetbook 6930p Dual Core Intel vPro 2.66 gHz
VOID (Linux) 64bit

cdbc

  • Hero Member
  • *****
  • Posts: 1079
    • http://www.cdbc.dk
Re: TProcess Thread handing HOW TO?
« Reply #1 on: January 30, 2014, 08:54:38 pm »
Hi
It could be an infinite loop, maybe in your while section, do you remember to incement i ?
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

userx-bw

  • Full Member
  • ***
  • Posts: 178
Re: TProcess Thread handing HOW TO?
« Reply #2 on: January 30, 2014, 09:32:19 pm »
Hi
It could be an infinite loop, maybe in your while section, do you remember to incement i ?
Regards Benny

Yeah it is right here at the bottom of my code, because it has to be in an infinite Loop.

Quote
  i := i + 1;
       
        // keep cycling through images
        if i = FindImageFiles.Count then i := 0;

My Finished Projects
https://sourceforge.net/projects/mhsetroot/
https://sourceforge.net/projects/gmhsetrootfreepascalfrontend/

HP Elitetbook 6930p Dual Core Intel vPro 2.66 gHz
VOID (Linux) 64bit

Leledumbo

  • Hero Member
  • *****
  • Posts: 8757
  • Programming + Glam Metal + Tae Kwon Do = Me
Re: TProcess Thread handing HOW TO?
« Reply #3 on: January 31, 2014, 04:36:51 am »
Quote
BProcess.Options := BProcess.Options + [poWaitOnExit, poUsePipes];
       
        BProcess.Execute;
Well, what do you expect? You have an infinite loop executing external process in a single (main) thread. Of course your form will freeze. If you want something that keeps working behind the scene while you still can work on the main UI then use a thread (which may have process execution inside). Take a look at how (ridiculous, but working) I implement such a functionality to one of my apps.

User137

  • Hero Member
  • *****
  • Posts: 1791
    • Nxpascal home
Re: TProcess Thread handing HOW TO?
« Reply #4 on: January 31, 2014, 11:23:21 am »
1 way to make repeating loop is like this:
Code: [Select]
i:=0;
while true do begin
  // .. do something

  i:=(i+1) mod n; // Will keep i between 0..n-1
end;
You need exit condition for your loop though, if using a thread, you can terminate it from outside (like some button tells thread.Terminate;) and changing loop to:
Code: [Select]
repeat
...
until Terminated;

userx-bw

  • Full Member
  • ***
  • Posts: 178
Re: TProcess Thread handing HOW TO?
« Reply #5 on: January 31, 2014, 02:19:49 pm »
Quote
BProcess.Options := BProcess.Options + [poWaitOnExit, poUsePipes];
       
        BProcess.Execute;
Well, what do you expect? You have an infinite loop executing external process in a single (main) thread. Of course your form will freeze. If you want something that keeps working behind the scene while you still can work on the main UI then use a thread (which may have process execution inside). Take a look at how (ridiculous, but working) I implement such a functionality to one of my apps.


the Script that I wrote to do the very same thing does not have this problem, but everybody wants a GUI in linux, they get away from Windows and get Linux and still want everything to be like Windows. go get windows people ....   :-[

so yeah with

Script: eternal loop no freezing up it just works.
GUI:    eternal loop freezing up it doesn't work.

So that is my problem I have no idea how to deal with a thread. I just started programming, with not a whole lot of experience time under my belt, and the examples that I have found with Google do not help me as they are written for someone that knows how to deal with threads or has more knowledge of an OS and how they work then I do.


what I have tried when dealing with a thread just crashed it and such.

MOD: ADDED:

I am researching this fork thing and threads. is that a good idea?




« Last Edit: January 31, 2014, 03:18:06 pm by userx-bw »
My Finished Projects
https://sourceforge.net/projects/mhsetroot/
https://sourceforge.net/projects/gmhsetrootfreepascalfrontend/

HP Elitetbook 6930p Dual Core Intel vPro 2.66 gHz
VOID (Linux) 64bit

userx-bw

  • Full Member
  • ***
  • Posts: 178
Re: TProcess Thread handing HOW TO?
« Reply #6 on: January 31, 2014, 04:24:49 pm »
Quote
1 way to make repeating loop is like this:
Code: [Select]
i:=0;
while true do begin
  // .. do something

  i:=(i+1) mod n; // Will keep i between 0..n-1
end;


whats n?
besides a variable not declared.
My Finished Projects
https://sourceforge.net/projects/mhsetroot/
https://sourceforge.net/projects/gmhsetrootfreepascalfrontend/

HP Elitetbook 6930p Dual Core Intel vPro 2.66 gHz
VOID (Linux) 64bit

snorkel

  • Hero Member
  • *****
  • Posts: 817
Re: TProcess Thread handing HOW TO?
« Reply #7 on: January 31, 2014, 04:41:20 pm »
do the loop in a thread object.
I do endless loops in threads for TCP/IP servers all the time.

Just use a While not terminated do

You should also use a sleep in your loop or it will consume all the CPU cycles.
***Snorkel***
If I forget, I always use the latest stable 32bit version of Lazarus and FPC. At the time of this signature that is Laz 3.0RC2 and FPC 3.2.2
OS: Windows 10 64 bit

userx-bw

  • Full Member
  • ***
  • Posts: 178
Re: TProcess Thread handing HOW TO?
« Reply #8 on: January 31, 2014, 05:31:57 pm »
do the loop in a thread object.
I do endless loops in threads for TCP/IP servers all the time.

Just use a While not terminated do

You should also use a sleep in your loop or it will consume all the CPU cycles.

well I do have the sleep within the loop that too was something I didn't understand as it stops the loop therefore stops using the CPU cycles to run the loop. as the loop only runs once then sleeps for a minute min time before it runs again.

well I guess I just got a "Google learn" how to do that now  :o  :D
thanks
My Finished Projects
https://sourceforge.net/projects/mhsetroot/
https://sourceforge.net/projects/gmhsetrootfreepascalfrontend/

HP Elitetbook 6930p Dual Core Intel vPro 2.66 gHz
VOID (Linux) 64bit

Leledumbo

  • Hero Member
  • *****
  • Posts: 8757
  • Programming + Glam Metal + Tae Kwon Do = Me
Re: TProcess Thread handing HOW TO?
« Reply #9 on: February 01, 2014, 12:08:06 am »
Quote
BProcess.Options := BProcess.Options + [poWaitOnExit, poUsePipes];
       
        BProcess.Execute;
Well, what do you expect? You have an infinite loop executing external process in a single (main) thread. Of course your form will freeze. If you want something that keeps working behind the scene while you still can work on the main UI then use a thread (which may have process execution inside). Take a look at how (ridiculous, but working) I implement such a functionality to one of my apps.


the Script that I wrote to do the very same thing does not have this problem, but everybody wants a GUI in linux, they get away from Windows and get Linux and still want everything to be like Windows. go get windows people ....   :-[

so yeah with

Script: eternal loop no freezing up it just works.
GUI:    eternal loop freezing up it doesn't work.

So that is my problem I have no idea how to deal with a thread. I just started programming, with not a whole lot of experience time under my belt, and the examples that I have found with Google do not help me as they are written for someone that knows how to deal with threads or has more knowledge of an OS and how they work then I do.


what I have tried when dealing with a thread just crashed it and such.

MOD: ADDED:

I am researching this fork thing and threads. is that a good idea?
my app works at least on windows and linux, and I dare to say it would work on Mac as well. No problem with OS if you know how to work with TThread correctly.

Don't use fork for cross platform app, it's available on *nix only (though some morons write "cross platform" apps with it with the help of cygwin), TProcess wraps fork on *nix and CreateProcess on windows so it's all you need, you just have to figure out how to use it correctly along with TThread (again, see my app).

userx-bw

  • Full Member
  • ***
  • Posts: 178
Re: TProcess Thread handing HOW TO?
« Reply #10 on: February 01, 2014, 03:54:36 pm »
my app works at least on windows and linux, and I dare to say it would work on Mac as well. No problem with OS if you know how to work with TThread correctly.

Don't use fork for cross platform app, it's available on *nix only (though some morons write "cross platform" apps with it with the help of cygwin), TProcess wraps fork on *nix and CreateProcess on windows so it's all you need, you just have to figure out how to use it correctly along with TThread (again, see my app).

are you taking about this code?

https://bitbucket.org/leledumbo/express/src/4a077d0e75fb1223ca3bcbb6b6a80bdf0083e45f/src/include/linux/browserdetection.inc?at=default


and No I have no idea about how to handle a Thread, yet TProcess is a Thread and others keep advising me to put TProcess into a Thread. That confuses me as it seems to take away the ability to use the TProcess.Parameters.Add () ?

I am thinking of trying to split it up and to figure out how to use the TTimer or just use the SLEEP(time), function then put the find file in a loop then  call the procedure to use the TProcess code then have it load from there, putting just the find file loop in a sleep state instead of having the find file and Tprocess in a loop and putting everything to sleep and see what that does.

from looking at your code it is not in a loop when you call to create a TProcess. it is a straight call to create it then execute it the free it.

where I am finding the file then creating a TProcess, executing it, freeing it, then finding the next file ,creating TProcess , executing it, then freeing it within the loop. If I figure out how to take the TProcess out of the loop and just send it a file to load that may help.


Quote
BProcess.Options := BProcess.Options + [poWaitOnExit, poUsePipes];
I took that out.

as well as I tried using the TProcess.suppend and TProcess resume and it freed up the button but the form itself is still sucking up too many resources so it does not refresh until it sets another image to the desktop then it will refresh then freeze up again.
My Finished Projects
https://sourceforge.net/projects/mhsetroot/
https://sourceforge.net/projects/gmhsetrootfreepascalfrontend/

HP Elitetbook 6930p Dual Core Intel vPro 2.66 gHz
VOID (Linux) 64bit

Rails

  • Guest
Re: TProcess Thread handing HOW TO?
« Reply #11 on: February 01, 2014, 04:27:54 pm »

So that is my problem I have no idea how to deal with a thread. I just started programming, with not a whole lot of experience time under my belt, and the examples that I have found with Google do not help me as they are written for someone that knows how to deal with threads or has more knowledge of an OS and how they work then I do.


what I have tried when dealing with a thread just crashed it and such.

MOD: ADDED:

I am researching this fork thing and threads. is that a good idea?

It seems like you are beating your head against the wall.

If you just started programming, why are you trying to deal with advanced subjects, such as threads and processes? I've been programming for my own purposes on and off since the late 1970's. I have never had to concern myself with threads or processes, beyond knowing what they are through osmosis, so to speak. The Lazarus IDE handles all that stuff for you, for all but the most sophisticated applications.

Why not give the folks here the big picture first? Tell them what you want to accomplish, starting with the intended purpose of the application itself, and let them suggest a good approach for a beginning programmer.  It will be whole lot less painful than what you are doing now.


userx-bw

  • Full Member
  • ***
  • Posts: 178
Re: TProcess Thread handing HOW TO?
« Reply #12 on: February 01, 2014, 05:42:56 pm »

So that is my problem I have no idea how to deal with a thread. I just started programming, with not a whole lot of experience time under my belt, and the examples that I have found with Google do not help me as they are written for someone that knows how to deal with threads or has more knowledge of an OS and how they work then I do.


what I have tried when dealing with a thread just crashed it and such.
I changed the files to txt extension

MOD: ADDED:

I am researching this fork thing and threads. is that a good idea?

It seems like you are beating your head against the wall.

If you just started programming, why are you trying to deal with advanced subjects, such as threads and processes? I've been programming for my own purposes on and off since the late 1970's. I have never had to concern myself with threads or processes, beyond knowing what they are through osmosis, so to speak. The Lazarus IDE handles all that stuff for you, for all but the most sophisticated applications.

Why not give the folks here the big picture first? Tell them what you want to accomplish, starting with the intended purpose of the application itself, and let them suggest a good approach for a beginning programmer.  It will be whole lot less painful than what you are doing now.

I keep beating my head on the wall over threads because everyone keeps telling me that if I put this code which works with no problems other then it locks up the GUI had or should be put into a thread in order for it to stop locking up the GUI (form).

Quote
Why not give the folks here the big picture first?
I forked a program called hsetroot added more features to it like being able to tell the program what size you want the image and how to set it on the desktop, then  renamed it mhsetroot put it in sourceforge. It is a Linux command line program that sets the background image and colors to your desktop.

I wrote a script for it with a loop in it that changes the images and colors on the desktop  at a given time, and adding in the script the ability to be able to change the time that you wanted the image and colors to change just by using the command line.

then came the GUI so I could learn how to do that now.

I chose Lazarus because it had drop the stuff you need on a form easyness,  and I found that they have the TProcess to use command line programs in Linux trough Lazarus therefore I need to use the TProcess. therefore it met my needs to be able to write a GUI that can use my command line program I wrote/added to.

I already wrote the program in Lazarus to do everything you can do with mhsetroot,  but the changing the image and colors on the background at a given time, called it gmhsetroot, then I put that up in sourcefouge.

then comes my next challenge, making it do what I did with the script. What I am now trying to do now by adding the ability to be able to have the image and colors change on the desktop at a given time that the user sets.

I did it with the images so far, I can watch it change the image on the background at the given time it is suppose to, but giving me this  problem of the button I clicked to make it do that freezing up then I got it to not do that then found that the form freezes up too. It works no problem, as it changes the image at the given time, but leaving the form froze and not refreshing. in other words it is when you run out of memory and that window just goes blank not seeing any buttons just the gray colored from. you can still move it all around the screen just no longer see any buttons and such.

that is one problem I have yet to tackle and writing the code to change the colors, and  the other is killing everything when I close the form to free up the TProcess so the form closes properly. as I keep getting some errors when I close it, that I have not paid much mind to as I am dealing with this one first, and that is about all I have left to do off the top of my head to get this "upgrade' done.
 

in my first post  it was suggested that I just put that into a thread and then it would just work. As I have no idea how to deal with threads, but if that is all I can do to get this to work then I have to use a thread, but TProcess is a Thread or has Thread Properties as it has suspend and resume. therefore a thread.

it seems that I have to deal with a Thread due to the nature of my program needing to use a loop so that it can cycle through the images and pick a different one (and colors too) then call the external program mhsetroot and have it set / change the image and colors on the desktop.

Because from what I understand that putting things in loops when using a GUI just eats up resources that is why it is locking up even though within the loop I put it to sleep.

Here is all of my CODE that has to do with this, as this form only allows so much to be written within a post I give it to all to read in a file to download, everything I have written thus far pertaining to my program mhsetroot, gmhsetroot. ie. the Big Picture.


I hope that is enough information. they are in "txt"


mod:
 actually I need to not really close the program  but be "hidden" so that the program if it is set to change the image and color to stay running in order to keep the TProcess running to change the image and colors(in random order). I just haven't gotten that far yet.
« Last Edit: February 01, 2014, 07:03:35 pm by userx-bw »
My Finished Projects
https://sourceforge.net/projects/mhsetroot/
https://sourceforge.net/projects/gmhsetrootfreepascalfrontend/

HP Elitetbook 6930p Dual Core Intel vPro 2.66 gHz
VOID (Linux) 64bit

Leledumbo

  • Hero Member
  • *****
  • Posts: 8757
  • Programming + Glam Metal + Tae Kwon Do = Me
Re: TProcess Thread handing HOW TO?
« Reply #13 on: February 01, 2014, 07:27:06 pm »
Quote
are you taking about this code?

https://bitbucket.org/leledumbo/express/src/4a077d0e75fb1223ca3bcbb6b6a80bdf0083e45f/src/include/linux/browserdetection.inc?at=default
No, this one:
https://bitbucket.org/leledumbo/express/src/4a077d0e75fb1223ca3bcbb6b6a80bdf0083e45f/src/FormMain.pp?at=default
See TUPXThread and TUPXThreadRunner classes. TUPXRunner is a thread which will create a pool of threads executing TProcess.
Quote
and No I have no idea about how to handle a Thread, yet TProcess is a Thread and others keep advising me to put TProcess into a Thread. That confuses me as it seems to take away the ability to use the TProcess.Parameters.Add () ?
No, you mix up processes and threads. They are NOT the same. Process does contain at least one (main) thread, and may create more threads. If you have a quite advanced task manager or similar app, you can see child process when you create a process from your program, but you can't see your threads. Contrary to processes, threads don't have process ID, they live in the same address space as the main program.
Quote
I am thinking of trying to split it up and to figure out how to use the TTimer or just use the SLEEP(time), function then put the find file in a loop then  call the procedure to use the TProcess code then have it load from there, putting just the find file loop in a sleep state instead of having the find file and Tprocess in a loop and putting everything to sleep and see what that does.
TTimer won't help, even it could make things worse. TTimer.OnTimer is not exclusively locked, when it's time to enter the event, it will enter. If the previous code is not yet finished, it will still be executed. Not just your program, but the whole OS might be locked up (though modern OSes are smart enough to kill processes that don't answer signal because it eats too much CPU).
Quote
where I am finding the file then creating a TProcess, executing it, freeing it, then finding the next file ,creating TProcess , executing it, then freeing it within the loop. If I figure out how to take the TProcess out of the loop and just send it a file to load that may help.
You still have single threaded programming mind, you're not ready for multithreaded one. Read up and understand the concept first, you can't code advanced things without understanding how things work in the background. The relevant wiki article also has information about the limitation of threads and GUI.

userx-bw

  • Full Member
  • ***
  • Posts: 178
Re: TProcess Thread handing HOW TO?
« Reply #14 on: February 01, 2014, 07:48:51 pm »
Quote
are you taking about this code?

https://bitbucket.org/leledumbo/express/src/4a077d0e75fb1223ca3bcbb6b6a80bdf0083e45f/src/include/linux/browserdetection.inc?at=default
No, this one:
https://bitbucket.org/leledumbo/express/src/4a077d0e75fb1223ca3bcbb6b6a80bdf0083e45f/src/FormMain.pp?at=default
See TUPXThread and TUPXThreadRunner classes. TUPXRunner is a thread which will create a pool of threads executing TProcess.
Quote
and No I have no idea about how to handle a Thread, yet TProcess is a Thread and others keep advising me to put TProcess into a Thread. That confuses me as it seems to take away the ability to use the TProcess.Parameters.Add () ?
No, you mix up processes and threads. They are NOT the same. Process does contain at least one (main) thread, and may create more threads. If you have a quite advanced task manager or similar app, you can see child process when you create a process from your program, but you can't see your threads. Contrary to processes, threads don't have process ID, they live in the same address space as the main program.
Quote
I am thinking of trying to split it up and to figure out how to use the TTimer or just use the SLEEP(time), function then put the find file in a loop then  call the procedure to use the TProcess code then have it load from there, putting just the find file loop in a sleep state instead of having the find file and Tprocess in a loop and putting everything to sleep and see what that does.
TTimer won't help, even it could make things worse. TTimer.OnTimer is not exclusively locked, when it's time to enter the event, it will enter. If the previous code is not yet finished, it will still be executed. Not just your program, but the whole OS might be locked up (though modern OSes are smart enough to kill processes that don't answer signal because it eats too much CPU).
Quote
where I am finding the file then creating a TProcess, executing it, freeing it, then finding the next file ,creating TProcess , executing it, then freeing it within the loop. If I figure out how to take the TProcess out of the loop and just send it a file to load that may help.
You still have single threaded programming mind, you're not ready for multithreaded one. Read up and understand the concept first, you can't code advanced things without understanding how things work in the background. The relevant wiki article also has information about the limitation of threads and GUI.

aaahh SO you do put the TProcess inside the thread and then the thread just handles it. (?)

just call Thread.Create(true) in a suspended state suspending the TProcess and when I need to use it just Thread execute then suspend it for the time limit I need then call resume to have it execute the TProcess again? 

I hope I got that logic right.
My Finished Projects
https://sourceforge.net/projects/mhsetroot/
https://sourceforge.net/projects/gmhsetrootfreepascalfrontend/

HP Elitetbook 6930p Dual Core Intel vPro 2.66 gHz
VOID (Linux) 64bit

 

TinyPortal © 2005-2018