vga4bit ScrollDownPrintWindow() issues

tomicdesu

Well-known member
I'm trying to implement a terminal emulator (H19/H89) "insert line" function and failing. I'm assuming I'm doing something dumb and overlooking something obvious and maybe fresh eyes will see my blunder. I'm using the latest library, VGA_4BIT_T4-VGA_4BIT_T4_Fixes.

"Insert line" is implemented by a scroll down. I fudged the background color to yellow so that I could see what was happening.

It was maddeningly difficult to get this visual example, crappy as it is (CP/M doesn't do print screen, yet anyway, I think I will add it). I am using a text editor (PMATE) to put text on the screen and do one INSERT LINE function. I normally use 800x600, but and this problem exhibits there, but I switched to 640x480 to see if it was dependent (doesn't seem to be). The text editor has correct screen dimensions (30x80).

Code:
        case 'L':
          h19Col= 0;
          L.header ("INSERT LINE row "); L.print (h19Row);
          L.print (" height "); L.print (h19_maxRows - h19Row - 1);
          L.println();
          vga4bit.setPrintCWindow (h19Col, h19Row,  h19_maxCols, h19_maxRows - h19Row - 1);
          vga4bit.setBackgroundColor (VGA_YELLOW);  // DEBUG
          vga4bit.scrollDownPrintWindow();
          vga4bit.setBackgroundColor (VGA_BLACK);  // DEBUG
          vga4bit.unsetPrintWindow();
          fini= true;
          break;

The debug print out for this example is:

Code:
00:27:30 INSERT LINE row 14 height 15

By not giving PMATE the "insert line" escape code it repaints the screen and displays spaces to erase, and it paints screen correctly 100% of the time, it's just slower. So I'm certain that it is my insert line/scroll down that's failing.

So 0, 0 is top left, and 29,79 is the lower right corner, right? Variables H19Row and H19Col are 0,14 at the start, and h19_maxCols=80, h19_maxRows=30.

Am I calc'ing the scroll box right?

Worse, I swear it works right under some circumstances.

Before and after photos. Terrible photos!
 

Attachments

  • IMG_20250629_133855_401.jpg
    IMG_20250629_133855_401.jpg
    250.6 KB · Views: 83
  • IMG_20250629_133936_378.jpg
    IMG_20250629_133936_378.jpg
    225.1 KB · Views: 74
Are you trying to insert one complete line or a certain number of characters in a line? I have an appointment to go to right now but will check this out when I get back. I will make sure there is no issue with scrollDownPrintWindow(). I believe insert line should move all text starting at the insertion point to the right, and down if crossing the right margin. I have the Kilo editor working as one of the example sketches in VGA_4bit_T4. Can check that out and see how it does the insert. Being off by one with boundaries is a common problem in the code...
 
Ok, This code works:
Code:
// testing.ino

#include "VGA_4bit_T4.h"
#include "VGA_T4_Config.h"

FlexIO2VGA vga4bit;
#define FONTSIZE 16


// Uncomment one of the following screen resolutions. Try them all:)
//const vga_timing *timing = &t1024x768x60; // Not enough memory avilable for this screen size in this sketch.
const vga_timing *timing = &t800x600x60;
//const vga_timing *timing = &t640x480x60;
//const vga_timing *timing = &t640x400x70;

uint8_t h19Col = 0, h19Row = 14,  h19_maxCols = 80, h19_maxRows = 30;

void setup() {
  // Wait for USB Serial
  while (!Serial && (millis() < 5000)) {
    yield();
  }   
  vga4bit.stop();
  // Setup VGA display: 800x600x60
  //                    double Height = false
  //                    double Width  = false
  //                    Color Depth   = 4 bits, Mono not supported yet.
  vga4bit.begin(*timing, false, false, 4);
  // Set fontsize 8x8 or (8x16 available)
  vga4bit.setFontSize(FONTSIZE, false);
  // Set default foreground and background colors
  vga4bit.setBackgroundColor(VGA_BLUE);
  vga4bit.initCursor(0,0,7,FONTSIZE - 1,false,30);
  // Turn cursor on
  vga4bit.tCursorOn();
  vga4bit.textxy(0,0); //reset text xy after setting print window
  for(int y = h19Col; y < h19_maxRows; y++) {
    for(int x = h19Col; x < h19_maxCols; x++) {
      vga4bit.printf("%c",y + 33);
    }
    vga4bit.printf("\n");
  }
  vga4bit.setPrintCWindow (h19Col, h19Row,  h19_maxCols, h19_maxRows - h19Row - 1);
  vga4bit.textxy(0,0); //reset text xy after setting print window, As @KurtE would say BUGBUG!!
  waitforInput();
  vga4bit.tCursorOff();
  vga4bit.scrollDownPrintWindow();
  vga4bit.tCursorOn();
//  vga4bit.unsetPrintWindow();
 
}

void loop() {
  delay(100);
}

void waitforInput()
{
  Serial.println("Press anykey to continue");
  while (Serial.read() == -1) ;
  while (Serial.read() != -1) ;
}
Before:
Insert_line-1.jpg


After key press:
Insert_line-2.jpg


But there are a couple of issues I uncovered that need fixing. The "setPrintCWindow()" function does not set the cursor position to 0,0 in the print window. Needs fixing. Also, when the cursor is in blink mode and you and you run "unsetPrintWindow()" you get this:
Insert_line-3.jpg

Notice the upper left corner of the screen, 0,0 shows a space and 0,1 shows "/". Some how those two lines beginning a x==0 are copies of the same characters located at the current position of the cursor:confused:

More to fix. I let you know the fix when I figure it out. Probably tomorrow...
 
@tomicdesu - Finally finished with the fixes. I pushed them up to VGA_4BIT_FIXES branch. What was happening was first, "setPrintCWindow()" was not saving the current position of the text cursor and "unSetPrintWindow()" was not restoring the print position. Second, the random blinking block cursor showing or not showing was due to timing. When the cursor turns on it saves the character under the cursor and when it turns off it restores it to video memory. So if the cursor is on and it gets moved before it is turned off, the stored character will be printed at the new cursor position. Anyway, The solution was this code which is used in the "write()" function but needed to be added to the text scroll functions and the two print window functions:
Code:
  bool isActive = false; // Let's say cursor is not active for now.
  // If text cursor is active, set flag and turn off cursor.
  if(tCursor.active) {
    tCursorOff(); // Turn cursor off for now.
    isActive = true; // Set cursor was active flag.
  }
other code...
  if(isActive) tCursorOn(); // Cursor was active. Turn on cursor.

Resulting in moving screen starting at x==0,y==14 and ending at x==0,y==30 down one line and repositioning cursor to the new empty line:
eZ80_insert_Line_Fix.jpg


Here is the sketch for reference:
Code:
// testing.ino

#include "VGA_4bit_T4.h"
#include "VGA_T4_Config.h"

FlexIO2VGA vga4bit;
#define FONTSIZE 16


// Uncomment one of the following screen resolutions. Try them all:)
//const vga_timing *timing = &t1024x768x60; // Not enough memory avilable for this screen size in this sketch.
const vga_timing *timing = &t800x600x60;
//const vga_timing *timing = &t640x480x60;
//const vga_timing *timing = &t640x400x70;

uint8_t h19Col = 0, h19Row = 14,  h19_maxCols = 80, h19_maxRows = 30;

void setup() {
  // Wait for USB Serial
  while (!Serial && (millis() < 5000)) {
    yield();
  }   
  vga4bit.stop();
  // Setup VGA display: 800x600x60
  //                    double Height = false
  //                    double Width  = false
  //                    Color Depth   = 4 bits, Mono not supported yet.
  vga4bit.begin(*timing, false, false, 4);
  // Set fontsize 8x8 or (8x16 available)
  vga4bit.setFontSize(FONTSIZE, false);
  // Set default foreground and background colors
  vga4bit.setBackgroundColor(VGA_BLUE);
  vga4bit.initCursor(0,0,7,FONTSIZE - 1,true,30);

  // Turn cursor on
  vga4bit.tCursorOn();

  for(int y = h19Col; y < h19_maxRows; y++) {
    for(int x = h19Col; x < h19_maxCols; x++) {
      vga4bit.printf("%c",y + 33);
    }
    vga4bit.printf("\n");
  } // At this point the text cursor is at x==0,y==30.

  // This section does the deed.
  vga4bit.textxy(0,h19Row); // Set text cursor to new print window starting position so
                            // it will be restored to the start of our new blank line.
  vga4bit.setPrintCWindow (h19Col, h19Row,  h19_maxCols, h19_maxRows - h19Row - 1);
  waitforInput();
  vga4bit.scrollDownPrintWindow(); // Scroll down one line.
  vga4bit.unsetPrintWindow(); // Restore cursor position.

  vga4bit.printf("Done, I'm here...");
}

void loop() {
  delay(100);
}

void waitforInput()
{
  Serial.println("Press anykey to continue");
  while (Serial.read() == -1) ;
  while (Serial.read() != -1) ;
}
Hopefully this fixes your issue:)
 
OK cool! I will install the latest library and check this out. That was fast work!

I'll try this out ASAP. If I have any issues, I'll write a small test program to replicate it. I've already thought that out.
 
I'm busy going through your docs and code. So far everything seems to work as intended (y)
 
I cobbled your code into this test program with manual commands, so that I could watch how text xy moves.

It appears to work correctly, maybe an off-by-1 in height but that could be easily fixed with the call to scroll, and its as likely my FU and I will look closely later after I have lunch (BRANE WANT FUUD).

I'm testing using CP/M text editors, PMATE and WordStar. Both have the nice feature that if I deny them the escape sequences to do insert and delete line, they re-paint the screen and so I can separate out general cursor stuff from ins/del. Or so I thought.

If vga4bit had bugs before, it doesn't seem to now.

I should have written this explicit test program before I bugged you with this, sorry!

tom


Code:
// testing.ino
// testing.ino

#include "VGA_4bit_T4.h"
#include "VGA_T4_Config.h"

FlexIO2VGA vga4bit;
#define FONTSIZE 16


// Uncomment one of the following screen resolutions. Try them all:)
//const vga_timing *timing = &t1024x768x60; // Not enough memory avilable for this screen size in this sketch.
const vga_timing *timing = &t800x600x60;
//const vga_timing *timing = &t640x480x60;
//const vga_timing *timing = &t640x400x70;

uint8_t h19Col = 0, h19Row = 14,  h19_maxCols = 80, h19_maxRows = 30;
int height;

void setup() {
  // Wait for USB Serial
  while (!Serial && (millis() < 5000)) {
    yield();
  }  
  vga4bit.stop();
  // Setup VGA display: 800x600x60
  //                    double Height = false
  //                    double Width  = false
  //                    Color Depth   = 4 bits, Mono not supported yet.
  vga4bit.begin(*timing, false, false, 4);
  // Set fontsize 8x8 or (8x16 available)
  vga4bit.setFontSize(FONTSIZE, false);
  // Set default foreground and background colors
  vga4bit.setBackgroundColor(VGA_BLUE);
  vga4bit.initCursor(0,0,7,FONTSIZE - 1,true,30);

  // Turn cursor on
  vga4bit.tCursorOn();
  vga4bit.clear (VGA_BLUE);
  h19Row= h19Col= 0;
  height= 10;

  vga4bit.println ("e-erase f-fill    u-up line d-down line");
  vga4bit.println ("h-decrease height H-increase height");
  vga4bit.println ("s-scroll");
  vga4bit.println ("Any command prints row, col after");

}

void loop() {

  char c= 0;
  if (Serial.available()) c= Serial.read();

  switch (c) {

    case 'e':
      vga4bit.clear (VGA_BLUE);
      h19Row= h19Col= 0;
      height= 10;
      vga4bit.textxy (h19Col, h19Row);
      break;
 
    case 'f':
      for(int y = h19Col; y < h19_maxRows; y++) {
        for(int x = h19Col; x < h19_maxCols; x++) {
          vga4bit.printf("%c",y + '0');
        }
        vga4bit.printf("\n");
      }
      break;

    case 'u':
      vga4bit.tCursorOff();
      --h19Row;
      vga4bit.textxy (0, h19Row);
      vga4bit.tCursorOn();
      break;

    case 'd':
      vga4bit.tCursorOff();
      ++h19Row;
      vga4bit.textxy (0, h19Row);
      vga4bit.tCursorOn();
      break;

     case 'h': --height; break;
     case 'H': ++height; break;
   
    case 's':
      // This section does the deed.
      vga4bit.tCursorOff();              // remove cursor pixels
      vga4bit.textxy(h19Col, h19Row);    // Set text cursor to new print window starting position so
                                         // it will be restored to the start of our new blank line.
      vga4bit.setPrintCWindow (h19Col, h19Row,  h19_maxCols, height);
      vga4bit.scrollDownPrintWindow();   // Scroll down one line.
      vga4bit.unsetPrintWindow();        // Restore cursor position.
      vga4bit.tCursorOn();
      break;

    default: break;
 
  }
  if (c) {
    c= 0;
      h19Col= vga4bit.getTextX();
      h19Row= vga4bit.getTextY();
      Serial.print ("h19Row "); Serial.print (h19Row);
      Serial.print (" h19Col "); Serial.println (h19Col);
      Serial.print (" SCROLL HEIGHT "); Serial.println (height);
  }
}
 
I had to add explicit cursor control else they'd dot the screen with dead cursors. No big deal.
 
I see what you are talking about. There is still a problem with with the blinking cursor and scrolling. It happens when doing multiple consecutive scrolls. Won't sleep tonight until I check it out:D I am trying to avoid using tCursorOff() and tCursorOn() every time you do a scroll...

EDIT: It is intermittent so it is still the same problem.
 
Hey here's something to think about on your "cursor problem" -- I don't think it is --

vga4bit is usable as-is as a "glass tty", in ancient parlance. But it's a hell of a set of primitives for more sophisticated stuff, like emulating VT100 or in my case H19/H89.

I recognize the blinking-cursor problem, and I think the dilemma might be one of strategy, not code tactics.

My larger state machine for the H19, 99% of the time just sends the char to the driver. But some functions explicitly mess with vga4bit's state -- ESCape sequences, ascii CR, etc -- in way that maybe should not even be vga4bit's responsibility.

When my H19 gets an ESC it turns off the cursor (and does other things). Given ESC, I know it's likely that the cursor will move. In this case, of course vga4bit can't know or predict, no problem. Numerically-many ESC sequences don't effect cursor location, but the human writing the code knows there's "physics" behind ESC sequences -- they are work to arrange, and to think about results, etc -- so though a limited number of ESC sequences affect the cursor, those are used disproportionately. So it's efficient, time- and code-wise, to turn cursor off at the ESC character. And programmers are clever SOBs who do weird and silly things on purpose.

The problem arises I think when folks use vga4bit as-is as a terminal, expecting one set of behaviors, vs. people like me writing an emulator and wrapping vga4bit in higher-level abstraction where turning the cursor off and on is just part of the gamel.

ASCII CR is one of those. My state machine does it because I want CR to move the cursor to column 0 (tty but also the cheapest form of tty graphics, the one line progress indicator).

So I don't think your code is broke! (Other than bugs you find and fix and there does not seem to be many!!) Maybe a cooked vs raw mode, print() vs write() kinda thing, where (making this up) write() assumes some nitwit (me) has wrappered the thing and controlling it all, and print() supports some minimal declared set of functions, and that's that. Caveat programmer.

Your code is great and it's made my project even possible!
 
It's funny because this (my test version):
Code:
  // This section does the deed.
  vga4bit.textxy(0,h19Row); // Set text cursor to new print window starting position so
                            // it will be restored to the start of our new blank line.
  vga4bit.setPrintCWindow (h19Col, h19Row,  h19_maxCols, h19_maxRows - h19Row - 1);
  waitforInput();
  vga4bit.scrollDownPrintWindow(); // Scroll down one line.
  vga4bit.unsetPrintWindow(); // Restore cursor position.
Is basically the same as your version:
Code:
      // This section does the deed.
//      vga4bit.tCursorOff();            // remove cursor pixels
      vga4bit.textxy(h19Col, h19Row);    // Set text cursor to new print window starting position so
                                         // it will be restored to the start of our new blank line.
      vga4bit.setPrintCWindow (h19Col, h19Row,  h19_maxCols, height);
      vga4bit.scrollDownPrintWindow();   // Scroll down one line.
      vga4bit.unsetPrintWindow();        // Restore cursor position.
//      vga4bit.tCursorOn();

If I run your version without turning the cursor off/on (commented out), I get this:
Cursor_error.jpg

Now if I comment out the "textxy(h19Col, h19Row)":
Code:
      // This section does the deed.
//      vga4bit.tCursorOff();            // remove cursor pixels
//      vga4bit.textxy(h19Col, h19Row);    // Set text cursor to new print window starting position so
                                         // it will be restored to the start of our new blank line.
      vga4bit.setPrintCWindow (h19Col, h19Row,  h19_maxCols, height-1);
      vga4bit.scrollDownPrintWindow();   // Scroll down one line.
      vga4bit.unsetPrintWindow();        // Restore cursor position.
//      vga4bit.tCursorOn();
Then I get the correct results:
Cursor_correct.jpg


So there are two things I need to understand her:
1. Since the two codes are the same why does mine work and the other have issues?
2. Why does commenting out the textxy() call fix the issue?

Just can't leave it alone:LOL:
 
Found it :D
It's a case of rouge code in textxy():
Code:
  bool isActive = false;
  if(tCursor.active) isActive = true;
  getChar(tCursorX(),tCursorY(),tCursor.char_under_cursor);
  tCursorOff();
Shoud be:
Code:
  bool isActive = false;
  if(tCursor.active) {
    isActive = true;
//  getChar(tCursorX(),tCursorY(),tCursor.char_under_cursor);
    tCursorOff();
  }

I really hate when I do this. Modify code without noting the change or changing it back after testing if the change did not work.
Edited repo to reflect the change. Your test sketch should work now, I will have to go through all of the examples to make sure nothing else was affected by the change...
 
AH!!! Nice! OK got it, and that makes sense.

So scroll does the tCursorOff() only if it's displaying at scroll time? So I can remove my explicit controls?

I also did explicit tCursorOn() after moving the cursor so that it immediately displays in the new location, as opposed to having to wait for the blink cycle to come around. Am I doing that wrong?

With these changes I'll go back over my H19 driver machine.

Thanks for this quality close work.
 
For completeness here is your sketch without the cursor controls being used. Their was a problem with the erase function leaving a dead currsor in none blink mode. That problem was also solved with the fix. Just not sure how or why :unsure:

Code:
// testing.ino
// testing.ino

#include "VGA_4bit_T4.h"
#include "VGA_T4_Config.h"

FlexIO2VGA vga4bit;
#define FONTSIZE 16


// Uncomment one of the following screen resolutions. Try them all:)
//const vga_timing *timing = &t1024x768x60; // Not enough memory avilable for this screen size in this sketch.
const vga_timing *timing = &t800x600x60;
//const vga_timing *timing = &t640x480x60;
//const vga_timing *timing = &t640x400x70;

uint8_t h19Col = 0, h19Row = 14,  h19_maxCols = 80, h19_maxRows = 30;
int height;

void setup() {
  // Wait for USB Serial
  while (!Serial && (millis() < 5000)) {
    yield();
  } 
  vga4bit.stop();
  // Setup VGA display: 800x600x60
  //                    double Height = false
  //                    double Width  = false
  //                    Color Depth   = 4 bits, Mono not supported yet.
  vga4bit.begin(*timing, false, false, 4);
  // Set fontsize 8x8 or (8x16 available)
  vga4bit.setFontSize(FONTSIZE, false);
  // Set default foreground and background colors
  vga4bit.setBackgroundColor(VGA_BLUE);
  vga4bit.initCursor(0,0,7,FONTSIZE - 1,true,30);

  // Turn cursor on
  vga4bit.tCursorOn();
  vga4bit.clear (VGA_BLUE);
  h19Row= h19Col= 0;
  height= 10;

  vga4bit.println ("e-erase f-fill    u-up line d-down line");
  vga4bit.println ("h-decrease height H-increase height");
  vga4bit.println ("s-scroll");
  vga4bit.println ("Any command prints row, col after");

}

void loop() {

  char c= 0;
  if (Serial.available()) c= Serial.read();

  switch (c) {

    case 'e':
      vga4bit.clear (VGA_BLUE);
      h19Row= h19Col= 0;
      height= 10;
      vga4bit.textxy (h19Col, h19Row);
      break;
 
    case 'f':
      for(int y = h19Col; y < h19_maxRows; y++) {
        for(int x = h19Col; x < h19_maxCols; x++) {
          vga4bit.printf("%c",y + '0');
        }
        vga4bit.printf("\n");
      }
      break;

    case 'u':
      --h19Row;
      vga4bit.textxy (0, h19Row);
      break;

    case 'd':
      ++h19Row;
      vga4bit.textxy (0, h19Row);
      break;

     case 'h': --height; break;
     case 'H': ++height; break;
  
    case 's':
      // This section does the deed.
      vga4bit.textxy(h19Col, h19Row);    // Set text cursor to new print window starting position so
                                         // it will be restored to the start of our new blank line.
      vga4bit.setPrintCWindow (h19Col, h19Row,  h19_maxCols, height-1);
      vga4bit.scrollDownPrintWindow();   // Scroll down one line.
      vga4bit.unsetPrintWindow();        // Restore cursor position.
      break;

    default: break;
 
  }
  if (c) {
    c= 0;
      h19Col= vga4bit.getTextX();
      h19Row= vga4bit.getTextY();
      Serial.print ("h19Row "); Serial.print (h19Row);
      Serial.print (" h19Col "); Serial.println (h19Col);
      Serial.print (" SCROLL HEIGHT "); Serial.println (height);
  }
}

I am not sure if you saw these in the VGA 4bit_T4.cpp file. They are a few supporting functions for VT100 usage that might be able to be used:
Code:
//==================================================
// Support function for VT100: Clear to End Of Line.
//==================================================
FLASHMEM void FlexIO2VGA::clreol(void) {
  int16_t tempX = cursor_x;
  int16_t tempY = cursor_y;
  bool isActive = false;
  if(tCursor.active) isActive = true;

  for(int i = 0; i < (print_window_w-tempX); i++) {
    write(0x20);
    tCursorOff();
  }
  textxy(tempX,tempY);
  if(isActive) tCursorOn();

}

//====================================================
// Support function for VT100: Clear to End Of Screen.
//====================================================
FLASHMEM void FlexIO2VGA::clreos(void) {
  int16_t tempX = cursor_x;
  int16_t tempY = cursor_y;

  bool isActive = false;
  if(tCursor.active) isActive = true;

  clreol();

  for(uint16_t y = 0; y < (print_window_h - tempY); y++)
    for(int16_t x = 0; x < (print_window_w-tempX); x++)
      write(0x20);

  textxy(tempX,tempY);
  if(isActive) tCursorOn();
}

//========================================================
// Support function for VT100: Clear to beginning of line.
//========================================================
FLASHMEM void FlexIO2VGA::clrbol(void) {
  int16_t tempX = cursor_x;
  int16_t tempY = cursor_y;

  bool isActive = false;
  if(tCursor.active) isActive = true;

  for(int16_t x = tempX; x > 0; x--) write(0x7f);

  textxy(tempX,tempY);
  if(isActive) tCursorOn();
}

//=========================================================
// Support function for VT100: Clear to begining of Screen.
//=========================================================
FLASHMEM void FlexIO2VGA::clrbos(void) {
  int16_t tempX = cursor_x;
  int16_t tempY = cursor_y;
 
  bool isActive = false;
  if(tCursor.active) isActive = true;

  clrbol();
  textxy(0,0);
 
  for(uint16_t y = 0; y < tempY; y++)
    for(int16_t x = 0; x < print_window_w; x++)
      write(0x20);

  textxy(tempX,tempY);
  if(isActive) tCursorOn();
}

//========================================
// Support function for VT100: Clear Line.
//========================================
FLASHMEM void FlexIO2VGA::clrlin(void) {
  int16_t tempX = cursor_x;
  int16_t tempY = cursor_y;
  bool isActive = false;
  if(tCursor.active) isActive = true;

  textxy(0,tempY);
  for(int16_t x = 0; x < print_window_w; x++) write(0x20);

  textxy(tempX,tempY);
  if(isActive) tCursorOn();
}

//=========================================
// Reverse forground and background colors.
//=========================================
FLASHMEM void FlexIO2VGA::reverseVid(bool onOff) {
  getChar(tCursorX(),tCursorY(),tCursor.char_under_cursor);
  if(onOff) {
    SaveRVFGC = vga4bit.getTextFGC();
    SaveRVBGC = vga4bit.getTextBGC();
    vga4bit.textColor(SaveRVBGC, SaveRVFGC);
  } else {
    vga4bit.textColor(SaveRVFGC, SaveRVBGC);
  }
}

I just finished getting all three drives "a,b,c" working. I was having issues with intermittent SD error "SD_CARD_ERROR_ACMD41". I think you had mentioned something about this in the docs...
 
Back
Top